[chore] Update gin to v1.9.0 (#1553)
This commit is contained in:
parent
689a10fe17
commit
ecdc8379fa
10
go.mod
10
go.mod
|
@ -24,7 +24,7 @@ require (
|
||||||
github.com/gin-contrib/cors v1.4.0
|
github.com/gin-contrib/cors v1.4.0
|
||||||
github.com/gin-contrib/gzip v0.0.6
|
github.com/gin-contrib/gzip v0.0.6
|
||||||
github.com/gin-contrib/sessions v0.0.5
|
github.com/gin-contrib/sessions v0.0.5
|
||||||
github.com/gin-gonic/gin v1.8.2
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/go-fed/httpsig v1.1.0
|
github.com/go-fed/httpsig v1.1.0
|
||||||
github.com/go-playground/validator/v10 v10.11.2
|
github.com/go-playground/validator/v10 v10.11.2
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
@ -76,6 +76,8 @@ require (
|
||||||
codeberg.org/gruf/go-maps v1.0.3 // indirect
|
codeberg.org/gruf/go-maps v1.0.3 // indirect
|
||||||
codeberg.org/gruf/go-pools v1.1.0 // indirect
|
codeberg.org/gruf/go-pools v1.1.0 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
|
github.com/bytedance/sonic v1.8.0 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/cilium/ebpf v0.4.0 // indirect
|
github.com/cilium/ebpf v0.4.0 // indirect
|
||||||
github.com/containerd/cgroups v1.0.4 // indirect
|
github.com/containerd/cgroups v1.0.4 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||||
|
@ -95,7 +97,7 @@ require (
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 // indirect
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 // indirect
|
||||||
github.com/goccy/go-json v0.9.11 // indirect
|
github.com/goccy/go-json v0.10.0 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
|
@ -141,9 +143,11 @@ require (
|
||||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.6.4 // indirect
|
github.com/tdewolff/parse/v2 v2.6.4 // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35 // indirect
|
golang.org/x/mod v0.6.0-dev.0.20220907135952-02c991387e35 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.1.12 // indirect
|
golang.org/x/tools v0.1.12 // indirect
|
||||||
|
|
23
go.sum
23
go.sum
|
@ -102,8 +102,14 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/buckket/go-blurhash v1.1.0 h1:X5M6r0LIvwdvKiUtiNcRL2YlmOfMzYobI3VCKCZc9Do=
|
github.com/buckket/go-blurhash v1.1.0 h1:X5M6r0LIvwdvKiUtiNcRL2YlmOfMzYobI3VCKCZc9Do=
|
||||||
github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8=
|
github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8=
|
||||||
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
|
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
|
||||||
|
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
|
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
|
||||||
|
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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
@ -185,8 +191,8 @@ github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs9
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
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-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.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||||
github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
|
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||||
github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398=
|
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||||
|
@ -218,8 +224,8 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 h1:PSPmmucxGiFBtbQcttHTUc4LQ3P09AW+ldO2qspyKdY=
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 h1:PSPmmucxGiFBtbQcttHTUc4LQ3P09AW+ldO2qspyKdY=
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
|
@ -396,6 +402,7 @@ github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQan
|
||||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
@ -568,9 +575,12 @@ github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ
|
||||||
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ=
|
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ=
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||||
|
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 v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
|
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
|
||||||
|
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/ulule/limiter/v3 v3.11.0 h1:9hXMyS0K8Z+EYfrtwPMwmWYflPimswsC/EOMsO2sHx4=
|
github.com/ulule/limiter/v3 v3.11.0 h1:9hXMyS0K8Z+EYfrtwPMwmWYflPimswsC/EOMsO2sHx4=
|
||||||
github.com/ulule/limiter/v3 v3.11.0/go.mod h1:OiKIiMs9dXLMk5TwtIBZlswhPigov9fGmwO4xYbmFkY=
|
github.com/ulule/limiter/v3 v3.11.0/go.mod h1:OiKIiMs9dXLMk5TwtIBZlswhPigov9fGmwO4xYbmFkY=
|
||||||
github.com/uptrace/bun v1.1.10 h1:wx80lg32mWlOV1gNvmPTbR9znt6A2fYvvURzU5dzSPE=
|
github.com/uptrace/bun v1.1.10 h1:wx80lg32mWlOV1gNvmPTbR9znt6A2fYvvURzU5dzSPE=
|
||||||
|
@ -636,6 +646,8 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
@ -1066,5 +1078,6 @@ modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
|
||||||
mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=
|
mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=
|
||||||
mvdan.cc/xurls/v2 v2.4.0/go.mod h1:+GEjq9uNjqs8LQfM9nVnM8rff0OQ5Iash5rzX+N1CSg=
|
mvdan.cc/xurls/v2 v2.4.0/go.mod h1:+GEjq9uNjqs8LQfM9nVnM8rff0OQ5Iash5rzX+N1CSg=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
*.o
|
||||||
|
*.swp
|
||||||
|
*.swm
|
||||||
|
*.swn
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
_testmain.go
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
*.rar
|
||||||
|
*.zip
|
||||||
|
*.gz
|
||||||
|
*.psd
|
||||||
|
*.bmd
|
||||||
|
*.cfg
|
||||||
|
*.pptx
|
||||||
|
*.log
|
||||||
|
*nohup.out
|
||||||
|
*settings.pyc
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
.DS_Store
|
||||||
|
/.idea/
|
||||||
|
/.vscode/
|
||||||
|
/output/
|
||||||
|
/vendor/
|
||||||
|
/Gopkg.lock
|
||||||
|
/Gopkg.toml
|
||||||
|
coverage.html
|
||||||
|
coverage.out
|
||||||
|
coverage.xml
|
||||||
|
junit.xml
|
||||||
|
*.profile
|
||||||
|
*.svg
|
||||||
|
*.out
|
||||||
|
ast/test.out
|
||||||
|
ast/bench.sh
|
||||||
|
|
||||||
|
!testdata/*.json.gz
|
||||||
|
fuzz/testdata
|
||||||
|
*__debug_bin
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "tools/asm2asm"]
|
||||||
|
path = tools/asm2asm
|
||||||
|
url = https://github.com/chenzhuoyu/asm2asm
|
|
@ -0,0 +1,24 @@
|
||||||
|
header:
|
||||||
|
license:
|
||||||
|
spdx-id: Apache-2.0
|
||||||
|
copyright-owner: ByteDance Inc.
|
||||||
|
|
||||||
|
paths:
|
||||||
|
- '**/*.go'
|
||||||
|
- '**/*.s'
|
||||||
|
|
||||||
|
paths-ignore:
|
||||||
|
- 'ast/asm.s' # empty file
|
||||||
|
- 'decoder/asm.s' # empty file
|
||||||
|
- 'encoder/asm.s' # empty file
|
||||||
|
- 'internal/caching/asm.s' # empty file
|
||||||
|
- 'internal/jit/asm.s' # empty file
|
||||||
|
- 'internal/native/avx/native_amd64.s' # auto-generated by asm2asm
|
||||||
|
- 'internal/native/avx/native_subr_amd64.go' # auto-generated by asm2asm
|
||||||
|
- 'internal/native/avx2/native_amd64.s' # auto-generated by asm2asm
|
||||||
|
- 'internal/native/avx2/native_subr_amd64.go' # auto-generated by asm2asm
|
||||||
|
- 'internal/resolver/asm.s' # empty file
|
||||||
|
- 'internal/rt/asm.s' # empty file
|
||||||
|
- 'internal/loader/asm.s' # empty file
|
||||||
|
|
||||||
|
comment: on-failure
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
wudi.daniel@bytedance.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
|
@ -0,0 +1,63 @@
|
||||||
|
# How to Contribute
|
||||||
|
|
||||||
|
## Your First Pull Request
|
||||||
|
We use GitHub for our codebase. You can start by reading [How To Pull Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests).
|
||||||
|
|
||||||
|
## Without Semantic Versioning
|
||||||
|
We keep the stable code in branch `main` like `golang.org/x`. Development base on branch `develop`. We promise the **Forward Compatibility** by adding new package directory with suffix `v2/v3` when code has break changes.
|
||||||
|
|
||||||
|
## Branch Organization
|
||||||
|
We use [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) as our branch organization, as known as [FDD](https://en.wikipedia.org/wiki/Feature-driven_development)
|
||||||
|
|
||||||
|
|
||||||
|
## Bugs
|
||||||
|
### 1. How to Find Known Issues
|
||||||
|
We are using [Github Issues](https://github.com/bytedance/sonic/issues) for our public bugs. We keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new task, try to make sure your problem doesn’t already exist.
|
||||||
|
|
||||||
|
### 2. Reporting New Issues
|
||||||
|
Providing a reduced test code is a recommended way for reporting issues. Then can be placed in:
|
||||||
|
- Just in issues
|
||||||
|
- [Golang Playground](https://play.golang.org/)
|
||||||
|
|
||||||
|
### 3. Security Bugs
|
||||||
|
Please do not report the safe disclosure of bugs to public issues. Contact us by [Support Email](mailto:sonic@bytedance.com)
|
||||||
|
|
||||||
|
## How to Get in Touch
|
||||||
|
- [Email](mailto:wudi.daniel@bytedance.com)
|
||||||
|
|
||||||
|
## Submit a Pull Request
|
||||||
|
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||||
|
1. Search [GitHub](https://github.com/bytedance/sonic/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate existing efforts.
|
||||||
|
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. Discussing the design upfront helps to ensure that we're ready to accept your work.
|
||||||
|
3. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the bytedance/sonic repo.
|
||||||
|
4. In your forked repository, make your changes in a new git branch:
|
||||||
|
```
|
||||||
|
git checkout -b bugfix/security_bug develop
|
||||||
|
```
|
||||||
|
5. Create your patch, including appropriate test cases.
|
||||||
|
6. Follow our [Style Guides](#code-style-guides).
|
||||||
|
7. Commit your changes using a descriptive commit message that follows [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit).
|
||||||
|
Adherence to these conventions is necessary because release notes will be automatically generated from these messages.
|
||||||
|
8. Push your branch to GitHub:
|
||||||
|
```
|
||||||
|
git push origin bugfix/security_bug
|
||||||
|
```
|
||||||
|
9. In GitHub, send a pull request to `sonic:main`
|
||||||
|
|
||||||
|
Note: you must use one of `optimize/feature/bugfix/doc/ci/test/refactor` following a slash(`/`) as the branch prefix.
|
||||||
|
|
||||||
|
Your pr title and commit message should follow https://www.conventionalcommits.org/.
|
||||||
|
|
||||||
|
## Contribution Prerequisites
|
||||||
|
- Our development environment keeps up with [Go Official](https://golang.org/project/).
|
||||||
|
- You need fully checking with lint tools before submit your pull request. [gofmt](https://golang.org/pkg/cmd/gofmt/) & [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||||
|
- You are familiar with [Github](https://github.com)
|
||||||
|
- Maybe you need familiar with [Actions](https://github.com/features/actions)(our default workflow tool).
|
||||||
|
|
||||||
|
## Code Style Guides
|
||||||
|
See [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||||
|
|
||||||
|
Good resources:
|
||||||
|
- [Effective Go](https://golang.org/doc/effective_go)
|
||||||
|
- [Pingcap General advice](https://pingcap.github.io/style-guide/general.html)
|
||||||
|
- [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md)
|
|
@ -0,0 +1,48 @@
|
||||||
|
# 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.
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
|
@ -0,0 +1,112 @@
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
ARCH := avx avx2 sse
|
||||||
|
TMP_DIR := output
|
||||||
|
OUT_DIR := internal/native
|
||||||
|
SRC_FILE := native/native.c
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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_sse := -msse -mno-sse4 -mno-avx -mno-avx2 -mpclmul
|
||||||
|
|
||||||
|
CC_amd64 := clang
|
||||||
|
ASM2ASM_amd64 := tools/asm2asm/asm2asm.py
|
||||||
|
|
||||||
|
CFLAGS := -mno-red-zone
|
||||||
|
CFLAGS += -target x86_64-apple-macos11
|
||||||
|
CFLAGS += -fno-asynchronous-unwind-tables
|
||||||
|
CFLAGS += -fno-builtin
|
||||||
|
CFLAGS += -fno-exceptions
|
||||||
|
CFLAGS += -fno-rtti
|
||||||
|
CFLAGS += -fno-stack-protector
|
||||||
|
CFLAGS += -nostdlib
|
||||||
|
CFLAGS += -O3
|
||||||
|
CFLAGS += -Wall -Werror
|
||||||
|
|
||||||
|
NATIVE_SRC := $(wildcard native/*.h)
|
||||||
|
NATIVE_SRC += $(wildcard native/*.c)
|
||||||
|
|
||||||
|
.PHONY: all clean ${ARCH}
|
||||||
|
|
||||||
|
define build_tmpl
|
||||||
|
$(eval @arch := $(1))
|
||||||
|
$(eval @tmpl := $(2))
|
||||||
|
$(eval @dest := $(3))
|
||||||
|
|
||||||
|
${@dest}: ${@tmpl}
|
||||||
|
mkdir -p $(dir ${@dest})
|
||||||
|
echo '// Code generated by Makefile, DO NOT EDIT.' > ${@dest}
|
||||||
|
echo >> ${@dest}
|
||||||
|
sed -e 's/{{PACKAGE}}/${@arch}/g' ${@tmpl} >> ${@dest}
|
||||||
|
endef
|
||||||
|
|
||||||
|
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 @stubin := ${OUT_DIR}/native_${@cpu}.tmpl)
|
||||||
|
$(eval @stubout := ${OUT_DIR}/$(1)/native_${@cpu}.go)
|
||||||
|
|
||||||
|
$(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}
|
||||||
|
|
||||||
|
$(eval $(call \
|
||||||
|
build_tmpl, \
|
||||||
|
$(1), \
|
||||||
|
${@stubin}, \
|
||||||
|
${@stubout} \
|
||||||
|
))
|
||||||
|
|
||||||
|
$(foreach \
|
||||||
|
tmpl, \
|
||||||
|
$(value TMPL_$(1)), \
|
||||||
|
$(eval $(call \
|
||||||
|
build_tmpl, \
|
||||||
|
$(1), \
|
||||||
|
${OUT_DIR}/${tmpl}.tmpl, \
|
||||||
|
${OUT_DIR}/$(1)/${tmpl}.go \
|
||||||
|
)) \
|
||||||
|
)
|
||||||
|
endef
|
||||||
|
|
||||||
|
all: ${ARCH}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
for arch in ${ARCH}; do \
|
||||||
|
rm -vfr ${TMP_DIR}/$${arch}; \
|
||||||
|
rm -vfr ${OUT_DIR}/$${arch}; \
|
||||||
|
done
|
||||||
|
|
||||||
|
$(foreach \
|
||||||
|
arch, \
|
||||||
|
${ARCH}, \
|
||||||
|
$(eval $(call build_arch,${arch})) \
|
||||||
|
)
|
|
@ -0,0 +1,359 @@
|
||||||
|
# Sonic
|
||||||
|
|
||||||
|
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
|
||||||
|
- Amd64 ARCH
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Runtime object binding without code generation
|
||||||
|
- Complete APIs for JSON value manipulation
|
||||||
|
- Fast, fast, fast!
|
||||||
|
|
||||||
|
## 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
|
||||||
|
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
|
||||||
|
```
|
||||||
|
- [Small](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 keys, 3 layers)
|
||||||
|
![small benchmarks](bench-small.png)
|
||||||
|
- [Large](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635KB, 10000+ key, 6 layers)
|
||||||
|
![large benchmarks](bench-large.png)
|
||||||
|
|
||||||
|
See [bench.sh](https://github.com/bytedance/sonic/blob/main/bench.sh) for benchmark codes.
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
See [INTRODUCTION.md](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"
|
||||||
|
|
||||||
|
var data YourSchema
|
||||||
|
// Marshal
|
||||||
|
output, err := sonic.Marshal(&data)
|
||||||
|
// Unmarshal
|
||||||
|
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.
|
||||||
|
- encoder
|
||||||
|
```go
|
||||||
|
import "github.com/bytedance/sonic/encoder"
|
||||||
|
|
||||||
|
var o1 = map[string]interface{}{
|
||||||
|
"a": "b",
|
||||||
|
}
|
||||||
|
var o2 = 1
|
||||||
|
var w = bytes.NewBuffer(nil)
|
||||||
|
var enc = encoder.NewStreamEncoder(w)
|
||||||
|
enc.Encode(o1)
|
||||||
|
enc.Encode(o2)
|
||||||
|
println(w.String()) // "{"a":"b"}\n1"
|
||||||
|
```
|
||||||
|
- decoder
|
||||||
|
```go
|
||||||
|
import "github.com/bytedance/sonic/decoder"
|
||||||
|
|
||||||
|
var o = map[string]interface{}{}
|
||||||
|
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
|
||||||
|
var dec = decoder.NewStreamDecoder(r)
|
||||||
|
dec.Decode(&o)
|
||||||
|
dec.Decode(&o)
|
||||||
|
fmt.Printf("%+v", o) // map[1:2 a:b]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use Number/Use 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
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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"
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
```
|
||||||
|
### 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"
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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"
|
||||||
|
|
||||||
|
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 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"
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
**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"
|
||||||
|
|
||||||
|
// 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"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Serialize
|
||||||
|
To encode `ast.Node` as json, use `MarshalJson()` or `json.Marshal()` (MUST pass the node's pointer)
|
||||||
|
```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
|
||||||
|
- 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]()`
|
||||||
|
- go-type packing: `NewRaw()`, `NewNumber()`, `NewNull()`, `NewBool()`, `NewString()`, `NewObject()`, `NewArray()`
|
||||||
|
- iteration: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
|
||||||
|
- modification: `Set()`, `SetByIndex()`, `Add()`
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
## 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"
|
||||||
|
"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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### 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).
|
||||||
|
|
||||||
|
|
||||||
|
### 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"
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
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)
|
||||||
|
```
|
||||||
|
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{}`
|
||||||
|
|
||||||
|
## Community
|
||||||
|
Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* 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 sonic
|
||||||
|
|
||||||
|
import (
|
||||||
|
`io`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/ast`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is a combination of sonic/encoder.Options and sonic/decoder.Options
|
||||||
|
type Config struct {
|
||||||
|
// 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 bool
|
||||||
|
|
||||||
|
// SortMapKeys indicates encoder that the keys of a map needs to be sorted
|
||||||
|
// before serializing into JSON.
|
||||||
|
// WARNING: This hurts performance A LOT, USE WITH CARE.
|
||||||
|
SortMapKeys bool
|
||||||
|
|
||||||
|
// CompactMarshaler indicates encoder that the output JSON from json.Marshaler
|
||||||
|
// is always compact and needs no validation
|
||||||
|
CompactMarshaler bool
|
||||||
|
|
||||||
|
// NoQuoteTextMarshaler indicates encoder that the output text from encoding.TextMarshaler
|
||||||
|
// is always escaped string and needs no quoting
|
||||||
|
NoQuoteTextMarshaler bool
|
||||||
|
|
||||||
|
// NoNullSliceOrMap indicates encoder that all empty Array or Object are encoded as '[]' or '{}',
|
||||||
|
// instead of 'null'
|
||||||
|
NoNullSliceOrMap bool
|
||||||
|
|
||||||
|
// UseInt64 indicates decoder to unmarshal an integer into an interface{} as an
|
||||||
|
// int64 instead of as a float64.
|
||||||
|
UseInt64 bool
|
||||||
|
|
||||||
|
// UseNumber indicates decoder to unmarshal a number into an interface{} as a
|
||||||
|
// json.Number instead of as a float64.
|
||||||
|
UseNumber bool
|
||||||
|
|
||||||
|
// UseUnicodeErrors indicates decoder to return an error when encounter invalid
|
||||||
|
// UTF-8 escape sequences.
|
||||||
|
UseUnicodeErrors bool
|
||||||
|
|
||||||
|
// DisallowUnknownFields indicates 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.
|
||||||
|
DisallowUnknownFields bool
|
||||||
|
|
||||||
|
// CopyString indicates decoder to decode string values by copying instead of referring.
|
||||||
|
CopyString bool
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ConfigDefault is the default config of APIs, aiming at efficiency and safty.
|
||||||
|
ConfigDefault = Config{}.Froze()
|
||||||
|
|
||||||
|
// ConfigStd is the standard config of APIs, aiming at being compatible with encoding/json.
|
||||||
|
ConfigStd = Config{
|
||||||
|
EscapeHTML : true,
|
||||||
|
SortMapKeys: true,
|
||||||
|
CompactMarshaler: true,
|
||||||
|
CopyString : true,
|
||||||
|
ValidateString : true,
|
||||||
|
}.Froze()
|
||||||
|
|
||||||
|
// ConfigFastest is the fastest config of APIs, aiming at speed.
|
||||||
|
ConfigFastest = Config{
|
||||||
|
NoQuoteTextMarshaler: true,
|
||||||
|
}.Froze()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// API is a binding of specific config.
|
||||||
|
// This interface is inspired by github.com/json-iterator/go,
|
||||||
|
// and has same behaviors under equavilent config.
|
||||||
|
type API interface {
|
||||||
|
// MarshalToString returns the JSON encoding string of v
|
||||||
|
MarshalToString(v interface{}) (string, error)
|
||||||
|
// Marshal returns the JSON encoding bytes of v.
|
||||||
|
Marshal(v interface{}) ([]byte, error)
|
||||||
|
// MarshalIndent returns the JSON encoding bytes with indent and prefix.
|
||||||
|
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
||||||
|
// UnmarshalFromString parses the JSON-encoded bytes and stores the result in the value pointed to by v.
|
||||||
|
UnmarshalFromString(str string, v interface{}) error
|
||||||
|
// Unmarshal parses the JSON-encoded string and stores the result in the value pointed to by v.
|
||||||
|
Unmarshal(data []byte, v interface{}) error
|
||||||
|
// NewEncoder create a Encoder holding writer
|
||||||
|
NewEncoder(writer io.Writer) Encoder
|
||||||
|
// NewDecoder create a Decoder holding reader
|
||||||
|
NewDecoder(reader io.Reader) Decoder
|
||||||
|
// Valid validates the JSON-encoded bytes and reportes if it is valid
|
||||||
|
Valid(data []byte) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoder encodes JSON into io.Writer
|
||||||
|
type Encoder interface {
|
||||||
|
// Encode writes the JSON encoding of v to the stream, followed by a newline character.
|
||||||
|
Encode(val interface{}) error
|
||||||
|
// SetEscapeHTML specifies whether problematic HTML characters
|
||||||
|
// should be escaped inside JSON quoted strings.
|
||||||
|
// The default behavior NOT ESCAPE
|
||||||
|
SetEscapeHTML(on bool)
|
||||||
|
// SetIndent instructs the encoder to format each subsequent encoded value
|
||||||
|
// as if indented by the package-level function Indent(dst, src, prefix, indent).
|
||||||
|
// Calling SetIndent("", "") disables indentation
|
||||||
|
SetIndent(prefix, indent string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoder decodes JSON from io.Read
|
||||||
|
type Decoder interface {
|
||||||
|
// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
|
||||||
|
Decode(val interface{}) error
|
||||||
|
// Buffered returns a reader of the data remaining in the Decoder's buffer.
|
||||||
|
// The reader is valid until the next call to Decode.
|
||||||
|
Buffered() io.Reader
|
||||||
|
// DisallowUnknownFields causes 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.
|
||||||
|
DisallowUnknownFields()
|
||||||
|
// More reports whether there is another element in the current array or object being parsed.
|
||||||
|
More() bool
|
||||||
|
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.
|
||||||
|
UseNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the JSON encoding bytes of v.
|
||||||
|
func Marshal(val interface{}) ([]byte, error) {
|
||||||
|
return ConfigDefault.Marshal(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalString returns the JSON encoding string of v.
|
||||||
|
func MarshalString(val interface{}) (string, error) {
|
||||||
|
return ConfigDefault.MarshalToString(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||||
|
// NOTICE: This API copies given buffer by default,
|
||||||
|
// if you want to pass JSON more efficiently, use UnmarshalString instead.
|
||||||
|
func Unmarshal(buf []byte, val interface{}) error {
|
||||||
|
return ConfigDefault.Unmarshal(buf, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalString is like Unmarshal, except buf is a string.
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
// Each path arg must be integer or string:
|
||||||
|
// - Integer means searching current node as array
|
||||||
|
// - String means searching current node as object
|
||||||
|
//
|
||||||
|
// Note, the api expects the json is well-formed at least,
|
||||||
|
// otherwise it may return unexpected result.
|
||||||
|
func Get(src []byte, path ...interface{}) (ast.Node, error) {
|
||||||
|
return GetFromString(string(src), path...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFromString is same with Get except src is string,
|
||||||
|
// which can reduce unnecessary memory copy.
|
||||||
|
func GetFromString(src string, path ...interface{}) (ast.Node, error) {
|
||||||
|
return ast.NewSearcher(src).GetByPath(path...)
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
// +build amd64,go1.15,!go1.21
|
||||||
|
|
||||||
|
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
`runtime`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/encoder`
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
uq `github.com/bytedance/sonic/unquote`
|
||||||
|
`github.com/chenzhuoyu/base64x`
|
||||||
|
)
|
||||||
|
|
||||||
|
var typeByte = rt.UnpackEface(byte(0)).Type
|
||||||
|
|
||||||
|
func quote(buf *[]byte, val string) {
|
||||||
|
*buf = append(*buf, '"')
|
||||||
|
if len(val) == 0 {
|
||||||
|
*buf = append(*buf, '"')
|
||||||
|
}
|
||||||
|
|
||||||
|
sp := rt.IndexChar(val, 0)
|
||||||
|
nb := len(val)
|
||||||
|
b := (*rt.GoSlice)(unsafe.Pointer(buf))
|
||||||
|
|
||||||
|
// input buffer
|
||||||
|
for nb > 0 {
|
||||||
|
// output buffer
|
||||||
|
dp := unsafe.Pointer(uintptr(b.Ptr) + uintptr(b.Len))
|
||||||
|
dn := b.Cap - b.Len
|
||||||
|
// call native.Quote, dn is byte count it outputs
|
||||||
|
ret := native.Quote(sp, nb, dp, &dn, 0)
|
||||||
|
// update *buf length
|
||||||
|
b.Len += dn
|
||||||
|
|
||||||
|
// no need more output
|
||||||
|
if ret >= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// double buf size
|
||||||
|
*b = growslice(typeByte, *b, b.Cap*2)
|
||||||
|
// ret is the complement of consumed input
|
||||||
|
ret = ^ret
|
||||||
|
// update input buffer
|
||||||
|
nb -= ret
|
||||||
|
sp = unsafe.Pointer(uintptr(sp) + uintptr(ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(buf)
|
||||||
|
runtime.KeepAlive(sp)
|
||||||
|
*buf = append(*buf, '"')
|
||||||
|
}
|
||||||
|
|
||||||
|
func unquote(src string) (string, types.ParsingError) {
|
||||||
|
return uq.String(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBase64(src string) ([]byte, error) {
|
||||||
|
return base64x.StdEncoding.DecodeString(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeBase64(src []byte) string {
|
||||||
|
return base64x.StdEncoding.EncodeToString(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) skip() (int, types.ParsingError) {
|
||||||
|
fsm := types.NewStateMachine()
|
||||||
|
start := native.SkipOne(&self.s, &self.p, fsm, 0)
|
||||||
|
types.FreeStateMachine(fsm)
|
||||||
|
|
||||||
|
if start < 0 {
|
||||||
|
return self.p, types.ParsingError(-start)
|
||||||
|
}
|
||||||
|
return start, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||||
|
//WARN: NOT compatible with json.Encoder
|
||||||
|
return encoder.EncodeInto(buf, self.packAny(), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) skipFast() (int, types.ParsingError) {
|
||||||
|
start := native.SkipOneFast(&self.s, &self.p)
|
||||||
|
if start < 0 {
|
||||||
|
return self.p, types.ParsingError(-start)
|
||||||
|
}
|
||||||
|
return start, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) getByPath(path ...interface{}) (int, types.ParsingError) {
|
||||||
|
start := native.GetByPath(&self.s, &self.p, &path)
|
||||||
|
runtime.KeepAlive(path)
|
||||||
|
if start < 0 {
|
||||||
|
return self.p, types.ParsingError(-start)
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
// +build !amd64 go1.21
|
||||||
|
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/base64`
|
||||||
|
`encoding/json`
|
||||||
|
`fmt`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
func quote(buf *[]byte, val string) {
|
||||||
|
quoteString(buf, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unquote(src string) (string, types.ParsingError) {
|
||||||
|
sp := rt.IndexChar(src, -1)
|
||||||
|
out, ok := unquoteBytes(rt.BytesFrom(sp, len(src)+2, len(src)+2))
|
||||||
|
if !ok {
|
||||||
|
return "", types.ERR_INVALID_ESCAPE
|
||||||
|
}
|
||||||
|
return rt.Mem2Str(out), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func decodeBase64(src string) ([]byte, error) {
|
||||||
|
return base64.StdEncoding.DecodeString(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeBase64(src []byte) string {
|
||||||
|
return base64.StdEncoding.EncodeToString(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) decodeValue() (val types.JsonState) {
|
||||||
|
e, v := decodeValue(self.s, self.p)
|
||||||
|
if e < 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
self.p = e
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) skip() (int, types.ParsingError) {
|
||||||
|
e, s := skipValue(self.s, self.p)
|
||||||
|
if e < 0 {
|
||||||
|
return self.p, types.ParsingError(-e)
|
||||||
|
}
|
||||||
|
self.p = e
|
||||||
|
return s, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) skipFast() (int, types.ParsingError) {
|
||||||
|
return self.skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||||
|
out, err := json.Marshal(self.packAny())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*buf = append(*buf, out...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
|
||||||
|
self.parser.p = 0
|
||||||
|
|
||||||
|
var err types.ParsingError
|
||||||
|
for _, p := range path {
|
||||||
|
switch p := p.(type) {
|
||||||
|
case int:
|
||||||
|
if err = self.parser.searchIndex(p); err != 0 {
|
||||||
|
return Node{}, self.parser.ExportError(err)
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
if err = self.parser.searchKey(p); err != 0 {
|
||||||
|
return Node{}, self.parser.ExportError(err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("path must be either int 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newRawNode(self.parser.s[start:self.parser.p], t), nil
|
||||||
|
}
|
|
@ -0,0 +1,430 @@
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/base64`
|
||||||
|
`runtime`
|
||||||
|
`strconv`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
const _blankCharsMask = (1 << ' ') | (1 << '\t') | (1 << '\r') | (1 << '\n')
|
||||||
|
|
||||||
|
const (
|
||||||
|
bytesNull = "null"
|
||||||
|
bytesTrue = "true"
|
||||||
|
bytesFalse = "false"
|
||||||
|
bytesObject = "{}"
|
||||||
|
bytesArray = "[]"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isSpace(c byte) bool {
|
||||||
|
return (int(1<<c) & _blankCharsMask) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipBlank(src string, pos int) int {
|
||||||
|
se := uintptr(rt.IndexChar(src, len(src)))
|
||||||
|
sp := uintptr(rt.IndexChar(src, pos))
|
||||||
|
|
||||||
|
for sp < se {
|
||||||
|
if !isSpace(*(*byte)(unsafe.Pointer(sp))) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sp += 1
|
||||||
|
}
|
||||||
|
if sp >= se {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(src)
|
||||||
|
return int(sp - uintptr(rt.IndexChar(src, 0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeNull(src string, pos int) (ret int) {
|
||||||
|
ret = pos + 4
|
||||||
|
if ret > len(src) {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
if src[pos:ret] == bytesNull {
|
||||||
|
return ret
|
||||||
|
} else {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTrue(src string, pos int) (ret int) {
|
||||||
|
ret = pos + 4
|
||||||
|
if ret > len(src) {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
if src[pos:ret] == bytesTrue {
|
||||||
|
return ret
|
||||||
|
} else {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeFalse(src string, pos int) (ret int) {
|
||||||
|
ret = pos + 5
|
||||||
|
if ret > len(src) {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
if src[pos:ret] == bytesFalse {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeString(src string, pos int) (ret int, v string) {
|
||||||
|
ret, ep := skipString(src, pos)
|
||||||
|
if ep == -1 {
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&v)).Ptr = rt.IndexChar(src, pos+1)
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&v)).Len = ret - pos - 2
|
||||||
|
return ret, v
|
||||||
|
}
|
||||||
|
|
||||||
|
vv, ok := unquoteBytes(rt.Str2Mem(src[pos:ret]))
|
||||||
|
if !ok {
|
||||||
|
return -int(types.ERR_INVALID_CHAR), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(src)
|
||||||
|
return ret, rt.Mem2Str(vv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBinary(src string, pos int) (ret int, v []byte) {
|
||||||
|
var vv string
|
||||||
|
ret, vv = decodeString(src, pos)
|
||||||
|
if ret < 0 {
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
v, err = base64.StdEncoding.DecodeString(vv)
|
||||||
|
if err != nil {
|
||||||
|
return -int(types.ERR_INVALID_CHAR), nil
|
||||||
|
}
|
||||||
|
return ret, v
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDigit(c byte) bool {
|
||||||
|
return c >= '0' && c <= '9'
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeInt64(src string, pos int) (ret int, v int64, err error) {
|
||||||
|
sp := uintptr(rt.IndexChar(src, pos))
|
||||||
|
ss := uintptr(sp)
|
||||||
|
se := uintptr(rt.IndexChar(src, len(src)))
|
||||||
|
if uintptr(sp) >= se {
|
||||||
|
return -int(types.ERR_EOF), 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c := *(*byte)(unsafe.Pointer(sp)); c == '-' {
|
||||||
|
sp += 1
|
||||||
|
}
|
||||||
|
if sp == se {
|
||||||
|
return -int(types.ERR_EOF), 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; sp < se; sp += uintptr(1) {
|
||||||
|
if !isDigit(*(*byte)(unsafe.Pointer(sp))) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp < se {
|
||||||
|
if c := *(*byte)(unsafe.Pointer(sp)); c == '.' || c == 'e' || c == 'E' {
|
||||||
|
return -int(types.ERR_INVALID_NUMBER_FMT), 0, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var vv string
|
||||||
|
ret = int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&vv)).Ptr = unsafe.Pointer(ss)
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&vv)).Len = ret - pos
|
||||||
|
|
||||||
|
v, err = strconv.ParseInt(vv, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
//NOTICE: allow overflow here
|
||||||
|
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
||||||
|
return ret, 0, err
|
||||||
|
}
|
||||||
|
return -int(types.ERR_INVALID_CHAR), 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(src)
|
||||||
|
return ret, v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNumberChars(c byte) bool {
|
||||||
|
return (c >= '0' && c <= '9') || c == '+' || c == '-' || c == 'e' || c == 'E' || c == '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeFloat64(src string, pos int) (ret int, v float64, err error) {
|
||||||
|
sp := uintptr(rt.IndexChar(src, pos))
|
||||||
|
ss := uintptr(sp)
|
||||||
|
se := uintptr(rt.IndexChar(src, len(src)))
|
||||||
|
if uintptr(sp) >= se {
|
||||||
|
return -int(types.ERR_EOF), 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c := *(*byte)(unsafe.Pointer(sp)); c == '-' {
|
||||||
|
sp += 1
|
||||||
|
}
|
||||||
|
if sp == se {
|
||||||
|
return -int(types.ERR_EOF), 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; sp < se; sp += uintptr(1) {
|
||||||
|
if !isNumberChars(*(*byte)(unsafe.Pointer(sp))) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var vv string
|
||||||
|
ret = int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&vv)).Ptr = unsafe.Pointer(ss)
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&vv)).Len = ret - pos
|
||||||
|
|
||||||
|
v, err = strconv.ParseFloat(vv, 64)
|
||||||
|
if err != nil {
|
||||||
|
//NOTICE: allow overflow here
|
||||||
|
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
||||||
|
return ret, 0, err
|
||||||
|
}
|
||||||
|
return -int(types.ERR_INVALID_CHAR), 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(src)
|
||||||
|
return ret, v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeValue(src string, pos int) (ret int, v types.JsonState) {
|
||||||
|
pos = skipBlank(src, pos)
|
||||||
|
if pos < 0 {
|
||||||
|
return pos, types.JsonState{Vt: types.ValueType(pos)}
|
||||||
|
}
|
||||||
|
switch c := src[pos]; c {
|
||||||
|
case 'n':
|
||||||
|
ret = decodeNull(src, pos)
|
||||||
|
if ret < 0 {
|
||||||
|
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||||
|
}
|
||||||
|
return ret, types.JsonState{Vt: types.V_NULL}
|
||||||
|
case '"':
|
||||||
|
var ep int
|
||||||
|
ret, ep = skipString(src, pos)
|
||||||
|
if ret < 0 {
|
||||||
|
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||||
|
}
|
||||||
|
return ret, types.JsonState{Vt: types.V_STRING, Iv: int64(pos + 1), Ep: ep}
|
||||||
|
case '{':
|
||||||
|
return pos + 1, types.JsonState{Vt: types.V_OBJECT}
|
||||||
|
case '[':
|
||||||
|
return pos + 1, types.JsonState{Vt: types.V_ARRAY}
|
||||||
|
case 't':
|
||||||
|
ret = decodeTrue(src, pos)
|
||||||
|
if ret < 0 {
|
||||||
|
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||||
|
}
|
||||||
|
return ret, types.JsonState{Vt: types.V_TRUE}
|
||||||
|
case 'f':
|
||||||
|
ret = decodeFalse(src, pos)
|
||||||
|
if ret < 0 {
|
||||||
|
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||||
|
}
|
||||||
|
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}
|
||||||
|
} 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)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipNumber(src string, pos int) (ret int) {
|
||||||
|
sp := uintptr(rt.IndexChar(src, pos))
|
||||||
|
se := uintptr(rt.IndexChar(src, len(src)))
|
||||||
|
if uintptr(sp) >= se {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c := *(*byte)(unsafe.Pointer(sp)); c == '-' {
|
||||||
|
sp += 1
|
||||||
|
}
|
||||||
|
ss := sp
|
||||||
|
|
||||||
|
var pointer bool
|
||||||
|
var exponent bool
|
||||||
|
var lastIsDigit bool
|
||||||
|
var nextNeedDigit = true
|
||||||
|
|
||||||
|
for ; sp < se; sp += uintptr(1) {
|
||||||
|
c := *(*byte)(unsafe.Pointer(sp))
|
||||||
|
if isDigit(c) {
|
||||||
|
lastIsDigit = true
|
||||||
|
nextNeedDigit = false
|
||||||
|
continue
|
||||||
|
} else if nextNeedDigit {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
} else if c == '.' {
|
||||||
|
if !lastIsDigit || pointer || sp == ss {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
pointer = true
|
||||||
|
lastIsDigit = false
|
||||||
|
nextNeedDigit = true
|
||||||
|
continue
|
||||||
|
} else if c == 'e' || c == 'E' {
|
||||||
|
if !lastIsDigit || exponent {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
if sp == se-1 {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
exponent = true
|
||||||
|
lastIsDigit = false
|
||||||
|
nextNeedDigit = false
|
||||||
|
continue
|
||||||
|
} else if c == '-' || c == '+' {
|
||||||
|
if prev := *(*byte)(unsafe.Pointer(sp - 1)); prev != 'e' && prev != 'E' {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
lastIsDigit = false
|
||||||
|
nextNeedDigit = true
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextNeedDigit {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(src)
|
||||||
|
return int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipString(src string, pos int) (ret int, ep int) {
|
||||||
|
if pos+1 >= len(src) {
|
||||||
|
return -int(types.ERR_EOF), -1
|
||||||
|
}
|
||||||
|
|
||||||
|
sp := uintptr(rt.IndexChar(src, pos))
|
||||||
|
se := uintptr(rt.IndexChar(src, len(src)))
|
||||||
|
|
||||||
|
if *(*byte)(unsafe.Pointer(sp)) != '"' {
|
||||||
|
return -int(types.ERR_INVALID_CHAR), -1
|
||||||
|
}
|
||||||
|
sp += 1
|
||||||
|
|
||||||
|
ep = -1
|
||||||
|
for sp < se {
|
||||||
|
c := *(*byte)(unsafe.Pointer(sp))
|
||||||
|
if c == '\\' {
|
||||||
|
if ep == -1 {
|
||||||
|
ep = int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||||
|
}
|
||||||
|
sp += 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sp += 1
|
||||||
|
if c == '"' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp > se {
|
||||||
|
return -int(types.ERR_EOF), -1
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(src)
|
||||||
|
return int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr)), ep
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipPair(src string, pos int, lchar byte, rchar byte) (ret int) {
|
||||||
|
if pos+1 >= len(src) {
|
||||||
|
return -int(types.ERR_EOF)
|
||||||
|
}
|
||||||
|
|
||||||
|
sp := uintptr(rt.IndexChar(src, pos))
|
||||||
|
se := uintptr(rt.IndexChar(src, len(src)))
|
||||||
|
|
||||||
|
if *(*byte)(unsafe.Pointer(sp)) != lchar {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
sp += 1
|
||||||
|
nbrace := 1
|
||||||
|
inquote := false
|
||||||
|
|
||||||
|
for sp < se {
|
||||||
|
c := *(*byte)(unsafe.Pointer(sp))
|
||||||
|
if c == '\\' {
|
||||||
|
sp += 2
|
||||||
|
continue
|
||||||
|
} else if c == '"' {
|
||||||
|
inquote = !inquote
|
||||||
|
} else if c == lchar {
|
||||||
|
if !inquote {
|
||||||
|
nbrace += 1
|
||||||
|
}
|
||||||
|
} else if c == rchar {
|
||||||
|
if !inquote {
|
||||||
|
nbrace -= 1
|
||||||
|
if nbrace == 0 {
|
||||||
|
sp += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sp += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if nbrace != 0 {
|
||||||
|
return -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.KeepAlive(src)
|
||||||
|
return int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipValue(src string, pos int) (ret int, start int) {
|
||||||
|
pos = skipBlank(src, pos)
|
||||||
|
if pos < 0 {
|
||||||
|
return pos, -1
|
||||||
|
}
|
||||||
|
switch c := src[pos]; c {
|
||||||
|
case 'n':
|
||||||
|
ret = decodeNull(src, pos)
|
||||||
|
case '"':
|
||||||
|
ret, _ = skipString(src, pos)
|
||||||
|
case '{':
|
||||||
|
ret = skipPair(src, pos, '{', '}')
|
||||||
|
case '[':
|
||||||
|
ret = skipPair(src, pos, '[', ']')
|
||||||
|
case 't':
|
||||||
|
ret = decodeTrue(src, pos)
|
||||||
|
case 'f':
|
||||||
|
ret = decodeFalse(src, pos)
|
||||||
|
case '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
ret = skipNumber(src, pos)
|
||||||
|
default:
|
||||||
|
ret = -int(types.ERR_INVALID_CHAR)
|
||||||
|
}
|
||||||
|
return ret, pos
|
||||||
|
}
|
|
@ -0,0 +1,259 @@
|
||||||
|
/*
|
||||||
|
* 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 (
|
||||||
|
`sync`
|
||||||
|
`unicode/utf8`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_MaxBuffer = 1024 // 1KB buffer size
|
||||||
|
)
|
||||||
|
|
||||||
|
func quoteString(e *[]byte, s string) {
|
||||||
|
*e = append(*e, '"')
|
||||||
|
start := 0
|
||||||
|
for i := 0; i < len(s); {
|
||||||
|
if b := s[i]; b < utf8.RuneSelf {
|
||||||
|
if safeSet[b] {
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if start < i {
|
||||||
|
*e = append(*e, s[start:i]...)
|
||||||
|
}
|
||||||
|
*e = append(*e, '\\')
|
||||||
|
switch b {
|
||||||
|
case '\\', '"':
|
||||||
|
*e = append(*e, b)
|
||||||
|
case '\n':
|
||||||
|
*e = append(*e, 'n')
|
||||||
|
case '\r':
|
||||||
|
*e = append(*e, 'r')
|
||||||
|
case '\t':
|
||||||
|
*e = append(*e, 't')
|
||||||
|
default:
|
||||||
|
// This encodes bytes < 0x20 except for \t, \n and \r.
|
||||||
|
// If escapeHTML is set, it also escapes <, >, and &
|
||||||
|
// because they can lead to security holes when
|
||||||
|
// user-controlled strings are rendered into JSON
|
||||||
|
// and served to some browsers.
|
||||||
|
*e = append(*e, `u00`...)
|
||||||
|
*e = append(*e, hex[b>>4])
|
||||||
|
*e = append(*e, hex[b&0xF])
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
start = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c, size := utf8.DecodeRuneInString(s[i:])
|
||||||
|
// if c == utf8.RuneError && size == 1 {
|
||||||
|
// if start < i {
|
||||||
|
// e.Write(s[start:i])
|
||||||
|
// }
|
||||||
|
// e.WriteString(`\ufffd`)
|
||||||
|
// i += size
|
||||||
|
// start = i
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
if c == '\u2028' || c == '\u2029' {
|
||||||
|
if start < i {
|
||||||
|
*e = append(*e, s[start:i]...)
|
||||||
|
}
|
||||||
|
*e = append(*e, `\u202`...)
|
||||||
|
*e = append(*e, hex[c&0xF])
|
||||||
|
i += size
|
||||||
|
start = i
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i += size
|
||||||
|
}
|
||||||
|
if start < len(s) {
|
||||||
|
*e = append(*e, s[start:]...)
|
||||||
|
}
|
||||||
|
*e = append(*e, '"')
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytesPool = sync.Pool{}
|
||||||
|
|
||||||
|
func (self *Node) MarshalJSON() ([]byte, error) {
|
||||||
|
buf := newBuffer()
|
||||||
|
err := self.encode(buf)
|
||||||
|
if err != nil {
|
||||||
|
freeBuffer(buf)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]byte, len(*buf))
|
||||||
|
copy(ret, *buf)
|
||||||
|
freeBuffer(buf)
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuffer() *[]byte {
|
||||||
|
if ret := bytesPool.Get(); ret != nil {
|
||||||
|
return ret.(*[]byte)
|
||||||
|
} else {
|
||||||
|
buf := make([]byte, 0, _MaxBuffer)
|
||||||
|
return &buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeBuffer(buf *[]byte) {
|
||||||
|
*buf = (*buf)[:0]
|
||||||
|
bytesPool.Put(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encode(buf *[]byte) error {
|
||||||
|
if self.IsRaw() {
|
||||||
|
return self.encodeRaw(buf)
|
||||||
|
}
|
||||||
|
switch self.Type() {
|
||||||
|
case V_NONE : return ErrNotExist
|
||||||
|
case V_ERROR : return self.Check()
|
||||||
|
case V_NULL : return self.encodeNull(buf)
|
||||||
|
case V_TRUE : return self.encodeTrue(buf)
|
||||||
|
case V_FALSE : return self.encodeFalse(buf)
|
||||||
|
case V_ARRAY : return self.encodeArray(buf)
|
||||||
|
case V_OBJECT: return self.encodeObject(buf)
|
||||||
|
case V_STRING: return self.encodeString(buf)
|
||||||
|
case V_NUMBER: return self.encodeNumber(buf)
|
||||||
|
case V_ANY : return self.encodeInterface(buf)
|
||||||
|
default : return ErrUnsupportType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeRaw(buf *[]byte) error {
|
||||||
|
raw, err := self.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*buf = append(*buf, raw...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeNull(buf *[]byte) error {
|
||||||
|
*buf = append(*buf, bytesNull...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeTrue(buf *[]byte) error {
|
||||||
|
*buf = append(*buf, bytesTrue...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeFalse(buf *[]byte) error {
|
||||||
|
*buf = append(*buf, bytesFalse...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeNumber(buf *[]byte) error {
|
||||||
|
str := rt.StrFrom(self.p, self.v)
|
||||||
|
*buf = append(*buf, str...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeString(buf *[]byte) error {
|
||||||
|
if self.v == 0 {
|
||||||
|
*buf = append(*buf, '"', '"')
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
quote(buf, rt.StrFrom(self.p, self.v))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeArray(buf *[]byte) error {
|
||||||
|
if self.isLazy() {
|
||||||
|
if err := self.skipAllIndex(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nb := self.len()
|
||||||
|
if nb == 0 {
|
||||||
|
*buf = append(*buf, bytesArray...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
*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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf = append(*buf, ']')
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Pair) encode(buf *[]byte) error {
|
||||||
|
if len(*buf) == 0 {
|
||||||
|
*buf = append(*buf, '"', '"', ':')
|
||||||
|
return self.Value.encode(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
quote(buf, self.Key)
|
||||||
|
*buf = append(*buf, ':')
|
||||||
|
|
||||||
|
return self.Value.encode(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) encodeObject(buf *[]byte) error {
|
||||||
|
if self.isLazy() {
|
||||||
|
if err := self.skipAllKey(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nb := self.len()
|
||||||
|
if nb == 0 {
|
||||||
|
*buf = append(*buf, bytesObject...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
*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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*buf = append(*buf, '}')
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`strings`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`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 newSyntaxError(err SyntaxError) *Node {
|
||||||
|
msg := err.Description()
|
||||||
|
return &Node{
|
||||||
|
t: V_ERROR,
|
||||||
|
v: int64(err.Code),
|
||||||
|
p: unsafe.Pointer(&msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyntaxError struct {
|
||||||
|
Pos int
|
||||||
|
Src string
|
||||||
|
Code types.ParsingError
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) Error() string {
|
||||||
|
return fmt.Sprintf("%q", self.Description())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) Description() string {
|
||||||
|
return "Syntax error " + self.description()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) description() string {
|
||||||
|
i := 16
|
||||||
|
p := self.Pos - i
|
||||||
|
q := self.Pos + i
|
||||||
|
|
||||||
|
/* check for empty source */
|
||||||
|
if self.Src == "" {
|
||||||
|
return fmt.Sprintf("no sources available: %#v", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prevent slicing before the beginning */
|
||||||
|
if p < 0 {
|
||||||
|
p, q, i = 0, q - p, i + p
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prevent slicing beyond the end */
|
||||||
|
if n := len(self.Src); q > n {
|
||||||
|
n = q - n
|
||||||
|
q = len(self.Src)
|
||||||
|
|
||||||
|
/* move the left bound if possible */
|
||||||
|
if p > n {
|
||||||
|
i += n
|
||||||
|
p -= n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* left and right length */
|
||||||
|
x := clamp_zero(i)
|
||||||
|
y := clamp_zero(q - p - i - 1)
|
||||||
|
|
||||||
|
/* compose the error description */
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"at index %d: %s\n\n\t%s\n\t%s^%s\n",
|
||||||
|
self.Pos,
|
||||||
|
self.Message(),
|
||||||
|
self.Src[p:q],
|
||||||
|
strings.Repeat(".", x),
|
||||||
|
strings.Repeat(".", y),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) Message() string {
|
||||||
|
if self.Msg == "" {
|
||||||
|
return self.Code.Message()
|
||||||
|
}
|
||||||
|
return self.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func clamp_zero(v int) int {
|
||||||
|
if v < 0 {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* 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 (
|
||||||
|
`fmt`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pair struct {
|
||||||
|
Key string
|
||||||
|
Value Node
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns iterator for array's children traversal
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Properties returns iterator for object's children traversal
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
type Iterator struct {
|
||||||
|
i int
|
||||||
|
p *Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Iterator) Pos() int {
|
||||||
|
return self.i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Iterator) Len() int {
|
||||||
|
return self.p.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasNext reports if it is the end of iteration or has error.
|
||||||
|
func (self *Iterator) HasNext() bool {
|
||||||
|
if !self.p.isLazy() {
|
||||||
|
return self.p.Valid() && self.i < self.p.len()
|
||||||
|
} else if self.p.t == _V_ARRAY_LAZY {
|
||||||
|
return self.p.skipNextNode().Valid()
|
||||||
|
} else if self.p.t == _V_OBJECT_LAZY {
|
||||||
|
pair := self.p.skipNextPair()
|
||||||
|
if pair == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return pair.Value.Valid()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListIterator is specialized iterator for V_ARRAY
|
||||||
|
type ListIterator struct {
|
||||||
|
Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectIterator is specialized iterator for V_ARRAY
|
||||||
|
type ObjectIterator struct {
|
||||||
|
Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
*v, self.i = *self.p.nodeAt(self.i), self.i + 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
*p, self.i = *self.p.pairAt(self.i), self.i + 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequence represents scanning path of single-layer nodes.
|
||||||
|
// Index indicates the value's order in both V_ARRAY and V_OBJECT json.
|
||||||
|
// Key is the value's key (for V_OBJECT json only, otherwise it will be nil).
|
||||||
|
type Sequence struct {
|
||||||
|
Index int
|
||||||
|
Key *string
|
||||||
|
// Level int
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is string representation of one Sequence
|
||||||
|
func (s Sequence) String() string {
|
||||||
|
k := ""
|
||||||
|
if s.Key != nil {
|
||||||
|
k = *s.Key
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Sequence(%d, %q)", s.Index, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scanner func(path Sequence, node *Node) bool
|
||||||
|
|
||||||
|
// ForEach scans one V_OBJECT node's children from JSON head to tail,
|
||||||
|
// and pass the Sequence and Node of corresponding JSON value.
|
||||||
|
//
|
||||||
|
// Especailly, if the node is not V_ARRAY or V_OBJECT,
|
||||||
|
// the node itself will be returned and Sequence.Index == -1.
|
||||||
|
func (self *Node) ForEach(sc Scanner) error {
|
||||||
|
switch self.itype() {
|
||||||
|
case types.V_ARRAY:
|
||||||
|
ns, err := self.UnsafeArray()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range ns {
|
||||||
|
if !sc(Sequence{i, nil}, &ns[i]) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case types.V_OBJECT:
|
||||||
|
ns, err := self.UnsafeMap()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range ns {
|
||||||
|
if !sc(Sequence{i, &ns[i].Key}, &ns[i].Value) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
sc(Sequence{-1, nil}, self)
|
||||||
|
}
|
||||||
|
return self.Check()
|
||||||
|
}
|
||||||
|
|
||||||
|
type PairSlice []Pair
|
||||||
|
|
||||||
|
func (self PairSlice) Sort() {
|
||||||
|
radixQsort(self, 0, maxDepth(len(self)))
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,618 @@
|
||||||
|
/*
|
||||||
|
* 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 (
|
||||||
|
`fmt`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
const _DEFAULT_NODE_CAP int = 16
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ERR_NOT_FOUND types.ParsingError = 33
|
||||||
|
_ERR_UNSUPPORT_TYPE types.ParsingError = 34
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotExist error = newError(_ERR_NOT_FOUND, "value not exists")
|
||||||
|
ErrUnsupportType error = newError(_ERR_UNSUPPORT_TYPE, "unsupported type")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
p int
|
||||||
|
s string
|
||||||
|
noLazy bool
|
||||||
|
skipValue bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parser Private Methods **/
|
||||||
|
|
||||||
|
func (self *Parser) delim() types.ParsingError {
|
||||||
|
n := len(self.s)
|
||||||
|
p := self.lspace(self.p)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if p >= n {
|
||||||
|
return types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the delimtier */
|
||||||
|
if self.s[p] != ':' {
|
||||||
|
return types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the read pointer */
|
||||||
|
self.p = p + 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) object() types.ParsingError {
|
||||||
|
n := len(self.s)
|
||||||
|
p := self.lspace(self.p)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if p >= n {
|
||||||
|
return types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the delimtier */
|
||||||
|
if self.s[p] != '{' {
|
||||||
|
return types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the read pointer */
|
||||||
|
self.p = p + 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) array() types.ParsingError {
|
||||||
|
n := len(self.s)
|
||||||
|
p := self.lspace(self.p)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if p >= n {
|
||||||
|
return types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the delimtier */
|
||||||
|
if self.s[p] != '[' {
|
||||||
|
return types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the read pointer */
|
||||||
|
self.p = p + 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) lspace(sp int) int {
|
||||||
|
ns := len(self.s)
|
||||||
|
for ; sp<ns && isSpace(self.s[sp]); sp+=1 {}
|
||||||
|
|
||||||
|
return sp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) decodeArray(ret []Node) (Node, types.ParsingError) {
|
||||||
|
sp := self.p
|
||||||
|
ns := len(self.s)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if self.p = self.lspace(sp); self.p >= ns {
|
||||||
|
return Node{}, types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for empty array */
|
||||||
|
if self.s[self.p] == ']' {
|
||||||
|
self.p++
|
||||||
|
return emptyArrayNode, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate array space and parse every element */
|
||||||
|
for {
|
||||||
|
var val Node
|
||||||
|
var err types.ParsingError
|
||||||
|
|
||||||
|
if self.skipValue {
|
||||||
|
/* skip the value */
|
||||||
|
var start int
|
||||||
|
if start, err = self.skipFast(); err != 0 {
|
||||||
|
return Node{}, err
|
||||||
|
}
|
||||||
|
if self.p > ns {
|
||||||
|
return Node{}, types.ERR_EOF
|
||||||
|
}
|
||||||
|
t := switchRawType(self.s[start])
|
||||||
|
if t == _V_NONE {
|
||||||
|
return Node{}, types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
val = newRawNode(self.s[start:self.p], t)
|
||||||
|
}else{
|
||||||
|
/* decode the value */
|
||||||
|
if val, err = self.Parse(); err != 0 {
|
||||||
|
return Node{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the value to result */
|
||||||
|
ret = append(ret, val)
|
||||||
|
self.p = self.lspace(self.p)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if self.p >= ns {
|
||||||
|
return Node{}, types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) decodeObject(ret []Pair) (Node, types.ParsingError) {
|
||||||
|
sp := self.p
|
||||||
|
ns := len(self.s)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if self.p = self.lspace(sp); self.p >= ns {
|
||||||
|
return Node{}, types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for empty object */
|
||||||
|
if self.s[self.p] == '}' {
|
||||||
|
self.p++
|
||||||
|
return emptyObjectNode, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decode each pair */
|
||||||
|
for {
|
||||||
|
var val Node
|
||||||
|
var njs types.JsonState
|
||||||
|
var err types.ParsingError
|
||||||
|
|
||||||
|
/* decode the key */
|
||||||
|
if njs = self.decodeValue(); njs.Vt != types.V_STRING {
|
||||||
|
return Node{}, types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract the key */
|
||||||
|
idx := self.p - 1
|
||||||
|
key := self.s[njs.Iv:idx]
|
||||||
|
|
||||||
|
/* check for escape sequence */
|
||||||
|
if njs.Ep != -1 {
|
||||||
|
if key, err = unquote(key); err != 0 {
|
||||||
|
return Node{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* expect a ':' delimiter */
|
||||||
|
if err = self.delim(); err != 0 {
|
||||||
|
return Node{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if self.skipValue {
|
||||||
|
/* skip the value */
|
||||||
|
var start int
|
||||||
|
if start, err = self.skipFast(); err != 0 {
|
||||||
|
return Node{}, err
|
||||||
|
}
|
||||||
|
if self.p > ns {
|
||||||
|
return Node{}, types.ERR_EOF
|
||||||
|
}
|
||||||
|
t := switchRawType(self.s[start])
|
||||||
|
if t == _V_NONE {
|
||||||
|
return Node{}, types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
val = newRawNode(self.s[start:self.p], t)
|
||||||
|
} else {
|
||||||
|
/* decode the value */
|
||||||
|
if val, err = self.Parse(); err != 0 {
|
||||||
|
return Node{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the value to result */
|
||||||
|
ret = append(ret, Pair{Key: key, Value: val})
|
||||||
|
self.p = self.lspace(self.p)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if self.p >= ns {
|
||||||
|
return Node{}, types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the next character */
|
||||||
|
switch self.s[self.p] {
|
||||||
|
case ',' : self.p++
|
||||||
|
case '}' : self.p++; return NewObject(ret), 0
|
||||||
|
default:
|
||||||
|
if val.isLazy() {
|
||||||
|
return newLazyObject(self, ret), 0
|
||||||
|
}
|
||||||
|
return Node{}, types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) decodeString(iv int64, ep int) (Node, types.ParsingError) {
|
||||||
|
p := self.p - 1
|
||||||
|
s := self.s[iv:p]
|
||||||
|
|
||||||
|
/* fast path: no escape sequence */
|
||||||
|
if ep == -1 {
|
||||||
|
return NewString(s), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unquote the string */
|
||||||
|
out, err := unquote(s)
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != 0 {
|
||||||
|
return Node{}, err
|
||||||
|
} else {
|
||||||
|
return newBytes(rt.Str2Mem(out)), 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parser Interface **/
|
||||||
|
|
||||||
|
func (self *Parser) Pos() int {
|
||||||
|
return self.p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) Parse() (Node, types.ParsingError) {
|
||||||
|
switch val := self.decodeValue(); val.Vt {
|
||||||
|
case types.V_EOF : return Node{}, types.ERR_EOF
|
||||||
|
case types.V_NULL : return nullNode, 0
|
||||||
|
case types.V_TRUE : return trueNode, 0
|
||||||
|
case types.V_FALSE : return falseNode, 0
|
||||||
|
case types.V_STRING : return self.decodeString(val.Iv, val.Ep)
|
||||||
|
case types.V_ARRAY:
|
||||||
|
if self.noLazy {
|
||||||
|
return self.decodeArray(make([]Node, 0, _DEFAULT_NODE_CAP))
|
||||||
|
}
|
||||||
|
return newLazyArray(self, make([]Node, 0, _DEFAULT_NODE_CAP)), 0
|
||||||
|
case types.V_OBJECT:
|
||||||
|
if self.noLazy {
|
||||||
|
return self.decodeObject(make([]Pair, 0, _DEFAULT_NODE_CAP))
|
||||||
|
}
|
||||||
|
return newLazyObject(self, make([]Pair, 0, _DEFAULT_NODE_CAP)), 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) searchKey(match string) types.ParsingError {
|
||||||
|
ns := len(self.s)
|
||||||
|
if err := self.object(); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if self.p = self.lspace(self.p); self.p >= ns {
|
||||||
|
return types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for empty object */
|
||||||
|
if self.s[self.p] == '}' {
|
||||||
|
self.p++
|
||||||
|
return _ERR_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
var njs types.JsonState
|
||||||
|
var err types.ParsingError
|
||||||
|
/* decode each pair */
|
||||||
|
for {
|
||||||
|
|
||||||
|
/* decode the key */
|
||||||
|
if njs = self.decodeValue(); njs.Vt != types.V_STRING {
|
||||||
|
return types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract the key */
|
||||||
|
idx := self.p - 1
|
||||||
|
key := self.s[njs.Iv:idx]
|
||||||
|
|
||||||
|
/* check for escape sequence */
|
||||||
|
if njs.Ep != -1 {
|
||||||
|
if key, err = unquote(key); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* expect a ':' delimiter */
|
||||||
|
if err = self.delim(); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip value */
|
||||||
|
if key != match {
|
||||||
|
if _, err = self.skip(); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
self.p = self.lspace(self.p)
|
||||||
|
if self.p >= ns {
|
||||||
|
return types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the next character */
|
||||||
|
switch self.s[self.p] {
|
||||||
|
case ',':
|
||||||
|
self.p++
|
||||||
|
case '}':
|
||||||
|
self.p++
|
||||||
|
return _ERR_NOT_FOUND
|
||||||
|
default:
|
||||||
|
return types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Parser) searchIndex(idx int) types.ParsingError {
|
||||||
|
ns := len(self.s)
|
||||||
|
if err := self.array(); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if self.p = self.lspace(self.p); self.p >= ns {
|
||||||
|
return types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for empty array */
|
||||||
|
if self.s[self.p] == ']' {
|
||||||
|
self.p++
|
||||||
|
return _ERR_NOT_FOUND
|
||||||
|
}
|
||||||
|
|
||||||
|
var err types.ParsingError
|
||||||
|
/* allocate array space and parse every element */
|
||||||
|
for i := 0; i < idx; i++ {
|
||||||
|
|
||||||
|
/* decode the value */
|
||||||
|
if _, err = self.skip(); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
self.p = self.lspace(self.p)
|
||||||
|
if self.p >= ns {
|
||||||
|
return types.ERR_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the next character */
|
||||||
|
switch self.s[self.p] {
|
||||||
|
case ',':
|
||||||
|
self.p++
|
||||||
|
case ']':
|
||||||
|
self.p++
|
||||||
|
return _ERR_NOT_FOUND
|
||||||
|
default:
|
||||||
|
return types.ERR_INVALID_CHAR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) skipNextNode() *Node {
|
||||||
|
if !self.isLazy() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parser, stack := self.getParserAndArrayStack()
|
||||||
|
ret := stack.v
|
||||||
|
sp := parser.p
|
||||||
|
ns := len(parser.s)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if parser.p = parser.lspace(sp); parser.p >= ns {
|
||||||
|
return newSyntaxError(parser.syntaxError(types.ERR_EOF))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for empty array */
|
||||||
|
if parser.s[parser.p] == ']' {
|
||||||
|
parser.p++
|
||||||
|
self.setArray(ret)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var val Node
|
||||||
|
/* skip the value */
|
||||||
|
if start, err := parser.skipFast(); err != 0 {
|
||||||
|
return newSyntaxError(parser.syntaxError(err))
|
||||||
|
} else {
|
||||||
|
t := switchRawType(parser.s[start])
|
||||||
|
if t == _V_NONE {
|
||||||
|
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||||
|
}
|
||||||
|
val = newRawNode(parser.s[start:parser.p], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the value to result */
|
||||||
|
ret = append(ret, val)
|
||||||
|
parser.p = parser.lspace(parser.p)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if parser.p >= ns {
|
||||||
|
return newSyntaxError(parser.syntaxError(types.ERR_EOF))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the next character */
|
||||||
|
switch parser.s[parser.p] {
|
||||||
|
case ',':
|
||||||
|
parser.p++
|
||||||
|
self.setLazyArray(parser, ret)
|
||||||
|
return &ret[len(ret)-1]
|
||||||
|
case ']':
|
||||||
|
parser.p++
|
||||||
|
self.setArray(ret)
|
||||||
|
return &ret[len(ret)-1]
|
||||||
|
default:
|
||||||
|
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Node) skipNextPair() (*Pair) {
|
||||||
|
if !self.isLazy() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parser, stack := self.getParserAndObjectStack()
|
||||||
|
ret := stack.v
|
||||||
|
sp := parser.p
|
||||||
|
ns := len(parser.s)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if parser.p = parser.lspace(sp); parser.p >= ns {
|
||||||
|
return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for empty object */
|
||||||
|
if parser.s[parser.p] == '}' {
|
||||||
|
parser.p++
|
||||||
|
self.setObject(ret)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decode one pair */
|
||||||
|
var val Node
|
||||||
|
var njs types.JsonState
|
||||||
|
var err types.ParsingError
|
||||||
|
|
||||||
|
/* decode the key */
|
||||||
|
if njs = parser.decodeValue(); njs.Vt != types.V_STRING {
|
||||||
|
return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract the key */
|
||||||
|
idx := parser.p - 1
|
||||||
|
key := parser.s[njs.Iv:idx]
|
||||||
|
|
||||||
|
/* check for escape sequence */
|
||||||
|
if njs.Ep != -1 {
|
||||||
|
if key, err = unquote(key); err != 0 {
|
||||||
|
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* expect a ':' delimiter */
|
||||||
|
if err = parser.delim(); err != 0 {
|
||||||
|
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip the value */
|
||||||
|
if start, err := parser.skipFast(); err != 0 {
|
||||||
|
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
|
||||||
|
} else {
|
||||||
|
t := switchRawType(parser.s[start])
|
||||||
|
if t == _V_NONE {
|
||||||
|
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
|
||||||
|
}
|
||||||
|
val = newRawNode(parser.s[start:parser.p], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the value to result */
|
||||||
|
ret = append(ret, Pair{Key: key, Value: val})
|
||||||
|
parser.p = parser.lspace(parser.p)
|
||||||
|
|
||||||
|
/* check for EOF */
|
||||||
|
if parser.p >= ns {
|
||||||
|
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for the next character */
|
||||||
|
switch parser.s[parser.p] {
|
||||||
|
case ',':
|
||||||
|
parser.p++
|
||||||
|
self.setLazyObject(parser, ret)
|
||||||
|
return &ret[len(ret)-1]
|
||||||
|
case '}':
|
||||||
|
parser.p++
|
||||||
|
self.setObject(ret)
|
||||||
|
return &ret[len(ret)-1]
|
||||||
|
default:
|
||||||
|
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Parser Factory **/
|
||||||
|
|
||||||
|
// Loads parse all json into interface{}
|
||||||
|
func Loads(src string) (int, interface{}, error) {
|
||||||
|
ps := &Parser{s: src}
|
||||||
|
np, err := ps.Parse()
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != 0 {
|
||||||
|
return 0, nil, ps.ExportError(err)
|
||||||
|
} else {
|
||||||
|
x, err := np.Interface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
return ps.Pos(), x, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadsUseNumber parse all json into interface{}, with numeric nodes casted to json.Number
|
||||||
|
func LoadsUseNumber(src string) (int, interface{}, error) {
|
||||||
|
ps := &Parser{s: src}
|
||||||
|
np, err := ps.Parse()
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != 0 {
|
||||||
|
return 0, nil, err
|
||||||
|
} else {
|
||||||
|
x, err := np.InterfaceUseNumber()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
return ps.Pos(), x, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParser(src string) *Parser {
|
||||||
|
return &Parser{s: src}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportError converts types.ParsingError to std Error
|
||||||
|
func (self *Parser) ExportError(err types.ParsingError) error {
|
||||||
|
if err == _ERR_NOT_FOUND {
|
||||||
|
return ErrNotExist
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%q", SyntaxError{
|
||||||
|
Pos : self.p,
|
||||||
|
Src : self.s,
|
||||||
|
Code: err,
|
||||||
|
}.Description())
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
type Searcher struct {
|
||||||
|
parser Parser
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSearcher(str string) *Searcher {
|
||||||
|
return &Searcher{
|
||||||
|
parser: Parser{
|
||||||
|
s: str,
|
||||||
|
noLazy: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// +build !go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 (
|
||||||
|
`unsafe`
|
||||||
|
`unicode/utf8`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname unsafe_NewArray reflect.unsafe_NewArray
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func unsafe_NewArray(typ *rt.GoType, n int) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname growslice runtime.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func mem2ptr(s []byte) unsafe.Pointer {
|
||||||
|
return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:linkname safeSet encoding/json.safeSet
|
||||||
|
safeSet [utf8.RuneSelf]bool
|
||||||
|
|
||||||
|
//go:linkname hex encoding/json.hex
|
||||||
|
hex string
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname unquoteBytes encoding/json.unquoteBytes
|
||||||
|
func unquoteBytes(s []byte) (t []byte, ok bool)
|
|
@ -0,0 +1,55 @@
|
||||||
|
// +build go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 (
|
||||||
|
`unsafe`
|
||||||
|
`unicode/utf8`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname unsafe_NewArray reflect.unsafe_NewArray
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func unsafe_NewArray(typ *rt.GoType, n int) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname growslice reflect.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func mem2ptr(s []byte) unsafe.Pointer {
|
||||||
|
return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:linkname safeSet encoding/json.safeSet
|
||||||
|
safeSet [utf8.RuneSelf]bool
|
||||||
|
|
||||||
|
//go:linkname hex encoding/json.hex
|
||||||
|
hex string
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname unquoteBytes encoding/json.unquoteBytes
|
||||||
|
func unquoteBytes(s []byte) (t []byte, ok bool)
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/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
|
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
After Width: | Height: | Size: 86 KiB |
|
@ -0,0 +1,134 @@
|
||||||
|
#!/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()
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,131 @@
|
||||||
|
// +build !amd64 go1.21
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 sonic
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`encoding/json`
|
||||||
|
`io`
|
||||||
|
`reflect`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/option`
|
||||||
|
)
|
||||||
|
|
||||||
|
type frozenConfig struct {
|
||||||
|
Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Froze convert the Config to API
|
||||||
|
func (cfg Config) Froze() API {
|
||||||
|
api := &frozenConfig{Config: cfg}
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg frozenConfig) marshalOptions(val interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
w := bytes.NewBuffer([]byte{})
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetEscapeHTML(cfg.EscapeHTML)
|
||||||
|
enc.SetIndent(prefix, indent)
|
||||||
|
err := enc.Encode(val)
|
||||||
|
out := w.Bytes()
|
||||||
|
|
||||||
|
// json.Encoder always appends '\n' after encoding,
|
||||||
|
// which is not same with json.Marshal()
|
||||||
|
if len(out) > 0 && out[len(out)-1] == '\n' {
|
||||||
|
out = out[:len(out)-1]
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal is implemented by sonic
|
||||||
|
func (cfg frozenConfig) Marshal(val interface{}) ([]byte, error) {
|
||||||
|
if !cfg.EscapeHTML {
|
||||||
|
return cfg.marshalOptions(val, "", "")
|
||||||
|
}
|
||||||
|
return json.Marshal(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalToString is implemented by sonic
|
||||||
|
func (cfg frozenConfig) MarshalToString(val interface{}) (string, error) {
|
||||||
|
out, err := cfg.Marshal(val)
|
||||||
|
return string(out), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalIndent is implemented by sonic
|
||||||
|
func (cfg frozenConfig) MarshalIndent(val interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
if !cfg.EscapeHTML {
|
||||||
|
return cfg.marshalOptions(val, prefix, indent)
|
||||||
|
}
|
||||||
|
return json.MarshalIndent(val, prefix, indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalFromString is implemented by sonic
|
||||||
|
func (cfg frozenConfig) UnmarshalFromString(buf string, val interface{}) error {
|
||||||
|
r := bytes.NewBufferString(buf)
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
if cfg.UseNumber {
|
||||||
|
dec.UseNumber()
|
||||||
|
}
|
||||||
|
if cfg.DisallowUnknownFields {
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
}
|
||||||
|
return dec.Decode(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal is implemented by sonic
|
||||||
|
func (cfg frozenConfig) Unmarshal(buf []byte, val interface{}) error {
|
||||||
|
return cfg.UnmarshalFromString(string(buf), val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder is implemented by sonic
|
||||||
|
func (cfg frozenConfig) NewEncoder(writer io.Writer) Encoder {
|
||||||
|
enc := json.NewEncoder(writer)
|
||||||
|
if !cfg.EscapeHTML {
|
||||||
|
enc.SetEscapeHTML(cfg.EscapeHTML)
|
||||||
|
}
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder is implemented by sonic
|
||||||
|
func (cfg frozenConfig) NewDecoder(reader io.Reader) Decoder {
|
||||||
|
dec := json.NewDecoder(reader)
|
||||||
|
if cfg.UseNumber {
|
||||||
|
dec.UseNumber()
|
||||||
|
}
|
||||||
|
if cfg.DisallowUnknownFields {
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
}
|
||||||
|
return dec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid is implemented by sonic
|
||||||
|
func (cfg frozenConfig) Valid(data []byte) bool {
|
||||||
|
return json.Valid(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
|
||||||
|
// order to reduce the first-hit latency at **amd64** Arch.
|
||||||
|
// 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.
|
||||||
|
// * This is the none implement for !amd64.
|
||||||
|
// It will be useful for someone who develop with !amd64 arch,like Mac M1.
|
||||||
|
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
1943
vendor/github.com/bytedance/sonic/decoder/assembler_amd64_go116.go
generated
vendored
Normal file
1943
vendor/github.com/bytedance/sonic/decoder/assembler_amd64_go116.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1922
vendor/github.com/bytedance/sonic/decoder/assembler_amd64_go117.go
generated
vendored
Normal file
1922
vendor/github.com/bytedance/sonic/decoder/assembler_amd64_go117.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`os`
|
||||||
|
`runtime`
|
||||||
|
`runtime/debug`
|
||||||
|
`strings`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var (
|
||||||
|
debugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
|
||||||
|
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_Instr_End _Instr = newInsOp(_OP_nil_1)
|
||||||
|
|
||||||
|
_F_gc = jit.Func(runtime.GC)
|
||||||
|
_F_force_gc = jit.Func(debug.FreeOSMemory)
|
||||||
|
_F_println = jit.Func(println_wrapper)
|
||||||
|
_F_print = jit.Func(print)
|
||||||
|
)
|
||||||
|
|
||||||
|
func println_wrapper(i int, op1 int, op2 int){
|
||||||
|
println(i, " Intrs ", op1, _OpNames[op1], "next: ", op2, _OpNames[op2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func print(i int){
|
||||||
|
println(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) force_gc() {
|
||||||
|
self.call_go(_F_gc)
|
||||||
|
self.call_go(_F_force_gc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) debug_instr(i int, v *_Instr) {
|
||||||
|
if debugSyncGC {
|
||||||
|
if (i+1 == len(self.p)) {
|
||||||
|
self.print_gc(i, v, &_Instr_End)
|
||||||
|
} else {
|
||||||
|
next := &(self.p[i+1])
|
||||||
|
self.print_gc(i, v, next)
|
||||||
|
name := _OpNames[next.op()]
|
||||||
|
if strings.Contains(name, "save") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.force_gc()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`reflect`
|
||||||
|
`runtime`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
`github.com/bytedance/sonic/option`
|
||||||
|
`github.com/bytedance/sonic/utf8`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_F_use_int64 = iota
|
||||||
|
_F_use_number
|
||||||
|
_F_disable_urc
|
||||||
|
_F_disable_unknown
|
||||||
|
_F_copy_string
|
||||||
|
_F_validate_string
|
||||||
|
|
||||||
|
_F_allow_control = 31
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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 SyntaxError {
|
||||||
|
Src : buf,
|
||||||
|
Pos : pos,
|
||||||
|
Code : types.ERR_INVALID_CHAR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
/* validate json if needed */
|
||||||
|
if (self.f & (1 << _F_validate_string)) != 0 && !utf8.ValidateString(self.s){
|
||||||
|
dbuf := utf8.CorrectWith(nil, rt.Str2Mem(self.s), "\ufffd")
|
||||||
|
self.s = rt.Mem2Str(dbuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
vv := rt.UnpackEface(val)
|
||||||
|
vp := vv.Value
|
||||||
|
|
||||||
|
/* check for nil type */
|
||||||
|
if vv.Type == nil {
|
||||||
|
return &json.InvalidUnmarshalError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* must be a non-nil pointer */
|
||||||
|
if vp == nil || vv.Type.Kind() != reflect.Ptr {
|
||||||
|
return &json.InvalidUnmarshalError{Type: vv.Type.Pack()}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create a new stack, and call the decoder */
|
||||||
|
sb, etp := newStack(), rt.PtrElem(vv.Type)
|
||||||
|
nb, err := decodeTypedPointer(self.s, self.i, etp, vp, sb, self.f)
|
||||||
|
/* return the stack back */
|
||||||
|
self.i = nb
|
||||||
|
freeStack(sb)
|
||||||
|
|
||||||
|
/* avoid GC ahead */
|
||||||
|
runtime.KeepAlive(vv)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
cfg := option.DefaultCompileOptions()
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&cfg)
|
||||||
|
}
|
||||||
|
return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Type]bool, error) {
|
||||||
|
/* compile function */
|
||||||
|
compiler := newCompiler().apply(opts)
|
||||||
|
decoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
|
||||||
|
if pp, err := compiler.compile(_vt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
as := newAssembler(pp)
|
||||||
|
as.name = _vt.String()
|
||||||
|
return as.Load(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find or compile */
|
||||||
|
vt := rt.UnpackType(_vt)
|
||||||
|
if val := programCache.Get(vt); val != nil {
|
||||||
|
return nil, nil
|
||||||
|
} else if _, err := programCache.Compute(vt, decoder); err == nil {
|
||||||
|
return compiler.rec, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
|
||||||
|
if opts.RecursiveDepth < 0 || len(vtm) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
next := make(map[reflect.Type]bool)
|
||||||
|
for vt := range(vtm) {
|
||||||
|
sub, err := pretouchType(vt, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for svt := range(sub) {
|
||||||
|
next[svt] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opts.RecursiveDepth -= 1
|
||||||
|
return pretouchRec(next, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func Skip(data []byte) (start int, end int) {
|
||||||
|
s := rt.Mem2Str(data)
|
||||||
|
p := 0
|
||||||
|
m := types.NewStateMachine()
|
||||||
|
ret := native.SkipOne(&s, &p, m, uint64(0))
|
||||||
|
types.FreeStateMachine(m)
|
||||||
|
return ret, p
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`errors`
|
||||||
|
`fmt`
|
||||||
|
`reflect`
|
||||||
|
`strconv`
|
||||||
|
`strings`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyntaxError struct {
|
||||||
|
Pos int
|
||||||
|
Src string
|
||||||
|
Code types.ParsingError
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) Error() string {
|
||||||
|
return fmt.Sprintf("%q", self.Description())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) Description() string {
|
||||||
|
return "Syntax error " + self.description()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) description() string {
|
||||||
|
i := 16
|
||||||
|
p := self.Pos - i
|
||||||
|
q := self.Pos + i
|
||||||
|
|
||||||
|
/* check for empty source */
|
||||||
|
if self.Src == "" {
|
||||||
|
return fmt.Sprintf("no sources available: %#v", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prevent slicing before the beginning */
|
||||||
|
if p < 0 {
|
||||||
|
p, q, i = 0, q - p, i + p
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prevent slicing beyond the end */
|
||||||
|
if n := len(self.Src); q > n {
|
||||||
|
n = q - n
|
||||||
|
q = len(self.Src)
|
||||||
|
|
||||||
|
/* move the left bound if possible */
|
||||||
|
if p > n {
|
||||||
|
i += n
|
||||||
|
p -= n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* left and right length */
|
||||||
|
x := clamp_zero(i)
|
||||||
|
y := clamp_zero(q - p - i - 1)
|
||||||
|
|
||||||
|
/* compose the error description */
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"at index %d: %s\n\n\t%s\n\t%s^%s\n",
|
||||||
|
self.Pos,
|
||||||
|
self.Message(),
|
||||||
|
self.Src[p:q],
|
||||||
|
strings.Repeat(".", x),
|
||||||
|
strings.Repeat(".", y),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self SyntaxError) Message() string {
|
||||||
|
if self.Msg == "" {
|
||||||
|
return self.Code.Message()
|
||||||
|
}
|
||||||
|
return self.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func clamp_zero(v int) int {
|
||||||
|
if v < 0 {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** JIT Error Helpers **/
|
||||||
|
|
||||||
|
var stackOverflow = &json.UnsupportedValueError {
|
||||||
|
Str : "Value nesting too deep",
|
||||||
|
Value : reflect.ValueOf("..."),
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func error_wrap(src string, pos int, code types.ParsingError) error {
|
||||||
|
return SyntaxError {
|
||||||
|
Pos : pos,
|
||||||
|
Src : src,
|
||||||
|
Code : code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func error_type(vt *rt.GoType) error {
|
||||||
|
return &json.UnmarshalTypeError{Type: vt.Pack()}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MismatchTypeError struct {
|
||||||
|
Pos int
|
||||||
|
Src string
|
||||||
|
Type reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func swithchJSONType (src string, pos int) string {
|
||||||
|
var val string
|
||||||
|
switch src[pos] {
|
||||||
|
case 'f': fallthrough
|
||||||
|
case 't': val = "bool"
|
||||||
|
case '"': val = "string"
|
||||||
|
case '{': val = "object"
|
||||||
|
case '[': val = "array"
|
||||||
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val = "number"
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self MismatchTypeError) Error() string {
|
||||||
|
se := SyntaxError {
|
||||||
|
Pos : self.Pos,
|
||||||
|
Src : self.Src,
|
||||||
|
Code : types.ERR_MISMATCH,
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Mismatch type %s with value %s %q", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self MismatchTypeError) Description() string {
|
||||||
|
se := SyntaxError {
|
||||||
|
Pos : self.Pos,
|
||||||
|
Src : self.Src,
|
||||||
|
Code : types.ERR_MISMATCH,
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Mismatch type %s with value %s %s", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func error_mismatch(src string, pos int, vt *rt.GoType) error {
|
||||||
|
return &MismatchTypeError {
|
||||||
|
Pos : pos,
|
||||||
|
Src : src,
|
||||||
|
Type : vt.Pack(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func error_field(name string) error {
|
||||||
|
return errors.New("json: unknown field " + strconv.Quote(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func error_value(value string, vtype reflect.Type) error {
|
||||||
|
return &json.UnmarshalTypeError {
|
||||||
|
Type : vtype,
|
||||||
|
Value : value,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,776 @@
|
||||||
|
// +build go1.15,!go1.17
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`fmt`
|
||||||
|
`reflect`
|
||||||
|
`strconv`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj/x86`
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Crucial Registers:
|
||||||
|
*
|
||||||
|
* ST(BX) : ro, decoder stack
|
||||||
|
* DF(R10) : ro, decoder flags
|
||||||
|
* EP(R11) : wo, error pointer
|
||||||
|
* IP(R12) : ro, input pointer
|
||||||
|
* IL(R13) : ro, input length
|
||||||
|
* IC(R14) : rw, input cursor
|
||||||
|
* VP(R15) : ro, value pointer (to an interface{})
|
||||||
|
*/
|
||||||
|
|
||||||
|
const (
|
||||||
|
_VD_args = 8 // 8 bytes for passing arguments to this functions
|
||||||
|
_VD_fargs = 64 // 64 bytes for passing arguments to other Go functions
|
||||||
|
_VD_saves = 40 // 40 bytes for saving the registers before CALL instructions
|
||||||
|
_VD_locals = 88 // 88 bytes for local variables
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_VD_offs = _VD_fargs + _VD_saves + _VD_locals
|
||||||
|
_VD_size = _VD_offs + 8 // 8 bytes for the parent frame pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_VAR_ss = _VAR_ss_Vt
|
||||||
|
_VAR_df = jit.Ptr(_SP, _VD_fargs + _VD_saves)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_VAR_ss_Vt = jit.Ptr(_SP, _VD_fargs + _VD_saves + 8)
|
||||||
|
_VAR_ss_Dv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 16)
|
||||||
|
_VAR_ss_Iv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 24)
|
||||||
|
_VAR_ss_Ep = jit.Ptr(_SP, _VD_fargs + _VD_saves + 32)
|
||||||
|
_VAR_ss_Db = jit.Ptr(_SP, _VD_fargs + _VD_saves + 40)
|
||||||
|
_VAR_ss_Dc = jit.Ptr(_SP, _VD_fargs + _VD_saves + 48)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 56)
|
||||||
|
_VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64)
|
||||||
|
_VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72)
|
||||||
|
_VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80)
|
||||||
|
)
|
||||||
|
|
||||||
|
type _ValueDecoder struct {
|
||||||
|
jit.BaseAssembler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) build() uintptr {
|
||||||
|
self.Init(self.compile)
|
||||||
|
return *(*uintptr)(self.Load("decode_value", _VD_size, _VD_args, argPtrs_generic, localPtrs_generic))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Function Calling Helpers **/
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) save(r ...obj.Addr) {
|
||||||
|
for i, v := range r {
|
||||||
|
if i > _VD_saves / 8 - 1 {
|
||||||
|
panic("too many registers to save")
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", v, jit.Ptr(_SP, _VD_fargs + int64(i) * 8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) load(r ...obj.Addr) {
|
||||||
|
for i, v := range r {
|
||||||
|
if i > _VD_saves / 8 - 1 {
|
||||||
|
panic("too many registers to load")
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, _VD_fargs + int64(i) * 8), v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) call(fn obj.Addr) {
|
||||||
|
self.Emit("MOVQ", fn, _AX) // MOVQ ${fn}, AX
|
||||||
|
self.Rjmp("CALL", _AX) // CALL AX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) call_go(fn obj.Addr) {
|
||||||
|
self.save(_REG_go...) // SAVE $REG_go
|
||||||
|
self.call(fn) // CALL ${fn}
|
||||||
|
self.load(_REG_go...) // LOAD $REG_go
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decoder Assembler **/
|
||||||
|
|
||||||
|
const (
|
||||||
|
_S_val = iota + 1
|
||||||
|
_S_arr
|
||||||
|
_S_arr_0
|
||||||
|
_S_obj
|
||||||
|
_S_obj_0
|
||||||
|
_S_obj_delim
|
||||||
|
_S_obj_sep
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_S_omask_key = (1 << _S_obj_0) | (1 << _S_obj_sep)
|
||||||
|
_S_omask_end = (1 << _S_obj_0) | (1 << _S_obj)
|
||||||
|
_S_vmask = (1 << _S_val) | (1 << _S_arr_0)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_A_init_len = 1
|
||||||
|
_A_init_cap = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ST_Sp = 0
|
||||||
|
_ST_Vt = _PtrBytes
|
||||||
|
_ST_Vp = _PtrBytes * (types.MAX_RECURSE + 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_V_true = jit.Imm(int64(pbool(true)))
|
||||||
|
_V_false = jit.Imm(int64(pbool(false)))
|
||||||
|
_F_value = jit.Imm(int64(native.S_value))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_V_max = jit.Imm(int64(types.V_MAX))
|
||||||
|
_E_eof = jit.Imm(int64(types.ERR_EOF))
|
||||||
|
_E_invalid = jit.Imm(int64(types.ERR_INVALID_CHAR))
|
||||||
|
_E_recurse = jit.Imm(int64(types.ERR_RECURSE_EXCEED_MAX))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_F_convTslice = jit.Func(convTslice)
|
||||||
|
_F_convTstring = jit.Func(convTstring)
|
||||||
|
_F_invalid_vtype = jit.Func(invalid_vtype)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_T_map = jit.Type(reflect.TypeOf((map[string]interface{})(nil)))
|
||||||
|
_T_bool = jit.Type(reflect.TypeOf(false))
|
||||||
|
_T_int64 = jit.Type(reflect.TypeOf(int64(0)))
|
||||||
|
_T_eface = jit.Type(reflect.TypeOf((*interface{})(nil)).Elem())
|
||||||
|
_T_slice = jit.Type(reflect.TypeOf(([]interface{})(nil)))
|
||||||
|
_T_string = jit.Type(reflect.TypeOf(""))
|
||||||
|
_T_number = jit.Type(reflect.TypeOf(json.Number("")))
|
||||||
|
_T_float64 = jit.Type(reflect.TypeOf(float64(0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
var _R_tab = map[int]string {
|
||||||
|
'[': "_decode_V_ARRAY",
|
||||||
|
'{': "_decode_V_OBJECT",
|
||||||
|
':': "_decode_V_KEY_SEP",
|
||||||
|
',': "_decode_V_ELEM_SEP",
|
||||||
|
']': "_decode_V_ARRAY_END",
|
||||||
|
'}': "_decode_V_OBJECT_END",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) compile() {
|
||||||
|
self.Emit("SUBQ", jit.Imm(_VD_size), _SP) // SUBQ $_VD_size, SP
|
||||||
|
self.Emit("MOVQ", _BP, jit.Ptr(_SP, _VD_offs)) // MOVQ BP, _VD_offs(SP)
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_SP, _VD_offs), _BP) // LEAQ _VD_offs(SP), BP
|
||||||
|
|
||||||
|
/* initialize the state machine */
|
||||||
|
self.Emit("XORL", _CX, _CX) // XORL CX, CX
|
||||||
|
self.Emit("MOVQ", _DF, _VAR_df) // MOVQ DF, df
|
||||||
|
/* initialize digital buffer first */
|
||||||
|
self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_ss_Dc) // MOVQ $_MaxDigitNums, ss.Dcap
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_ST, _DbufOffset), _AX) // LEAQ _DbufOffset(ST), AX
|
||||||
|
self.Emit("MOVQ", _AX, _VAR_ss_Db) // MOVQ AX, ss.Dbuf
|
||||||
|
/* add ST offset */
|
||||||
|
self.Emit("ADDQ", jit.Imm(_FsmOffset), _ST) // ADDQ _FsmOffset, _ST
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.WriteRecNotAX(0, _VP, jit.Ptr(_ST, _ST_Vp), false) // MOVQ VP, ST.Vp[0]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_val), jit.Ptr(_ST, _ST_Vt)) // MOVQ _S_val, ST.Vt[0]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* set the value from previous round */
|
||||||
|
self.Link("_set_value") // _set_value:
|
||||||
|
self.Emit("MOVL" , jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
|
||||||
|
self.Sjmp("JNC" , "_vtype_error") // JNC _vtype_error
|
||||||
|
self.Emit("XORL" , _SI, _SI) // XORL SI, SI
|
||||||
|
self.Emit("SUBQ" , jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
|
||||||
|
self.Emit("XCHGQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // XCHGQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ" , _R8, jit.Ptr(_SI, 0)) // MOVQ R8, (SI)
|
||||||
|
self.WriteRecNotAX(1, _R9, jit.Ptr(_SI, 8), false) // MOVQ R9, 8(SI)
|
||||||
|
|
||||||
|
/* check for value stack */
|
||||||
|
self.Link("_next") // _next:
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _AX) // MOVQ ST.Sp, AX
|
||||||
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JS" , "_return") // JS _return
|
||||||
|
|
||||||
|
/* fast path: test up to 4 characters manually */
|
||||||
|
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
|
||||||
|
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
|
||||||
|
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
|
||||||
|
self.Emit("MOVQ" , jit.Imm(_BM_space), _DX) // MOVQ _BM_space, DX
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' '
|
||||||
|
self.Sjmp("JA" , "_decode_fast") // JA _decode_fast
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX
|
||||||
|
self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast
|
||||||
|
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
|
||||||
|
|
||||||
|
/* at least 1 to 3 spaces */
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
|
||||||
|
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
|
||||||
|
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' '
|
||||||
|
self.Sjmp("JA" , "_decode_fast") // JA _decode_fast
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX
|
||||||
|
self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast
|
||||||
|
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
|
||||||
|
}
|
||||||
|
|
||||||
|
/* at least 4 spaces */
|
||||||
|
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
|
||||||
|
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
|
||||||
|
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
|
||||||
|
|
||||||
|
/* fast path: use lookup table to select decoder */
|
||||||
|
self.Link("_decode_fast") // _decode_fast:
|
||||||
|
self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI
|
||||||
|
self.Sref("_decode_tab", 4) // .... &_decode_tab
|
||||||
|
self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, 0), _AX) // MOVLQSX (DI)(AX*4), AX
|
||||||
|
self.Emit("TESTQ" , _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JZ" , "_decode_native") // JZ _decode_native
|
||||||
|
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
|
||||||
|
self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX
|
||||||
|
self.Rjmp("JMP" , _AX) // JMP AX
|
||||||
|
|
||||||
|
/* decode with native decoder */
|
||||||
|
self.Link("_decode_native") // _decode_native:
|
||||||
|
self.Emit("MOVQ", _IP, _DI) // MOVQ IP, DI
|
||||||
|
self.Emit("MOVQ", _IL, _SI) // MOVQ IL, SI
|
||||||
|
self.Emit("MOVQ", _IC, _DX) // MOVQ IC, DX
|
||||||
|
self.Emit("LEAQ", _VAR_ss, _CX) // LEAQ ss, CX
|
||||||
|
self.Emit("MOVQ", _VAR_df, _R8) // MOVQ $df, R8
|
||||||
|
self.Emit("BTSQ", jit.Imm(_F_allow_control), _R8) // ANDQ $1<<_F_allow_control, R8
|
||||||
|
self.call(_F_value) // CALL value
|
||||||
|
self.Emit("MOVQ", _AX, _IC) // MOVQ AX, IC
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Vt, _AX) // MOVQ ss.Vt, AX
|
||||||
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JS" , "_parsing_error")
|
||||||
|
self.Sjmp("JZ" , "_invalid_vtype") // JZ _invalid_vtype
|
||||||
|
self.Emit("CMPQ" , _AX, _V_max) // CMPQ AX, _V_max
|
||||||
|
self.Sjmp("JA" , "_invalid_vtype") // JA _invalid_vtype
|
||||||
|
|
||||||
|
/* jump table selector */
|
||||||
|
self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI
|
||||||
|
self.Sref("_switch_table", 4) // .... &_switch_table
|
||||||
|
self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, -4), _AX) // MOVLQSX -4(DI)(AX*4), AX
|
||||||
|
self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX
|
||||||
|
self.Rjmp("JMP" , _AX) // JMP AX
|
||||||
|
|
||||||
|
/** V_EOF **/
|
||||||
|
self.Link("_decode_V_EOF") // _decode_V_EOF:
|
||||||
|
self.Emit("MOVL", _E_eof, _EP) // MOVL _E_eof, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
|
||||||
|
/** V_NULL **/
|
||||||
|
self.Link("_decode_V_NULL") // _decode_V_NULL:
|
||||||
|
self.Emit("XORL", _R8, _R8) // XORL R8, R8
|
||||||
|
self.Emit("XORL", _R9, _R9) // XORL R9, R9
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_TRUE **/
|
||||||
|
self.Link("_decode_V_TRUE") // _decode_V_TRUE:
|
||||||
|
self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8
|
||||||
|
// TODO: maybe modified by users?
|
||||||
|
self.Emit("MOVQ", _V_true, _R9) // MOVQ _V_true, R9
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_FALSE **/
|
||||||
|
self.Link("_decode_V_FALSE") // _decode_V_FALSE:
|
||||||
|
self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8
|
||||||
|
self.Emit("MOVQ", _V_false, _R9) // MOVQ _V_false, R9
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_IC, -5), _DI) // LEAQ -5(IC), DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_ARRAY **/
|
||||||
|
self.Link("_decode_V_ARRAY") // _decode_V_ARRAY
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
|
||||||
|
self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char
|
||||||
|
|
||||||
|
/* create a new array */
|
||||||
|
self.Emit("MOVQ", _T_eface, _AX) // MOVQ _T_eface, AX
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_len), jit.Ptr(_SP, 8)) // MOVQ _A_init_len, 8(SP)
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_cap), jit.Ptr(_SP, 16)) // MOVQ _A_init_cap, 16(SP)
|
||||||
|
self.call_go(_F_makeslice) // CALL_GO runtime.makeslice
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 24), _DX) // MOVQ 24(SP), DX
|
||||||
|
|
||||||
|
/* pack into an interface */
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SP, 0)) // MOVQ DX, (SP)
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_len), jit.Ptr(_SP, 8)) // MOVQ _A_init_len, 8(SP)
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_cap), jit.Ptr(_SP, 16)) // MOVQ _A_init_cap, 16(SP)
|
||||||
|
self.call_go(_F_convTslice) // CALL_GO runtime.convTslice
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 24), _R8) // MOVQ 24(SP), R8
|
||||||
|
|
||||||
|
/* replace current state with an array */
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_arr), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr, ST.Vt[CX]
|
||||||
|
self.Emit("MOVQ", _T_slice, _AX) // MOVQ _T_slice, AX
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SI, 0)) // MOVQ AX, (SI)
|
||||||
|
self.WriteRecNotAX(2, _R8, jit.Ptr(_SI, 8), false) // MOVQ R8, 8(SI)
|
||||||
|
|
||||||
|
/* add a new slot for the first element */
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
|
||||||
|
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
|
||||||
|
self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_R8, 0), _AX) // MOVQ (R8), AX
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.WritePtrAX(3, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_arr_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr_0, ST.Vt[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_OBJECT **/
|
||||||
|
self.Link("_decode_V_OBJECT") // _decode_V_OBJECT:
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
|
||||||
|
self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char
|
||||||
|
self.call_go(_F_makemap_small) // CALL_GO runtime.makemap_small
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 0), _AX) // MOVQ (SP), AX
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_obj_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj, ST.Vt[CX]
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", _T_map, _DX) // MOVQ _T_map, DX
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 0)) // MOVQ DX, (SI)
|
||||||
|
self.WritePtrAX(4, jit.Ptr(_SI, 8), false) // MOVQ AX, 8(SI)
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_STRING **/
|
||||||
|
self.Link("_decode_V_STRING") // _decode_V_STRING:
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||||
|
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
||||||
|
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
||||||
|
|
||||||
|
/* check for escapes */
|
||||||
|
self.Emit("CMPQ", _VAR_ss_Ep, jit.Imm(-1)) // CMPQ ss.Ep, $-1
|
||||||
|
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX
|
||||||
|
self.Emit("LEAQ", jit.Sib(_IP, _CX, 1, 0), _R8) // LEAQ (IP)(CX), R8
|
||||||
|
self.Byte(0x48, 0x8d, 0x3d) // LEAQ (PC), DI
|
||||||
|
self.Sref("_copy_string_end", 4)
|
||||||
|
self.Emit("BTQ", jit.Imm(_F_copy_string), _VAR_df)
|
||||||
|
self.Sjmp("JC", "copy_string")
|
||||||
|
self.Link("_copy_string_end")
|
||||||
|
self.Emit("XORL", _DX, _DX) // XORL DX, DX
|
||||||
|
/* strings with no escape sequences */
|
||||||
|
self.Link("_noescape") // _noescape:
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_omask_key), _DI) // MOVL _S_omask, DI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _SI) // MOVQ ST.Vt[CX], SI
|
||||||
|
self.Emit("BTQ" , _SI, _DI) // BTQ SI, DI
|
||||||
|
self.Sjmp("JC" , "_object_key") // JC _object_key
|
||||||
|
|
||||||
|
/* check for pre-packed strings, avoid 1 allocation */
|
||||||
|
self.Emit("TESTQ", _DX, _DX) // TESTQ DX, DX
|
||||||
|
self.Sjmp("JNZ" , "_packed_str") // JNZ _packed_str
|
||||||
|
self.Emit("MOVQ" , _R8, jit.Ptr(_SP, 0)) // MOVQ R8, (SP)
|
||||||
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 8)) // MOVQ AX, 8(SP)
|
||||||
|
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SP, 16), _R9) // MOVQ 16(SP), R9
|
||||||
|
|
||||||
|
/* packed string already in R9 */
|
||||||
|
self.Link("_packed_str") // _packed_str:
|
||||||
|
self.Emit("MOVQ", _T_string, _R8) // MOVQ _T_string, R8
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Iv, _DI) // MOVQ ss.Iv, DI
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), _DI) // SUBQ $1, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/* the string is an object key, get the map */
|
||||||
|
self.Link("_object_key")
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
|
||||||
|
/* add a new delimiter */
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
|
||||||
|
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
|
||||||
|
self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_obj_delim), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj_delim, ST.Vt[CX]
|
||||||
|
|
||||||
|
/* add a new slot int the map */
|
||||||
|
self.Emit("MOVQ", _T_map, _DX) // MOVQ _T_map, DX
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SP, 0)) // MOVQ DX, (SP)
|
||||||
|
self.Emit("MOVQ", _SI, jit.Ptr(_SP, 8)) // MOVQ SI, 8(SP)
|
||||||
|
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 16)) // MOVQ R9, 16(SP)
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 24)) // MOVQ AX, 24(SP)
|
||||||
|
self.call_go(_F_mapassign_faststr) // CALL_GO runtime.mapassign_faststr
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 32), _AX) // MOVQ 32(SP), AX
|
||||||
|
|
||||||
|
/* add to the pointer stack */
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.WritePtrAX(6, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* allocate memory to store the string header and unquoted result */
|
||||||
|
self.Link("_unquote") // _unquote:
|
||||||
|
self.Emit("ADDQ", jit.Imm(15), _AX) // ADDQ $15, AX
|
||||||
|
self.Emit("MOVQ", _T_byte, _CX) // MOVQ _T_byte, CX
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP)
|
||||||
|
self.Emit("MOVB", jit.Imm(0), jit.Ptr(_SP, 16)) // MOVB $0, 16(SP)
|
||||||
|
self.call_go(_F_mallocgc) // CALL_GO runtime.mallocgc
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 24), _R9) // MOVQ 24(SP), R9
|
||||||
|
|
||||||
|
/* prepare the unquoting parameters */
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||||
|
self.Emit("LEAQ" , jit.Sib(_IP, _CX, 1, 0), _DI) // LEAQ (IP)(CX), DI
|
||||||
|
self.Emit("NEGQ" , _CX) // NEGQ CX
|
||||||
|
self.Emit("LEAQ" , jit.Sib(_IC, _CX, 1, -1), _SI) // LEAQ -1(IC)(CX), SI
|
||||||
|
self.Emit("LEAQ" , jit.Ptr(_R9, 16), _DX) // LEAQ 16(R8), DX
|
||||||
|
self.Emit("LEAQ" , _VAR_ss_Ep, _CX) // LEAQ ss.Ep, CX
|
||||||
|
self.Emit("XORL" , _R8, _R8) // XORL R8, R8
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _VAR_df) // BTQ ${_F_disable_urc}, fv
|
||||||
|
self.Emit("SETCC", _R8) // SETCC R8
|
||||||
|
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8
|
||||||
|
|
||||||
|
/* unquote the string, with R9 been preserved */
|
||||||
|
self.save(_R9) // SAVE R9
|
||||||
|
self.call(_F_unquote) // CALL unquote
|
||||||
|
self.load(_R9) // LOAD R9
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JS" , "_unquote_error") // JS _unquote_error
|
||||||
|
self.Emit("MOVL" , jit.Imm(1), _DX) // MOVL $1, DX
|
||||||
|
self.Emit("LEAQ" , jit.Ptr(_R9, 16), _R8) // ADDQ $16, R8
|
||||||
|
self.Emit("MOVQ" , _R8, jit.Ptr(_R9, 0)) // MOVQ R8, (R9)
|
||||||
|
self.Emit("MOVQ" , _AX, jit.Ptr(_R9, 8)) // MOVQ AX, 8(R9)
|
||||||
|
self.Sjmp("JMP" , "_noescape") // JMP _noescape
|
||||||
|
|
||||||
|
/** V_DOUBLE **/
|
||||||
|
self.Link("_decode_V_DOUBLE") // _decode_V_DOUBLE:
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
|
||||||
|
self.Sjmp("JC" , "_use_number") // JC _use_number
|
||||||
|
self.Emit("MOVSD", _VAR_ss_Dv, _X0) // MOVSD ss.Dv, X0
|
||||||
|
self.Sjmp("JMP" , "_use_float64") // JMP _use_float64
|
||||||
|
|
||||||
|
/** V_INTEGER **/
|
||||||
|
self.Link("_decode_V_INTEGER") // _decode_V_INTEGER:
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
|
||||||
|
self.Sjmp("JC" , "_use_number") // JC _use_number
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_use_int64), _VAR_df) // BTQ _F_use_int64, df
|
||||||
|
self.Sjmp("JC" , "_use_int64") // JC _use_int64
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Iv, _AX) // MOVQ ss.Iv, AX
|
||||||
|
self.Emit("CVTSQ2SD", _AX, _X0) // CVTSQ2SD AX, X0
|
||||||
|
|
||||||
|
/* represent numbers as `float64` */
|
||||||
|
self.Link("_use_float64") // _use_float64:
|
||||||
|
self.Emit("MOVSD", _X0, jit.Ptr(_SP, 0)) // MOVSD X0, (SP)
|
||||||
|
self.call_go(_F_convT64) // CALL_GO runtime.convT64
|
||||||
|
self.Emit("MOVQ" , _T_float64, _R8) // MOVQ _T_float64, R8
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SP, 8), _R9) // MOVQ 8(SP), R9
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/* represent numbers as `json.Number` */
|
||||||
|
self.Link("_use_number") // _use_number
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Ep, _AX) // MOVQ ss.Ep, AX
|
||||||
|
self.Emit("LEAQ", jit.Sib(_IP, _AX, 1, 0), _SI) // LEAQ (IP)(AX), SI
|
||||||
|
self.Emit("MOVQ", _IC, _CX) // MOVQ IC, CX
|
||||||
|
self.Emit("SUBQ", _AX, _CX) // SUBQ AX, CX
|
||||||
|
self.Emit("MOVQ", _SI, jit.Ptr(_SP, 0)) // MOVQ SI, (SP)
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP)
|
||||||
|
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
|
||||||
|
self.Emit("MOVQ", _T_number, _R8) // MOVQ _T_number, R8
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 16), _R9) // MOVQ 16(SP), R9
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/* represent numbers as `int64` */
|
||||||
|
self.Link("_use_int64") // _use_int64:
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Iv, _AX) // MOVQ ss.Iv, AX
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
||||||
|
self.call_go(_F_convT64) // CALL_GO runtime.convT64
|
||||||
|
self.Emit("MOVQ", _T_int64, _R8) // MOVQ _T_int64, R8
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 8), _R9) // MOVQ 8(SP), R9
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_KEY_SEP **/
|
||||||
|
self.Link("_decode_V_KEY_SEP") // _decode_V_KEY_SEP:
|
||||||
|
// self.Byte(0xcc)
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("CMPQ", _AX, jit.Imm(_S_obj_delim)) // CMPQ AX, _S_obj_delim
|
||||||
|
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_obj), jit.Sib(_ST, _CX, 8, _ST_Vt - 8)) // MOVQ _S_obj, ST.Vt[CX - 1]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_ELEM_SEP **/
|
||||||
|
self.Link("_decode_V_ELEM_SEP") // _decode_V_ELEM_SEP:
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(_S_arr)) // CMPQ _AX, _S_arr
|
||||||
|
self.Sjmp("JE" , "_array_sep") // JZ _next
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(_S_obj)) // CMPQ _AX, _S_arr
|
||||||
|
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("MOVQ" , jit.Imm(_S_obj_sep), jit.Sib(_ST, _CX, 8, _ST_Vt))
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* arrays */
|
||||||
|
self.Link("_array_sep")
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _DX) // MOVQ 8(SI), DX
|
||||||
|
self.Emit("CMPQ", _DX, jit.Ptr(_SI, 16)) // CMPQ DX, 16(SI)
|
||||||
|
self.Sjmp("JAE" , "_array_more") // JAE _array_more
|
||||||
|
|
||||||
|
/* add a slot for the new element */
|
||||||
|
self.Link("_array_append") // _array_append:
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), jit.Ptr(_SI, 8)) // ADDQ $1, 8(SI)
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 0), _SI) // MOVQ (SI), SI
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
|
||||||
|
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
|
||||||
|
self.Sjmp("JAE" , "_stack_overflow")
|
||||||
|
self.Emit("SHLQ", jit.Imm(1), _DX) // SHLQ $1, DX
|
||||||
|
self.Emit("LEAQ", jit.Sib(_SI, _DX, 8, 0), _SI) // LEAQ (SI)(DX*8), SI
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.WriteRecNotAX(7 , _SI, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ SI, ST.Vp[CX]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX}
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_ARRAY_END **/
|
||||||
|
self.Link("_decode_V_ARRAY_END") // _decode_V_ARRAY_END:
|
||||||
|
self.Emit("XORL", _DX, _DX) // XORL DX, DX
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("CMPQ", _AX, jit.Imm(_S_arr_0)) // CMPQ AX, _S_arr_0
|
||||||
|
self.Sjmp("JE" , "_first_item") // JE _first_item
|
||||||
|
self.Emit("CMPQ", _AX, jit.Imm(_S_arr)) // CMPQ AX, _S_arr
|
||||||
|
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
|
||||||
|
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* first element of an array */
|
||||||
|
self.Link("_first_item") // _first_item:
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("SUBQ", jit.Imm(2), jit.Ptr(_ST, _ST_Sp)) // SUBQ $2, ST.Sp
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp - 8), _SI) // MOVQ ST.Vp[CX - 1], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp - 8)) // MOVQ DX, ST.Vp[CX - 1]
|
||||||
|
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX]
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI)
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_OBJECT_END **/
|
||||||
|
self.Link("_decode_V_OBJECT_END") // _decode_V_OBJECT_END:
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_omask_end), _DX) // MOVL _S_omask, DI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DX)
|
||||||
|
self.Sjmp("JNC" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("XORL", _AX, _AX) // XORL AX, AX
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
|
||||||
|
self.Emit("MOVQ", _AX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ AX, ST.Vp[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* return from decoder */
|
||||||
|
self.Link("_return") // _return:
|
||||||
|
self.Emit("XORL", _EP, _EP) // XORL EP, EP
|
||||||
|
self.Emit("MOVQ", _EP, jit.Ptr(_ST, _ST_Vp)) // MOVQ EP, ST.Vp[0]
|
||||||
|
self.Link("_epilogue") // _epilogue:
|
||||||
|
self.Emit("SUBQ", jit.Imm(_FsmOffset), _ST) // SUBQ _FsmOffset, _ST
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, _VD_offs), _BP) // MOVQ _VD_offs(SP), BP
|
||||||
|
self.Emit("ADDQ", jit.Imm(_VD_size), _SP) // ADDQ $_VD_size, SP
|
||||||
|
self.Emit("RET") // RET
|
||||||
|
|
||||||
|
/* array expand */
|
||||||
|
self.Link("_array_more") // _array_more:
|
||||||
|
self.Emit("MOVQ" , _T_eface, _AX) // MOVQ _T_eface, AX
|
||||||
|
self.Emit("MOVOU", jit.Ptr(_SI, 0), _X0) // MOVOU (SI), X0
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SI, 16), _DX) // MOVQ 16(SI), DX
|
||||||
|
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
||||||
|
self.Emit("MOVOU", _X0, jit.Ptr(_SP, 8)) // MOVOU X0, 8(SP)
|
||||||
|
self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 24)) // MOVQ DX, 24(SP)
|
||||||
|
self.Emit("SHLQ" , jit.Imm(1), _DX) // SHLQ $1, DX
|
||||||
|
self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 32)) // MOVQ DX, 32(SP)
|
||||||
|
self.call_go(_F_growslice) // CALL_GO runtime.growslice
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _DI) // MOVOU 40(SP), DI
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _DX) // MOVOU 48(SP), DX
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SP, 56), _AX) // MOVQ 56(SP), AX
|
||||||
|
|
||||||
|
/* update the slice */
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI)
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SI, 16)) // MOVQ AX, 16(AX)
|
||||||
|
self.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI)
|
||||||
|
self.Sjmp("JMP" , "_array_append") // JMP _array_append
|
||||||
|
|
||||||
|
/* copy string */
|
||||||
|
self.Link("copy_string") // pointer: R8, length: AX, return addr: DI
|
||||||
|
// self.Byte(0xcc)
|
||||||
|
self.Emit("MOVQ", _R8, _VAR_cs_p)
|
||||||
|
self.Emit("MOVQ", _AX, _VAR_cs_n)
|
||||||
|
self.Emit("MOVQ", _DI, _VAR_cs_LR)
|
||||||
|
self.Emit("MOVQ", _T_byte, jit.Ptr(_SP, 0))
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 8))
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
|
||||||
|
self.call_go(_F_makeslice)
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, 24), _R8)
|
||||||
|
self.Emit("MOVQ", _R8, _VAR_cs_d)
|
||||||
|
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 0))
|
||||||
|
self.Emit("MOVQ", _VAR_cs_p, _R8)
|
||||||
|
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 8))
|
||||||
|
self.Emit("MOVQ", _VAR_cs_n, _AX)
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
|
||||||
|
self.call_go(_F_memmove)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_d, _R8)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_n, _AX)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_LR, _DI)
|
||||||
|
// self.Byte(0xcc)
|
||||||
|
self.Rjmp("JMP", _DI)
|
||||||
|
|
||||||
|
/* error handlers */
|
||||||
|
self.Link("_stack_overflow")
|
||||||
|
self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
self.Link("_vtype_error") // _vtype_error:
|
||||||
|
self.Emit("MOVQ" , _DI, _IC) // MOVQ DI, IC
|
||||||
|
self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
self.Link("_invalid_char") // _invalid_char:
|
||||||
|
self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC
|
||||||
|
self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
self.Link("_unquote_error") // _unquote_error:
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Iv, _IC) // MOVQ ss.Iv, IC
|
||||||
|
self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC
|
||||||
|
self.Link("_parsing_error") // _parsing_error:
|
||||||
|
self.Emit("NEGQ" , _AX) // NEGQ AX
|
||||||
|
self.Emit("MOVQ" , _AX, _EP) // MOVQ AX, EP
|
||||||
|
self.Link("_error") // _error:
|
||||||
|
self.Emit("PXOR" , _X0, _X0) // PXOR X0, X0
|
||||||
|
self.Emit("MOVOU", _X0, jit.Ptr(_VP, 0)) // MOVOU X0, (VP)
|
||||||
|
self.Sjmp("JMP" , "_epilogue") // JMP _epilogue
|
||||||
|
|
||||||
|
/* invalid value type, never returns */
|
||||||
|
self.Link("_invalid_vtype")
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
|
||||||
|
self.call(_F_invalid_vtype) // CALL invalid_type
|
||||||
|
self.Emit("UD2") // UD2
|
||||||
|
|
||||||
|
/* switch jump table */
|
||||||
|
self.Link("_switch_table") // _switch_table:
|
||||||
|
self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0
|
||||||
|
self.Sref("_decode_V_NULL", -4) // SREF &_decode_V_NULL, $-4
|
||||||
|
self.Sref("_decode_V_TRUE", -8) // SREF &_decode_V_TRUE, $-8
|
||||||
|
self.Sref("_decode_V_FALSE", -12) // SREF &_decode_V_FALSE, $-12
|
||||||
|
self.Sref("_decode_V_ARRAY", -16) // SREF &_decode_V_ARRAY, $-16
|
||||||
|
self.Sref("_decode_V_OBJECT", -20) // SREF &_decode_V_OBJECT, $-20
|
||||||
|
self.Sref("_decode_V_STRING", -24) // SREF &_decode_V_STRING, $-24
|
||||||
|
self.Sref("_decode_V_DOUBLE", -28) // SREF &_decode_V_DOUBLE, $-28
|
||||||
|
self.Sref("_decode_V_INTEGER", -32) // SREF &_decode_V_INTEGER, $-32
|
||||||
|
self.Sref("_decode_V_KEY_SEP", -36) // SREF &_decode_V_KEY_SEP, $-36
|
||||||
|
self.Sref("_decode_V_ELEM_SEP", -40) // SREF &_decode_V_ELEM_SEP, $-40
|
||||||
|
self.Sref("_decode_V_ARRAY_END", -44) // SREF &_decode_V_ARRAY_END, $-44
|
||||||
|
self.Sref("_decode_V_OBJECT_END", -48) // SREF &_decode_V_OBJECT_END, $-48
|
||||||
|
|
||||||
|
/* fast character lookup table */
|
||||||
|
self.Link("_decode_tab") // _decode_tab:
|
||||||
|
self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0
|
||||||
|
|
||||||
|
/* generate rest of the tabs */
|
||||||
|
for i := 1; i < 256; i++ {
|
||||||
|
if to, ok := _R_tab[i]; ok {
|
||||||
|
self.Sref(to, -int64(i) * 4)
|
||||||
|
} else {
|
||||||
|
self.Byte(0x00, 0x00, 0x00, 0x00)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
|
||||||
|
self.Emit("MOVQ", _V_writeBarrier, _R10)
|
||||||
|
self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
|
||||||
|
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
if saveDI {
|
||||||
|
self.save(_DI)
|
||||||
|
}
|
||||||
|
self.Emit("LEAQ", rec, _DI)
|
||||||
|
self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10) // MOVQ ${fn}, AX
|
||||||
|
self.Rjmp("CALL", _R10)
|
||||||
|
if saveDI {
|
||||||
|
self.load(_DI)
|
||||||
|
}
|
||||||
|
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Emit("MOVQ", _AX, rec)
|
||||||
|
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) WriteRecNotAX(i int, ptr obj.Addr, rec obj.Addr, saveDI bool) {
|
||||||
|
if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
|
||||||
|
panic("rec contains AX!")
|
||||||
|
}
|
||||||
|
self.Emit("MOVQ", _V_writeBarrier, _R10)
|
||||||
|
self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
|
||||||
|
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Emit("MOVQ", ptr, _AX)
|
||||||
|
if saveDI {
|
||||||
|
self.save(_DI)
|
||||||
|
}
|
||||||
|
self.Emit("LEAQ", rec, _DI)
|
||||||
|
self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10) // MOVQ ${fn}, AX
|
||||||
|
self.Rjmp("CALL", _R10)
|
||||||
|
if saveDI {
|
||||||
|
self.load(_DI)
|
||||||
|
}
|
||||||
|
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Emit("MOVQ", ptr, rec)
|
||||||
|
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generic Decoder **/
|
||||||
|
|
||||||
|
var (
|
||||||
|
_subr_decode_value = new(_ValueDecoder).build()
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func invalid_vtype(vt types.ValueType) {
|
||||||
|
throw(fmt.Sprintf("invalid value type: %d", vt))
|
||||||
|
}
|
|
@ -0,0 +1,772 @@
|
||||||
|
//go:build go1.17 && !go1.21
|
||||||
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`fmt`
|
||||||
|
`reflect`
|
||||||
|
`strconv`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj/x86`
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Crucial Registers:
|
||||||
|
*
|
||||||
|
* ST(R13) && 0(SP) : ro, decoder stack
|
||||||
|
* DF(AX) : ro, decoder flags
|
||||||
|
* EP(BX) : wo, error pointer
|
||||||
|
* IP(R10) : ro, input pointer
|
||||||
|
* IL(R12) : ro, input length
|
||||||
|
* IC(R11) : rw, input cursor
|
||||||
|
* VP(R15) : ro, value pointer (to an interface{})
|
||||||
|
*/
|
||||||
|
|
||||||
|
const (
|
||||||
|
_VD_args = 8 // 8 bytes for passing arguments to this functions
|
||||||
|
_VD_fargs = 64 // 64 bytes for passing arguments to other Go functions
|
||||||
|
_VD_saves = 48 // 48 bytes for saving the registers before CALL instructions
|
||||||
|
_VD_locals = 96 // 96 bytes for local variables
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_VD_offs = _VD_fargs + _VD_saves + _VD_locals
|
||||||
|
_VD_size = _VD_offs + 8 // 8 bytes for the parent frame pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_VAR_ss = _VAR_ss_Vt
|
||||||
|
_VAR_df = jit.Ptr(_SP, _VD_fargs + _VD_saves)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_VAR_ss_Vt = jit.Ptr(_SP, _VD_fargs + _VD_saves + 8)
|
||||||
|
_VAR_ss_Dv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 16)
|
||||||
|
_VAR_ss_Iv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 24)
|
||||||
|
_VAR_ss_Ep = jit.Ptr(_SP, _VD_fargs + _VD_saves + 32)
|
||||||
|
_VAR_ss_Db = jit.Ptr(_SP, _VD_fargs + _VD_saves + 40)
|
||||||
|
_VAR_ss_Dc = jit.Ptr(_SP, _VD_fargs + _VD_saves + 48)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_VAR_R9 = jit.Ptr(_SP, _VD_fargs + _VD_saves + 56)
|
||||||
|
)
|
||||||
|
type _ValueDecoder struct {
|
||||||
|
jit.BaseAssembler
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64)
|
||||||
|
_VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72)
|
||||||
|
_VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80)
|
||||||
|
_VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 88)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) build() uintptr {
|
||||||
|
self.Init(self.compile)
|
||||||
|
return *(*uintptr)(self.Load("decode_value", _VD_size, _VD_args, argPtrs_generic, localPtrs_generic))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Function Calling Helpers **/
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) save(r ...obj.Addr) {
|
||||||
|
for i, v := range r {
|
||||||
|
if i > _VD_saves / 8 - 1 {
|
||||||
|
panic("too many registers to save")
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", v, jit.Ptr(_SP, _VD_fargs + int64(i) * 8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) load(r ...obj.Addr) {
|
||||||
|
for i, v := range r {
|
||||||
|
if i > _VD_saves / 8 - 1 {
|
||||||
|
panic("too many registers to load")
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, _VD_fargs + int64(i) * 8), v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) call(fn obj.Addr) {
|
||||||
|
self.Emit("MOVQ", fn, _R9) // MOVQ ${fn}, AX
|
||||||
|
self.Rjmp("CALL", _R9) // CALL AX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) call_go(fn obj.Addr) {
|
||||||
|
self.save(_REG_go...) // SAVE $REG_go
|
||||||
|
self.call(fn) // CALL ${fn}
|
||||||
|
self.load(_REG_go...) // LOAD $REG_go
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) callc(fn obj.Addr) {
|
||||||
|
self.Emit("XCHGQ", _IP, _BP)
|
||||||
|
self.call(fn)
|
||||||
|
self.Emit("XCHGQ", _IP, _BP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) call_c(fn obj.Addr) {
|
||||||
|
self.Emit("XCHGQ", _IC, _BX)
|
||||||
|
self.callc(fn)
|
||||||
|
self.Emit("XCHGQ", _IC, _BX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decoder Assembler **/
|
||||||
|
|
||||||
|
const (
|
||||||
|
_S_val = iota + 1
|
||||||
|
_S_arr
|
||||||
|
_S_arr_0
|
||||||
|
_S_obj
|
||||||
|
_S_obj_0
|
||||||
|
_S_obj_delim
|
||||||
|
_S_obj_sep
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_S_omask_key = (1 << _S_obj_0) | (1 << _S_obj_sep)
|
||||||
|
_S_omask_end = (1 << _S_obj_0) | (1 << _S_obj)
|
||||||
|
_S_vmask = (1 << _S_val) | (1 << _S_arr_0)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_A_init_len = 1
|
||||||
|
_A_init_cap = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ST_Sp = 0
|
||||||
|
_ST_Vt = _PtrBytes
|
||||||
|
_ST_Vp = _PtrBytes * (types.MAX_RECURSE + 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_V_true = jit.Imm(int64(pbool(true)))
|
||||||
|
_V_false = jit.Imm(int64(pbool(false)))
|
||||||
|
_F_value = jit.Imm(int64(native.S_value))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_V_max = jit.Imm(int64(types.V_MAX))
|
||||||
|
_E_eof = jit.Imm(int64(types.ERR_EOF))
|
||||||
|
_E_invalid = jit.Imm(int64(types.ERR_INVALID_CHAR))
|
||||||
|
_E_recurse = jit.Imm(int64(types.ERR_RECURSE_EXCEED_MAX))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_F_convTslice = jit.Func(convTslice)
|
||||||
|
_F_convTstring = jit.Func(convTstring)
|
||||||
|
_F_invalid_vtype = jit.Func(invalid_vtype)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_T_map = jit.Type(reflect.TypeOf((map[string]interface{})(nil)))
|
||||||
|
_T_bool = jit.Type(reflect.TypeOf(false))
|
||||||
|
_T_int64 = jit.Type(reflect.TypeOf(int64(0)))
|
||||||
|
_T_eface = jit.Type(reflect.TypeOf((*interface{})(nil)).Elem())
|
||||||
|
_T_slice = jit.Type(reflect.TypeOf(([]interface{})(nil)))
|
||||||
|
_T_string = jit.Type(reflect.TypeOf(""))
|
||||||
|
_T_number = jit.Type(reflect.TypeOf(json.Number("")))
|
||||||
|
_T_float64 = jit.Type(reflect.TypeOf(float64(0)))
|
||||||
|
)
|
||||||
|
|
||||||
|
var _R_tab = map[int]string {
|
||||||
|
'[': "_decode_V_ARRAY",
|
||||||
|
'{': "_decode_V_OBJECT",
|
||||||
|
':': "_decode_V_KEY_SEP",
|
||||||
|
',': "_decode_V_ELEM_SEP",
|
||||||
|
']': "_decode_V_ARRAY_END",
|
||||||
|
'}': "_decode_V_OBJECT_END",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) compile() {
|
||||||
|
self.Emit("SUBQ", jit.Imm(_VD_size), _SP) // SUBQ $_VD_size, SP
|
||||||
|
self.Emit("MOVQ", _BP, jit.Ptr(_SP, _VD_offs)) // MOVQ BP, _VD_offs(SP)
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_SP, _VD_offs), _BP) // LEAQ _VD_offs(SP), BP
|
||||||
|
|
||||||
|
/* initialize the state machine */
|
||||||
|
self.Emit("XORL", _CX, _CX) // XORL CX, CX
|
||||||
|
self.Emit("MOVQ", _DF, _VAR_df) // MOVQ DF, df
|
||||||
|
/* initialize digital buffer first */
|
||||||
|
self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_ss_Dc) // MOVQ $_MaxDigitNums, ss.Dcap
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_ST, _DbufOffset), _AX) // LEAQ _DbufOffset(ST), AX
|
||||||
|
self.Emit("MOVQ", _AX, _VAR_ss_Db) // MOVQ AX, ss.Dbuf
|
||||||
|
/* add ST offset */
|
||||||
|
self.Emit("ADDQ", jit.Imm(_FsmOffset), _ST) // ADDQ _FsmOffset, _ST
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.WriteRecNotAX(0, _VP, jit.Ptr(_ST, _ST_Vp), false) // MOVQ VP, ST.Vp[0]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_val), jit.Ptr(_ST, _ST_Vt)) // MOVQ _S_val, ST.Vt[0]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* set the value from previous round */
|
||||||
|
self.Link("_set_value") // _set_value:
|
||||||
|
self.Emit("MOVL" , jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
|
||||||
|
self.Sjmp("JNC" , "_vtype_error") // JNC _vtype_error
|
||||||
|
self.Emit("XORL" , _SI, _SI) // XORL SI, SI
|
||||||
|
self.Emit("SUBQ" , jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
|
||||||
|
self.Emit("XCHGQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // XCHGQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ" , _R8, jit.Ptr(_SI, 0)) // MOVQ R8, (SI)
|
||||||
|
self.WriteRecNotAX(1, _R9, jit.Ptr(_SI, 8), false) // MOVQ R9, 8(SI)
|
||||||
|
|
||||||
|
/* check for value stack */
|
||||||
|
self.Link("_next") // _next:
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _AX) // MOVQ ST.Sp, AX
|
||||||
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JS" , "_return") // JS _return
|
||||||
|
|
||||||
|
/* fast path: test up to 4 characters manually */
|
||||||
|
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
|
||||||
|
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
|
||||||
|
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
|
||||||
|
self.Emit("MOVQ" , jit.Imm(_BM_space), _DX) // MOVQ _BM_space, DX
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' '
|
||||||
|
self.Sjmp("JA" , "_decode_fast") // JA _decode_fast
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX
|
||||||
|
self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast
|
||||||
|
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
|
||||||
|
|
||||||
|
/* at least 1 to 3 spaces */
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
|
||||||
|
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
|
||||||
|
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' '
|
||||||
|
self.Sjmp("JA" , "_decode_fast") // JA _decode_fast
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX
|
||||||
|
self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast
|
||||||
|
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
|
||||||
|
}
|
||||||
|
|
||||||
|
/* at least 4 spaces */
|
||||||
|
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
|
||||||
|
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
|
||||||
|
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
|
||||||
|
|
||||||
|
/* fast path: use lookup table to select decoder */
|
||||||
|
self.Link("_decode_fast") // _decode_fast:
|
||||||
|
self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI
|
||||||
|
self.Sref("_decode_tab", 4) // .... &_decode_tab
|
||||||
|
self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, 0), _AX) // MOVLQSX (DI)(AX*4), AX
|
||||||
|
self.Emit("TESTQ" , _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JZ" , "_decode_native") // JZ _decode_native
|
||||||
|
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
|
||||||
|
self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX
|
||||||
|
self.Rjmp("JMP" , _AX) // JMP AX
|
||||||
|
|
||||||
|
/* decode with native decoder */
|
||||||
|
self.Link("_decode_native") // _decode_native:
|
||||||
|
self.Emit("MOVQ", _IP, _DI) // MOVQ IP, DI
|
||||||
|
self.Emit("MOVQ", _IL, _SI) // MOVQ IL, SI
|
||||||
|
self.Emit("MOVQ", _IC, _DX) // MOVQ IC, DX
|
||||||
|
self.Emit("LEAQ", _VAR_ss, _CX) // LEAQ ss, CX
|
||||||
|
self.Emit("MOVQ", _VAR_df, _R8) // MOVQ $df, R8
|
||||||
|
self.Emit("BTSQ", jit.Imm(_F_allow_control), _R8) // ANDQ $1<<_F_allow_control, R8
|
||||||
|
self.callc(_F_value) // CALL value
|
||||||
|
self.Emit("MOVQ", _AX, _IC) // MOVQ AX, IC
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Vt, _AX) // MOVQ ss.Vt, AX
|
||||||
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JS" , "_parsing_error")
|
||||||
|
self.Sjmp("JZ" , "_invalid_vtype") // JZ _invalid_vtype
|
||||||
|
self.Emit("CMPQ" , _AX, _V_max) // CMPQ AX, _V_max
|
||||||
|
self.Sjmp("JA" , "_invalid_vtype") // JA _invalid_vtype
|
||||||
|
|
||||||
|
/* jump table selector */
|
||||||
|
self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI
|
||||||
|
self.Sref("_switch_table", 4) // .... &_switch_table
|
||||||
|
self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, -4), _AX) // MOVLQSX -4(DI)(AX*4), AX
|
||||||
|
self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX
|
||||||
|
self.Rjmp("JMP" , _AX) // JMP AX
|
||||||
|
|
||||||
|
/** V_EOF **/
|
||||||
|
self.Link("_decode_V_EOF") // _decode_V_EOF:
|
||||||
|
self.Emit("MOVL", _E_eof, _EP) // MOVL _E_eof, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
|
||||||
|
/** V_NULL **/
|
||||||
|
self.Link("_decode_V_NULL") // _decode_V_NULL:
|
||||||
|
self.Emit("XORL", _R8, _R8) // XORL R8, R8
|
||||||
|
self.Emit("XORL", _R9, _R9) // XORL R9, R9
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_TRUE **/
|
||||||
|
self.Link("_decode_V_TRUE") // _decode_V_TRUE:
|
||||||
|
self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8
|
||||||
|
// TODO: maybe modified by users?
|
||||||
|
self.Emit("MOVQ", _V_true, _R9) // MOVQ _V_true, R9
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_FALSE **/
|
||||||
|
self.Link("_decode_V_FALSE") // _decode_V_FALSE:
|
||||||
|
self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8
|
||||||
|
self.Emit("MOVQ", _V_false, _R9) // MOVQ _V_false, R9
|
||||||
|
self.Emit("LEAQ", jit.Ptr(_IC, -5), _DI) // LEAQ -5(IC), DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_ARRAY **/
|
||||||
|
self.Link("_decode_V_ARRAY") // _decode_V_ARRAY
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
|
||||||
|
self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char
|
||||||
|
|
||||||
|
/* create a new array */
|
||||||
|
self.Emit("MOVQ", _T_eface, _AX) // MOVQ _T_eface, AX
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_len), _BX) // MOVQ _A_init_len, BX
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_cap), _CX) // MOVQ _A_init_cap, CX
|
||||||
|
self.call_go(_F_makeslice) // CALL_GO runtime.makeslice
|
||||||
|
|
||||||
|
/* pack into an interface */
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_len), _BX) // MOVQ _A_init_len, BX
|
||||||
|
self.Emit("MOVQ", jit.Imm(_A_init_cap), _CX) // MOVQ _A_init_cap, CX
|
||||||
|
self.call_go(_F_convTslice) // CALL_GO runtime.convTslice
|
||||||
|
self.Emit("MOVQ", _AX, _R8) // MOVQ AX, R8
|
||||||
|
|
||||||
|
/* replace current state with an array */
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_arr), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr, ST.Vt[CX]
|
||||||
|
self.Emit("MOVQ", _T_slice, _AX) // MOVQ _T_slice, AX
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SI, 0)) // MOVQ AX, (SI)
|
||||||
|
self.WriteRecNotAX(2, _R8, jit.Ptr(_SI, 8), false) // MOVQ R8, 8(SI)
|
||||||
|
|
||||||
|
/* add a new slot for the first element */
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
|
||||||
|
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
|
||||||
|
self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_R8, 0), _AX) // MOVQ (R8), AX
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.WritePtrAX(3, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_arr_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr_0, ST.Vt[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_OBJECT **/
|
||||||
|
self.Link("_decode_V_OBJECT") // _decode_V_OBJECT:
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
|
||||||
|
self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char
|
||||||
|
self.call_go(_F_makemap_small) // CALL_GO runtime.makemap_small
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_obj_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj_0, ST.Vt[CX]
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", _T_map, _DX) // MOVQ _T_map, DX
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 0)) // MOVQ DX, (SI)
|
||||||
|
self.WritePtrAX(4, jit.Ptr(_SI, 8), false) // MOVQ AX, 8(SI)
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_STRING **/
|
||||||
|
self.Link("_decode_V_STRING") // _decode_V_STRING:
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||||
|
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
||||||
|
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
|
||||||
|
|
||||||
|
/* check for escapes */
|
||||||
|
self.Emit("CMPQ", _VAR_ss_Ep, jit.Imm(-1)) // CMPQ ss.Ep, $-1
|
||||||
|
self.Sjmp("JNE" , "_unquote") // JNE _unquote
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX
|
||||||
|
self.Emit("LEAQ", jit.Sib(_IP, _CX, 1, 0), _R8) // LEAQ (IP)(CX), R8
|
||||||
|
self.Byte(0x48, 0x8d, 0x3d) // LEAQ (PC), DI
|
||||||
|
self.Sref("_copy_string_end", 4)
|
||||||
|
self.Emit("BTQ", jit.Imm(_F_copy_string), _VAR_df)
|
||||||
|
self.Sjmp("JC", "copy_string")
|
||||||
|
self.Link("_copy_string_end")
|
||||||
|
self.Emit("XORL", _DX, _DX)
|
||||||
|
|
||||||
|
/* strings with no escape sequences */
|
||||||
|
self.Link("_noescape") // _noescape:
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_omask_key), _DI) // MOVL _S_omask, DI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _SI) // MOVQ ST.Vt[CX], SI
|
||||||
|
self.Emit("BTQ" , _SI, _DI) // BTQ SI, DI
|
||||||
|
self.Sjmp("JC" , "_object_key") // JC _object_key
|
||||||
|
|
||||||
|
/* check for pre-packed strings, avoid 1 allocation */
|
||||||
|
self.Emit("TESTQ", _DX, _DX) // TESTQ DX, DX
|
||||||
|
self.Sjmp("JNZ" , "_packed_str") // JNZ _packed_str
|
||||||
|
self.Emit("MOVQ" , _AX, _BX) // MOVQ AX, BX
|
||||||
|
self.Emit("MOVQ" , _R8, _AX) // MOVQ R8, AX
|
||||||
|
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
|
||||||
|
self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, R9
|
||||||
|
|
||||||
|
/* packed string already in R9 */
|
||||||
|
self.Link("_packed_str") // _packed_str:
|
||||||
|
self.Emit("MOVQ", _T_string, _R8) // MOVQ _T_string, R8
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Iv, _DI) // MOVQ ss.Iv, DI
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), _DI) // SUBQ $1, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/* the string is an object key, get the map */
|
||||||
|
self.Link("_object_key")
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
|
||||||
|
/* add a new delimiter */
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
|
||||||
|
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
|
||||||
|
self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_obj_delim), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj_delim, ST.Vt[CX]
|
||||||
|
|
||||||
|
/* add a new slot int the map */
|
||||||
|
self.Emit("MOVQ", _AX, _DI) // MOVQ AX, DI
|
||||||
|
self.Emit("MOVQ", _T_map, _AX) // MOVQ _T_map, AX
|
||||||
|
self.Emit("MOVQ", _SI, _BX) // MOVQ SI, BX
|
||||||
|
self.Emit("MOVQ", _R8, _CX) // MOVQ R9, CX
|
||||||
|
self.call_go(_F_mapassign_faststr) // CALL_GO runtime.mapassign_faststr
|
||||||
|
|
||||||
|
/* add to the pointer stack */
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.WritePtrAX(6, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* allocate memory to store the string header and unquoted result */
|
||||||
|
self.Link("_unquote") // _unquote:
|
||||||
|
self.Emit("ADDQ", jit.Imm(15), _AX) // ADDQ $15, AX
|
||||||
|
self.Emit("MOVQ", _T_byte, _BX) // MOVQ _T_byte, BX
|
||||||
|
self.Emit("MOVB", jit.Imm(0), _CX) // MOVB $0, CX
|
||||||
|
self.call_go(_F_mallocgc) // CALL_GO runtime.mallocgc
|
||||||
|
self.Emit("MOVQ", _AX, _R9) // MOVQ AX, R9
|
||||||
|
|
||||||
|
/* prepare the unquoting parameters */
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
|
||||||
|
self.Emit("LEAQ" , jit.Sib(_IP, _CX, 1, 0), _DI) // LEAQ (IP)(CX), DI
|
||||||
|
self.Emit("NEGQ" , _CX) // NEGQ CX
|
||||||
|
self.Emit("LEAQ" , jit.Sib(_IC, _CX, 1, -1), _SI) // LEAQ -1(IC)(CX), SI
|
||||||
|
self.Emit("LEAQ" , jit.Ptr(_R9, 16), _DX) // LEAQ 16(R8), DX
|
||||||
|
self.Emit("LEAQ" , _VAR_ss_Ep, _CX) // LEAQ ss.Ep, CX
|
||||||
|
self.Emit("XORL" , _R8, _R8) // XORL R8, R8
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _VAR_df) // BTQ ${_F_disable_urc}, fv
|
||||||
|
self.Emit("SETCC", _R8) // SETCC R8
|
||||||
|
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8
|
||||||
|
|
||||||
|
/* unquote the string, with R9 been preserved */
|
||||||
|
self.Emit("MOVQ", _R9, _VAR_R9) // SAVE R9
|
||||||
|
self.call_c(_F_unquote) // CALL unquote
|
||||||
|
self.Emit("MOVQ", _VAR_R9, _R9) // LOAD R9
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
self.Sjmp("JS" , "_unquote_error") // JS _unquote_error
|
||||||
|
self.Emit("MOVL" , jit.Imm(1), _DX) // MOVL $1, DX
|
||||||
|
self.Emit("LEAQ" , jit.Ptr(_R9, 16), _R8) // ADDQ $16, R8
|
||||||
|
self.Emit("MOVQ" , _R8, jit.Ptr(_R9, 0)) // MOVQ R8, (R9)
|
||||||
|
self.Emit("MOVQ" , _AX, jit.Ptr(_R9, 8)) // MOVQ AX, 8(R9)
|
||||||
|
self.Sjmp("JMP" , "_noescape") // JMP _noescape
|
||||||
|
|
||||||
|
/** V_DOUBLE **/
|
||||||
|
self.Link("_decode_V_DOUBLE") // _decode_V_DOUBLE:
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
|
||||||
|
self.Sjmp("JC" , "_use_number") // JC _use_number
|
||||||
|
self.Emit("MOVSD", _VAR_ss_Dv, _X0) // MOVSD ss.Dv, X0
|
||||||
|
self.Sjmp("JMP" , "_use_float64") // JMP _use_float64
|
||||||
|
|
||||||
|
/** V_INTEGER **/
|
||||||
|
self.Link("_decode_V_INTEGER") // _decode_V_INTEGER:
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
|
||||||
|
self.Sjmp("JC" , "_use_number") // JC _use_number
|
||||||
|
self.Emit("BTQ" , jit.Imm(_F_use_int64), _VAR_df) // BTQ _F_use_int64, df
|
||||||
|
self.Sjmp("JC" , "_use_int64") // JC _use_int64
|
||||||
|
//TODO: use ss.Dv directly
|
||||||
|
self.Emit("MOVSD", _VAR_ss_Dv, _X0) // MOVSD ss.Dv, X0
|
||||||
|
|
||||||
|
/* represent numbers as `float64` */
|
||||||
|
self.Link("_use_float64") // _use_float64:
|
||||||
|
self.Emit("MOVQ" , _X0, _AX) // MOVQ X0, AX
|
||||||
|
self.call_go(_F_convT64) // CALL_GO runtime.convT64
|
||||||
|
self.Emit("MOVQ" , _T_float64, _R8) // MOVQ _T_float64, R8
|
||||||
|
self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, R9
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/* represent numbers as `json.Number` */
|
||||||
|
self.Link("_use_number") // _use_number
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Ep, _AX) // MOVQ ss.Ep, AX
|
||||||
|
self.Emit("LEAQ", jit.Sib(_IP, _AX, 1, 0), _SI) // LEAQ (IP)(AX), SI
|
||||||
|
self.Emit("MOVQ", _IC, _CX) // MOVQ IC, CX
|
||||||
|
self.Emit("SUBQ", _AX, _CX) // SUBQ AX, CX
|
||||||
|
self.Emit("MOVQ", _SI, _AX) // MOVQ SI, AX
|
||||||
|
self.Emit("MOVQ", _CX, _BX) // MOVQ CX, BX
|
||||||
|
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
|
||||||
|
self.Emit("MOVQ", _T_number, _R8) // MOVQ _T_number, R8
|
||||||
|
self.Emit("MOVQ", _AX, _R9) // MOVQ AX, R9
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/* represent numbers as `int64` */
|
||||||
|
self.Link("_use_int64") // _use_int64:
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Iv, _AX) // MOVQ ss.Iv, AX
|
||||||
|
self.call_go(_F_convT64) // CALL_GO runtime.convT64
|
||||||
|
self.Emit("MOVQ", _T_int64, _R8) // MOVQ _T_int64, R8
|
||||||
|
self.Emit("MOVQ", _AX, _R9) // MOVQ AX, R9
|
||||||
|
self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
|
||||||
|
self.Sjmp("JMP" , "_set_value") // JMP _set_value
|
||||||
|
|
||||||
|
/** V_KEY_SEP **/
|
||||||
|
self.Link("_decode_V_KEY_SEP") // _decode_V_KEY_SEP:
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("CMPQ", _AX, jit.Imm(_S_obj_delim)) // CMPQ AX, _S_obj_delim
|
||||||
|
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_obj), jit.Sib(_ST, _CX, 8, _ST_Vt - 8)) // MOVQ _S_obj, ST.Vt[CX - 1]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_ELEM_SEP **/
|
||||||
|
self.Link("_decode_V_ELEM_SEP") // _decode_V_ELEM_SEP:
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(_S_arr))
|
||||||
|
self.Sjmp("JE" , "_array_sep") // JZ _next
|
||||||
|
self.Emit("CMPQ" , _AX, jit.Imm(_S_obj)) // CMPQ _AX, _S_arr
|
||||||
|
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("MOVQ" , jit.Imm(_S_obj_sep), jit.Sib(_ST, _CX, 8, _ST_Vt))
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* arrays */
|
||||||
|
self.Link("_array_sep")
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _DX) // MOVQ 8(SI), DX
|
||||||
|
self.Emit("CMPQ", _DX, jit.Ptr(_SI, 16)) // CMPQ DX, 16(SI)
|
||||||
|
self.Sjmp("JAE" , "_array_more") // JAE _array_more
|
||||||
|
|
||||||
|
/* add a slot for the new element */
|
||||||
|
self.Link("_array_append") // _array_append:
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), jit.Ptr(_SI, 8)) // ADDQ $1, 8(SI)
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 0), _SI) // MOVQ (SI), SI
|
||||||
|
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
|
||||||
|
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
|
||||||
|
self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow
|
||||||
|
self.Emit("SHLQ", jit.Imm(1), _DX) // SHLQ $1, DX
|
||||||
|
self.Emit("LEAQ", jit.Sib(_SI, _DX, 8, 0), _SI) // LEAQ (SI)(DX*8), SI
|
||||||
|
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
|
||||||
|
self.WriteRecNotAX(7 , _SI, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ SI, ST.Vp[CX]
|
||||||
|
self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX}
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_ARRAY_END **/
|
||||||
|
self.Link("_decode_V_ARRAY_END") // _decode_V_ARRAY_END:
|
||||||
|
self.Emit("XORL", _DX, _DX) // XORL DX, DX
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("CMPQ", _AX, jit.Imm(_S_arr_0)) // CMPQ AX, _S_arr_0
|
||||||
|
self.Sjmp("JE" , "_first_item") // JE _first_item
|
||||||
|
self.Emit("CMPQ", _AX, jit.Imm(_S_arr)) // CMPQ AX, _S_arr
|
||||||
|
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
|
||||||
|
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* first element of an array */
|
||||||
|
self.Link("_first_item") // _first_item:
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("SUBQ", jit.Imm(2), jit.Ptr(_ST, _ST_Sp)) // SUBQ $2, ST.Sp
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp - 8), _SI) // MOVQ ST.Vp[CX - 1], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp - 8)) // MOVQ DX, ST.Vp[CX - 1]
|
||||||
|
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX]
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI)
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/** V_OBJECT_END **/
|
||||||
|
self.Link("_decode_V_OBJECT_END") // _decode_V_OBJECT_END:
|
||||||
|
self.Emit("MOVL", jit.Imm(_S_omask_end), _DI) // MOVL _S_omask, DI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
|
||||||
|
self.Emit("BTQ" , _AX, _DI)
|
||||||
|
self.Sjmp("JNC" , "_invalid_char") // JNE _invalid_char
|
||||||
|
self.Emit("XORL", _AX, _AX) // XORL AX, AX
|
||||||
|
self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
|
||||||
|
self.Emit("MOVQ", _AX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ AX, ST.Vp[CX]
|
||||||
|
self.Sjmp("JMP" , "_next") // JMP _next
|
||||||
|
|
||||||
|
/* return from decoder */
|
||||||
|
self.Link("_return") // _return:
|
||||||
|
self.Emit("XORL", _EP, _EP) // XORL EP, EP
|
||||||
|
self.Emit("MOVQ", _EP, jit.Ptr(_ST, _ST_Vp)) // MOVQ EP, ST.Vp[0]
|
||||||
|
self.Link("_epilogue") // _epilogue:
|
||||||
|
self.Emit("SUBQ", jit.Imm(_FsmOffset), _ST) // SUBQ _FsmOffset, _ST
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, _VD_offs), _BP) // MOVQ _VD_offs(SP), BP
|
||||||
|
self.Emit("ADDQ", jit.Imm(_VD_size), _SP) // ADDQ $_VD_size, SP
|
||||||
|
self.Emit("RET") // RET
|
||||||
|
|
||||||
|
/* array expand */
|
||||||
|
self.Link("_array_more") // _array_more:
|
||||||
|
self.Emit("MOVQ" , _T_eface, _AX) // MOVQ _T_eface, AX
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SI, 0), _BX) // MOVQ (SI), BX
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SI, 8), _CX) // MOVQ 8(SI), CX
|
||||||
|
self.Emit("MOVQ" , jit.Ptr(_SI, 16), _DI) // MOVQ 16(SI), DI
|
||||||
|
self.Emit("MOVQ" , _DI, _SI) // MOVQ DI, 24(SP)
|
||||||
|
self.Emit("SHLQ" , jit.Imm(1), _SI) // SHLQ $1, SI
|
||||||
|
self.call_go(_F_growslice) // CALL_GO runtime.growslice
|
||||||
|
self.Emit("MOVQ" , _AX, _DI) // MOVQ AX, DI
|
||||||
|
self.Emit("MOVQ" , _BX, _DX) // MOVQ BX, DX
|
||||||
|
self.Emit("MOVQ" , _CX, _AX) // MOVQ CX, AX
|
||||||
|
|
||||||
|
/* update the slice */
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
|
||||||
|
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
|
||||||
|
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI)
|
||||||
|
self.Emit("MOVQ", _AX, jit.Ptr(_SI, 16)) // MOVQ AX, 16(AX)
|
||||||
|
self.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI)
|
||||||
|
self.Sjmp("JMP" , "_array_append") // JMP _array_append
|
||||||
|
|
||||||
|
/* copy string */
|
||||||
|
self.Link("copy_string") // pointer: R8, length: AX, return addr: DI
|
||||||
|
self.Emit("MOVQ", _R8, _VAR_cs_p)
|
||||||
|
self.Emit("MOVQ", _AX, _VAR_cs_n)
|
||||||
|
self.Emit("MOVQ", _DI, _VAR_cs_LR)
|
||||||
|
self.Emit("MOVQ", _AX, _BX)
|
||||||
|
self.Emit("MOVQ", _AX, _CX)
|
||||||
|
self.Emit("MOVQ", _T_byte, _AX)
|
||||||
|
self.call_go(_F_makeslice)
|
||||||
|
self.Emit("MOVQ", _AX, _VAR_cs_d)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_p, _BX)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_n, _CX)
|
||||||
|
self.call_go(_F_memmove)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_d, _R8)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_n, _AX)
|
||||||
|
self.Emit("MOVQ", _VAR_cs_LR, _DI)
|
||||||
|
self.Rjmp("JMP", _DI)
|
||||||
|
|
||||||
|
/* error handlers */
|
||||||
|
self.Link("_stack_overflow")
|
||||||
|
self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
self.Link("_vtype_error") // _vtype_error:
|
||||||
|
self.Emit("MOVQ" , _DI, _IC) // MOVQ DI, IC
|
||||||
|
self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
self.Link("_invalid_char") // _invalid_char:
|
||||||
|
self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC
|
||||||
|
self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP
|
||||||
|
self.Sjmp("JMP" , "_error") // JMP _error
|
||||||
|
self.Link("_unquote_error") // _unquote_error:
|
||||||
|
self.Emit("MOVQ" , _VAR_ss_Iv, _IC) // MOVQ ss.Iv, IC
|
||||||
|
self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC
|
||||||
|
self.Link("_parsing_error") // _parsing_error:
|
||||||
|
self.Emit("NEGQ" , _AX) // NEGQ AX
|
||||||
|
self.Emit("MOVQ" , _AX, _EP) // MOVQ AX, EP
|
||||||
|
self.Link("_error") // _error:
|
||||||
|
self.Emit("PXOR" , _X0, _X0) // PXOR X0, X0
|
||||||
|
self.Emit("MOVOU", _X0, jit.Ptr(_VP, 0)) // MOVOU X0, (VP)
|
||||||
|
self.Sjmp("JMP" , "_epilogue") // JMP _epilogue
|
||||||
|
|
||||||
|
/* invalid value type, never returns */
|
||||||
|
self.Link("_invalid_vtype")
|
||||||
|
self.call_go(_F_invalid_vtype) // CALL invalid_type
|
||||||
|
self.Emit("UD2") // UD2
|
||||||
|
|
||||||
|
/* switch jump table */
|
||||||
|
self.Link("_switch_table") // _switch_table:
|
||||||
|
self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0
|
||||||
|
self.Sref("_decode_V_NULL", -4) // SREF &_decode_V_NULL, $-4
|
||||||
|
self.Sref("_decode_V_TRUE", -8) // SREF &_decode_V_TRUE, $-8
|
||||||
|
self.Sref("_decode_V_FALSE", -12) // SREF &_decode_V_FALSE, $-12
|
||||||
|
self.Sref("_decode_V_ARRAY", -16) // SREF &_decode_V_ARRAY, $-16
|
||||||
|
self.Sref("_decode_V_OBJECT", -20) // SREF &_decode_V_OBJECT, $-20
|
||||||
|
self.Sref("_decode_V_STRING", -24) // SREF &_decode_V_STRING, $-24
|
||||||
|
self.Sref("_decode_V_DOUBLE", -28) // SREF &_decode_V_DOUBLE, $-28
|
||||||
|
self.Sref("_decode_V_INTEGER", -32) // SREF &_decode_V_INTEGER, $-32
|
||||||
|
self.Sref("_decode_V_KEY_SEP", -36) // SREF &_decode_V_KEY_SEP, $-36
|
||||||
|
self.Sref("_decode_V_ELEM_SEP", -40) // SREF &_decode_V_ELEM_SEP, $-40
|
||||||
|
self.Sref("_decode_V_ARRAY_END", -44) // SREF &_decode_V_ARRAY_END, $-44
|
||||||
|
self.Sref("_decode_V_OBJECT_END", -48) // SREF &_decode_V_OBJECT_END, $-48
|
||||||
|
|
||||||
|
/* fast character lookup table */
|
||||||
|
self.Link("_decode_tab") // _decode_tab:
|
||||||
|
self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0
|
||||||
|
|
||||||
|
/* generate rest of the tabs */
|
||||||
|
for i := 1; i < 256; i++ {
|
||||||
|
if to, ok := _R_tab[i]; ok {
|
||||||
|
self.Sref(to, -int64(i) * 4)
|
||||||
|
} else {
|
||||||
|
self.Byte(0x00, 0x00, 0x00, 0x00)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
|
||||||
|
self.Emit("MOVQ", _V_writeBarrier, _R9)
|
||||||
|
self.Emit("CMPL", jit.Ptr(_R9, 0), jit.Imm(0))
|
||||||
|
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
if saveDI {
|
||||||
|
self.save(_DI)
|
||||||
|
}
|
||||||
|
self.Emit("LEAQ", rec, _DI)
|
||||||
|
self.call(_F_gcWriteBarrierAX)
|
||||||
|
if saveDI {
|
||||||
|
self.load(_DI)
|
||||||
|
}
|
||||||
|
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Emit("MOVQ", _AX, rec)
|
||||||
|
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ValueDecoder) WriteRecNotAX(i int, ptr obj.Addr, rec obj.Addr, saveDI bool) {
|
||||||
|
if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
|
||||||
|
panic("rec contains AX!")
|
||||||
|
}
|
||||||
|
self.Emit("MOVQ", _V_writeBarrier, _AX)
|
||||||
|
self.Emit("CMPL", jit.Ptr(_AX, 0), jit.Imm(0))
|
||||||
|
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Emit("MOVQ", ptr, _AX)
|
||||||
|
if saveDI {
|
||||||
|
self.save(_DI)
|
||||||
|
}
|
||||||
|
self.Emit("LEAQ", rec, _DI)
|
||||||
|
self.call(_F_gcWriteBarrierAX)
|
||||||
|
if saveDI {
|
||||||
|
self.load(_DI)
|
||||||
|
}
|
||||||
|
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
self.Emit("MOVQ", ptr, rec)
|
||||||
|
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generic Decoder **/
|
||||||
|
|
||||||
|
var (
|
||||||
|
_subr_decode_value = new(_ValueDecoder).build()
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func invalid_vtype(vt types.ValueType) {
|
||||||
|
throw(fmt.Sprintf("invalid value type: %d", vt))
|
||||||
|
}
|
37
vendor/github.com/bytedance/sonic/decoder/generic_amd64_go117_test.s
generated
vendored
Normal file
37
vendor/github.com/bytedance/sonic/decoder/generic_amd64_go117_test.s
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "funcdata.h"
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·decodeValueStub(SB), NOSPLIT, $0 - 72
|
||||||
|
NO_LOCAL_POINTERS
|
||||||
|
PXOR X0, X0
|
||||||
|
MOVOU X0, rv+48(FP)
|
||||||
|
MOVQ st+0(FP) , R13
|
||||||
|
MOVQ sp+8(FP) , R10
|
||||||
|
MOVQ sn+16(FP), R12
|
||||||
|
MOVQ ic+24(FP), R11
|
||||||
|
MOVQ vp+32(FP), R15
|
||||||
|
MOVQ df+40(FP), AX
|
||||||
|
MOVQ ·_subr_decode_value(SB), BX
|
||||||
|
CALL BX
|
||||||
|
MOVQ R11, rp+48(FP)
|
||||||
|
MOVQ BX, ex+56(FP)
|
||||||
|
RET
|
|
@ -0,0 +1,37 @@
|
||||||
|
// +build go1.15,!go1.17
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "funcdata.h"
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·decodeValueStub(SB), NOSPLIT, $0 - 72
|
||||||
|
NO_LOCAL_POINTERS
|
||||||
|
PXOR X0, X0
|
||||||
|
MOVOU X0, rv+48(FP)
|
||||||
|
MOVQ st+0(FP), BX
|
||||||
|
MOVQ sp+8(FP), R12
|
||||||
|
MOVQ sn+16(FP), R13
|
||||||
|
MOVQ ic+24(FP), R14
|
||||||
|
MOVQ vp+32(FP), R15
|
||||||
|
MOVQ df+40(FP), R10
|
||||||
|
MOVQ ·_subr_decode_value(SB), AX
|
||||||
|
CALL AX
|
||||||
|
MOVQ R14, rp+48(FP)
|
||||||
|
MOVQ R11, ex+56(FP)
|
||||||
|
RET
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`sync`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/caching`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_MinSlice = 16
|
||||||
|
_MaxStack = 4096 // 4k slots
|
||||||
|
_MaxStackBytes = _MaxStack * _PtrBytes
|
||||||
|
_MaxDigitNums = 800 // used in atof fallback algorithm
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_PtrBytes = _PTR_SIZE / 8
|
||||||
|
_FsmOffset = (_MaxStack + 1) * _PtrBytes
|
||||||
|
_DbufOffset = _FsmOffset + int64(unsafe.Sizeof(types.StateMachine{})) + types.MAX_RECURSE * _PtrBytes
|
||||||
|
_StackSize = unsafe.Sizeof(_Stack{})
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
stackPool = sync.Pool{}
|
||||||
|
valueCache = []unsafe.Pointer(nil)
|
||||||
|
fieldCache = []*caching.FieldMap(nil)
|
||||||
|
fieldCacheMux = sync.Mutex{}
|
||||||
|
programCache = caching.CreateProgramCache()
|
||||||
|
)
|
||||||
|
|
||||||
|
type _Stack struct {
|
||||||
|
sp uintptr
|
||||||
|
sb [_MaxStack]unsafe.Pointer
|
||||||
|
mm types.StateMachine
|
||||||
|
vp [types.MAX_RECURSE]unsafe.Pointer
|
||||||
|
dp [_MaxDigitNums]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type _Decoder func(
|
||||||
|
s string,
|
||||||
|
i int,
|
||||||
|
vp unsafe.Pointer,
|
||||||
|
sb *_Stack,
|
||||||
|
fv uint64,
|
||||||
|
sv string, // DO NOT pass value to this arguement, since it is only used for local _VAR_sv
|
||||||
|
vk unsafe.Pointer, // DO NOT pass value to this arguement, since it is only used for local _VAR_vk
|
||||||
|
) (int, error)
|
||||||
|
|
||||||
|
var _KeepAlive struct {
|
||||||
|
s string
|
||||||
|
i int
|
||||||
|
vp unsafe.Pointer
|
||||||
|
sb *_Stack
|
||||||
|
fv uint64
|
||||||
|
sv string
|
||||||
|
vk unsafe.Pointer
|
||||||
|
|
||||||
|
ret int
|
||||||
|
err error
|
||||||
|
|
||||||
|
frame_decoder [_FP_offs]byte
|
||||||
|
frame_generic [_VD_offs]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
argPtrs = []bool{true, false, false, true, true, false, true, false, true}
|
||||||
|
localPtrs = []bool{}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
argPtrs_generic = []bool{true}
|
||||||
|
localPtrs_generic = []bool{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newStack() *_Stack {
|
||||||
|
if ret := stackPool.Get(); ret == nil {
|
||||||
|
return new(_Stack)
|
||||||
|
} else {
|
||||||
|
return ret.(*_Stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetStack(p *_Stack) {
|
||||||
|
memclrNoHeapPointers(unsafe.Pointer(p), _StackSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeStack(p *_Stack) {
|
||||||
|
p.sp = 0
|
||||||
|
stackPool.Put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freezeValue(v unsafe.Pointer) uintptr {
|
||||||
|
valueCache = append(valueCache, v)
|
||||||
|
return uintptr(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freezeFields(v *caching.FieldMap) int64 {
|
||||||
|
fieldCacheMux.Lock()
|
||||||
|
fieldCache = append(fieldCache, v)
|
||||||
|
fieldCacheMux.Unlock()
|
||||||
|
return referenceFields(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func referenceFields(v *caching.FieldMap) int64 {
|
||||||
|
return int64(uintptr(unsafe.Pointer(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeDecoder(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
|
||||||
|
if pp, err := newCompiler().compile(vt.Pack()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
return newAssembler(pp).Load(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findOrCompile(vt *rt.GoType) (_Decoder, error) {
|
||||||
|
if val := programCache.Get(vt); val != nil {
|
||||||
|
return val.(_Decoder), nil
|
||||||
|
} else if ret, err := programCache.Compute(vt, makeDecoder); err == nil {
|
||||||
|
return ret.(_Decoder), nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding`
|
||||||
|
`encoding/json`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
func decodeTypedPointer(s string, i int, vt *rt.GoType, vp unsafe.Pointer, sb *_Stack, fv uint64) (int, error) {
|
||||||
|
if fn, err := findOrCompile(vt); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
rt.MoreStack(_FP_size + _VD_size + native.MaxFrameSize)
|
||||||
|
rt.StopProf()
|
||||||
|
ret, err := fn(s, i, vp, sb, fv, "", nil)
|
||||||
|
rt.StartProf()
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeJsonUnmarshaler(vv interface{}, s string) error {
|
||||||
|
return vv.(json.Unmarshaler).UnmarshalJSON(rt.Str2Mem(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTextUnmarshaler(vv interface{}, s string) error {
|
||||||
|
return vv.(encoding.TextUnmarshaler).UnmarshalText(rt.Str2Mem(s))
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`io`
|
||||||
|
`sync`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultBufferSize uint = 4096
|
||||||
|
growSliceFactorShift uint = 1
|
||||||
|
minLeftBufferShift uint = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamDecoder struct {
|
||||||
|
r io.Reader
|
||||||
|
buf []byte
|
||||||
|
scanp int
|
||||||
|
scanned int64
|
||||||
|
err error
|
||||||
|
Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func () interface{} {
|
||||||
|
return make([]byte, 0, defaultBufferSize)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStreamDecoder adapts to encoding/json.NewDecoder API.
|
||||||
|
//
|
||||||
|
// NewStreamDecoder returns a new decoder that reads from r.
|
||||||
|
func NewStreamDecoder(r io.Reader) *StreamDecoder {
|
||||||
|
return &StreamDecoder{r : r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes input stream into val with corresponding data.
|
||||||
|
// Redundantly bytes may be read and left in its buffer, and can be used at next call.
|
||||||
|
// Either io error from underlying io.Reader (except io.EOF)
|
||||||
|
// or syntax error from data will be recorded and stop subsequently decoding.
|
||||||
|
func (self *StreamDecoder) Decode(val interface{}) (err error) {
|
||||||
|
if self.err != nil {
|
||||||
|
return self.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf = self.buf[self.scanp:]
|
||||||
|
var p = 0
|
||||||
|
var recycle bool
|
||||||
|
if cap(buf) == 0 {
|
||||||
|
buf = bufPool.Get().([]byte)
|
||||||
|
recycle = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var first = true
|
||||||
|
var repeat = true
|
||||||
|
read_more:
|
||||||
|
for {
|
||||||
|
l := len(buf)
|
||||||
|
realloc(&buf)
|
||||||
|
n, err := self.r.Read(buf[l:cap(buf)])
|
||||||
|
buf = buf[:l+n]
|
||||||
|
if err != nil {
|
||||||
|
repeat = false
|
||||||
|
if err == io.EOF {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
self.err = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n > 0 || first {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
|
||||||
|
l := len(buf)
|
||||||
|
if l > 0 {
|
||||||
|
self.Decoder.Reset(string(buf))
|
||||||
|
err = self.Decoder.Decode(val)
|
||||||
|
if err != nil {
|
||||||
|
if repeat && self.repeatable(err) {
|
||||||
|
goto read_more
|
||||||
|
}
|
||||||
|
self.err = err
|
||||||
|
}
|
||||||
|
|
||||||
|
p = self.Decoder.Pos()
|
||||||
|
self.scanned += int64(p)
|
||||||
|
self.scanp = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if l > p {
|
||||||
|
// remain undecoded bytes, so copy them into self.buf
|
||||||
|
self.buf = append(self.buf[:0], buf[p:]...)
|
||||||
|
} else {
|
||||||
|
self.buf = nil
|
||||||
|
recycle = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if recycle {
|
||||||
|
buf = buf[:0]
|
||||||
|
bufPool.Put(buf)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self StreamDecoder) repeatable(err error) bool {
|
||||||
|
if ee, ok := err.(SyntaxError); ok &&
|
||||||
|
(ee.Code == types.ERR_EOF || (ee.Code == types.ERR_INVALID_CHAR && self.i >= len(self.s)-1)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputOffset returns the input stream byte offset of the current decoder position.
|
||||||
|
// The offset gives the location of the end of the most recently returned token and the beginning of the next token.
|
||||||
|
func (self *StreamDecoder) InputOffset() int64 {
|
||||||
|
return self.scanned + int64(self.scanp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffered returns a reader of the data remaining in the Decoder's buffer.
|
||||||
|
// The reader is valid until the next call to Decode.
|
||||||
|
func (self *StreamDecoder) Buffered() io.Reader {
|
||||||
|
return bytes.NewReader(self.buf[self.scanp:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// More reports whether there is another element in the
|
||||||
|
// current array or object being parsed.
|
||||||
|
func (self *StreamDecoder) More() bool {
|
||||||
|
if self.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
c, err := self.peek()
|
||||||
|
return err == nil && c != ']' && c != '}'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StreamDecoder) peek() (byte, error) {
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
for i := self.scanp; i < len(self.buf); i++ {
|
||||||
|
c := self.buf[i]
|
||||||
|
if isSpace(c) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
self.scanp = i
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
// buffer has been scanned, now report any error
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
self.err = err
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = self.refill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpace(c byte) bool {
|
||||||
|
return types.SPACE_MASK & (1 << c) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StreamDecoder) refill() error {
|
||||||
|
// Make room to read more into the buffer.
|
||||||
|
// First slide down data already consumed.
|
||||||
|
if self.scanp > 0 {
|
||||||
|
self.scanned += int64(self.scanp)
|
||||||
|
n := copy(self.buf, self.buf[self.scanp:])
|
||||||
|
self.buf = self.buf[:n]
|
||||||
|
self.scanp = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow buffer if not large enough.
|
||||||
|
realloc(&self.buf)
|
||||||
|
|
||||||
|
// Read. Delay error for next iteration (after scan).
|
||||||
|
n, err := self.r.Read(self.buf[len(self.buf):cap(self.buf)])
|
||||||
|
self.buf = self.buf[0 : len(self.buf)+n]
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func realloc(buf *[]byte) {
|
||||||
|
l := uint(len(*buf))
|
||||||
|
c := uint(cap(*buf))
|
||||||
|
if c - l <= c >> minLeftBufferShift {
|
||||||
|
e := l+(l>>minLeftBufferShift)
|
||||||
|
if e < defaultBufferSize {
|
||||||
|
e = defaultBufferSize
|
||||||
|
}
|
||||||
|
tmp := make([]byte, l, e)
|
||||||
|
copy(tmp, *buf)
|
||||||
|
*buf = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
// +build go1.15,!go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
`reflect`
|
||||||
|
|
||||||
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname _subr__b64decode github.com/chenzhuoyu/base64x._subr__b64decode
|
||||||
|
var _subr__b64decode uintptr
|
||||||
|
|
||||||
|
// runtime.maxElementSize
|
||||||
|
const _max_map_element_size uintptr = 128
|
||||||
|
|
||||||
|
func mapfast(vt reflect.Type) bool {
|
||||||
|
return vt.Elem().Size() <= _max_map_element_size
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:linkname throw runtime.throw
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func throw(s string)
|
||||||
|
|
||||||
|
//go:linkname convT64 runtime.convT64
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convT64(v uint64) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname convTslice runtime.convTslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convTslice(v []byte) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname convTstring runtime.convTstring
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convTstring(v string) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memequal runtime.memequal
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname mallocgc runtime.mallocgc
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname makeslice runtime.makeslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func makeslice(et *rt.GoType, len int, cap int) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname growslice runtime.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:linkname makemap_small runtime.makemap_small
|
||||||
|
func makemap_small() unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign runtime.mapassign
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast32 runtime.mapassign_fast32
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast32(t *rt.GoType, h unsafe.Pointer, k uint32) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast64 runtime.mapassign_fast64
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast64(t *rt.GoType, h unsafe.Pointer, k uint64) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast64ptr(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_faststr runtime.mapassign_faststr
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_faststr(t *rt.GoType, h unsafe.Pointer, s string) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:linkname memclrHasPointers runtime.memclrHasPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrHasPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
|
@ -0,0 +1,111 @@
|
||||||
|
// +build go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
`reflect`
|
||||||
|
|
||||||
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname _subr__b64decode github.com/chenzhuoyu/base64x._subr__b64decode
|
||||||
|
var _subr__b64decode uintptr
|
||||||
|
|
||||||
|
// runtime.maxElementSize
|
||||||
|
const _max_map_element_size uintptr = 128
|
||||||
|
|
||||||
|
func mapfast(vt reflect.Type) bool {
|
||||||
|
return vt.Elem().Size() <= _max_map_element_size
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:linkname throw runtime.throw
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func throw(s string)
|
||||||
|
|
||||||
|
//go:linkname convT64 runtime.convT64
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convT64(v uint64) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname convTslice runtime.convTslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convTslice(v []byte) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname convTstring runtime.convTstring
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convTstring(v string) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memequal runtime.memequal
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname mallocgc runtime.mallocgc
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname makeslice runtime.makeslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func makeslice(et *rt.GoType, len int, cap int) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname growslice reflect.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:linkname makemap_small runtime.makemap_small
|
||||||
|
func makemap_small() unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign runtime.mapassign
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast32 runtime.mapassign_fast32
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast32(t *rt.GoType, h unsafe.Pointer, k uint32) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast64 runtime.mapassign_fast64
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast64(t *rt.GoType, h unsafe.Pointer, k uint64) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast64ptr(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_faststr runtime.mapassign_faststr
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_faststr(t *rt.GoType, h unsafe.Pointer, s string) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:linkname memclrHasPointers runtime.memclrHasPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrHasPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding`
|
||||||
|
`encoding/base64`
|
||||||
|
`encoding/json`
|
||||||
|
`reflect`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
byteType = reflect.TypeOf(byte(0))
|
||||||
|
intType = reflect.TypeOf(int(0))
|
||||||
|
int8Type = reflect.TypeOf(int8(0))
|
||||||
|
int16Type = reflect.TypeOf(int16(0))
|
||||||
|
int32Type = reflect.TypeOf(int32(0))
|
||||||
|
int64Type = reflect.TypeOf(int64(0))
|
||||||
|
uintType = reflect.TypeOf(uint(0))
|
||||||
|
uint8Type = reflect.TypeOf(uint8(0))
|
||||||
|
uint16Type = reflect.TypeOf(uint16(0))
|
||||||
|
uint32Type = reflect.TypeOf(uint32(0))
|
||||||
|
uint64Type = reflect.TypeOf(uint64(0))
|
||||||
|
float32Type = reflect.TypeOf(float32(0))
|
||||||
|
float64Type = reflect.TypeOf(float64(0))
|
||||||
|
stringType = reflect.TypeOf("")
|
||||||
|
bytesType = reflect.TypeOf([]byte(nil))
|
||||||
|
jsonNumberType = reflect.TypeOf(json.Number(""))
|
||||||
|
base64CorruptInputError = reflect.TypeOf(base64.CorruptInputError(0))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
jsonUnmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||||
|
encodingTextUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
func rtype(t reflect.Type) (*rt.GoItab, *rt.GoType) {
|
||||||
|
p := (*rt.GoIface)(unsafe.Pointer(&t))
|
||||||
|
return p.Itab, (*rt.GoType)(p.Value)
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/loader`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func pbool(v bool) uintptr {
|
||||||
|
return freezeValue(unsafe.Pointer(&v))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func ptodec(p loader.Function) _Decoder {
|
||||||
|
return *(*_Decoder)(unsafe.Pointer(&p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func assert_eq(v int64, exp int64, msg string) {
|
||||||
|
if v != exp {
|
||||||
|
panic(msg)
|
||||||
|
}
|
||||||
|
}
|
1198
vendor/github.com/bytedance/sonic/encoder/assembler_amd64_go116.go
generated
vendored
Normal file
1198
vendor/github.com/bytedance/sonic/encoder/assembler_amd64_go116.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1201
vendor/github.com/bytedance/sonic/encoder/assembler_amd64_go117.go
generated
vendored
Normal file
1201
vendor/github.com/bytedance/sonic/encoder/assembler_amd64_go117.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,885 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`reflect`
|
||||||
|
`strconv`
|
||||||
|
`strings`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/resolver`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
`github.com/bytedance/sonic/option`
|
||||||
|
)
|
||||||
|
|
||||||
|
type _Op uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
_OP_null _Op = iota + 1
|
||||||
|
_OP_empty_arr
|
||||||
|
_OP_empty_obj
|
||||||
|
_OP_bool
|
||||||
|
_OP_i8
|
||||||
|
_OP_i16
|
||||||
|
_OP_i32
|
||||||
|
_OP_i64
|
||||||
|
_OP_u8
|
||||||
|
_OP_u16
|
||||||
|
_OP_u32
|
||||||
|
_OP_u64
|
||||||
|
_OP_f32
|
||||||
|
_OP_f64
|
||||||
|
_OP_str
|
||||||
|
_OP_bin
|
||||||
|
_OP_quote
|
||||||
|
_OP_number
|
||||||
|
_OP_eface
|
||||||
|
_OP_iface
|
||||||
|
_OP_byte
|
||||||
|
_OP_text
|
||||||
|
_OP_deref
|
||||||
|
_OP_index
|
||||||
|
_OP_load
|
||||||
|
_OP_save
|
||||||
|
_OP_drop
|
||||||
|
_OP_drop_2
|
||||||
|
_OP_recurse
|
||||||
|
_OP_is_nil
|
||||||
|
_OP_is_nil_p1
|
||||||
|
_OP_is_zero_1
|
||||||
|
_OP_is_zero_2
|
||||||
|
_OP_is_zero_4
|
||||||
|
_OP_is_zero_8
|
||||||
|
_OP_is_zero_map
|
||||||
|
_OP_goto
|
||||||
|
_OP_map_iter
|
||||||
|
_OP_map_stop
|
||||||
|
_OP_map_check_key
|
||||||
|
_OP_map_write_key
|
||||||
|
_OP_map_value_next
|
||||||
|
_OP_slice_len
|
||||||
|
_OP_slice_next
|
||||||
|
_OP_marshal
|
||||||
|
_OP_marshal_p
|
||||||
|
_OP_marshal_text
|
||||||
|
_OP_marshal_text_p
|
||||||
|
_OP_cond_set
|
||||||
|
_OP_cond_testc
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_INT_SIZE = 32 << (^uint(0) >> 63)
|
||||||
|
_PTR_SIZE = 32 << (^uintptr(0) >> 63)
|
||||||
|
_PTR_BYTE = unsafe.Sizeof(uintptr(0))
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
|
||||||
|
_MAX_FIELDS = 50 // cutoff at 50 fields struct
|
||||||
|
)
|
||||||
|
|
||||||
|
var _OpNames = [256]string {
|
||||||
|
_OP_null : "null",
|
||||||
|
_OP_empty_arr : "empty_arr",
|
||||||
|
_OP_empty_obj : "empty_obj",
|
||||||
|
_OP_bool : "bool",
|
||||||
|
_OP_i8 : "i8",
|
||||||
|
_OP_i16 : "i16",
|
||||||
|
_OP_i32 : "i32",
|
||||||
|
_OP_i64 : "i64",
|
||||||
|
_OP_u8 : "u8",
|
||||||
|
_OP_u16 : "u16",
|
||||||
|
_OP_u32 : "u32",
|
||||||
|
_OP_u64 : "u64",
|
||||||
|
_OP_f32 : "f32",
|
||||||
|
_OP_f64 : "f64",
|
||||||
|
_OP_str : "str",
|
||||||
|
_OP_bin : "bin",
|
||||||
|
_OP_quote : "quote",
|
||||||
|
_OP_number : "number",
|
||||||
|
_OP_eface : "eface",
|
||||||
|
_OP_iface : "iface",
|
||||||
|
_OP_byte : "byte",
|
||||||
|
_OP_text : "text",
|
||||||
|
_OP_deref : "deref",
|
||||||
|
_OP_index : "index",
|
||||||
|
_OP_load : "load",
|
||||||
|
_OP_save : "save",
|
||||||
|
_OP_drop : "drop",
|
||||||
|
_OP_drop_2 : "drop_2",
|
||||||
|
_OP_recurse : "recurse",
|
||||||
|
_OP_is_nil : "is_nil",
|
||||||
|
_OP_is_nil_p1 : "is_nil_p1",
|
||||||
|
_OP_is_zero_1 : "is_zero_1",
|
||||||
|
_OP_is_zero_2 : "is_zero_2",
|
||||||
|
_OP_is_zero_4 : "is_zero_4",
|
||||||
|
_OP_is_zero_8 : "is_zero_8",
|
||||||
|
_OP_is_zero_map : "is_zero_map",
|
||||||
|
_OP_goto : "goto",
|
||||||
|
_OP_map_iter : "map_iter",
|
||||||
|
_OP_map_stop : "map_stop",
|
||||||
|
_OP_map_check_key : "map_check_key",
|
||||||
|
_OP_map_write_key : "map_write_key",
|
||||||
|
_OP_map_value_next : "map_value_next",
|
||||||
|
_OP_slice_len : "slice_len",
|
||||||
|
_OP_slice_next : "slice_next",
|
||||||
|
_OP_marshal : "marshal",
|
||||||
|
_OP_marshal_p : "marshal_p",
|
||||||
|
_OP_marshal_text : "marshal_text",
|
||||||
|
_OP_marshal_text_p : "marshal_text_p",
|
||||||
|
_OP_cond_set : "cond_set",
|
||||||
|
_OP_cond_testc : "cond_testc",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Op) String() string {
|
||||||
|
if ret := _OpNames[self]; ret != "" {
|
||||||
|
return ret
|
||||||
|
} else {
|
||||||
|
return "<invalid>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _OP_int() _Op {
|
||||||
|
switch _INT_SIZE {
|
||||||
|
case 32: return _OP_i32
|
||||||
|
case 64: return _OP_i64
|
||||||
|
default: panic("unsupported int size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _OP_uint() _Op {
|
||||||
|
switch _INT_SIZE {
|
||||||
|
case 32: return _OP_u32
|
||||||
|
case 64: return _OP_u64
|
||||||
|
default: panic("unsupported uint size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _OP_uintptr() _Op {
|
||||||
|
switch _PTR_SIZE {
|
||||||
|
case 32: return _OP_u32
|
||||||
|
case 64: return _OP_u64
|
||||||
|
default: panic("unsupported pointer size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _OP_is_zero_ints() _Op {
|
||||||
|
switch _INT_SIZE {
|
||||||
|
case 32: return _OP_is_zero_4
|
||||||
|
case 64: return _OP_is_zero_8
|
||||||
|
default: panic("unsupported integer size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _Instr struct {
|
||||||
|
u uint64 // union {op: 8, _: 8, vi: 48}, vi maybe int or len(str)
|
||||||
|
p unsafe.Pointer // maybe GoString.Ptr, or *GoType
|
||||||
|
}
|
||||||
|
|
||||||
|
func packOp(op _Op) uint64 {
|
||||||
|
return uint64(op) << 56
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInsOp(op _Op) _Instr {
|
||||||
|
return _Instr{u: packOp(op)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInsVi(op _Op, vi int) _Instr {
|
||||||
|
return _Instr{u: packOp(op) | rt.PackInt(vi)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInsVs(op _Op, vs string) _Instr {
|
||||||
|
return _Instr {
|
||||||
|
u: packOp(op) | rt.PackInt(len(vs)),
|
||||||
|
p: (*rt.GoString)(unsafe.Pointer(&vs)).Ptr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInsVt(op _Op, vt reflect.Type) _Instr {
|
||||||
|
return _Instr {
|
||||||
|
u: packOp(op),
|
||||||
|
p: unsafe.Pointer(rt.UnpackType(vt)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInsVp(op _Op, vt reflect.Type, pv bool) _Instr {
|
||||||
|
i := 0
|
||||||
|
if pv {
|
||||||
|
i = 1
|
||||||
|
}
|
||||||
|
return _Instr {
|
||||||
|
u: packOp(op) | rt.PackInt(i),
|
||||||
|
p: unsafe.Pointer(rt.UnpackType(vt)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) op() _Op {
|
||||||
|
return _Op(self.u >> 56)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) vi() int {
|
||||||
|
return rt.UnpackInt(self.u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) vf() uint8 {
|
||||||
|
return (*rt.GoType)(self.p).KindFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) vs() (v string) {
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&v)).Ptr = self.p
|
||||||
|
(*rt.GoString)(unsafe.Pointer(&v)).Len = self.vi()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) vk() reflect.Kind {
|
||||||
|
return (*rt.GoType)(self.p).Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) vt() reflect.Type {
|
||||||
|
return (*rt.GoType)(self.p).Pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) vp() (vt reflect.Type, pv bool) {
|
||||||
|
return (*rt.GoType)(self.p).Pack(), rt.UnpackInt(self.u) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) i64() int64 {
|
||||||
|
return int64(self.vi())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) vlen() int {
|
||||||
|
return int((*rt.GoType)(self.p).Size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) isBranch() bool {
|
||||||
|
switch self.op() {
|
||||||
|
case _OP_goto : fallthrough
|
||||||
|
case _OP_is_nil : fallthrough
|
||||||
|
case _OP_is_nil_p1 : fallthrough
|
||||||
|
case _OP_is_zero_1 : fallthrough
|
||||||
|
case _OP_is_zero_2 : fallthrough
|
||||||
|
case _OP_is_zero_4 : fallthrough
|
||||||
|
case _OP_is_zero_8 : fallthrough
|
||||||
|
case _OP_map_check_key : fallthrough
|
||||||
|
case _OP_map_write_key : fallthrough
|
||||||
|
case _OP_slice_next : fallthrough
|
||||||
|
case _OP_cond_testc : return true
|
||||||
|
default : return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Instr) disassemble() string {
|
||||||
|
switch self.op() {
|
||||||
|
case _OP_byte : return fmt.Sprintf("%-18s%s", self.op().String(), strconv.QuoteRune(rune(self.vi())))
|
||||||
|
case _OP_text : return fmt.Sprintf("%-18s%s", self.op().String(), strconv.Quote(self.vs()))
|
||||||
|
case _OP_index : return fmt.Sprintf("%-18s%d", self.op().String(), self.vi())
|
||||||
|
case _OP_recurse : fallthrough
|
||||||
|
case _OP_map_iter : fallthrough
|
||||||
|
case _OP_marshal : fallthrough
|
||||||
|
case _OP_marshal_p : fallthrough
|
||||||
|
case _OP_marshal_text : fallthrough
|
||||||
|
case _OP_marshal_text_p : return fmt.Sprintf("%-18s%s", self.op().String(), self.vt())
|
||||||
|
case _OP_goto : fallthrough
|
||||||
|
case _OP_is_nil : fallthrough
|
||||||
|
case _OP_is_nil_p1 : fallthrough
|
||||||
|
case _OP_is_zero_1 : fallthrough
|
||||||
|
case _OP_is_zero_2 : fallthrough
|
||||||
|
case _OP_is_zero_4 : fallthrough
|
||||||
|
case _OP_is_zero_8 : fallthrough
|
||||||
|
case _OP_is_zero_map : fallthrough
|
||||||
|
case _OP_cond_testc : fallthrough
|
||||||
|
case _OP_map_check_key : fallthrough
|
||||||
|
case _OP_map_write_key : return fmt.Sprintf("%-18sL_%d", self.op().String(), self.vi())
|
||||||
|
case _OP_slice_next : return fmt.Sprintf("%-18sL_%d, %s", self.op().String(), self.vi(), self.vt())
|
||||||
|
default : return self.op().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
_Program []_Instr
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self _Program) pc() int {
|
||||||
|
return len(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Program) tag(n int) {
|
||||||
|
if n >= _MaxStack {
|
||||||
|
panic("type nesting too deep")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Program) pin(i int) {
|
||||||
|
v := &self[i]
|
||||||
|
v.u &= 0xffff000000000000
|
||||||
|
v.u |= rt.PackInt(self.pc())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Program) rel(v []int) {
|
||||||
|
for _, i := range v {
|
||||||
|
self.pin(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Program) add(op _Op) {
|
||||||
|
*self = append(*self, newInsOp(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Program) key(op _Op) {
|
||||||
|
*self = append(*self,
|
||||||
|
newInsVi(_OP_byte, '"'),
|
||||||
|
newInsOp(op),
|
||||||
|
newInsVi(_OP_byte, '"'),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Program) int(op _Op, vi int) {
|
||||||
|
*self = append(*self, newInsVi(op, vi))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Program) str(op _Op, vs string) {
|
||||||
|
*self = append(*self, newInsVs(op, vs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Program) rtt(op _Op, vt reflect.Type) {
|
||||||
|
*self = append(*self, newInsVt(op, vt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Program) vp(op _Op, vt reflect.Type, pv bool) {
|
||||||
|
*self = append(*self, newInsVp(op, vt, pv))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self _Program) disassemble() string {
|
||||||
|
nb := len(self)
|
||||||
|
tab := make([]bool, nb + 1)
|
||||||
|
ret := make([]string, 0, nb + 1)
|
||||||
|
|
||||||
|
/* prescan to get all the labels */
|
||||||
|
for _, ins := range self {
|
||||||
|
if ins.isBranch() {
|
||||||
|
tab[ins.vi()] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* disassemble each instruction */
|
||||||
|
for i, ins := range self {
|
||||||
|
if !tab[i] {
|
||||||
|
ret = append(ret, "\t" + ins.disassemble())
|
||||||
|
} else {
|
||||||
|
ret = append(ret, fmt.Sprintf("L_%d:\n\t%s", i, ins.disassemble()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the last label, if needed */
|
||||||
|
if tab[nb] {
|
||||||
|
ret = append(ret, fmt.Sprintf("L_%d:", nb))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add an "end" indicator, and join all the strings */
|
||||||
|
return strings.Join(append(ret, "\tend"), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
type _Compiler struct {
|
||||||
|
opts option.CompileOptions
|
||||||
|
pv bool
|
||||||
|
tab map[reflect.Type]bool
|
||||||
|
rec map[reflect.Type]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCompiler() *_Compiler {
|
||||||
|
return &_Compiler {
|
||||||
|
opts: option.DefaultCompileOptions(),
|
||||||
|
tab: map[reflect.Type]bool{},
|
||||||
|
rec: map[reflect.Type]uint8{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) apply(opts option.CompileOptions) *_Compiler {
|
||||||
|
self.opts = opts
|
||||||
|
if self.opts.RecursiveDepth > 0 {
|
||||||
|
self.rec = map[reflect.Type]uint8{}
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) rescue(ep *error) {
|
||||||
|
if val := recover(); val != nil {
|
||||||
|
if err, ok := val.(error); ok {
|
||||||
|
*ep = err
|
||||||
|
} else {
|
||||||
|
panic(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compile(vt reflect.Type, pv bool) (ret _Program, err error) {
|
||||||
|
defer self.rescue(&err)
|
||||||
|
self.compileOne(&ret, 0, vt, pv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileOne(p *_Program, sp int, vt reflect.Type, pv bool) {
|
||||||
|
if self.tab[vt] {
|
||||||
|
p.vp(_OP_recurse, vt, pv)
|
||||||
|
} else {
|
||||||
|
self.compileRec(p, sp, vt, pv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileRec(p *_Program, sp int, vt reflect.Type, pv bool) {
|
||||||
|
pr := self.pv
|
||||||
|
pt := reflect.PtrTo(vt)
|
||||||
|
|
||||||
|
/* check for addressable `json.Marshaler` with pointer receiver */
|
||||||
|
if pv && pt.Implements(jsonMarshalerType) {
|
||||||
|
p.rtt(_OP_marshal_p, pt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for `json.Marshaler` */
|
||||||
|
if vt.Implements(jsonMarshalerType) {
|
||||||
|
self.compileMarshaler(p, _OP_marshal, vt, jsonMarshalerType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for addressable `encoding.TextMarshaler` with pointer receiver */
|
||||||
|
if pv && pt.Implements(encodingTextMarshalerType) {
|
||||||
|
p.rtt(_OP_marshal_text_p, pt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for `encoding.TextMarshaler` */
|
||||||
|
if vt.Implements(encodingTextMarshalerType) {
|
||||||
|
self.compileMarshaler(p, _OP_marshal_text, vt, encodingTextMarshalerType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enter the recursion, and compile the type */
|
||||||
|
self.pv = pv
|
||||||
|
self.tab[vt] = true
|
||||||
|
self.compileOps(p, sp, vt)
|
||||||
|
|
||||||
|
/* exit the recursion */
|
||||||
|
self.pv = pr
|
||||||
|
delete(self.tab, vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileOps(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
switch vt.Kind() {
|
||||||
|
case reflect.Bool : p.add(_OP_bool)
|
||||||
|
case reflect.Int : p.add(_OP_int())
|
||||||
|
case reflect.Int8 : p.add(_OP_i8)
|
||||||
|
case reflect.Int16 : p.add(_OP_i16)
|
||||||
|
case reflect.Int32 : p.add(_OP_i32)
|
||||||
|
case reflect.Int64 : p.add(_OP_i64)
|
||||||
|
case reflect.Uint : p.add(_OP_uint())
|
||||||
|
case reflect.Uint8 : p.add(_OP_u8)
|
||||||
|
case reflect.Uint16 : p.add(_OP_u16)
|
||||||
|
case reflect.Uint32 : p.add(_OP_u32)
|
||||||
|
case reflect.Uint64 : p.add(_OP_u64)
|
||||||
|
case reflect.Uintptr : p.add(_OP_uintptr())
|
||||||
|
case reflect.Float32 : p.add(_OP_f32)
|
||||||
|
case reflect.Float64 : p.add(_OP_f64)
|
||||||
|
case reflect.String : self.compileString (p, vt)
|
||||||
|
case reflect.Array : self.compileArray (p, sp, vt.Elem(), vt.Len())
|
||||||
|
case reflect.Interface : self.compileInterface (p, vt)
|
||||||
|
case reflect.Map : self.compileMap (p, sp, vt)
|
||||||
|
case reflect.Ptr : self.compilePtr (p, sp, vt.Elem())
|
||||||
|
case reflect.Slice : self.compileSlice (p, sp, vt.Elem())
|
||||||
|
case reflect.Struct : self.compileStruct (p, sp, vt)
|
||||||
|
default : panic (error_type(vt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileNil(p *_Program, sp int, vt reflect.Type, nil_op _Op, fn func(*_Program, int, reflect.Type)) {
|
||||||
|
x := p.pc()
|
||||||
|
p.add(_OP_is_nil)
|
||||||
|
fn(p, sp, vt)
|
||||||
|
e := p.pc()
|
||||||
|
p.add(_OP_goto)
|
||||||
|
p.pin(x)
|
||||||
|
p.add(nil_op)
|
||||||
|
p.pin(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compilePtr(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
self.compileNil(p, sp, vt, _OP_null, self.compilePtrBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compilePtrBody(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
p.tag(sp)
|
||||||
|
p.add(_OP_save)
|
||||||
|
p.add(_OP_deref)
|
||||||
|
self.compileOne(p, sp + 1, vt, true)
|
||||||
|
p.add(_OP_drop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileMap(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
self.compileNil(p, sp, vt, _OP_empty_obj, self.compileMapBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileMapBody(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
p.tag(sp + 1)
|
||||||
|
p.int(_OP_byte, '{')
|
||||||
|
p.add(_OP_save)
|
||||||
|
p.rtt(_OP_map_iter, vt)
|
||||||
|
p.add(_OP_save)
|
||||||
|
i := p.pc()
|
||||||
|
p.add(_OP_map_check_key)
|
||||||
|
u := p.pc()
|
||||||
|
p.add(_OP_map_write_key)
|
||||||
|
self.compileMapBodyKey(p, vt.Key())
|
||||||
|
p.pin(u)
|
||||||
|
p.int(_OP_byte, ':')
|
||||||
|
p.add(_OP_map_value_next)
|
||||||
|
self.compileOne(p, sp + 2, vt.Elem(), false)
|
||||||
|
j := p.pc()
|
||||||
|
p.add(_OP_map_check_key)
|
||||||
|
p.int(_OP_byte, ',')
|
||||||
|
v := p.pc()
|
||||||
|
p.add(_OP_map_write_key)
|
||||||
|
self.compileMapBodyKey(p, vt.Key())
|
||||||
|
p.pin(v)
|
||||||
|
p.int(_OP_byte, ':')
|
||||||
|
p.add(_OP_map_value_next)
|
||||||
|
self.compileOne(p, sp + 2, vt.Elem(), false)
|
||||||
|
p.int(_OP_goto, j)
|
||||||
|
p.pin(i)
|
||||||
|
p.pin(j)
|
||||||
|
p.add(_OP_map_stop)
|
||||||
|
p.add(_OP_drop_2)
|
||||||
|
p.int(_OP_byte, '}')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileMapBodyKey(p *_Program, vk reflect.Type) {
|
||||||
|
if !vk.Implements(encodingTextMarshalerType) {
|
||||||
|
self.compileMapBodyTextKey(p, vk)
|
||||||
|
} else {
|
||||||
|
self.compileMapBodyUtextKey(p, vk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileMapBodyTextKey(p *_Program, vk reflect.Type) {
|
||||||
|
switch vk.Kind() {
|
||||||
|
case reflect.Invalid : panic("map key is nil")
|
||||||
|
case reflect.Bool : p.key(_OP_bool)
|
||||||
|
case reflect.Int : p.key(_OP_int())
|
||||||
|
case reflect.Int8 : p.key(_OP_i8)
|
||||||
|
case reflect.Int16 : p.key(_OP_i16)
|
||||||
|
case reflect.Int32 : p.key(_OP_i32)
|
||||||
|
case reflect.Int64 : p.key(_OP_i64)
|
||||||
|
case reflect.Uint : p.key(_OP_uint())
|
||||||
|
case reflect.Uint8 : p.key(_OP_u8)
|
||||||
|
case reflect.Uint16 : p.key(_OP_u16)
|
||||||
|
case reflect.Uint32 : p.key(_OP_u32)
|
||||||
|
case reflect.Uint64 : p.key(_OP_u64)
|
||||||
|
case reflect.Uintptr : p.key(_OP_uintptr())
|
||||||
|
case reflect.Float32 : p.key(_OP_f32)
|
||||||
|
case reflect.Float64 : p.key(_OP_f64)
|
||||||
|
case reflect.String : self.compileString(p, vk)
|
||||||
|
default : panic(error_type(vk))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileMapBodyUtextKey(p *_Program, vk reflect.Type) {
|
||||||
|
if vk.Kind() != reflect.Ptr {
|
||||||
|
p.rtt(_OP_marshal_text, vk)
|
||||||
|
} else {
|
||||||
|
self.compileMapBodyUtextPtr(p, vk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileMapBodyUtextPtr(p *_Program, vk reflect.Type) {
|
||||||
|
i := p.pc()
|
||||||
|
p.add(_OP_is_nil)
|
||||||
|
p.rtt(_OP_marshal_text, vk)
|
||||||
|
j := p.pc()
|
||||||
|
p.add(_OP_goto)
|
||||||
|
p.pin(i)
|
||||||
|
p.str(_OP_text, "\"\"")
|
||||||
|
p.pin(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileSlice(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
self.compileNil(p, sp, vt, _OP_empty_arr, self.compileSliceBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileSliceBody(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
if isSimpleByte(vt) {
|
||||||
|
p.add(_OP_bin)
|
||||||
|
} else {
|
||||||
|
self.compileSliceArray(p, sp, vt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileSliceArray(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
p.tag(sp)
|
||||||
|
p.int(_OP_byte, '[')
|
||||||
|
p.add(_OP_save)
|
||||||
|
p.add(_OP_slice_len)
|
||||||
|
i := p.pc()
|
||||||
|
p.rtt(_OP_slice_next, vt)
|
||||||
|
self.compileOne(p, sp + 1, vt, true)
|
||||||
|
j := p.pc()
|
||||||
|
p.rtt(_OP_slice_next, vt)
|
||||||
|
p.int(_OP_byte, ',')
|
||||||
|
self.compileOne(p, sp + 1, vt, true)
|
||||||
|
p.int(_OP_goto, j)
|
||||||
|
p.pin(i)
|
||||||
|
p.pin(j)
|
||||||
|
p.add(_OP_drop)
|
||||||
|
p.int(_OP_byte, ']')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileArray(p *_Program, sp int, vt reflect.Type, nb int) {
|
||||||
|
p.tag(sp)
|
||||||
|
p.int(_OP_byte, '[')
|
||||||
|
p.add(_OP_save)
|
||||||
|
|
||||||
|
/* first item */
|
||||||
|
if nb != 0 {
|
||||||
|
self.compileOne(p, sp + 1, vt, self.pv)
|
||||||
|
p.add(_OP_load)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remaining items */
|
||||||
|
for i := 1; i < nb; i++ {
|
||||||
|
p.int(_OP_byte, ',')
|
||||||
|
p.int(_OP_index, i * int(vt.Size()))
|
||||||
|
self.compileOne(p, sp + 1, vt, self.pv)
|
||||||
|
p.add(_OP_load)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of array */
|
||||||
|
p.add(_OP_drop)
|
||||||
|
p.int(_OP_byte, ']')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileString(p *_Program, vt reflect.Type) {
|
||||||
|
if vt != jsonNumberType {
|
||||||
|
p.add(_OP_str)
|
||||||
|
} else {
|
||||||
|
p.add(_OP_number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileStruct(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
if sp >= self.opts.MaxInlineDepth || p.pc() >= _MAX_ILBUF || (sp > 0 && vt.NumField() >= _MAX_FIELDS) {
|
||||||
|
p.vp(_OP_recurse, vt, self.pv)
|
||||||
|
if self.opts.RecursiveDepth > 0 {
|
||||||
|
if self.pv {
|
||||||
|
self.rec[vt] = 1
|
||||||
|
} else {
|
||||||
|
self.rec[vt] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.compileStructBody(p, sp, vt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
p.tag(sp)
|
||||||
|
p.int(_OP_byte, '{')
|
||||||
|
p.add(_OP_save)
|
||||||
|
p.add(_OP_cond_set)
|
||||||
|
|
||||||
|
/* compile each field */
|
||||||
|
for _, fv := range resolver.ResolveStruct(vt) {
|
||||||
|
var s []int
|
||||||
|
var o resolver.Offset
|
||||||
|
|
||||||
|
/* "omitempty" for arrays */
|
||||||
|
if fv.Type.Kind() == reflect.Array {
|
||||||
|
if fv.Type.Len() == 0 && (fv.Opts & resolver.F_omitempty) != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* index to the field */
|
||||||
|
for _, o = range fv.Path {
|
||||||
|
if p.int(_OP_index, int(o.Size)); o.Kind == resolver.F_deref {
|
||||||
|
s = append(s, p.pc())
|
||||||
|
p.add(_OP_is_nil)
|
||||||
|
p.add(_OP_deref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for "omitempty" option */
|
||||||
|
if fv.Type.Kind() != reflect.Struct && fv.Type.Kind() != reflect.Array && (fv.Opts & resolver.F_omitempty) != 0 {
|
||||||
|
s = append(s, p.pc())
|
||||||
|
self.compileStructFieldZero(p, fv.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the comma if not the first element */
|
||||||
|
i := p.pc()
|
||||||
|
p.add(_OP_cond_testc)
|
||||||
|
p.int(_OP_byte, ',')
|
||||||
|
p.pin(i)
|
||||||
|
|
||||||
|
/* compile the key and value */
|
||||||
|
ft := fv.Type
|
||||||
|
p.str(_OP_text, Quote(fv.Name) + ":")
|
||||||
|
|
||||||
|
/* check for "stringnize" option */
|
||||||
|
if (fv.Opts & resolver.F_stringize) == 0 {
|
||||||
|
self.compileOne(p, sp + 1, ft, self.pv)
|
||||||
|
} else {
|
||||||
|
self.compileStructFieldStr(p, sp + 1, ft)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* patch the skipping jumps and reload the struct pointer */
|
||||||
|
p.rel(s)
|
||||||
|
p.add(_OP_load)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of object */
|
||||||
|
p.add(_OP_drop)
|
||||||
|
p.int(_OP_byte, '}')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
pc := -1
|
||||||
|
ft := vt
|
||||||
|
sv := false
|
||||||
|
|
||||||
|
/* dereference the pointer if needed */
|
||||||
|
if ft.Kind() == reflect.Ptr {
|
||||||
|
ft = ft.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if it can be stringized */
|
||||||
|
switch ft.Kind() {
|
||||||
|
case reflect.Bool : sv = true
|
||||||
|
case reflect.Int : sv = true
|
||||||
|
case reflect.Int8 : sv = true
|
||||||
|
case reflect.Int16 : sv = true
|
||||||
|
case reflect.Int32 : sv = true
|
||||||
|
case reflect.Int64 : sv = true
|
||||||
|
case reflect.Uint : sv = true
|
||||||
|
case reflect.Uint8 : sv = true
|
||||||
|
case reflect.Uint16 : sv = true
|
||||||
|
case reflect.Uint32 : sv = true
|
||||||
|
case reflect.Uint64 : sv = true
|
||||||
|
case reflect.Uintptr : sv = true
|
||||||
|
case reflect.Float32 : sv = true
|
||||||
|
case reflect.Float64 : sv = true
|
||||||
|
case reflect.String : sv = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if it's not, ignore the "string" and follow the regular path */
|
||||||
|
if !sv {
|
||||||
|
self.compileOne(p, sp, vt, self.pv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dereference the pointer */
|
||||||
|
if vt.Kind() == reflect.Ptr {
|
||||||
|
pc = p.pc()
|
||||||
|
vt = vt.Elem()
|
||||||
|
p.add(_OP_is_nil)
|
||||||
|
p.add(_OP_deref)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* special case of a double-quoted string */
|
||||||
|
if ft != jsonNumberType && ft.Kind() == reflect.String {
|
||||||
|
p.add(_OP_quote)
|
||||||
|
} else {
|
||||||
|
self.compileStructFieldQuoted(p, sp, vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the "null" case of the pointer */
|
||||||
|
if pc != -1 {
|
||||||
|
e := p.pc()
|
||||||
|
p.add(_OP_goto)
|
||||||
|
p.pin(pc)
|
||||||
|
p.add(_OP_null)
|
||||||
|
p.pin(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileStructFieldZero(p *_Program, vt reflect.Type) {
|
||||||
|
switch vt.Kind() {
|
||||||
|
case reflect.Bool : p.add(_OP_is_zero_1)
|
||||||
|
case reflect.Int : p.add(_OP_is_zero_ints())
|
||||||
|
case reflect.Int8 : p.add(_OP_is_zero_1)
|
||||||
|
case reflect.Int16 : p.add(_OP_is_zero_2)
|
||||||
|
case reflect.Int32 : p.add(_OP_is_zero_4)
|
||||||
|
case reflect.Int64 : p.add(_OP_is_zero_8)
|
||||||
|
case reflect.Uint : p.add(_OP_is_zero_ints())
|
||||||
|
case reflect.Uint8 : p.add(_OP_is_zero_1)
|
||||||
|
case reflect.Uint16 : p.add(_OP_is_zero_2)
|
||||||
|
case reflect.Uint32 : p.add(_OP_is_zero_4)
|
||||||
|
case reflect.Uint64 : p.add(_OP_is_zero_8)
|
||||||
|
case reflect.Uintptr : p.add(_OP_is_nil)
|
||||||
|
case reflect.Float32 : p.add(_OP_is_zero_4)
|
||||||
|
case reflect.Float64 : p.add(_OP_is_zero_8)
|
||||||
|
case reflect.String : p.add(_OP_is_nil_p1)
|
||||||
|
case reflect.Interface : p.add(_OP_is_nil_p1)
|
||||||
|
case reflect.Map : p.add(_OP_is_zero_map)
|
||||||
|
case reflect.Ptr : p.add(_OP_is_nil)
|
||||||
|
case reflect.Slice : p.add(_OP_is_nil_p1)
|
||||||
|
default : panic(error_type(vt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileStructFieldQuoted(p *_Program, sp int, vt reflect.Type) {
|
||||||
|
p.int(_OP_byte, '"')
|
||||||
|
self.compileOne(p, sp, vt, self.pv)
|
||||||
|
p.int(_OP_byte, '"')
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileInterface(p *_Program, vt reflect.Type) {
|
||||||
|
x := p.pc()
|
||||||
|
p.add(_OP_is_nil_p1)
|
||||||
|
|
||||||
|
/* iface and efaces are different */
|
||||||
|
if vt.NumMethod() == 0 {
|
||||||
|
p.add(_OP_eface)
|
||||||
|
} else {
|
||||||
|
p.add(_OP_iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the "null" value */
|
||||||
|
e := p.pc()
|
||||||
|
p.add(_OP_goto)
|
||||||
|
p.pin(x)
|
||||||
|
p.add(_OP_null)
|
||||||
|
p.pin(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Compiler) compileMarshaler(p *_Program, op _Op, vt reflect.Type, mt reflect.Type) {
|
||||||
|
pc := p.pc()
|
||||||
|
vk := vt.Kind()
|
||||||
|
|
||||||
|
/* direct receiver */
|
||||||
|
if vk != reflect.Ptr {
|
||||||
|
p.rtt(op, vt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* value receiver with a pointer type, check for nil before calling the marshaler */
|
||||||
|
p.add(_OP_is_nil)
|
||||||
|
p.rtt(op, vt)
|
||||||
|
i := p.pc()
|
||||||
|
p.add(_OP_goto)
|
||||||
|
p.pin(pc)
|
||||||
|
p.add(_OP_null)
|
||||||
|
p.pin(i)
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// +build go1.15,!go1.17
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`os`
|
||||||
|
`strings`
|
||||||
|
`runtime`
|
||||||
|
`runtime/debug`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
debugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
|
||||||
|
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_Instr_End _Instr = newInsOp(_OP_null)
|
||||||
|
|
||||||
|
_F_gc = jit.Func(runtime.GC)
|
||||||
|
_F_force_gc = jit.Func(debug.FreeOSMemory)
|
||||||
|
_F_println = jit.Func(println_wrapper)
|
||||||
|
)
|
||||||
|
|
||||||
|
func println_wrapper(i int, op1 int, op2 int){
|
||||||
|
println(i, " Intrs ", op1, _OpNames[op1], "next: ", op2, _OpNames[op2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) force_gc() {
|
||||||
|
self.call_go(_F_gc)
|
||||||
|
self.call_go(_F_force_gc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) debug_instr(i int, v *_Instr) {
|
||||||
|
if debugSyncGC {
|
||||||
|
if (i+1 == len(self.p)) {
|
||||||
|
self.print_gc(i, v, &_Instr_End)
|
||||||
|
} else {
|
||||||
|
next := &(self.p[i+1])
|
||||||
|
self.print_gc(i, v, next)
|
||||||
|
name := _OpNames[next.op()]
|
||||||
|
if strings.Contains(name, "save") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.force_gc()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`os`
|
||||||
|
`runtime`
|
||||||
|
`strings`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
|
)
|
||||||
|
|
||||||
|
const _FP_debug = 128
|
||||||
|
|
||||||
|
var (
|
||||||
|
debugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
|
||||||
|
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||||
|
debugCheckPtr = os.Getenv("SONIC_CHECK_POINTER") != ""
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_Instr_End = newInsOp(_OP_is_nil)
|
||||||
|
|
||||||
|
_F_gc = jit.Func(gc)
|
||||||
|
_F_println = jit.Func(println_wrapper)
|
||||||
|
_F_print = jit.Func(print)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *_Assembler) dsave(r ...obj.Addr) {
|
||||||
|
for i, v := range r {
|
||||||
|
if i > _FP_debug / 8 - 1 {
|
||||||
|
panic("too many registers to save")
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", v, jit.Ptr(_SP, _FP_fargs + _FP_saves + _FP_locals + int64(i) * 8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) dload(r ...obj.Addr) {
|
||||||
|
for i, v := range r {
|
||||||
|
if i > _FP_debug / 8 - 1 {
|
||||||
|
panic("too many registers to load")
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", jit.Ptr(_SP, _FP_fargs + _FP_saves + _FP_locals + int64(i) * 8), v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func println_wrapper(i int, op1 int, op2 int){
|
||||||
|
println(i, " Intrs ", op1, _OpNames[op1], "next: ", op2, _OpNames[op2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func print(i int){
|
||||||
|
println(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gc() {
|
||||||
|
if !debugSyncGC {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runtime.GC()
|
||||||
|
// debug.FreeOSMemory()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) dcall(fn obj.Addr) {
|
||||||
|
self.Emit("MOVQ", fn, _R10) // MOVQ ${fn}, R10
|
||||||
|
self.Rjmp("CALL", _R10) // CALL R10
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) debug_gc() {
|
||||||
|
if !debugSyncGC {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.dsave(_REG_debug...)
|
||||||
|
self.dcall(_F_gc)
|
||||||
|
self.dload(_REG_debug...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) debug_instr(i int, v *_Instr) {
|
||||||
|
if debugSyncGC {
|
||||||
|
if i+1 == len(self.p) {
|
||||||
|
self.print_gc(i, v, &_Instr_End)
|
||||||
|
} else {
|
||||||
|
next := &(self.p[i+1])
|
||||||
|
self.print_gc(i, v, next)
|
||||||
|
name := _OpNames[next.op()]
|
||||||
|
if strings.Contains(name, "save") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// self.debug_gc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname checkptrBase runtime.checkptrBase
|
||||||
|
func checkptrBase(p unsafe.Pointer) uintptr
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname findObject runtime.findObject
|
||||||
|
func findObject(p, refBase, refOff uintptr) (base uintptr, s unsafe.Pointer, objIndex uintptr)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_F_checkptr = jit.Func(checkptr)
|
||||||
|
_F_printptr = jit.Func(printptr)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_R10 = jit.Reg("R10")
|
||||||
|
)
|
||||||
|
var _REG_debug = []obj.Addr {
|
||||||
|
jit.Reg("AX"),
|
||||||
|
jit.Reg("BX"),
|
||||||
|
jit.Reg("CX"),
|
||||||
|
jit.Reg("DX"),
|
||||||
|
jit.Reg("DI"),
|
||||||
|
jit.Reg("SI"),
|
||||||
|
jit.Reg("BP"),
|
||||||
|
jit.Reg("SP"),
|
||||||
|
jit.Reg("R8"),
|
||||||
|
jit.Reg("R9"),
|
||||||
|
jit.Reg("R10"),
|
||||||
|
jit.Reg("R11"),
|
||||||
|
jit.Reg("R12"),
|
||||||
|
jit.Reg("R13"),
|
||||||
|
jit.Reg("R14"),
|
||||||
|
jit.Reg("R15"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkptr(ptr uintptr) {
|
||||||
|
if ptr == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("pointer: %x\n", ptr)
|
||||||
|
f := checkptrBase(unsafe.Pointer(uintptr(ptr)))
|
||||||
|
if f == 0 {
|
||||||
|
fmt.Printf("! unknown-based pointer: %x\n", ptr)
|
||||||
|
} else if f == 1 {
|
||||||
|
fmt.Printf("! stack pointer: %x\n", ptr)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("base: %x\n", f)
|
||||||
|
}
|
||||||
|
findobj(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findobj(ptr uintptr) {
|
||||||
|
base, s, objIndex := findObject(ptr, 0, 0)
|
||||||
|
if s != nil && base == 0 {
|
||||||
|
fmt.Printf("! invalid pointer: %x\n", ptr)
|
||||||
|
}
|
||||||
|
fmt.Printf("objIndex: %d\n", objIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) check_ptr(ptr obj.Addr, lea bool) {
|
||||||
|
if !debugCheckPtr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dsave(_REG_debug...)
|
||||||
|
if lea {
|
||||||
|
self.Emit("LEAQ", ptr, _R10)
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", ptr, _R10)
|
||||||
|
}
|
||||||
|
self.Emit("MOVQ", _R10, jit.Ptr(_SP, 0))
|
||||||
|
self.dcall(_F_checkptr)
|
||||||
|
self.dload(_REG_debug...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printptr(i int, ptr uintptr) {
|
||||||
|
fmt.Printf("[%d] ptr: %x\n", i, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_Assembler) print_ptr(i int, ptr obj.Addr, lea bool) {
|
||||||
|
self.dsave(_REG_debug...)
|
||||||
|
if lea {
|
||||||
|
self.Emit("LEAQ", ptr, _R10)
|
||||||
|
} else {
|
||||||
|
self.Emit("MOVQ", ptr, _R10)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Emit("MOVQ", jit.Imm(int64(i)), _AX)
|
||||||
|
self.Emit("MOVQ", _R10, _BX)
|
||||||
|
self.dcall(_F_printptr)
|
||||||
|
self.dload(_REG_debug...)
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`encoding/json`
|
||||||
|
`reflect`
|
||||||
|
`runtime`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
`github.com/bytedance/sonic/utf8`
|
||||||
|
`github.com/bytedance/sonic/option`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options is a set of encoding options.
|
||||||
|
type Options uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
bitSortMapKeys = iota
|
||||||
|
bitEscapeHTML
|
||||||
|
bitCompactMarshaler
|
||||||
|
bitNoQuoteTextMarshaler
|
||||||
|
bitNoNullSliceOrMap
|
||||||
|
bitValidateString
|
||||||
|
|
||||||
|
// used for recursive compile
|
||||||
|
bitPointerValue = 63
|
||||||
|
)
|
||||||
|
|
||||||
|
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 = 1 << bitSortMapKeys
|
||||||
|
|
||||||
|
// 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 = 1 << bitEscapeHTML
|
||||||
|
|
||||||
|
// CompactMarshaler indicates that the output JSON from json.Marshaler
|
||||||
|
// is always compact and needs no validation
|
||||||
|
CompactMarshaler Options = 1 << bitCompactMarshaler
|
||||||
|
|
||||||
|
// NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler
|
||||||
|
// is always escaped string and needs no quoting
|
||||||
|
NoQuoteTextMarshaler Options = 1 << bitNoQuoteTextMarshaler
|
||||||
|
|
||||||
|
// NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}',
|
||||||
|
// instead of 'null'
|
||||||
|
NoNullSliceOrMap Options = 1 << bitNoNullSliceOrMap
|
||||||
|
|
||||||
|
// ValidateString indicates that encoder should validate the input string
|
||||||
|
// before encoding it into JSON.
|
||||||
|
ValidateString Options = 1 << bitValidateString
|
||||||
|
|
||||||
|
// CompatibleWithStd is used to be compatible with std encoder.
|
||||||
|
CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encoder represents a specific set of encoder configurations.
|
||||||
|
type Encoder struct {
|
||||||
|
Opts Options
|
||||||
|
prefix string
|
||||||
|
indent string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns the JSON encoding of v.
|
||||||
|
func (self *Encoder) Encode(v interface{}) ([]byte, error) {
|
||||||
|
if self.indent != "" || self.prefix != "" {
|
||||||
|
return EncodeIndented(v, self.prefix, self.indent, self.Opts)
|
||||||
|
}
|
||||||
|
return Encode(v, self.Opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortKeys enables the SortMapKeys option.
|
||||||
|
func (self *Encoder) SortKeys() *Encoder {
|
||||||
|
self.Opts |= SortMapKeys
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEscapeHTML specifies if option EscapeHTML opens
|
||||||
|
func (self *Encoder) SetEscapeHTML(f bool) {
|
||||||
|
if f {
|
||||||
|
self.Opts |= EscapeHTML
|
||||||
|
} else {
|
||||||
|
self.Opts &= ^EscapeHTML
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValidateString specifies if option ValidateString opens
|
||||||
|
func (self *Encoder) SetValidateString(f bool) {
|
||||||
|
if f {
|
||||||
|
self.Opts |= ValidateString
|
||||||
|
} else {
|
||||||
|
self.Opts &= ^ValidateString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCompactMarshaler specifies if option CompactMarshaler opens
|
||||||
|
func (self *Encoder) SetCompactMarshaler(f bool) {
|
||||||
|
if f {
|
||||||
|
self.Opts |= CompactMarshaler
|
||||||
|
} else {
|
||||||
|
self.Opts &= ^CompactMarshaler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoQuoteTextMarshaler specifies if option NoQuoteTextMarshaler opens
|
||||||
|
func (self *Encoder) SetNoQuoteTextMarshaler(f bool) {
|
||||||
|
if f {
|
||||||
|
self.Opts |= NoQuoteTextMarshaler
|
||||||
|
} else {
|
||||||
|
self.Opts &= ^NoQuoteTextMarshaler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIndent instructs the encoder to format each subsequent encoded
|
||||||
|
// value as if indented by the package-level function EncodeIndent().
|
||||||
|
// Calling SetIndent("", "") disables indentation.
|
||||||
|
func (enc *Encoder) SetIndent(prefix, indent string) {
|
||||||
|
enc.prefix = prefix
|
||||||
|
enc.indent = indent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quote returns the JSON-quoted version of s.
|
||||||
|
func Quote(s string) string {
|
||||||
|
var n int
|
||||||
|
var p []byte
|
||||||
|
|
||||||
|
/* check for empty string */
|
||||||
|
if s == "" {
|
||||||
|
return `""`
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate space for result */
|
||||||
|
n = len(s) + 2
|
||||||
|
p = make([]byte, 0, n)
|
||||||
|
|
||||||
|
/* call the encoder */
|
||||||
|
_ = encodeString(&p, s)
|
||||||
|
return rt.Mem2Str(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode returns the JSON encoding of val, encoded with opts.
|
||||||
|
func Encode(val interface{}, opts Options) ([]byte, error) {
|
||||||
|
buf := newBytes()
|
||||||
|
err := EncodeInto(&buf, val, opts)
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != nil {
|
||||||
|
freeBytes(buf)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts & EscapeHTML != 0 || opts & ValidateString != 0 {
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make a copy of the result */
|
||||||
|
ret := make([]byte, len(buf))
|
||||||
|
copy(ret, buf)
|
||||||
|
|
||||||
|
freeBytes(buf)
|
||||||
|
/* return the buffer into pool */
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeInto is like Encode but uses a user-supplied buffer instead of allocating
|
||||||
|
// a new one.
|
||||||
|
func EncodeInto(buf *[]byte, val interface{}, opts Options) error {
|
||||||
|
stk := newStack()
|
||||||
|
efv := rt.UnpackEface(val)
|
||||||
|
err := encodeTypedPointer(buf, efv.Type, &efv.Value, stk, uint64(opts))
|
||||||
|
|
||||||
|
/* return the stack into pool */
|
||||||
|
if err != nil {
|
||||||
|
resetStack(stk)
|
||||||
|
}
|
||||||
|
freeStack(stk)
|
||||||
|
|
||||||
|
/* EscapeHTML needs to allocate a new buffer*/
|
||||||
|
if opts & EscapeHTML != 0 {
|
||||||
|
dest := HTMLEscape(nil, *buf)
|
||||||
|
freeBytes(*buf) // free origin used buffer
|
||||||
|
*buf = dest
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts & ValidateString != 0 && !utf8.Validate(*buf) {
|
||||||
|
dest := utf8.CorrectWith(nil, *buf, `\ufffd`)
|
||||||
|
freeBytes(*buf) // free origin used buffer
|
||||||
|
*buf = dest
|
||||||
|
}
|
||||||
|
|
||||||
|
/* avoid GC ahead */
|
||||||
|
runtime.KeepAlive(buf)
|
||||||
|
runtime.KeepAlive(efv)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeByte = rt.UnpackType(reflect.TypeOf(byte(0)))
|
||||||
|
|
||||||
|
// 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 <script> tags.
|
||||||
|
// For historical reasons, web browsers don't honor standard HTML
|
||||||
|
// escaping within <script> tags, so an alternative JSON encoding must
|
||||||
|
// be used.
|
||||||
|
func HTMLEscape(dst []byte, src []byte) []byte {
|
||||||
|
return htmlEscape(dst, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) {
|
||||||
|
var err error
|
||||||
|
var out []byte
|
||||||
|
var buf *bytes.Buffer
|
||||||
|
|
||||||
|
/* encode into the buffer */
|
||||||
|
out = newBytes()
|
||||||
|
err = EncodeInto(&out, val, opts)
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != nil {
|
||||||
|
freeBytes(out)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* indent the JSON */
|
||||||
|
buf = newBuffer()
|
||||||
|
err = json.Indent(buf, out, prefix, indent)
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != nil {
|
||||||
|
freeBytes(out)
|
||||||
|
freeBuffer(buf)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy to the result buffer */
|
||||||
|
ret := make([]byte, buf.Len())
|
||||||
|
copy(ret, buf.Bytes())
|
||||||
|
|
||||||
|
/* return the buffers into pool */
|
||||||
|
freeBytes(out)
|
||||||
|
freeBuffer(buf)
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
cfg := option.DefaultCompileOptions()
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(&cfg)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return pretouchRec(map[reflect.Type]uint8{vt: 0}, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid validates json and returns first non-blank character position,
|
||||||
|
// if it is only one valid json value.
|
||||||
|
// Otherwise returns invalid character position using start.
|
||||||
|
//
|
||||||
|
// Note: it does not check for the invalid UTF-8 characters.
|
||||||
|
func Valid(data []byte) (ok bool, start int) {
|
||||||
|
n := len(data)
|
||||||
|
if n == 0 {
|
||||||
|
return false, -1
|
||||||
|
}
|
||||||
|
s := rt.Mem2Str(data)
|
||||||
|
p := 0
|
||||||
|
m := types.NewStateMachine()
|
||||||
|
ret := native.ValidateOne(&s, &p, m)
|
||||||
|
types.FreeStateMachine(m)
|
||||||
|
|
||||||
|
if ret < 0 {
|
||||||
|
return false, p-1
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for trailing spaces */
|
||||||
|
for ;p < n; p++ {
|
||||||
|
if (types.SPACE_MASK & (1 << data[p])) == 0 {
|
||||||
|
return false, p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, ret
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`fmt`
|
||||||
|
`reflect`
|
||||||
|
`strconv`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ERR_too_deep = &json.UnsupportedValueError {
|
||||||
|
Str : "Value nesting too deep",
|
||||||
|
Value : reflect.ValueOf("..."),
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ERR_nan_or_infinite = &json.UnsupportedValueError {
|
||||||
|
Str : "NaN or ±Infinite",
|
||||||
|
Value : reflect.ValueOf("NaN or ±Infinite"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func error_type(vtype reflect.Type) error {
|
||||||
|
return &json.UnsupportedTypeError{Type: vtype}
|
||||||
|
}
|
||||||
|
|
||||||
|
func error_number(number json.Number) error {
|
||||||
|
return &json.UnsupportedValueError {
|
||||||
|
Str : "invalid number literal: " + strconv.Quote(string(number)),
|
||||||
|
Value : reflect.ValueOf(number),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func error_marshaler(ret []byte, pos int) error {
|
||||||
|
return fmt.Errorf("invalid Marshaler output json syntax at %d: %q", pos, ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
panicNilPointerOfNonEmptyString int = 1 + iota
|
||||||
|
)
|
||||||
|
|
||||||
|
func goPanic(code int, val unsafe.Pointer) {
|
||||||
|
switch(code){
|
||||||
|
case panicNilPointerOfNonEmptyString:
|
||||||
|
panic(fmt.Sprintf("val: %#v has nil pointer while its length is not zero!", (*rt.GoString)(val)))
|
||||||
|
default:
|
||||||
|
panic("encoder error!")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/bytedance/sonic/internal/native"
|
||||||
|
"github.com/bytedance/sonic/internal/rt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type _MapPair struct {
|
||||||
|
k string // when the map key is integer, k is pointed to m
|
||||||
|
v unsafe.Pointer
|
||||||
|
m [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type _MapIterator struct {
|
||||||
|
it rt.GoMapIterator // must be the first field
|
||||||
|
kv rt.GoSlice // slice of _MapPair
|
||||||
|
ki int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
iteratorPool = sync.Pool{}
|
||||||
|
iteratorPair = rt.UnpackType(reflect.TypeOf(_MapPair{}))
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if unsafe.Offsetof(_MapIterator{}.it) != 0 {
|
||||||
|
panic("_MapIterator.it is not the first field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func newIterator() *_MapIterator {
|
||||||
|
if v := iteratorPool.Get(); v == nil {
|
||||||
|
return new(_MapIterator)
|
||||||
|
} else {
|
||||||
|
return resetIterator(v.(*_MapIterator))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetIterator(p *_MapIterator) *_MapIterator {
|
||||||
|
p.ki = 0
|
||||||
|
p.it = rt.GoMapIterator{}
|
||||||
|
p.kv.Len = 0
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) at(i int) *_MapPair {
|
||||||
|
return (*_MapPair)(unsafe.Pointer(uintptr(self.kv.Ptr) + uintptr(i) * unsafe.Sizeof(_MapPair{})))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) add() (p *_MapPair) {
|
||||||
|
p = self.at(self.kv.Len)
|
||||||
|
self.kv.Len++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) data() (p []_MapPair) {
|
||||||
|
*(*rt.GoSlice)(unsafe.Pointer(&p)) = self.kv
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) append(t *rt.GoType, k unsafe.Pointer, v unsafe.Pointer) (err error) {
|
||||||
|
p := self.add()
|
||||||
|
p.v = v
|
||||||
|
|
||||||
|
/* check for strings */
|
||||||
|
if tk := t.Kind(); tk != reflect.String {
|
||||||
|
return self.appendGeneric(p, t, tk, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fast path for strings */
|
||||||
|
p.k = *(*string)(k)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) appendGeneric(p *_MapPair, t *rt.GoType, v reflect.Kind, k unsafe.Pointer) error {
|
||||||
|
switch v {
|
||||||
|
case reflect.Int : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int)(k)))]) ; return nil
|
||||||
|
case reflect.Int8 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int8)(k)))]) ; return nil
|
||||||
|
case reflect.Int16 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int16)(k)))]) ; return nil
|
||||||
|
case reflect.Int32 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int32)(k)))]) ; return nil
|
||||||
|
case reflect.Int64 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], *(*int64)(k))]) ; return nil
|
||||||
|
case reflect.Uint : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint)(k)))]) ; return nil
|
||||||
|
case reflect.Uint8 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint8)(k)))]) ; return nil
|
||||||
|
case reflect.Uint16 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint16)(k)))]) ; return nil
|
||||||
|
case reflect.Uint32 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint32)(k)))]) ; return nil
|
||||||
|
case reflect.Uint64 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], *(*uint64)(k))]) ; return nil
|
||||||
|
case reflect.Uintptr : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uintptr)(k)))]) ; return nil
|
||||||
|
case reflect.Interface : return self.appendInterface(p, t, k)
|
||||||
|
case reflect.Struct, reflect.Ptr : return self.appendConcrete(p, t, k)
|
||||||
|
default : panic("unexpected map key type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) appendConcrete(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
|
||||||
|
// compiler has already checked that the type implements the encoding.MarshalText interface
|
||||||
|
if !t.Indirect() {
|
||||||
|
k = *(*unsafe.Pointer)(k)
|
||||||
|
}
|
||||||
|
eface := rt.GoEface{Value: k, Type: t}.Pack()
|
||||||
|
out, err := eface.(encoding.TextMarshaler).MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.k = rt.Mem2Str(out)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_MapIterator) appendInterface(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
|
||||||
|
if len(rt.IfaceType(t).Methods) == 0 {
|
||||||
|
panic("unexpected map key type")
|
||||||
|
} else if p.k, err = asText(k); err == nil {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iteratorStop(p *_MapIterator) {
|
||||||
|
iteratorPool.Put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func iteratorNext(p *_MapIterator) {
|
||||||
|
i := p.ki
|
||||||
|
t := &p.it
|
||||||
|
|
||||||
|
/* check for unordered iteration */
|
||||||
|
if i < 0 {
|
||||||
|
mapiternext(t)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for end of iteration */
|
||||||
|
if p.ki >= p.kv.Len {
|
||||||
|
t.K = nil
|
||||||
|
t.V = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the key-value pair, and increase the pointer */
|
||||||
|
t.K = unsafe.Pointer(&p.at(p.ki).k)
|
||||||
|
t.V = p.at(p.ki).v
|
||||||
|
p.ki++
|
||||||
|
}
|
||||||
|
|
||||||
|
func iteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*_MapIterator, error) {
|
||||||
|
it := newIterator()
|
||||||
|
mapiterinit(t, m, &it.it)
|
||||||
|
|
||||||
|
/* check for key-sorting, empty map don't need sorting */
|
||||||
|
if m.Count == 0 || (fv & uint64(SortMapKeys)) == 0 {
|
||||||
|
it.ki = -1
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pre-allocate space if needed */
|
||||||
|
if m.Count > it.kv.Cap {
|
||||||
|
it.kv = growslice(iteratorPair, it.kv, m.Count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dump all the key-value pairs */
|
||||||
|
for ; it.it.K != nil; mapiternext(&it.it) {
|
||||||
|
if err := it.append(t.Key, it.it.K, it.it.V); err != nil {
|
||||||
|
iteratorStop(it)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort the keys, map with only 1 item don't need sorting */
|
||||||
|
if it.ki = 1; m.Count > 1 {
|
||||||
|
radixQsort(it.data(), 0, maxDepth(it.kv.Len))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load the first pair into iterator */
|
||||||
|
it.it.V = it.at(0).v
|
||||||
|
it.it.K = unsafe.Pointer(&it.at(0).k)
|
||||||
|
return it, nil
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`sync`
|
||||||
|
`unsafe`
|
||||||
|
`errors`
|
||||||
|
`reflect`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/caching`
|
||||||
|
`github.com/bytedance/sonic/option`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_MaxStack = 4096 // 4k states
|
||||||
|
_MaxBuffer = 1048576 // 1MB buffer size
|
||||||
|
|
||||||
|
_StackSize = unsafe.Sizeof(_Stack{})
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bytesPool = sync.Pool{}
|
||||||
|
stackPool = sync.Pool{}
|
||||||
|
bufferPool = sync.Pool{}
|
||||||
|
programCache = caching.CreateProgramCache()
|
||||||
|
)
|
||||||
|
|
||||||
|
type _State struct {
|
||||||
|
x int
|
||||||
|
f uint64
|
||||||
|
p unsafe.Pointer
|
||||||
|
q unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
type _Stack struct {
|
||||||
|
sp uint64
|
||||||
|
sb [_MaxStack]_State
|
||||||
|
}
|
||||||
|
|
||||||
|
type _Encoder func(
|
||||||
|
rb *[]byte,
|
||||||
|
vp unsafe.Pointer,
|
||||||
|
sb *_Stack,
|
||||||
|
fv uint64,
|
||||||
|
) error
|
||||||
|
|
||||||
|
var _KeepAlive struct {
|
||||||
|
rb *[]byte
|
||||||
|
vp unsafe.Pointer
|
||||||
|
sb *_Stack
|
||||||
|
fv uint64
|
||||||
|
err error
|
||||||
|
frame [_FP_offs]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var errCallShadow = errors.New("DON'T CALL THIS!")
|
||||||
|
|
||||||
|
// Faker func of _Encoder, used to export its stackmap as _Encoder's
|
||||||
|
func _Encoder_Shadow(rb *[]byte, vp unsafe.Pointer, sb *_Stack, fv uint64) (err error) {
|
||||||
|
// align to assembler_amd64.go: _FP_offs
|
||||||
|
var frame [_FP_offs]byte
|
||||||
|
|
||||||
|
// must keep all args and frames noticeable to GC
|
||||||
|
_KeepAlive.rb = rb
|
||||||
|
_KeepAlive.vp = vp
|
||||||
|
_KeepAlive.sb = sb
|
||||||
|
_KeepAlive.fv = fv
|
||||||
|
_KeepAlive.err = err
|
||||||
|
_KeepAlive.frame = frame
|
||||||
|
|
||||||
|
return errCallShadow
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBytes() []byte {
|
||||||
|
if ret := bytesPool.Get(); ret != nil {
|
||||||
|
return ret.([]byte)
|
||||||
|
} else {
|
||||||
|
return make([]byte, 0, _MaxBuffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStack() *_Stack {
|
||||||
|
if ret := stackPool.Get(); ret == nil {
|
||||||
|
return new(_Stack)
|
||||||
|
} else {
|
||||||
|
return ret.(*_Stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetStack(p *_Stack) {
|
||||||
|
memclrNoHeapPointers(unsafe.Pointer(p), _StackSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBuffer() *bytes.Buffer {
|
||||||
|
if ret := bufferPool.Get(); ret != nil {
|
||||||
|
return ret.(*bytes.Buffer)
|
||||||
|
} else {
|
||||||
|
return bytes.NewBuffer(make([]byte, 0, _MaxBuffer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeBytes(p []byte) {
|
||||||
|
p = p[:0]
|
||||||
|
bytesPool.Put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeStack(p *_Stack) {
|
||||||
|
p.sp = 0
|
||||||
|
stackPool.Put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeBuffer(p *bytes.Buffer) {
|
||||||
|
p.Reset()
|
||||||
|
bufferPool.Put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeEncoder(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
|
||||||
|
if pp, err := newCompiler().compile(vt.Pack(), ex[0].(bool)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
as := newAssembler(pp)
|
||||||
|
as.name = vt.String()
|
||||||
|
return as.Load(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findOrCompile(vt *rt.GoType, pv bool) (_Encoder, error) {
|
||||||
|
if val := programCache.Get(vt); val != nil {
|
||||||
|
return val.(_Encoder), nil
|
||||||
|
} else if ret, err := programCache.Compute(vt, makeEncoder, pv); err == nil {
|
||||||
|
return ret.(_Encoder), nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pretouchType(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error) {
|
||||||
|
/* compile function */
|
||||||
|
compiler := newCompiler().apply(opts)
|
||||||
|
encoder := func(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
|
||||||
|
if pp, err := compiler.compile(_vt, ex[0].(bool)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
as := newAssembler(pp)
|
||||||
|
as.name = vt.String()
|
||||||
|
return as.Load(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find or compile */
|
||||||
|
vt := rt.UnpackType(_vt)
|
||||||
|
if val := programCache.Get(vt); val != nil {
|
||||||
|
return nil, nil
|
||||||
|
} else if _, err := programCache.Compute(vt, encoder, v == 1); err == nil {
|
||||||
|
return compiler.rec, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pretouchRec(vtm map[reflect.Type]uint8, opts option.CompileOptions) error {
|
||||||
|
if opts.RecursiveDepth < 0 || len(vtm) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
next := make(map[reflect.Type]uint8)
|
||||||
|
for vt, v := range vtm {
|
||||||
|
sub, err := pretouchType(vt, opts, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for svt, v := range sub {
|
||||||
|
next[svt] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opts.RecursiveDepth -= 1
|
||||||
|
return pretouchRec(next, opts)
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding`
|
||||||
|
`encoding/json`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Encoder Primitives **/
|
||||||
|
|
||||||
|
func encodeNil(rb *[]byte) error {
|
||||||
|
*rb = append(*rb, 'n', 'u', 'l', 'l')
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeString(buf *[]byte, val string) error {
|
||||||
|
var sidx int
|
||||||
|
var pbuf *rt.GoSlice
|
||||||
|
var pstr *rt.GoString
|
||||||
|
|
||||||
|
/* opening quote */
|
||||||
|
*buf = append(*buf, '"')
|
||||||
|
pbuf = (*rt.GoSlice)(unsafe.Pointer(buf))
|
||||||
|
pstr = (*rt.GoString)(unsafe.Pointer(&val))
|
||||||
|
|
||||||
|
/* encode with native library */
|
||||||
|
for sidx < pstr.Len {
|
||||||
|
sn := pstr.Len - sidx
|
||||||
|
dn := pbuf.Cap - pbuf.Len
|
||||||
|
sp := padd(pstr.Ptr, sidx)
|
||||||
|
dp := padd(pbuf.Ptr, pbuf.Len)
|
||||||
|
nb := native.Quote(sp, sn, dp, &dn, 0)
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if pbuf.Len += dn; nb >= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not enough space, grow the slice and try again */
|
||||||
|
sidx += ^nb
|
||||||
|
*pbuf = growslice(rt.UnpackType(byteType), *pbuf, pbuf.Cap * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* closing quote */
|
||||||
|
*buf = append(*buf, '"')
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeTypedPointer(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *_Stack, fv uint64) error {
|
||||||
|
if vt == nil {
|
||||||
|
return encodeNil(buf)
|
||||||
|
} else if fn, err := findOrCompile(vt, (fv&(1<<bitPointerValue)) != 0); err != nil {
|
||||||
|
return err
|
||||||
|
} else if vt.Indirect() {
|
||||||
|
rt.MoreStack(_FP_size + native.MaxFrameSize)
|
||||||
|
rt.StopProf()
|
||||||
|
err := fn(buf, *vp, sb, fv)
|
||||||
|
rt.StartProf()
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
rt.MoreStack(_FP_size + native.MaxFrameSize)
|
||||||
|
rt.StopProf()
|
||||||
|
err := fn(buf, unsafe.Pointer(vp), sb, fv)
|
||||||
|
rt.StartProf()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeJsonMarshaler(buf *[]byte, val json.Marshaler, opt Options) error {
|
||||||
|
if ret, err := val.MarshalJSON(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if opt & CompactMarshaler != 0 {
|
||||||
|
return compact(buf, ret)
|
||||||
|
}
|
||||||
|
if ok, s := Valid(ret); !ok {
|
||||||
|
return error_marshaler(ret, s)
|
||||||
|
}
|
||||||
|
*buf = append(*buf, ret...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeTextMarshaler(buf *[]byte, val encoding.TextMarshaler, opt Options) error {
|
||||||
|
if ret, err := val.MarshalText(); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if opt & NoQuoteTextMarshaler != 0 {
|
||||||
|
*buf = append(*buf, ret...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return encodeString(buf, rt.Mem2Str(ret) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func htmlEscape(dst []byte, src []byte) []byte {
|
||||||
|
var sidx int
|
||||||
|
|
||||||
|
dst = append(dst, src[:0]...) // avoid check nil dst
|
||||||
|
sbuf := (*rt.GoSlice)(unsafe.Pointer(&src))
|
||||||
|
dbuf := (*rt.GoSlice)(unsafe.Pointer(&dst))
|
||||||
|
|
||||||
|
/* grow dst if it is shorter */
|
||||||
|
if cap(dst) - len(dst) < len(src) + native.BufPaddingSize {
|
||||||
|
cap := len(src) * 3 / 2 + native.BufPaddingSize
|
||||||
|
*dbuf = growslice(typeByte, *dbuf, cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
for sidx < sbuf.Len {
|
||||||
|
sp := padd(sbuf.Ptr, sidx)
|
||||||
|
dp := padd(dbuf.Ptr, dbuf.Len)
|
||||||
|
|
||||||
|
sn := sbuf.Len - sidx
|
||||||
|
dn := dbuf.Cap - dbuf.Len
|
||||||
|
nb := native.HTMLEscape(sp, sn, dp, &dn)
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if dbuf.Len += dn; nb >= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not enough space, grow the slice and try again */
|
||||||
|
sidx += ^nb
|
||||||
|
*dbuf = growslice(typeByte, *dbuf, dbuf.Cap * 2)
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
argPtrs = []bool { true, true, true, false }
|
||||||
|
localPtrs = []bool{}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_F_assertI2I = jit.Func(assertI2I)
|
||||||
|
)
|
||||||
|
|
||||||
|
func asText(v unsafe.Pointer) (string, error) {
|
||||||
|
text := assertI2I(_T_encoding_TextMarshaler, *(*rt.GoIface)(v))
|
||||||
|
r, e := (*(*encoding.TextMarshaler)(unsafe.Pointer(&text))).MarshalText()
|
||||||
|
return rt.Mem2Str(r), e
|
||||||
|
}
|
||||||
|
|
||||||
|
func asJson(v unsafe.Pointer) (string, error) {
|
||||||
|
text := assertI2I(_T_json_Marshaler, *(*rt.GoIface)(v))
|
||||||
|
r, e := (*(*json.Marshaler)(unsafe.Pointer(&text))).MarshalJSON()
|
||||||
|
return rt.Mem2Str(r), e
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
// Algorithm 3-way Radix Quicksort, d means the radix.
|
||||||
|
// Reference: https://algs4.cs.princeton.edu/51radix/Quick3string.java.html
|
||||||
|
func radixQsort(kvs []_MapPair, 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].k, 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 []_MapPair, d int) {
|
||||||
|
for i := 1; i < len(kvs); i++ {
|
||||||
|
for j := i; j > 0 && lessFrom(kvs[j].k, kvs[j-1].k, d); j-- {
|
||||||
|
swap(kvs, j, j-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pivot(kvs []_MapPair, 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].k, d), byteAt(kvs[t].k, d), byteAt(kvs[2*t].k, d)),
|
||||||
|
medianThree(byteAt(kvs[m].k, d), byteAt(kvs[m-t].k, d), byteAt(kvs[m+t].k, d)),
|
||||||
|
medianThree(byteAt(kvs[len(kvs)-1].k, d),
|
||||||
|
byteAt(kvs[len(kvs)-1-t].k, d),
|
||||||
|
byteAt(kvs[len(kvs)-1-2*t].k, d)))
|
||||||
|
}
|
||||||
|
return medianThree(byteAt(kvs[0].k, d), byteAt(kvs[m].k, d), byteAt(kvs[len(kvs)-1].k, 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 []_MapPair, lo, hi, first int) {
|
||||||
|
root := lo
|
||||||
|
for {
|
||||||
|
child := 2*root + 1
|
||||||
|
if child >= hi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if child+1 < hi && kvs[first+child].k < kvs[first+child+1].k {
|
||||||
|
child++
|
||||||
|
}
|
||||||
|
if kvs[first+root].k >= kvs[first+child].k {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
swap(kvs, first+root, first+child)
|
||||||
|
root = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func heapSort(kvs []_MapPair, 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 _MapPair.k is NOT pointed to _MapPair.m when map key is integer after swap
|
||||||
|
func swap(kvs []_MapPair, a, b int) {
|
||||||
|
kvs[a].k, kvs[b].k = kvs[b].k, kvs[a].k
|
||||||
|
kvs[a].v, kvs[b].v = kvs[b].v, kvs[a].v
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`io`
|
||||||
|
)
|
||||||
|
|
||||||
|
// StreamEncoder uses io.Writer as
|
||||||
|
type StreamEncoder struct {
|
||||||
|
w io.Writer
|
||||||
|
Encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStreamEncoder adapts to encoding/json.NewDecoder API.
|
||||||
|
//
|
||||||
|
// NewStreamEncoder returns a new encoder that write to w.
|
||||||
|
func NewStreamEncoder(w io.Writer) *StreamEncoder {
|
||||||
|
return &StreamEncoder{w: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encodes interface{} as JSON to io.Writer
|
||||||
|
func (enc *StreamEncoder) Encode(val interface{}) (err error) {
|
||||||
|
out := newBytes()
|
||||||
|
|
||||||
|
/* encode into the buffer */
|
||||||
|
err = EncodeInto(&out, val, enc.Opts)
|
||||||
|
if err != nil {
|
||||||
|
goto free_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
if enc.indent != "" || enc.prefix != "" {
|
||||||
|
/* indent the JSON */
|
||||||
|
buf := newBuffer()
|
||||||
|
err = json.Indent(buf, out, enc.prefix, enc.indent)
|
||||||
|
if err != nil {
|
||||||
|
freeBuffer(buf)
|
||||||
|
goto free_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// according to standard library, terminate each value with a newline...
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
|
||||||
|
/* copy into io.Writer */
|
||||||
|
_, err = io.Copy(enc.w, buf)
|
||||||
|
if err != nil {
|
||||||
|
freeBuffer(buf)
|
||||||
|
goto free_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* copy into io.Writer */
|
||||||
|
var n int
|
||||||
|
for len(out) > 0 {
|
||||||
|
n, err = enc.w.Write(out)
|
||||||
|
out = out[n:]
|
||||||
|
if err != nil {
|
||||||
|
goto free_bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// according to standard library, terminate each value with a newline...
|
||||||
|
enc.w.Write([]byte{'\n'})
|
||||||
|
}
|
||||||
|
|
||||||
|
free_bytes:
|
||||||
|
freeBytes(out)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// +build go1.15,!go1.17
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname _subr__b64encode github.com/chenzhuoyu/base64x._subr__b64encode
|
||||||
|
var _subr__b64encode uintptr
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname growslice runtime.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:linkname assertI2I runtime.assertI2I
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func assertI2I(inter *rt.GoType, i rt.GoIface) rt.GoIface
|
||||||
|
|
||||||
|
//go:linkname mapiternext runtime.mapiternext
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiternext(it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname mapiterinit runtime.mapiterinit
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname isValidNumber encoding/json.isValidNumber
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func isValidNumber(s string) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
var _runtime_writeBarrier uintptr = rt.GcwbAddr()
|
||||||
|
|
||||||
|
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
|
||||||
|
func gcWriteBarrierAX()
|
|
@ -0,0 +1,66 @@
|
||||||
|
// +build go1.17,!go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname _subr__b64encode github.com/chenzhuoyu/base64x._subr__b64encode
|
||||||
|
var _subr__b64encode uintptr
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname growslice runtime.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:linkname assertI2I runtime.assertI2I2
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func assertI2I(inter *rt.GoType, i rt.GoIface) rt.GoIface
|
||||||
|
|
||||||
|
//go:linkname mapiternext runtime.mapiternext
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiternext(it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname mapiterinit runtime.mapiterinit
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname isValidNumber encoding/json.isValidNumber
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func isValidNumber(s string) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname _runtime_writeBarrier runtime.writeBarrier
|
||||||
|
var _runtime_writeBarrier uintptr
|
||||||
|
|
||||||
|
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
|
||||||
|
func gcWriteBarrierAX()
|
|
@ -0,0 +1,66 @@
|
||||||
|
// +build go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname _subr__b64encode github.com/chenzhuoyu/base64x._subr__b64encode
|
||||||
|
var _subr__b64encode uintptr
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname growslice reflect.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:linkname assertI2I runtime.assertI2I2
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func assertI2I(inter *rt.GoType, i rt.GoIface) rt.GoIface
|
||||||
|
|
||||||
|
//go:linkname mapiternext runtime.mapiternext
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiternext(it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname mapiterinit runtime.mapiterinit
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname isValidNumber encoding/json.isValidNumber
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func isValidNumber(s string) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname _runtime_writeBarrier runtime.writeBarrier
|
||||||
|
var _runtime_writeBarrier uintptr
|
||||||
|
|
||||||
|
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
|
||||||
|
func gcWriteBarrierAX()
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding`
|
||||||
|
`encoding/json`
|
||||||
|
`reflect`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
byteType = reflect.TypeOf(byte(0))
|
||||||
|
jsonNumberType = reflect.TypeOf(json.Number(""))
|
||||||
|
jsonUnsupportedValueType = reflect.TypeOf(new(json.UnsupportedValueError))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
jsonMarshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||||
|
encodingTextMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
|
func isSimpleByte(vt reflect.Type) bool {
|
||||||
|
if vt.Kind() != byteType.Kind() {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return !isEitherMarshaler(vt) && !isEitherMarshaler(reflect.PtrTo(vt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEitherMarshaler(vt reflect.Type) bool {
|
||||||
|
return vt.Implements(jsonMarshalerType) || vt.Implements(encodingTextMarshalerType)
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/json`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/loader`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func padd(p unsafe.Pointer, v int) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(uintptr(p) + uintptr(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func ptoenc(p loader.Function) _Encoder {
|
||||||
|
return *(*_Encoder)(unsafe.Pointer(&p))
|
||||||
|
}
|
||||||
|
|
||||||
|
func compact(p *[]byte, v []byte) error {
|
||||||
|
buf := newBuffer()
|
||||||
|
err := json.Compact(buf, v)
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add to result */
|
||||||
|
v = buf.Bytes()
|
||||||
|
*p = append(*p, v...)
|
||||||
|
|
||||||
|
/* return the buffer into pool */
|
||||||
|
freeBuffer(buf)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
use (
|
||||||
|
.
|
||||||
|
./generic_test
|
||||||
|
./fuzz
|
||||||
|
./external_jsonlib_test
|
||||||
|
)
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* 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 caching
|
||||||
|
|
||||||
|
import (
|
||||||
|
`strings`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
type FieldMap struct {
|
||||||
|
N uint64
|
||||||
|
b unsafe.Pointer
|
||||||
|
m map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
type FieldEntry struct {
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
Hash uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
FieldMap_N = int64(unsafe.Offsetof(FieldMap{}.N))
|
||||||
|
FieldMap_b = int64(unsafe.Offsetof(FieldMap{}.b))
|
||||||
|
FieldEntrySize = int64(unsafe.Sizeof(FieldEntry{}))
|
||||||
|
)
|
||||||
|
|
||||||
|
func newBucket(n int) unsafe.Pointer {
|
||||||
|
v := make([]FieldEntry, n)
|
||||||
|
return (*rt.GoSlice)(unsafe.Pointer(&v)).Ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateFieldMap(n int) *FieldMap {
|
||||||
|
return &FieldMap {
|
||||||
|
N: uint64(n * 2),
|
||||||
|
b: newBucket(n * 2), // LoadFactor = 0.5
|
||||||
|
m: make(map[string]int, n * 2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FieldMap) At(p uint64) *FieldEntry {
|
||||||
|
off := uintptr(p) * uintptr(FieldEntrySize)
|
||||||
|
return (*FieldEntry)(unsafe.Pointer(uintptr(self.b) + off))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get searches FieldMap by name. JIT generated assembly does NOT call this
|
||||||
|
// function, rather it implements its own version directly in assembly. So
|
||||||
|
// we must ensure this function stays in sync with the JIT generated one.
|
||||||
|
func (self *FieldMap) Get(name string) int {
|
||||||
|
h := StrHash(name)
|
||||||
|
p := h % self.N
|
||||||
|
s := self.At(p)
|
||||||
|
|
||||||
|
/* find the element;
|
||||||
|
* the hash map is never full, so the loop will always terminate */
|
||||||
|
for s.Hash != 0 {
|
||||||
|
if s.Hash == h && s.Name == name {
|
||||||
|
return s.ID
|
||||||
|
} else {
|
||||||
|
p = (p + 1) % self.N
|
||||||
|
s = self.At(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not found */
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FieldMap) Set(name string, i int) {
|
||||||
|
h := StrHash(name)
|
||||||
|
p := h % self.N
|
||||||
|
s := self.At(p)
|
||||||
|
|
||||||
|
/* searching for an empty slot;
|
||||||
|
* the hash map is never full, so the loop will always terminate */
|
||||||
|
for s.Hash != 0 {
|
||||||
|
p = (p + 1) % self.N
|
||||||
|
s = self.At(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the value */
|
||||||
|
s.ID = i
|
||||||
|
s.Hash = h
|
||||||
|
s.Name = name
|
||||||
|
|
||||||
|
/* add the case-insensitive version, prefer the one with smaller field ID */
|
||||||
|
key := strings.ToLower(name)
|
||||||
|
if v, ok := self.m[key]; !ok || i < v {
|
||||||
|
self.m[key] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FieldMap) GetCaseInsensitive(name string) int {
|
||||||
|
if i, ok := self.m[strings.ToLower(name)]; ok {
|
||||||
|
return i
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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 caching
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
V_strhash = rt.UnpackEface(strhash)
|
||||||
|
S_strhash = *(*uintptr)(V_strhash.Value)
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname strhash runtime.strhash
|
||||||
|
func strhash(_ unsafe.Pointer, _ uintptr) uintptr
|
||||||
|
|
||||||
|
func StrHash(s string) uint64 {
|
||||||
|
if v := strhash(unsafe.Pointer(&s), 0); v == 0 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return uint64(v)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* 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 caching
|
||||||
|
|
||||||
|
import (
|
||||||
|
`sync`
|
||||||
|
`sync/atomic`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Program Map **/
|
||||||
|
|
||||||
|
const (
|
||||||
|
_LoadFactor = 0.5
|
||||||
|
_InitCapacity = 4096 // must be a power of 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type _ProgramMap struct {
|
||||||
|
n uint64
|
||||||
|
m uint32
|
||||||
|
b []_ProgramEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ProgramEntry struct {
|
||||||
|
vt *rt.GoType
|
||||||
|
fn interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProgramMap() *_ProgramMap {
|
||||||
|
return &_ProgramMap {
|
||||||
|
n: 0,
|
||||||
|
m: _InitCapacity - 1,
|
||||||
|
b: make([]_ProgramEntry, _InitCapacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ProgramMap) copy() *_ProgramMap {
|
||||||
|
fork := &_ProgramMap{
|
||||||
|
n: self.n,
|
||||||
|
m: self.m,
|
||||||
|
b: make([]_ProgramEntry, len(self.b)),
|
||||||
|
}
|
||||||
|
for i, f := range self.b {
|
||||||
|
fork.b[i] = f
|
||||||
|
}
|
||||||
|
return fork
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ProgramMap) get(vt *rt.GoType) interface{} {
|
||||||
|
i := self.m + 1
|
||||||
|
p := vt.Hash & self.m
|
||||||
|
|
||||||
|
/* linear probing */
|
||||||
|
for ; i > 0; i-- {
|
||||||
|
if b := self.b[p]; b.vt == vt {
|
||||||
|
return b.fn
|
||||||
|
} else if b.vt == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
p = (p + 1) & self.m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not found */
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ProgramMap) add(vt *rt.GoType, fn interface{}) *_ProgramMap {
|
||||||
|
p := self.copy()
|
||||||
|
f := float64(atomic.LoadUint64(&p.n) + 1) / float64(p.m + 1)
|
||||||
|
|
||||||
|
/* check for load factor */
|
||||||
|
if f > _LoadFactor {
|
||||||
|
p = p.rehash()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* insert the value */
|
||||||
|
p.insert(vt, fn)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ProgramMap) rehash() *_ProgramMap {
|
||||||
|
c := (self.m + 1) << 1
|
||||||
|
r := &_ProgramMap{m: c - 1, b: make([]_ProgramEntry, int(c))}
|
||||||
|
|
||||||
|
/* rehash every entry */
|
||||||
|
for i := uint32(0); i <= self.m; i++ {
|
||||||
|
if b := self.b[i]; b.vt != nil {
|
||||||
|
r.insert(b.vt, b.fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rebuild successful */
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *_ProgramMap) insert(vt *rt.GoType, fn interface{}) {
|
||||||
|
h := vt.Hash
|
||||||
|
p := h & self.m
|
||||||
|
|
||||||
|
/* linear probing */
|
||||||
|
for i := uint32(0); i <= self.m; i++ {
|
||||||
|
if b := &self.b[p]; b.vt != nil {
|
||||||
|
p += 1
|
||||||
|
p &= self.m
|
||||||
|
} else {
|
||||||
|
b.vt = vt
|
||||||
|
b.fn = fn
|
||||||
|
atomic.AddUint64(&self.n, 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* should never happens */
|
||||||
|
panic("no available slots")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** RCU Program Cache **/
|
||||||
|
|
||||||
|
type ProgramCache struct {
|
||||||
|
m sync.Mutex
|
||||||
|
p unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateProgramCache() *ProgramCache {
|
||||||
|
return &ProgramCache {
|
||||||
|
m: sync.Mutex{},
|
||||||
|
p: unsafe.Pointer(newProgramMap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ProgramCache) Get(vt *rt.GoType) interface{} {
|
||||||
|
return (*_ProgramMap)(atomic.LoadPointer(&self.p)).get(vt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ProgramCache) Compute(vt *rt.GoType, compute func(*rt.GoType, ... interface{}) (interface{}, error), ex ...interface{}) (interface{}, error) {
|
||||||
|
var err error
|
||||||
|
var val interface{}
|
||||||
|
|
||||||
|
/* use defer to prevent inlining of this function */
|
||||||
|
self.m.Lock()
|
||||||
|
defer self.m.Unlock()
|
||||||
|
|
||||||
|
/* double check with write lock held */
|
||||||
|
if val = self.Get(vt); val != nil {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute the value */
|
||||||
|
if val, err = compute(vt, ex...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update the RCU cache */
|
||||||
|
atomic.StorePointer(&self.p, unsafe.Pointer((*_ProgramMap)(atomic.LoadPointer(&self.p)).add(vt, val)))
|
||||||
|
return val, nil
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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 cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`os`
|
||||||
|
|
||||||
|
`github.com/klauspost/cpuid/v2`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
HasAVX = cpuid.CPU.Has(cpuid.AVX)
|
||||||
|
HasAVX2 = cpuid.CPU.Has(cpuid.AVX2)
|
||||||
|
HasSSE = cpuid.CPU.Has(cpuid.SSE)
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
switch v := os.Getenv("SONIC_MODE"); v {
|
||||||
|
case "" : break
|
||||||
|
case "auto" : break
|
||||||
|
case "noavx" : HasAVX = false; fallthrough
|
||||||
|
case "noavx2" : HasAVX2 = false
|
||||||
|
default : panic(fmt.Sprintf("invalid mode: '%s', should be one of 'auto', 'noavx', 'noavx2'", v))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* 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 jit
|
||||||
|
|
||||||
|
import (
|
||||||
|
`github.com/twitchyliquid64/golang-asm/asm/arch`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_AC = arch.Set("amd64")
|
||||||
|
)
|
||||||
|
|
||||||
|
func As(op string) obj.As {
|
||||||
|
if ret, ok := _AC.Instructions[op]; ok {
|
||||||
|
return ret
|
||||||
|
} else {
|
||||||
|
panic("invalid instruction: " + op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Imm(imm int64) obj.Addr {
|
||||||
|
return obj.Addr {
|
||||||
|
Type : obj.TYPE_CONST,
|
||||||
|
Offset : imm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reg(reg string) obj.Addr {
|
||||||
|
if ret, ok := _AC.Register[reg]; ok {
|
||||||
|
return obj.Addr{Reg: ret, Type: obj.TYPE_REG}
|
||||||
|
} else {
|
||||||
|
panic("invalid register name: " + reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ptr(reg obj.Addr, offs int64) obj.Addr {
|
||||||
|
return obj.Addr {
|
||||||
|
Reg : reg.Reg,
|
||||||
|
Type : obj.TYPE_MEM,
|
||||||
|
Offset : offs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sib(reg obj.Addr, idx obj.Addr, scale int16, offs int64) obj.Addr {
|
||||||
|
return obj.Addr {
|
||||||
|
Reg : reg.Reg,
|
||||||
|
Index : idx.Reg,
|
||||||
|
Scale : scale,
|
||||||
|
Type : obj.TYPE_MEM,
|
||||||
|
Offset : offs,
|
||||||
|
}
|
||||||
|
}
|
269
vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go
generated
vendored
Normal file
269
vendor/github.com/bytedance/sonic/internal/jit/assembler_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* 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 jit
|
||||||
|
|
||||||
|
import (
|
||||||
|
`encoding/binary`
|
||||||
|
`strconv`
|
||||||
|
`strings`
|
||||||
|
`sync`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/loader`
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj/x86`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_LB_jump_pc = "_jump_pc_"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseAssembler struct {
|
||||||
|
i int
|
||||||
|
f func()
|
||||||
|
c []byte
|
||||||
|
o sync.Once
|
||||||
|
pb *Backend
|
||||||
|
xrefs map[string][]*obj.Prog
|
||||||
|
labels map[string]*obj.Prog
|
||||||
|
pendings map[string][]*obj.Prog
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Instruction Encoders **/
|
||||||
|
|
||||||
|
var _NOPS = [][16]byte {
|
||||||
|
{0x90}, // NOP
|
||||||
|
{0x66, 0x90}, // 66 NOP
|
||||||
|
{0x0f, 0x1f, 0x00}, // NOP DWORD ptr [EAX]
|
||||||
|
{0x0f, 0x1f, 0x40, 0x00}, // NOP DWORD ptr [EAX + 00H]
|
||||||
|
{0x0f, 0x1f, 0x44, 0x00, 0x00}, // NOP DWORD ptr [EAX + EAX*1 + 00H]
|
||||||
|
{0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00}, // 66 NOP DWORD ptr [EAX + EAX*1 + 00H]
|
||||||
|
{0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00}, // NOP DWORD ptr [EAX + 00000000H]
|
||||||
|
{0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, // NOP DWORD ptr [EAX + EAX*1 + 00000000H]
|
||||||
|
{0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, // 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) NOP() *obj.Prog {
|
||||||
|
p := self.pb.New()
|
||||||
|
p.As = obj.ANOP
|
||||||
|
self.pb.Append(p)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) NOPn(n int) {
|
||||||
|
for i := len(_NOPS); i > 0 && n > 0; i-- {
|
||||||
|
for ; n >= i; n -= i {
|
||||||
|
self.Byte(_NOPS[i - 1][:i]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Byte(v ...byte) {
|
||||||
|
for ; len(v) >= 8; v = v[8:] { self.From("QUAD", Imm(rt.Get64(v))) }
|
||||||
|
for ; len(v) >= 4; v = v[4:] { self.From("LONG", Imm(int64(rt.Get32(v)))) }
|
||||||
|
for ; len(v) >= 2; v = v[2:] { self.From("WORD", Imm(int64(rt.Get16(v)))) }
|
||||||
|
for ; len(v) >= 1; v = v[1:] { self.From("BYTE", Imm(int64(v[0]))) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Mark(pc int) {
|
||||||
|
self.i++
|
||||||
|
self.Link(_LB_jump_pc + strconv.Itoa(pc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Link(to string) {
|
||||||
|
var p *obj.Prog
|
||||||
|
var v []*obj.Prog
|
||||||
|
|
||||||
|
/* placeholder substitution */
|
||||||
|
if strings.Contains(to, "{n}") {
|
||||||
|
to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for duplications */
|
||||||
|
if _, ok := self.labels[to]; ok {
|
||||||
|
panic("label " + to + " has already been linked")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the pending links */
|
||||||
|
p = self.NOP()
|
||||||
|
v = self.pendings[to]
|
||||||
|
|
||||||
|
/* patch all the pending jumps */
|
||||||
|
for _, q := range v {
|
||||||
|
q.To.Val = p
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark the label as resolved */
|
||||||
|
self.labels[to] = p
|
||||||
|
delete(self.pendings, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Xref(pc int, d int64) {
|
||||||
|
self.Sref(_LB_jump_pc + strconv.Itoa(pc), d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Sref(to string, d int64) {
|
||||||
|
p := self.pb.New()
|
||||||
|
p.As = x86.ALONG
|
||||||
|
p.From = Imm(-d)
|
||||||
|
|
||||||
|
/* placeholder substitution */
|
||||||
|
if strings.Contains(to, "{n}") {
|
||||||
|
to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* record the patch point */
|
||||||
|
self.pb.Append(p)
|
||||||
|
self.xrefs[to] = append(self.xrefs[to], p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Xjmp(op string, to int) {
|
||||||
|
self.Sjmp(op, _LB_jump_pc + strconv.Itoa(to))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Sjmp(op string, to string) {
|
||||||
|
p := self.pb.New()
|
||||||
|
p.As = As(op)
|
||||||
|
|
||||||
|
/* placeholder substitution */
|
||||||
|
if strings.Contains(to, "{n}") {
|
||||||
|
to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for backward jumps */
|
||||||
|
if v, ok := self.labels[to]; ok {
|
||||||
|
p.To.Val = v
|
||||||
|
} else {
|
||||||
|
self.pendings[to] = append(self.pendings[to], p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark as a branch, and add to instruction buffer */
|
||||||
|
p.To.Type = obj.TYPE_BRANCH
|
||||||
|
self.pb.Append(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Rjmp(op string, to obj.Addr) {
|
||||||
|
p := self.pb.New()
|
||||||
|
p.To = to
|
||||||
|
p.As = As(op)
|
||||||
|
self.pb.Append(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) From(op string, val obj.Addr) {
|
||||||
|
p := self.pb.New()
|
||||||
|
p.As = As(op)
|
||||||
|
p.From = val
|
||||||
|
self.pb.Append(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Emit(op string, args ...obj.Addr) {
|
||||||
|
p := self.pb.New()
|
||||||
|
p.As = As(op)
|
||||||
|
self.assignOperands(p, args)
|
||||||
|
self.pb.Append(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) assignOperands(p *obj.Prog, args []obj.Addr) {
|
||||||
|
switch len(args) {
|
||||||
|
case 0 :
|
||||||
|
case 1 : p.To = args[0]
|
||||||
|
case 2 : p.To, p.From = args[1], args[0]
|
||||||
|
case 3 : p.To, p.From, p.RestArgs = args[2], args[0], args[1:2]
|
||||||
|
case 4 : p.To, p.From, p.RestArgs = args[2], args[3], args[:2]
|
||||||
|
default : panic("invalid operands")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Assembler Helpers **/
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Size() int {
|
||||||
|
self.build()
|
||||||
|
return len(self.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Init(f func()) {
|
||||||
|
self.i = 0
|
||||||
|
self.f = f
|
||||||
|
self.c = nil
|
||||||
|
self.o = sync.Once{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var jitLoader = loader.Loader{
|
||||||
|
Name: "sonic.jit.",
|
||||||
|
File: "github.com/bytedance/sonic/jit.go",
|
||||||
|
Options: loader.Options{
|
||||||
|
NoPreempt: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) Load(name string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) loader.Function {
|
||||||
|
self.build()
|
||||||
|
return jitLoader.LoadOne(self.c, name, frameSize, argSize, argStackmap, localStackmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Assembler Stages **/
|
||||||
|
|
||||||
|
func (self *BaseAssembler) init() {
|
||||||
|
self.pb = newBackend("amd64")
|
||||||
|
self.xrefs = map[string][]*obj.Prog{}
|
||||||
|
self.labels = map[string]*obj.Prog{}
|
||||||
|
self.pendings = map[string][]*obj.Prog{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) build() {
|
||||||
|
self.o.Do(func() {
|
||||||
|
self.init()
|
||||||
|
self.f()
|
||||||
|
self.validate()
|
||||||
|
self.assemble()
|
||||||
|
self.resolve()
|
||||||
|
self.release()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) release() {
|
||||||
|
self.pb.Release()
|
||||||
|
self.pb = nil
|
||||||
|
self.xrefs = nil
|
||||||
|
self.labels = nil
|
||||||
|
self.pendings = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) resolve() {
|
||||||
|
for s, v := range self.xrefs {
|
||||||
|
for _, prog := range v {
|
||||||
|
if prog.As != x86.ALONG {
|
||||||
|
panic("invalid RIP relative reference")
|
||||||
|
} else if p, ok := self.labels[s]; !ok {
|
||||||
|
panic("links are not fully resolved: " + s)
|
||||||
|
} else {
|
||||||
|
off := prog.From.Offset + p.Pc - prog.Pc
|
||||||
|
binary.LittleEndian.PutUint32(self.c[prog.Pc:], uint32(off))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) validate() {
|
||||||
|
for key := range self.pendings {
|
||||||
|
panic("links are not fully resolved: " + key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *BaseAssembler) assemble() {
|
||||||
|
self.c = self.pb.Assemble()
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* 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 jit
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`sync`
|
||||||
|
_ `unsafe`
|
||||||
|
|
||||||
|
`github.com/twitchyliquid64/golang-asm/asm/arch`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/objabi`
|
||||||
|
)
|
||||||
|
|
||||||
|
type Backend struct {
|
||||||
|
Ctxt *obj.Link
|
||||||
|
Arch *arch.Arch
|
||||||
|
Head *obj.Prog
|
||||||
|
Tail *obj.Prog
|
||||||
|
Prog []*obj.Prog
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_progPool sync.Pool
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:linkname throw runtime.throw
|
||||||
|
func throw(_ string)
|
||||||
|
|
||||||
|
func newProg() *obj.Prog {
|
||||||
|
if val := _progPool.Get(); val == nil {
|
||||||
|
return new(obj.Prog)
|
||||||
|
} else {
|
||||||
|
return remProg(val.(*obj.Prog))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func remProg(p *obj.Prog) *obj.Prog {
|
||||||
|
*p = obj.Prog{}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBackend(name string) (ret *Backend) {
|
||||||
|
ret = new(Backend)
|
||||||
|
ret.Arch = arch.Set(name)
|
||||||
|
ret.Ctxt = newLinkContext(ret.Arch.LinkArch)
|
||||||
|
ret.Arch.Init(ret.Ctxt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLinkContext(arch *obj.LinkArch) (ret *obj.Link) {
|
||||||
|
ret = obj.Linknew(arch)
|
||||||
|
ret.Headtype = objabi.Hlinux
|
||||||
|
ret.DiagFunc = diagLinkContext
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func diagLinkContext(str string, args ...interface{}) {
|
||||||
|
throw(fmt.Sprintf(str, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Backend) New() (ret *obj.Prog) {
|
||||||
|
ret = newProg()
|
||||||
|
ret.Ctxt = self.Ctxt
|
||||||
|
self.Prog = append(self.Prog, ret)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Backend) Append(p *obj.Prog) {
|
||||||
|
if self.Head == nil {
|
||||||
|
self.Head = p
|
||||||
|
self.Tail = p
|
||||||
|
} else {
|
||||||
|
self.Tail.Link = p
|
||||||
|
self.Tail = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Backend) Release() {
|
||||||
|
self.Arch = nil
|
||||||
|
self.Ctxt = nil
|
||||||
|
|
||||||
|
/* return all the progs into pool */
|
||||||
|
for _, p := range self.Prog {
|
||||||
|
_progPool.Put(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear all the references */
|
||||||
|
self.Head = nil
|
||||||
|
self.Tail = nil
|
||||||
|
self.Prog = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Backend) Assemble() []byte {
|
||||||
|
var sym obj.LSym
|
||||||
|
var fnv obj.FuncInfo
|
||||||
|
|
||||||
|
/* construct the function */
|
||||||
|
sym.Func = &fnv
|
||||||
|
fnv.Text = self.Head
|
||||||
|
|
||||||
|
/* call the assembler */
|
||||||
|
self.Arch.Assemble(self.Ctxt, &sym, self.New)
|
||||||
|
return sym.P
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* 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 jit
|
||||||
|
|
||||||
|
import (
|
||||||
|
`reflect`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname getitab runtime.getitab
|
||||||
|
//goland:noinspection ALL
|
||||||
|
func getitab(inter *rt.GoType, typ *rt.GoType, canfail bool) *rt.GoItab
|
||||||
|
|
||||||
|
func Func(f interface{}) obj.Addr {
|
||||||
|
if p := rt.UnpackEface(f); p.Type.Kind() != reflect.Func {
|
||||||
|
panic("f is not a function")
|
||||||
|
} else {
|
||||||
|
return Imm(*(*int64)(p.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Type(t reflect.Type) obj.Addr {
|
||||||
|
return Gtype(rt.UnpackType(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Itab(i *rt.GoType, t reflect.Type) obj.Addr {
|
||||||
|
return Imm(int64(uintptr(unsafe.Pointer(getitab(i, rt.UnpackType(t), false)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Gitab(i *rt.GoItab) obj.Addr {
|
||||||
|
return Imm(int64(uintptr(unsafe.Pointer(i))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Gtype(t *rt.GoType) obj.Addr {
|
||||||
|
return Imm(int64(uintptr(unsafe.Pointer(t))))
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* 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 loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
`reflect`
|
||||||
|
`sync`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname lastmoduledatap runtime.lastmoduledatap
|
||||||
|
//goland:noinspection GoUnusedGlobalVariable
|
||||||
|
var lastmoduledatap *_ModuleData
|
||||||
|
|
||||||
|
//go:linkname moduledataverify1 runtime.moduledataverify1
|
||||||
|
func moduledataverify1(_ *_ModuleData)
|
||||||
|
|
||||||
|
// PCDATA and FUNCDATA table indexes.
|
||||||
|
//
|
||||||
|
// See funcdata.h and $GROOT/src/cmd/internal/objabi/funcdata.go.
|
||||||
|
const (
|
||||||
|
_FUNCDATA_ArgsPointerMaps = 0
|
||||||
|
_FUNCDATA_LocalsPointerMaps = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type funcInfo struct {
|
||||||
|
*_Func
|
||||||
|
datap *_ModuleData
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname findfunc runtime.findfunc
|
||||||
|
func findfunc(pc uintptr) funcInfo
|
||||||
|
|
||||||
|
//go:linkname funcdata runtime.funcdata
|
||||||
|
func funcdata(f funcInfo, i uint8) unsafe.Pointer
|
||||||
|
|
||||||
|
var (
|
||||||
|
modLock sync.Mutex
|
||||||
|
modList []*_ModuleData
|
||||||
|
)
|
||||||
|
|
||||||
|
var emptyByte byte
|
||||||
|
|
||||||
|
func encodeVariant(v int) []byte {
|
||||||
|
var u int
|
||||||
|
var r []byte
|
||||||
|
|
||||||
|
/* split every 7 bits */
|
||||||
|
for v > 127 {
|
||||||
|
u = v & 0x7f
|
||||||
|
v = v >> 7
|
||||||
|
r = append(r, byte(u) | 0x80)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for last one */
|
||||||
|
if v == 0 {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the last one */
|
||||||
|
r = append(r, byte(v))
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerModule(mod *_ModuleData) {
|
||||||
|
modLock.Lock()
|
||||||
|
modList = append(modList, mod)
|
||||||
|
lastmoduledatap.next = mod
|
||||||
|
lastmoduledatap = mod
|
||||||
|
modLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func stackMap(f interface{}) (args uintptr, locals uintptr) {
|
||||||
|
fv := reflect.ValueOf(f)
|
||||||
|
if fv.Kind() != reflect.Func {
|
||||||
|
panic("f must be reflect.Func kind!")
|
||||||
|
}
|
||||||
|
fi := findfunc(fv.Pointer())
|
||||||
|
return uintptr(funcdata(fi, uint8(_FUNCDATA_ArgsPointerMaps))), uintptr(funcdata(fi, uint8(_FUNCDATA_LocalsPointerMaps)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var moduleCache = struct{
|
||||||
|
m map[*_ModuleData][]byte
|
||||||
|
l sync.Mutex
|
||||||
|
}{
|
||||||
|
m : make(map[*_ModuleData][]byte),
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheStackmap(argPtrs []bool, localPtrs []bool, mod *_ModuleData) (argptrs uintptr, localptrs uintptr) {
|
||||||
|
as := rt.StackMapBuilder{}
|
||||||
|
for _, b := range argPtrs {
|
||||||
|
as.AddField(b)
|
||||||
|
}
|
||||||
|
ab, _ := as.Build().MarshalBinary()
|
||||||
|
ls := rt.StackMapBuilder{}
|
||||||
|
for _, b := range localPtrs {
|
||||||
|
ls.AddField(b)
|
||||||
|
}
|
||||||
|
lb, _ := ls.Build().MarshalBinary()
|
||||||
|
cache := make([]byte, len(ab) + len(lb))
|
||||||
|
copy(cache, ab)
|
||||||
|
copy(cache[len(ab):], lb)
|
||||||
|
moduleCache.l.Lock()
|
||||||
|
moduleCache.m[mod] = cache
|
||||||
|
moduleCache.l.Unlock()
|
||||||
|
return uintptr(rt.IndexByte(cache, 0)), uintptr(rt.IndexByte(cache, len(ab)))
|
||||||
|
|
||||||
|
}
|
169
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go115.go
generated
vendored
Normal file
169
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go115.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// +build go1.15,!go1.16
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
type _Func struct {
|
||||||
|
entry uintptr // start pc
|
||||||
|
nameoff int32 // function name
|
||||||
|
args int32 // in/out args size
|
||||||
|
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||||
|
pcsp int32
|
||||||
|
pcfile int32
|
||||||
|
pcln int32
|
||||||
|
npcdata int32
|
||||||
|
funcID uint8 // set for certain special runtime functions
|
||||||
|
_ [2]int8 // unused
|
||||||
|
nfuncdata uint8 // must be last
|
||||||
|
argptrs uintptr
|
||||||
|
localptrs uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type _FuncTab struct {
|
||||||
|
entry uintptr
|
||||||
|
funcoff uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type _BitVector struct {
|
||||||
|
n int32 // # of bits
|
||||||
|
bytedata *uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type _PtabEntry struct {
|
||||||
|
name int32
|
||||||
|
typ int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _TextSection struct {
|
||||||
|
vaddr uintptr // prelinked section vaddr
|
||||||
|
length uintptr // section length
|
||||||
|
baseaddr uintptr // relocated section address
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ModuleData struct {
|
||||||
|
pclntable []byte
|
||||||
|
ftab []_FuncTab
|
||||||
|
filetab []uint32
|
||||||
|
findfunctab *_FindFuncBucket
|
||||||
|
minpc, maxpc uintptr
|
||||||
|
text, etext uintptr
|
||||||
|
noptrdata, enoptrdata uintptr
|
||||||
|
data, edata uintptr
|
||||||
|
bss, ebss uintptr
|
||||||
|
noptrbss, enoptrbss uintptr
|
||||||
|
end, gcdata, gcbss uintptr
|
||||||
|
types, etypes uintptr
|
||||||
|
textsectmap []_TextSection
|
||||||
|
typelinks []int32 // offsets from types
|
||||||
|
itablinks []*rt.GoItab
|
||||||
|
ptab []_PtabEntry
|
||||||
|
pluginpath string
|
||||||
|
pkghashes []byte
|
||||||
|
modulename string
|
||||||
|
modulehashes []byte
|
||||||
|
hasmain uint8 // 1 if module contains the main function, 0 otherwise
|
||||||
|
gcdatamask, gcbssmask _BitVector
|
||||||
|
typemap map[int32]*rt.GoType // offset to *_rtype in previous module
|
||||||
|
bad bool // module failed to load and should be ignored
|
||||||
|
next *_ModuleData
|
||||||
|
}
|
||||||
|
|
||||||
|
type _FindFuncBucket struct {
|
||||||
|
idx uint32
|
||||||
|
subbuckets [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var findFuncTab = &_FindFuncBucket {
|
||||||
|
idx: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
|
minpc := pc
|
||||||
|
maxpc := pc + size
|
||||||
|
|
||||||
|
/* build the PC & line table */
|
||||||
|
pclnt := []byte {
|
||||||
|
0xfb, 0xff, 0xff, 0xff, // magic : 0xfffffffb
|
||||||
|
0, // pad1 : 0
|
||||||
|
0, // pad2 : 0
|
||||||
|
1, // minLC : 1
|
||||||
|
4 << (^uintptr(0) >> 63), // ptrSize : 4 << (^uintptr(0) >> 63)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
|
/* add the function name */
|
||||||
|
noff := len(pclnt)
|
||||||
|
pclnt = append(append(pclnt, name...), 0)
|
||||||
|
|
||||||
|
/* add PCDATA */
|
||||||
|
pcsp := len(pclnt)
|
||||||
|
pclnt = append(pclnt, encodeVariant((fp + 1) << 1)...)
|
||||||
|
pclnt = append(pclnt, encodeVariant(int(size))...)
|
||||||
|
|
||||||
|
/* function entry */
|
||||||
|
fnv := _Func {
|
||||||
|
entry : pc,
|
||||||
|
nameoff : int32(noff),
|
||||||
|
args : int32(args),
|
||||||
|
pcsp : int32(pcsp),
|
||||||
|
nfuncdata : 2,
|
||||||
|
argptrs : uintptr(argptrs),
|
||||||
|
localptrs : uintptr(localptrs),
|
||||||
|
}
|
||||||
|
|
||||||
|
/* align the func to 8 bytes */
|
||||||
|
if p := len(pclnt) % 8; p != 0 {
|
||||||
|
pclnt = append(pclnt, make([]byte, 8 - p)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the function descriptor */
|
||||||
|
foff := len(pclnt)
|
||||||
|
pclnt = append(pclnt, (*(*[unsafe.Sizeof(_Func{})]byte)(unsafe.Pointer(&fnv)))[:]...)
|
||||||
|
|
||||||
|
/* function table */
|
||||||
|
tab := []_FuncTab {
|
||||||
|
{entry: pc, funcoff: uintptr(foff)},
|
||||||
|
{entry: pc, funcoff: uintptr(foff)},
|
||||||
|
{entry: maxpc},
|
||||||
|
}
|
||||||
|
|
||||||
|
/* module data */
|
||||||
|
*mod = _ModuleData {
|
||||||
|
pclntable : pclnt,
|
||||||
|
ftab : tab,
|
||||||
|
findfunctab : findFuncTab,
|
||||||
|
minpc : minpc,
|
||||||
|
maxpc : maxpc,
|
||||||
|
modulename : name,
|
||||||
|
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify and register the new module */
|
||||||
|
moduledataverify1(mod)
|
||||||
|
registerModule(mod)
|
||||||
|
}
|
175
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go116.go
generated
vendored
Normal file
175
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go116.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
//go:build go1.16 && !go1.18
|
||||||
|
// +build go1.16,!go1.18
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
)
|
||||||
|
|
||||||
|
type _Func struct {
|
||||||
|
entry uintptr // start pc
|
||||||
|
nameoff int32 // function name
|
||||||
|
args int32 // in/out args size
|
||||||
|
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||||
|
pcsp uint32
|
||||||
|
pcfile uint32
|
||||||
|
pcln uint32
|
||||||
|
npcdata uint32
|
||||||
|
cuOffset uint32 // runtime.cutab offset of this function's CU
|
||||||
|
funcID uint8 // set for certain special runtime functions
|
||||||
|
_ [2]byte // pad
|
||||||
|
nfuncdata uint8 // must be last
|
||||||
|
argptrs uintptr
|
||||||
|
localptrs uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type _FuncTab struct {
|
||||||
|
entry uintptr
|
||||||
|
funcoff uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type _PCHeader struct {
|
||||||
|
magic uint32 // 0xFFFFFFFA
|
||||||
|
pad1, pad2 uint8 // 0,0
|
||||||
|
minLC uint8 // min instruction size
|
||||||
|
ptrSize uint8 // size of a ptr in bytes
|
||||||
|
nfunc int // number of functions in the module
|
||||||
|
nfiles uint // number of entries in the file tab.
|
||||||
|
funcnameOffset uintptr // offset to the funcnametab variable from _PCHeader
|
||||||
|
cuOffset uintptr // offset to the cutab variable from _PCHeader
|
||||||
|
filetabOffset uintptr // offset to the filetab variable from _PCHeader
|
||||||
|
pctabOffset uintptr // offset to the pctab varible from _PCHeader
|
||||||
|
pclnOffset uintptr // offset to the pclntab variable from _PCHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
type _BitVector struct {
|
||||||
|
n int32 // # of bits
|
||||||
|
bytedata *uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type _PtabEntry struct {
|
||||||
|
name int32
|
||||||
|
typ int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _TextSection struct {
|
||||||
|
vaddr uintptr // prelinked section vaddr
|
||||||
|
length uintptr // section length
|
||||||
|
baseaddr uintptr // relocated section address
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ModuleData struct {
|
||||||
|
pcHeader *_PCHeader
|
||||||
|
funcnametab []byte
|
||||||
|
cutab []uint32
|
||||||
|
filetab []byte
|
||||||
|
pctab []byte
|
||||||
|
pclntable []_Func
|
||||||
|
ftab []_FuncTab
|
||||||
|
findfunctab *_FindFuncBucket
|
||||||
|
minpc, maxpc uintptr
|
||||||
|
text, etext uintptr
|
||||||
|
noptrdata, enoptrdata uintptr
|
||||||
|
data, edata uintptr
|
||||||
|
bss, ebss uintptr
|
||||||
|
noptrbss, enoptrbss uintptr
|
||||||
|
end, gcdata, gcbss uintptr
|
||||||
|
types, etypes uintptr
|
||||||
|
textsectmap []_TextSection
|
||||||
|
typelinks []int32
|
||||||
|
itablinks []unsafe.Pointer
|
||||||
|
ptab []_PtabEntry
|
||||||
|
pluginpath string
|
||||||
|
pkghashes []struct{}
|
||||||
|
modulename string
|
||||||
|
modulehashes []struct{}
|
||||||
|
hasmain uint8
|
||||||
|
gcdatamask, gcbssmask _BitVector
|
||||||
|
typemap map[int32]unsafe.Pointer
|
||||||
|
bad bool
|
||||||
|
next *_ModuleData
|
||||||
|
}
|
||||||
|
|
||||||
|
type _FindFuncBucket struct {
|
||||||
|
idx uint32
|
||||||
|
subbuckets [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var modHeader = &_PCHeader {
|
||||||
|
magic : 0xfffffffa,
|
||||||
|
minLC : 1,
|
||||||
|
nfunc : 1,
|
||||||
|
ptrSize : 4 << (^uintptr(0) >> 63),
|
||||||
|
}
|
||||||
|
|
||||||
|
var findFuncTab = &_FindFuncBucket {
|
||||||
|
idx: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func makePCtab(fp int) []byte {
|
||||||
|
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
|
|
||||||
|
minpc := pc
|
||||||
|
maxpc := pc + size
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
|
/* function entry */
|
||||||
|
lnt := []_Func {{
|
||||||
|
entry : pc,
|
||||||
|
nameoff : 1,
|
||||||
|
args : int32(args),
|
||||||
|
pcsp : 1,
|
||||||
|
nfuncdata : 2,
|
||||||
|
argptrs : uintptr(argptrs),
|
||||||
|
localptrs : uintptr(localptrs),
|
||||||
|
}}
|
||||||
|
|
||||||
|
/* function table */
|
||||||
|
tab := []_FuncTab {
|
||||||
|
{entry: pc},
|
||||||
|
{entry: pc},
|
||||||
|
{entry: maxpc},
|
||||||
|
}
|
||||||
|
|
||||||
|
/* module data */
|
||||||
|
*mod = _ModuleData {
|
||||||
|
pcHeader : modHeader,
|
||||||
|
funcnametab : append(append([]byte{0}, name...), 0),
|
||||||
|
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||||
|
pclntable : lnt,
|
||||||
|
ftab : tab,
|
||||||
|
findfunctab : findFuncTab,
|
||||||
|
minpc : minpc,
|
||||||
|
maxpc : maxpc,
|
||||||
|
modulename : name,
|
||||||
|
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify and register the new module */
|
||||||
|
moduledataverify1(mod)
|
||||||
|
registerModule(mod)
|
||||||
|
}
|
201
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go118.go
generated
vendored
Normal file
201
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go118.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// +build go1.18,!go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
// A FuncFlag holds bits about a function.
|
||||||
|
// This list must match the list in cmd/internal/objabi/funcid.go.
|
||||||
|
type funcFlag uint8
|
||||||
|
|
||||||
|
type _Func struct {
|
||||||
|
entryOff uint32 // start pc
|
||||||
|
nameoff int32 // function name
|
||||||
|
args int32 // in/out args size
|
||||||
|
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||||
|
pcsp uint32
|
||||||
|
pcfile uint32
|
||||||
|
pcln uint32
|
||||||
|
npcdata uint32
|
||||||
|
cuOffset uint32 // runtime.cutab offset of this function's CU
|
||||||
|
funcID uint8 // set for certain special runtime functions
|
||||||
|
flag funcFlag
|
||||||
|
_ [1]byte // pad
|
||||||
|
nfuncdata uint8 // must be last
|
||||||
|
argptrs uint32
|
||||||
|
localptrs uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _FuncTab struct {
|
||||||
|
entry uint32
|
||||||
|
funcoff uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _PCHeader struct {
|
||||||
|
magic uint32 // 0xFFFFFFF0
|
||||||
|
pad1, pad2 uint8 // 0,0
|
||||||
|
minLC uint8 // min instruction size
|
||||||
|
ptrSize uint8 // size of a ptr in bytes
|
||||||
|
nfunc int // number of functions in the module
|
||||||
|
nfiles uint // number of entries in the file tab
|
||||||
|
textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
|
||||||
|
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
|
||||||
|
cuOffset uintptr // offset to the cutab variable from pcHeader
|
||||||
|
filetabOffset uintptr // offset to the filetab variable from pcHeader
|
||||||
|
pctabOffset uintptr // offset to the pctab variable from pcHeader
|
||||||
|
pclnOffset uintptr // offset to the pclntab variable from pcHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
type _BitVector struct {
|
||||||
|
n int32 // # of bits
|
||||||
|
bytedata *uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type _PtabEntry struct {
|
||||||
|
name int32
|
||||||
|
typ int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _TextSection struct {
|
||||||
|
vaddr uintptr // prelinked section vaddr
|
||||||
|
length uintptr // section length
|
||||||
|
baseaddr uintptr // relocated section address
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ModuleData struct {
|
||||||
|
pcHeader *_PCHeader
|
||||||
|
funcnametab []byte
|
||||||
|
cutab []uint32
|
||||||
|
filetab []byte
|
||||||
|
pctab []byte
|
||||||
|
pclntable []byte
|
||||||
|
ftab []_FuncTab
|
||||||
|
findfunctab *_FindFuncBucket
|
||||||
|
minpc, maxpc uintptr
|
||||||
|
text, etext uintptr
|
||||||
|
noptrdata, enoptrdata uintptr
|
||||||
|
data, edata uintptr
|
||||||
|
bss, ebss uintptr
|
||||||
|
noptrbss, enoptrbss uintptr
|
||||||
|
end, gcdata, gcbss uintptr
|
||||||
|
types, etypes uintptr
|
||||||
|
rodata uintptr
|
||||||
|
gofunc uintptr
|
||||||
|
textsectmap []_TextSection
|
||||||
|
typelinks []int32
|
||||||
|
itablinks []unsafe.Pointer
|
||||||
|
ptab []_PtabEntry
|
||||||
|
pluginpath string
|
||||||
|
pkghashes []struct{}
|
||||||
|
modulename string
|
||||||
|
modulehashes []struct{}
|
||||||
|
hasmain uint8
|
||||||
|
gcdatamask, gcbssmask _BitVector
|
||||||
|
typemap map[int32]unsafe.Pointer
|
||||||
|
bad bool
|
||||||
|
next *_ModuleData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type _FindFuncBucket struct {
|
||||||
|
idx uint32
|
||||||
|
subbuckets [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func makePCtab(fp int) []byte {
|
||||||
|
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
|
|
||||||
|
minpc := pc
|
||||||
|
maxpc := pc + size
|
||||||
|
|
||||||
|
findFuncTab := make([]_FindFuncBucket, textSize/4096 + 1)
|
||||||
|
|
||||||
|
modHeader := &_PCHeader {
|
||||||
|
magic : 0xfffffff0,
|
||||||
|
minLC : 1,
|
||||||
|
nfunc : 1,
|
||||||
|
ptrSize : 4 << (^uintptr(0) >> 63),
|
||||||
|
textStart: minpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
|
base := argptrs
|
||||||
|
if argptrs > localptrs {
|
||||||
|
base = localptrs
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function entry */
|
||||||
|
lnt := []_Func {{
|
||||||
|
entryOff : 0,
|
||||||
|
nameoff : 1,
|
||||||
|
args : int32(args),
|
||||||
|
pcsp : 1,
|
||||||
|
nfuncdata : 2,
|
||||||
|
argptrs: uint32(argptrs - base),
|
||||||
|
localptrs: uint32(localptrs - base),
|
||||||
|
}}
|
||||||
|
nlnt := len(lnt)*int(unsafe.Sizeof(_Func{}))
|
||||||
|
plnt := unsafe.Pointer(&lnt[0])
|
||||||
|
|
||||||
|
/* function table */
|
||||||
|
ftab := []_FuncTab {
|
||||||
|
{entry : 0, funcoff : 16},
|
||||||
|
{entry : uint32(size)},
|
||||||
|
}
|
||||||
|
nftab := len(ftab)*int(unsafe.Sizeof(_FuncTab{}))
|
||||||
|
pftab := unsafe.Pointer(&ftab[0])
|
||||||
|
|
||||||
|
pclntab := make([]byte, 0, nftab + nlnt)
|
||||||
|
pclntab = append(pclntab, rt.BytesFrom(pftab, nftab, nftab)...)
|
||||||
|
pclntab = append(pclntab, rt.BytesFrom(plnt, nlnt, nlnt)...)
|
||||||
|
|
||||||
|
/* module data */
|
||||||
|
*mod = _ModuleData {
|
||||||
|
pcHeader : modHeader,
|
||||||
|
funcnametab : append(append([]byte{0}, name...), 0),
|
||||||
|
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||||
|
pclntable : pclntab,
|
||||||
|
ftab : ftab,
|
||||||
|
text : minpc,
|
||||||
|
etext : pc + textSize,
|
||||||
|
findfunctab : &findFuncTab[0],
|
||||||
|
minpc : minpc,
|
||||||
|
maxpc : maxpc,
|
||||||
|
modulename : name,
|
||||||
|
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gofunc: base,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify and register the new module */
|
||||||
|
moduledataverify1(mod)
|
||||||
|
registerModule(mod)
|
||||||
|
}
|
201
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go120.go
generated
vendored
Normal file
201
vendor/github.com/bytedance/sonic/internal/loader/funcdata_go120.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// +build go1.20
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
// A FuncFlag holds bits about a function.
|
||||||
|
// This list must match the list in cmd/internal/objabi/funcid.go.
|
||||||
|
type funcFlag uint8
|
||||||
|
|
||||||
|
type _Func struct {
|
||||||
|
entryOff uint32 // start pc
|
||||||
|
nameoff int32 // function name
|
||||||
|
args int32 // in/out args size
|
||||||
|
deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
|
||||||
|
pcsp uint32
|
||||||
|
pcfile uint32
|
||||||
|
pcln uint32
|
||||||
|
npcdata uint32
|
||||||
|
cuOffset uint32 // runtime.cutab offset of this function's CU
|
||||||
|
funcID uint8 // set for certain special runtime functions
|
||||||
|
flag funcFlag
|
||||||
|
_ [1]byte // pad
|
||||||
|
nfuncdata uint8 // must be last
|
||||||
|
argptrs uint32
|
||||||
|
localptrs uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _FuncTab struct {
|
||||||
|
entry uint32
|
||||||
|
funcoff uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _PCHeader struct {
|
||||||
|
magic uint32 // 0xFFFFFFF0
|
||||||
|
pad1, pad2 uint8 // 0,0
|
||||||
|
minLC uint8 // min instruction size
|
||||||
|
ptrSize uint8 // size of a ptr in bytes
|
||||||
|
nfunc int // number of functions in the module
|
||||||
|
nfiles uint // number of entries in the file tab
|
||||||
|
textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
|
||||||
|
funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
|
||||||
|
cuOffset uintptr // offset to the cutab variable from pcHeader
|
||||||
|
filetabOffset uintptr // offset to the filetab variable from pcHeader
|
||||||
|
pctabOffset uintptr // offset to the pctab variable from pcHeader
|
||||||
|
pclnOffset uintptr // offset to the pclntab variable from pcHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
type _BitVector struct {
|
||||||
|
n int32 // # of bits
|
||||||
|
bytedata *uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type _PtabEntry struct {
|
||||||
|
name int32
|
||||||
|
typ int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type _TextSection struct {
|
||||||
|
vaddr uintptr // prelinked section vaddr
|
||||||
|
length uintptr // section length
|
||||||
|
baseaddr uintptr // relocated section address
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ModuleData struct {
|
||||||
|
pcHeader *_PCHeader
|
||||||
|
funcnametab []byte
|
||||||
|
cutab []uint32
|
||||||
|
filetab []byte
|
||||||
|
pctab []byte
|
||||||
|
pclntable []byte
|
||||||
|
ftab []_FuncTab
|
||||||
|
findfunctab *_FindFuncBucket
|
||||||
|
minpc, maxpc uintptr
|
||||||
|
text, etext uintptr
|
||||||
|
noptrdata, enoptrdata uintptr
|
||||||
|
data, edata uintptr
|
||||||
|
bss, ebss uintptr
|
||||||
|
noptrbss, enoptrbss uintptr
|
||||||
|
end, gcdata, gcbss uintptr
|
||||||
|
types, etypes uintptr
|
||||||
|
rodata uintptr
|
||||||
|
gofunc uintptr
|
||||||
|
textsectmap []_TextSection
|
||||||
|
typelinks []int32
|
||||||
|
itablinks []unsafe.Pointer
|
||||||
|
ptab []_PtabEntry
|
||||||
|
pluginpath string
|
||||||
|
pkghashes []struct{}
|
||||||
|
modulename string
|
||||||
|
modulehashes []struct{}
|
||||||
|
hasmain uint8
|
||||||
|
gcdatamask, gcbssmask _BitVector
|
||||||
|
typemap map[int32]unsafe.Pointer
|
||||||
|
bad bool
|
||||||
|
next *_ModuleData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type _FindFuncBucket struct {
|
||||||
|
idx uint32
|
||||||
|
subbuckets [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func makePCtab(fp int) []byte {
|
||||||
|
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
|
|
||||||
|
minpc := pc
|
||||||
|
maxpc := pc + size
|
||||||
|
|
||||||
|
findFuncTab := make([]_FindFuncBucket, textSize/4096 + 1)
|
||||||
|
|
||||||
|
modHeader := &_PCHeader {
|
||||||
|
magic : 0xfffffff0,
|
||||||
|
minLC : 1,
|
||||||
|
nfunc : 1,
|
||||||
|
ptrSize : 4 << (^uintptr(0) >> 63),
|
||||||
|
textStart: minpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
|
base := argptrs
|
||||||
|
if argptrs > localptrs {
|
||||||
|
base = localptrs
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function entry */
|
||||||
|
lnt := []_Func {{
|
||||||
|
entryOff : 0,
|
||||||
|
nameoff : 1,
|
||||||
|
args : int32(args),
|
||||||
|
pcsp : 1,
|
||||||
|
nfuncdata : 2,
|
||||||
|
argptrs: uint32(argptrs - base),
|
||||||
|
localptrs: uint32(localptrs - base),
|
||||||
|
}}
|
||||||
|
nlnt := len(lnt)*int(unsafe.Sizeof(_Func{}))
|
||||||
|
plnt := unsafe.Pointer(&lnt[0])
|
||||||
|
|
||||||
|
/* function table */
|
||||||
|
ftab := []_FuncTab {
|
||||||
|
{entry : 0, funcoff : 16},
|
||||||
|
{entry : uint32(size)},
|
||||||
|
}
|
||||||
|
nftab := len(ftab)*int(unsafe.Sizeof(_FuncTab{}))
|
||||||
|
pftab := unsafe.Pointer(&ftab[0])
|
||||||
|
|
||||||
|
pclntab := make([]byte, 0, nftab + nlnt)
|
||||||
|
pclntab = append(pclntab, rt.BytesFrom(pftab, nftab, nftab)...)
|
||||||
|
pclntab = append(pclntab, rt.BytesFrom(plnt, nlnt, nlnt)...)
|
||||||
|
|
||||||
|
/* module data */
|
||||||
|
*mod = _ModuleData {
|
||||||
|
pcHeader : modHeader,
|
||||||
|
funcnametab : append(append([]byte{0}, name...), 0),
|
||||||
|
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||||
|
pclntable : pclntab,
|
||||||
|
ftab : ftab,
|
||||||
|
text : minpc,
|
||||||
|
etext : pc + textSize,
|
||||||
|
findfunctab : &findFuncTab[0],
|
||||||
|
minpc : minpc,
|
||||||
|
maxpc : maxpc,
|
||||||
|
modulename : name,
|
||||||
|
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gofunc: base,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify and register the new module */
|
||||||
|
moduledataverify1(mod)
|
||||||
|
registerModule(mod)
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
//go:build darwin || linux
|
||||||
|
// +build darwin linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`os`
|
||||||
|
`reflect`
|
||||||
|
`syscall`
|
||||||
|
`unsafe`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_AP = syscall.MAP_ANON | syscall.MAP_PRIVATE
|
||||||
|
_RX = syscall.PROT_READ | syscall.PROT_EXEC
|
||||||
|
_RW = syscall.PROT_READ | syscall.PROT_WRITE
|
||||||
|
)
|
||||||
|
|
||||||
|
type Loader []byte
|
||||||
|
type Function unsafe.Pointer
|
||||||
|
|
||||||
|
func (self Loader) Load(fn string, fp int, args int, argPtrs []bool, localPtrs []bool) (f Function) {
|
||||||
|
p := os.Getpagesize()
|
||||||
|
n := (((len(self) - 1) / p) + 1) * p
|
||||||
|
|
||||||
|
/* register the function */
|
||||||
|
m := mmap(n)
|
||||||
|
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
||||||
|
|
||||||
|
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argPtrs, localPtrs)
|
||||||
|
|
||||||
|
/* reference as a slice */
|
||||||
|
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
||||||
|
Data : m,
|
||||||
|
Cap : n,
|
||||||
|
Len : len(self),
|
||||||
|
}))
|
||||||
|
|
||||||
|
/* copy the machine code, and make it executable */
|
||||||
|
copy(s, self)
|
||||||
|
mprotect(m, n)
|
||||||
|
return Function(&m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mmap(nb int) uintptr {
|
||||||
|
if m, _, e := syscall.RawSyscall6(syscall.SYS_MMAP, 0, uintptr(nb), _RW, _AP, 0, 0); e != 0 {
|
||||||
|
panic(e)
|
||||||
|
} else {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mprotect(p uintptr, nb int) {
|
||||||
|
if _, _, err := syscall.RawSyscall(syscall.SYS_MPROTECT, p, uintptr(nb), _RX); err != 0 {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
111
vendor/github.com/bytedance/sonic/internal/loader/loader_windows.go
generated
vendored
Normal file
111
vendor/github.com/bytedance/sonic/internal/loader/loader_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`os`
|
||||||
|
`reflect`
|
||||||
|
`syscall`
|
||||||
|
`unsafe`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MEM_COMMIT = 0x00001000
|
||||||
|
MEM_RESERVE = 0x00002000
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
libKernel32 = syscall.NewLazyDLL("KERNEL32.DLL")
|
||||||
|
libKernel32_VirtualAlloc = libKernel32.NewProc("VirtualAlloc")
|
||||||
|
libKernel32_VirtualProtect = libKernel32.NewProc("VirtualProtect")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Loader []byte
|
||||||
|
type Function unsafe.Pointer
|
||||||
|
|
||||||
|
func (self Loader) Load(fn string, fp int, args int, argPtrs []bool, localPtrs []bool) (f Function) {
|
||||||
|
p := os.Getpagesize()
|
||||||
|
n := (((len(self) - 1) / p) + 1) * p
|
||||||
|
|
||||||
|
/* register the function */
|
||||||
|
m := mmap(n)
|
||||||
|
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
||||||
|
|
||||||
|
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argPtrs, localPtrs)
|
||||||
|
|
||||||
|
/* reference as a slice */
|
||||||
|
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
||||||
|
Data : m,
|
||||||
|
Cap : n,
|
||||||
|
Len : len(self),
|
||||||
|
}))
|
||||||
|
|
||||||
|
/* copy the machine code, and make it executable */
|
||||||
|
copy(s, self)
|
||||||
|
mprotect(m, n)
|
||||||
|
return Function(&m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mmap(nb int) uintptr {
|
||||||
|
addr, err := winapi_VirtualAlloc(0, nb, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func mprotect(p uintptr, nb int) (oldProtect int) {
|
||||||
|
err := winapi_VirtualProtect(p, nb, syscall.PAGE_EXECUTE_READ, &oldProtect)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// winapi_VirtualAlloc allocate memory
|
||||||
|
// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
|
||||||
|
func winapi_VirtualAlloc(lpAddr uintptr, dwSize int, flAllocationType int, flProtect int) (uintptr, error) {
|
||||||
|
r1, _, err := libKernel32_VirtualAlloc.Call(
|
||||||
|
lpAddr,
|
||||||
|
uintptr(dwSize),
|
||||||
|
uintptr(flAllocationType),
|
||||||
|
uintptr(flProtect),
|
||||||
|
)
|
||||||
|
if r1 == 0 {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return r1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// winapi_VirtualProtect change memory protection
|
||||||
|
// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect
|
||||||
|
func winapi_VirtualProtect(lpAddr uintptr, dwSize int, flNewProtect int, lpflOldProtect *int) error {
|
||||||
|
r1, _, err := libKernel32_VirtualProtect.Call(
|
||||||
|
lpAddr,
|
||||||
|
uintptr(dwSize),
|
||||||
|
uintptr(flNewProtect),
|
||||||
|
uintptr(unsafe.Pointer(lpflOldProtect)),
|
||||||
|
)
|
||||||
|
if r1 == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
135
vendor/github.com/bytedance/sonic/internal/native/avx/native_amd64.go
generated
vendored
Normal file
135
vendor/github.com/bytedance/sonic/internal/native/avx/native_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// Code generated by Makefile, DO NOT EDIT.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 avx
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __i64toa(out *byte, val int64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __u64toa(out *byte, val uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __f64toa(out *byte, val float64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __f32toa(out *byte, val float32) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __lspace(sp unsafe.Pointer, nb int, off int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __quote(sp unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __html_escape(sp unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __unquote(sp unsafe.Pointer, nb int, dp unsafe.Pointer, ep *int, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __value(s unsafe.Pointer, n int, p int, v *types.JsonState, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vstring(s *string, p *int, v *types.JsonState, flags uint64)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vnumber(s *string, p *int, v *types.JsonState)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vsigned(s *string, p *int, v *types.JsonState)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vunsigned(s *string, p *int, v *types.JsonState)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one_fast(s *string, p *int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_array(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_object(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_number(s *string, p *int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __get_by_path(s *string, p *int, path *[]interface{}) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __validate_utf8(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __validate_utf8_fast(s *string) (ret int)
|
15286
vendor/github.com/bytedance/sonic/internal/native/avx/native_amd64.s
generated
vendored
Normal file
15286
vendor/github.com/bytedance/sonic/internal/native/avx/native_amd64.s
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
49
vendor/github.com/bytedance/sonic/internal/native/avx/native_export_amd64.go
generated
vendored
Normal file
49
vendor/github.com/bytedance/sonic/internal/native/avx/native_export_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Code generated by Makefile, DO NOT EDIT.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 avx
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_f64toa = _subr__f64toa
|
||||||
|
S_f32toa = _subr__f32toa
|
||||||
|
S_i64toa = _subr__i64toa
|
||||||
|
S_u64toa = _subr__u64toa
|
||||||
|
S_lspace = _subr__lspace
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_quote = _subr__quote
|
||||||
|
S_unquote = _subr__unquote
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_value = _subr__value
|
||||||
|
S_vstring = _subr__vstring
|
||||||
|
S_vnumber = _subr__vnumber
|
||||||
|
S_vsigned = _subr__vsigned
|
||||||
|
S_vunsigned = _subr__vunsigned
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_skip_one = _subr__skip_one
|
||||||
|
S_skip_one_fast = _subr__skip_one_fast
|
||||||
|
S_skip_array = _subr__skip_array
|
||||||
|
S_skip_object = _subr__skip_object
|
||||||
|
S_skip_number = _subr__skip_number
|
||||||
|
S_get_by_path = _subr__get_by_path
|
||||||
|
)
|
109
vendor/github.com/bytedance/sonic/internal/native/avx/native_subr_amd64.go
generated
vendored
Normal file
109
vendor/github.com/bytedance/sonic/internal/native/avx/native_subr_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
// +build !noasm !appengine
|
||||||
|
// Code generated by asm2asm, DO NOT EDIT.
|
||||||
|
|
||||||
|
package avx
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection ALL
|
||||||
|
func __native_entry__() uintptr
|
||||||
|
|
||||||
|
var (
|
||||||
|
_subr__f32toa = __native_entry__() + 29744
|
||||||
|
_subr__f64toa = __native_entry__() + 496
|
||||||
|
_subr__get_by_path = __native_entry__() + 27424
|
||||||
|
_subr__html_escape = __native_entry__() + 9968
|
||||||
|
_subr__i64toa = __native_entry__() + 4112
|
||||||
|
_subr__lspace = __native_entry__() + 80
|
||||||
|
_subr__quote = __native_entry__() + 5792
|
||||||
|
_subr__skip_array = __native_entry__() + 20576
|
||||||
|
_subr__skip_number = __native_entry__() + 23920
|
||||||
|
_subr__skip_object = __native_entry__() + 22496
|
||||||
|
_subr__skip_one = __native_entry__() + 24080
|
||||||
|
_subr__skip_one_fast = __native_entry__() + 24320
|
||||||
|
_subr__u64toa = __native_entry__() + 4384
|
||||||
|
_subr__unquote = __native_entry__() + 7488
|
||||||
|
_subr__validate_one = __native_entry__() + 24144
|
||||||
|
_subr__validate_utf8 = __native_entry__() + 28464
|
||||||
|
_subr__validate_utf8_fast = __native_entry__() + 29136
|
||||||
|
_subr__value = __native_entry__() + 14672
|
||||||
|
_subr__vnumber = __native_entry__() + 18320
|
||||||
|
_subr__vsigned = __native_entry__() + 19856
|
||||||
|
_subr__vstring = __native_entry__() + 16864
|
||||||
|
_subr__vunsigned = __native_entry__() + 20208
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_stack__f32toa = 56
|
||||||
|
_stack__f64toa = 80
|
||||||
|
_stack__get_by_path = 312
|
||||||
|
_stack__html_escape = 64
|
||||||
|
_stack__i64toa = 16
|
||||||
|
_stack__lspace = 8
|
||||||
|
_stack__quote = 80
|
||||||
|
_stack__skip_array = 128
|
||||||
|
_stack__skip_number = 72
|
||||||
|
_stack__skip_object = 128
|
||||||
|
_stack__skip_one = 128
|
||||||
|
_stack__skip_one_fast = 208
|
||||||
|
_stack__u64toa = 8
|
||||||
|
_stack__unquote = 128
|
||||||
|
_stack__validate_one = 128
|
||||||
|
_stack__validate_utf8 = 48
|
||||||
|
_stack__validate_utf8_fast = 24
|
||||||
|
_stack__value = 368
|
||||||
|
_stack__vnumber = 280
|
||||||
|
_stack__vsigned = 16
|
||||||
|
_stack__vstring = 128
|
||||||
|
_stack__vunsigned = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ = _subr__f32toa
|
||||||
|
_ = _subr__f64toa
|
||||||
|
_ = _subr__get_by_path
|
||||||
|
_ = _subr__html_escape
|
||||||
|
_ = _subr__i64toa
|
||||||
|
_ = _subr__lspace
|
||||||
|
_ = _subr__quote
|
||||||
|
_ = _subr__skip_array
|
||||||
|
_ = _subr__skip_number
|
||||||
|
_ = _subr__skip_object
|
||||||
|
_ = _subr__skip_one
|
||||||
|
_ = _subr__skip_one_fast
|
||||||
|
_ = _subr__u64toa
|
||||||
|
_ = _subr__unquote
|
||||||
|
_ = _subr__validate_one
|
||||||
|
_ = _subr__validate_utf8
|
||||||
|
_ = _subr__validate_utf8_fast
|
||||||
|
_ = _subr__value
|
||||||
|
_ = _subr__vnumber
|
||||||
|
_ = _subr__vsigned
|
||||||
|
_ = _subr__vstring
|
||||||
|
_ = _subr__vunsigned
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ = _stack__f32toa
|
||||||
|
_ = _stack__f64toa
|
||||||
|
_ = _stack__get_by_path
|
||||||
|
_ = _stack__html_escape
|
||||||
|
_ = _stack__i64toa
|
||||||
|
_ = _stack__lspace
|
||||||
|
_ = _stack__quote
|
||||||
|
_ = _stack__skip_array
|
||||||
|
_ = _stack__skip_number
|
||||||
|
_ = _stack__skip_object
|
||||||
|
_ = _stack__skip_one
|
||||||
|
_ = _stack__skip_one_fast
|
||||||
|
_ = _stack__u64toa
|
||||||
|
_ = _stack__unquote
|
||||||
|
_ = _stack__validate_one
|
||||||
|
_ = _stack__validate_utf8
|
||||||
|
_ = _stack__validate_utf8_fast
|
||||||
|
_ = _stack__value
|
||||||
|
_ = _stack__vnumber
|
||||||
|
_ = _stack__vsigned
|
||||||
|
_ = _stack__vstring
|
||||||
|
_ = _stack__vunsigned
|
||||||
|
)
|
135
vendor/github.com/bytedance/sonic/internal/native/avx2/native_amd64.go
generated
vendored
Normal file
135
vendor/github.com/bytedance/sonic/internal/native/avx2/native_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// Code generated by Makefile, DO NOT EDIT.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 avx2
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __i64toa(out *byte, val int64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __u64toa(out *byte, val uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __f64toa(out *byte, val float64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __f32toa(out *byte, val float32) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __lspace(sp unsafe.Pointer, nb int, off int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __quote(sp unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __html_escape(sp unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __unquote(sp unsafe.Pointer, nb int, dp unsafe.Pointer, ep *int, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __value(s unsafe.Pointer, n int, p int, v *types.JsonState, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vstring(s *string, p *int, v *types.JsonState, flags uint64)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vnumber(s *string, p *int, v *types.JsonState)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vsigned(s *string, p *int, v *types.JsonState)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __vunsigned(s *string, p *int, v *types.JsonState)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_one_fast(s *string, p *int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_array(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_object(s *string, p *int, m *types.StateMachine, flags uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __skip_number(s *string, p *int) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __validate_one(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __get_by_path(s *string, p *int, path *[]interface{}) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __validate_utf8(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func __validate_utf8_fast(s *string) (ret int)
|
16629
vendor/github.com/bytedance/sonic/internal/native/avx2/native_amd64.s
generated
vendored
Normal file
16629
vendor/github.com/bytedance/sonic/internal/native/avx2/native_amd64.s
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
49
vendor/github.com/bytedance/sonic/internal/native/avx2/native_export_amd64.go
generated
vendored
Normal file
49
vendor/github.com/bytedance/sonic/internal/native/avx2/native_export_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// Code generated by Makefile, DO NOT EDIT.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 avx2
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_f64toa = _subr__f64toa
|
||||||
|
S_f32toa = _subr__f32toa
|
||||||
|
S_i64toa = _subr__i64toa
|
||||||
|
S_u64toa = _subr__u64toa
|
||||||
|
S_lspace = _subr__lspace
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_quote = _subr__quote
|
||||||
|
S_unquote = _subr__unquote
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_value = _subr__value
|
||||||
|
S_vstring = _subr__vstring
|
||||||
|
S_vnumber = _subr__vnumber
|
||||||
|
S_vsigned = _subr__vsigned
|
||||||
|
S_vunsigned = _subr__vunsigned
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_skip_one = _subr__skip_one
|
||||||
|
S_skip_one_fast = _subr__skip_one_fast
|
||||||
|
S_skip_array = _subr__skip_array
|
||||||
|
S_skip_object = _subr__skip_object
|
||||||
|
S_skip_number = _subr__skip_number
|
||||||
|
S_get_by_path = _subr__get_by_path
|
||||||
|
)
|
109
vendor/github.com/bytedance/sonic/internal/native/avx2/native_subr_amd64.go
generated
vendored
Normal file
109
vendor/github.com/bytedance/sonic/internal/native/avx2/native_subr_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
// +build !noasm !appengine
|
||||||
|
// Code generated by asm2asm, DO NOT EDIT.
|
||||||
|
|
||||||
|
package avx2
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection ALL
|
||||||
|
func __native_entry__() uintptr
|
||||||
|
|
||||||
|
var (
|
||||||
|
_subr__f32toa = __native_entry__() + 35216
|
||||||
|
_subr__f64toa = __native_entry__() + 752
|
||||||
|
_subr__get_by_path = __native_entry__() + 30384
|
||||||
|
_subr__html_escape = __native_entry__() + 11712
|
||||||
|
_subr__i64toa = __native_entry__() + 4368
|
||||||
|
_subr__lspace = __native_entry__() + 224
|
||||||
|
_subr__quote = __native_entry__() + 6160
|
||||||
|
_subr__skip_array = __native_entry__() + 22864
|
||||||
|
_subr__skip_number = __native_entry__() + 26928
|
||||||
|
_subr__skip_object = __native_entry__() + 24864
|
||||||
|
_subr__skip_one = __native_entry__() + 27088
|
||||||
|
_subr__skip_one_fast = __native_entry__() + 27504
|
||||||
|
_subr__u64toa = __native_entry__() + 4640
|
||||||
|
_subr__unquote = __native_entry__() + 8960
|
||||||
|
_subr__validate_one = __native_entry__() + 27152
|
||||||
|
_subr__validate_utf8 = __native_entry__() + 31552
|
||||||
|
_subr__validate_utf8_fast = __native_entry__() + 32496
|
||||||
|
_subr__value = __native_entry__() + 16816
|
||||||
|
_subr__vnumber = __native_entry__() + 20608
|
||||||
|
_subr__vsigned = __native_entry__() + 22144
|
||||||
|
_subr__vstring = __native_entry__() + 19312
|
||||||
|
_subr__vunsigned = __native_entry__() + 22496
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_stack__f32toa = 56
|
||||||
|
_stack__f64toa = 80
|
||||||
|
_stack__get_by_path = 320
|
||||||
|
_stack__html_escape = 72
|
||||||
|
_stack__i64toa = 16
|
||||||
|
_stack__lspace = 8
|
||||||
|
_stack__quote = 72
|
||||||
|
_stack__skip_array = 120
|
||||||
|
_stack__skip_number = 80
|
||||||
|
_stack__skip_object = 120
|
||||||
|
_stack__skip_one = 120
|
||||||
|
_stack__skip_one_fast = 216
|
||||||
|
_stack__u64toa = 8
|
||||||
|
_stack__unquote = 128
|
||||||
|
_stack__validate_one = 120
|
||||||
|
_stack__validate_utf8 = 48
|
||||||
|
_stack__validate_utf8_fast = 200
|
||||||
|
_stack__value = 368
|
||||||
|
_stack__vnumber = 280
|
||||||
|
_stack__vsigned = 16
|
||||||
|
_stack__vstring = 104
|
||||||
|
_stack__vunsigned = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ = _subr__f32toa
|
||||||
|
_ = _subr__f64toa
|
||||||
|
_ = _subr__get_by_path
|
||||||
|
_ = _subr__html_escape
|
||||||
|
_ = _subr__i64toa
|
||||||
|
_ = _subr__lspace
|
||||||
|
_ = _subr__quote
|
||||||
|
_ = _subr__skip_array
|
||||||
|
_ = _subr__skip_number
|
||||||
|
_ = _subr__skip_object
|
||||||
|
_ = _subr__skip_one
|
||||||
|
_ = _subr__skip_one_fast
|
||||||
|
_ = _subr__u64toa
|
||||||
|
_ = _subr__unquote
|
||||||
|
_ = _subr__validate_one
|
||||||
|
_ = _subr__validate_utf8
|
||||||
|
_ = _subr__validate_utf8_fast
|
||||||
|
_ = _subr__value
|
||||||
|
_ = _subr__vnumber
|
||||||
|
_ = _subr__vsigned
|
||||||
|
_ = _subr__vstring
|
||||||
|
_ = _subr__vunsigned
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ = _stack__f32toa
|
||||||
|
_ = _stack__f64toa
|
||||||
|
_ = _stack__get_by_path
|
||||||
|
_ = _stack__html_escape
|
||||||
|
_ = _stack__i64toa
|
||||||
|
_ = _stack__lspace
|
||||||
|
_ = _stack__quote
|
||||||
|
_ = _stack__skip_array
|
||||||
|
_ = _stack__skip_number
|
||||||
|
_ = _stack__skip_object
|
||||||
|
_ = _stack__skip_one
|
||||||
|
_ = _stack__skip_one_fast
|
||||||
|
_ = _stack__u64toa
|
||||||
|
_ = _stack__unquote
|
||||||
|
_ = _stack__validate_one
|
||||||
|
_ = _stack__validate_utf8
|
||||||
|
_ = _stack__validate_utf8_fast
|
||||||
|
_ = _stack__value
|
||||||
|
_ = _stack__vnumber
|
||||||
|
_ = _stack__vsigned
|
||||||
|
_ = _stack__vstring
|
||||||
|
_ = _stack__vunsigned
|
||||||
|
)
|
202
vendor/github.com/bytedance/sonic/internal/native/dispatch_amd64.go
generated
vendored
Normal file
202
vendor/github.com/bytedance/sonic/internal/native/dispatch_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* 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 native
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/cpu`
|
||||||
|
`github.com/bytedance/sonic/internal/native/avx`
|
||||||
|
`github.com/bytedance/sonic/internal/native/avx2`
|
||||||
|
`github.com/bytedance/sonic/internal/native/sse`
|
||||||
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxFrameSize uintptr = 400
|
||||||
|
BufPaddingSize int = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_f64toa uintptr
|
||||||
|
S_f32toa uintptr
|
||||||
|
S_i64toa uintptr
|
||||||
|
S_u64toa uintptr
|
||||||
|
S_lspace uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_quote uintptr
|
||||||
|
S_unquote uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_value uintptr
|
||||||
|
S_vstring uintptr
|
||||||
|
S_vnumber uintptr
|
||||||
|
S_vsigned uintptr
|
||||||
|
S_vunsigned uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
S_skip_one uintptr
|
||||||
|
S_skip_one_fast uintptr
|
||||||
|
S_get_by_path uintptr
|
||||||
|
S_skip_array uintptr
|
||||||
|
S_skip_object uintptr
|
||||||
|
S_skip_number uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func Quote(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int, flags uint64) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func Unquote(s unsafe.Pointer, nb int, dp unsafe.Pointer, ep *int, flags uint64) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func HTMLEscape(s unsafe.Pointer, nb int, dp unsafe.Pointer, dn *int) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func Value(s unsafe.Pointer, n int, p int, v *types.JsonState, flags uint64) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func SkipOne(s *string, p *int, m *types.StateMachine, flags uint64) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func SkipOneFast(s *string, p *int) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func GetByPath(s *string, p *int, path *[]interface{}) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func ValidateOne(s *string, p *int, m *types.StateMachine) int
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func I64toa(out *byte, val int64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func U64toa(out *byte, val uint64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func F64toa(out *byte, val float64) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func ValidateUTF8(s *string, p *int, m *types.StateMachine) (ret int)
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:noescape
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func ValidateUTF8Fast(s *string) (ret int)
|
||||||
|
|
||||||
|
func useAVX() {
|
||||||
|
S_f64toa = avx.S_f64toa
|
||||||
|
S_f32toa = avx.S_f32toa
|
||||||
|
S_i64toa = avx.S_i64toa
|
||||||
|
S_u64toa = avx.S_u64toa
|
||||||
|
S_lspace = avx.S_lspace
|
||||||
|
S_quote = avx.S_quote
|
||||||
|
S_unquote = avx.S_unquote
|
||||||
|
S_value = avx.S_value
|
||||||
|
S_vstring = avx.S_vstring
|
||||||
|
S_vnumber = avx.S_vnumber
|
||||||
|
S_vsigned = avx.S_vsigned
|
||||||
|
S_vunsigned = avx.S_vunsigned
|
||||||
|
S_skip_one = avx.S_skip_one
|
||||||
|
S_skip_one_fast = avx.S_skip_one_fast
|
||||||
|
S_skip_array = avx.S_skip_array
|
||||||
|
S_skip_object = avx.S_skip_object
|
||||||
|
S_skip_number = avx.S_skip_number
|
||||||
|
S_get_by_path = avx.S_get_by_path
|
||||||
|
}
|
||||||
|
|
||||||
|
func useAVX2() {
|
||||||
|
S_f64toa = avx2.S_f64toa
|
||||||
|
S_f32toa = avx2.S_f32toa
|
||||||
|
S_i64toa = avx2.S_i64toa
|
||||||
|
S_u64toa = avx2.S_u64toa
|
||||||
|
S_lspace = avx2.S_lspace
|
||||||
|
S_quote = avx2.S_quote
|
||||||
|
S_unquote = avx2.S_unquote
|
||||||
|
S_value = avx2.S_value
|
||||||
|
S_vstring = avx2.S_vstring
|
||||||
|
S_vnumber = avx2.S_vnumber
|
||||||
|
S_vsigned = avx2.S_vsigned
|
||||||
|
S_vunsigned = avx2.S_vunsigned
|
||||||
|
S_skip_one = avx2.S_skip_one
|
||||||
|
S_skip_one_fast = avx2.S_skip_one_fast
|
||||||
|
S_skip_array = avx2.S_skip_array
|
||||||
|
S_skip_object = avx2.S_skip_object
|
||||||
|
S_skip_number = avx2.S_skip_number
|
||||||
|
S_get_by_path = avx2.S_get_by_path
|
||||||
|
}
|
||||||
|
|
||||||
|
func useSSE() {
|
||||||
|
S_f64toa = sse.S_f64toa
|
||||||
|
S_f32toa = sse.S_f32toa
|
||||||
|
S_i64toa = sse.S_i64toa
|
||||||
|
S_u64toa = sse.S_u64toa
|
||||||
|
S_lspace = sse.S_lspace
|
||||||
|
S_quote = sse.S_quote
|
||||||
|
S_unquote = sse.S_unquote
|
||||||
|
S_value = sse.S_value
|
||||||
|
S_vstring = sse.S_vstring
|
||||||
|
S_vnumber = sse.S_vnumber
|
||||||
|
S_vsigned = sse.S_vsigned
|
||||||
|
S_vunsigned = sse.S_vunsigned
|
||||||
|
S_skip_one = sse.S_skip_one
|
||||||
|
S_skip_one_fast = sse.S_skip_one_fast
|
||||||
|
S_skip_array = sse.S_skip_array
|
||||||
|
S_skip_object = sse.S_skip_object
|
||||||
|
S_skip_number = sse.S_skip_number
|
||||||
|
S_get_by_path = sse.S_get_by_path
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if cpu.HasAVX2 {
|
||||||
|
useAVX2()
|
||||||
|
} else if cpu.HasAVX {
|
||||||
|
useAVX()
|
||||||
|
} else if cpu.HasSSE {
|
||||||
|
useSSE()
|
||||||
|
} else {
|
||||||
|
panic("Unsupported CPU, maybe it's too old to run Sonic.")
|
||||||
|
}
|
||||||
|
}
|
137
vendor/github.com/bytedance/sonic/internal/native/dispatch_amd64.s
generated
vendored
Normal file
137
vendor/github.com/bytedance/sonic/internal/native/dispatch_amd64.s
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "go_asm.h"
|
||||||
|
#include "funcdata.h"
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
TEXT ·Quote(SB), NOSPLIT, $0 - 48
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__quote(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__quote(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__quote(SB)
|
||||||
|
|
||||||
|
TEXT ·Unquote(SB), NOSPLIT, $0 - 48
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__unquote(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__unquote(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__unquote(SB)
|
||||||
|
|
||||||
|
TEXT ·HTMLEscape(SB), NOSPLIT, $0 - 40
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__html_escape(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__html_escape(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__html_escape(SB)
|
||||||
|
|
||||||
|
TEXT ·Value(SB), NOSPLIT, $0 - 48
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__value(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__value(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__value(SB)
|
||||||
|
|
||||||
|
TEXT ·SkipOne(SB), NOSPLIT, $0 - 40
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__skip_one(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__skip_one(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__skip_one(SB)
|
||||||
|
|
||||||
|
TEXT ·SkipOneFast(SB), NOSPLIT, $0 - 24
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__skip_one_fast(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__skip_one_fast(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__skip_one_fast(SB)
|
||||||
|
|
||||||
|
TEXT ·GetByPath(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__get_by_path(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__get_by_path(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__get_by_path(SB)
|
||||||
|
|
||||||
|
TEXT ·ValidateOne(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__validate_one(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__validate_one(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__validate_one(SB)
|
||||||
|
|
||||||
|
TEXT ·ValidateUTF8(SB), NOSPLIT, $0 - 40
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__validate_utf8(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__validate_utf8(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__validate_utf8(SB)
|
||||||
|
|
||||||
|
TEXT ·ValidateUTF8Fast(SB), NOSPLIT, $0 - 16
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__validate_utf8_fast(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__validate_utf8_fast(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__validate_utf8_fast(SB)
|
||||||
|
|
||||||
|
TEXT ·I64toa(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__i64toa(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__i64toa(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__i64toa(SB)
|
||||||
|
|
||||||
|
TEXT ·U64toa(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__u64toa(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__u64toa(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__u64toa(SB)
|
||||||
|
|
||||||
|
TEXT ·F64toa(SB), NOSPLIT, $0 - 32
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX2(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx2·__f64toa(SB)
|
||||||
|
CMPB github·com∕bytedance∕sonic∕internal∕cpu·HasAVX(SB), $0
|
||||||
|
JE 2(PC)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕avx·__f64toa(SB)
|
||||||
|
JMP github·com∕bytedance∕sonic∕internal∕native∕sse·__f64toa(SB)
|
||||||
|
|
138
vendor/github.com/bytedance/sonic/internal/native/fastfloat_amd64_test.tmpl
generated
vendored
Normal file
138
vendor/github.com/bytedance/sonic/internal/native/fastfloat_amd64_test.tmpl
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* 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 {{PACKAGE}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
`math`
|
||||||
|
`strconv`
|
||||||
|
`testing`
|
||||||
|
`math/rand`
|
||||||
|
`encoding/json`
|
||||||
|
|
||||||
|
`github.com/stretchr/testify/assert`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFastFloat_Encode(t *testing.T) {
|
||||||
|
var buf [64]byte
|
||||||
|
assert.Equal(t, "0" , string(buf[:__f64toa(&buf[0], 0)]))
|
||||||
|
assert.Equal(t, "-0" , string(buf[:__f64toa(&buf[0], math.Float64frombits(0x8000000000000000))]))
|
||||||
|
assert.Equal(t, "12340000000" , string(buf[:__f64toa(&buf[0], 1234e7)]))
|
||||||
|
assert.Equal(t, "12.34" , string(buf[:__f64toa(&buf[0], 1234e-2)]))
|
||||||
|
assert.Equal(t, "0.001234" , string(buf[:__f64toa(&buf[0], 1234e-6)]))
|
||||||
|
assert.Equal(t, "1e+30" , string(buf[:__f64toa(&buf[0], 1e30)]))
|
||||||
|
assert.Equal(t, "1.234e+33" , string(buf[:__f64toa(&buf[0], 1234e30)]))
|
||||||
|
assert.Equal(t, "1.234e+308" , string(buf[:__f64toa(&buf[0], 1234e305)]))
|
||||||
|
assert.Equal(t, "1.234e-317" , string(buf[:__f64toa(&buf[0], 1234e-320)]))
|
||||||
|
assert.Equal(t, "1.7976931348623157e+308" , string(buf[:__f64toa(&buf[0], 1.7976931348623157e308)]))
|
||||||
|
assert.Equal(t, "-12340000000" , string(buf[:__f64toa(&buf[0], -1234e7)]))
|
||||||
|
assert.Equal(t, "-12.34" , string(buf[:__f64toa(&buf[0], -1234e-2)]))
|
||||||
|
assert.Equal(t, "-0.001234" , string(buf[:__f64toa(&buf[0], -1234e-6)]))
|
||||||
|
assert.Equal(t, "-1e+30" , string(buf[:__f64toa(&buf[0], -1e30)]))
|
||||||
|
assert.Equal(t, "-1.234e+33" , string(buf[:__f64toa(&buf[0], -1234e30)]))
|
||||||
|
assert.Equal(t, "-1.234e+308" , string(buf[:__f64toa(&buf[0], -1234e305)]))
|
||||||
|
assert.Equal(t, "-1.234e-317" , string(buf[:__f64toa(&buf[0], -1234e-320)]))
|
||||||
|
assert.Equal(t, "-2.2250738585072014e-308" , string(buf[:__f64toa(&buf[0], -2.2250738585072014e-308)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFastFloat_Random(t *testing.T) {
|
||||||
|
var buf [64]byte
|
||||||
|
N := 10000
|
||||||
|
for i := 0; i < N; i++ {
|
||||||
|
b64 := uint64(rand.Uint32())<<32 | uint64(rand.Uint32())
|
||||||
|
f64 := math.Float64frombits(b64)
|
||||||
|
|
||||||
|
jout, jerr := json.Marshal(f64)
|
||||||
|
n := __f64toa(&buf[0], f64)
|
||||||
|
if jerr == nil {
|
||||||
|
assert.Equal(t, jout, buf[:n])
|
||||||
|
} else {
|
||||||
|
assert.True(t, n == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 := math.Float32frombits(rand.Uint32())
|
||||||
|
jout, jerr = json.Marshal(f32)
|
||||||
|
n = __f32toa(&buf[0], f32)
|
||||||
|
if jerr == nil {
|
||||||
|
assert.Equal(t, jout, buf[:n])
|
||||||
|
} else {
|
||||||
|
assert.True(t, n == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkParseFloat64(b *testing.B) {
|
||||||
|
var f64toaBenches = []struct {
|
||||||
|
name string
|
||||||
|
float float64
|
||||||
|
}{
|
||||||
|
{"Zero", 0},
|
||||||
|
{"Decimal", 33909},
|
||||||
|
{"Float", 339.7784},
|
||||||
|
{"Exp", -5.09e75},
|
||||||
|
{"NegExp", -5.11e-95},
|
||||||
|
{"LongExp", 1.234567890123456e-78},
|
||||||
|
{"Big", 123456789123456789123456789},
|
||||||
|
|
||||||
|
}
|
||||||
|
for _, c := range f64toaBenches {
|
||||||
|
f64bench := []struct {
|
||||||
|
name string
|
||||||
|
test func(*testing.B)
|
||||||
|
}{{
|
||||||
|
name: "StdLib",
|
||||||
|
test: func(b *testing.B) { var buf [64]byte; for i := 0; i < b.N; i++ { strconv.AppendFloat(buf[:0], c.float, 'g', -1, 64) }},
|
||||||
|
}, {
|
||||||
|
name: "FastFloat",
|
||||||
|
test: func(b *testing.B) { var buf [64]byte; for i := 0; i < b.N; i++ { __f64toa(&buf[0], c.float) }},
|
||||||
|
}}
|
||||||
|
for _, bm := range f64bench {
|
||||||
|
name := bm.name + "_" + c.name
|
||||||
|
b.Run(name, bm.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkParseFloat32(b *testing.B) {
|
||||||
|
var f32toaBenches = []struct {
|
||||||
|
name string
|
||||||
|
float float32
|
||||||
|
}{
|
||||||
|
{"Zero", 0},
|
||||||
|
{"Integer", 33909},
|
||||||
|
{"ExactFraction", 3.375},
|
||||||
|
{"Point", 339.7784},
|
||||||
|
{"Exp", -5.09e25},
|
||||||
|
{"NegExp", -5.11e-25},
|
||||||
|
{"Shortest", 1.234567e-8},
|
||||||
|
}
|
||||||
|
for _, c := range f32toaBenches {
|
||||||
|
bench := []struct {
|
||||||
|
name string
|
||||||
|
test func(*testing.B)
|
||||||
|
}{{
|
||||||
|
name: "StdLib32",
|
||||||
|
test: func(b *testing.B) { var buf [64]byte; for i := 0; i < b.N; i++ { strconv.AppendFloat(buf[:0], float64(c.float), 'g', -1, 32) }},
|
||||||
|
}, {
|
||||||
|
name: "FastFloat32",
|
||||||
|
test: func(b *testing.B) { var buf [64]byte; for i := 0; i < b.N; i++ { __f32toa(&buf[0], c.float) }},
|
||||||
|
}}
|
||||||
|
for _, bm := range bench {
|
||||||
|
name := bm.name + "_" + c.name
|
||||||
|
b.Run(name, bm.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
151
vendor/github.com/bytedance/sonic/internal/native/fastint_amd64_test.tmpl
generated
vendored
Normal file
151
vendor/github.com/bytedance/sonic/internal/native/fastint_amd64_test.tmpl
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* 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 {{PACKAGE}}
|
||||||
|
|
||||||
|
import (
|
||||||
|
`strconv`
|
||||||
|
`testing`
|
||||||
|
`fmt`
|
||||||
|
|
||||||
|
`github.com/stretchr/testify/assert`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFastInt_IntToString(t *testing.T) {
|
||||||
|
var buf [32]byte
|
||||||
|
assert.Equal(t, "0" , string(buf[:__i64toa(&buf[0], 0)]))
|
||||||
|
assert.Equal(t, "1" , string(buf[:__i64toa(&buf[0], 1)]))
|
||||||
|
assert.Equal(t, "12" , string(buf[:__i64toa(&buf[0], 12)]))
|
||||||
|
assert.Equal(t, "123" , string(buf[:__i64toa(&buf[0], 123)]))
|
||||||
|
assert.Equal(t, "1234" , string(buf[:__i64toa(&buf[0], 1234)]))
|
||||||
|
assert.Equal(t, "12345" , string(buf[:__i64toa(&buf[0], 12345)]))
|
||||||
|
assert.Equal(t, "123456" , string(buf[:__i64toa(&buf[0], 123456)]))
|
||||||
|
assert.Equal(t, "1234567" , string(buf[:__i64toa(&buf[0], 1234567)]))
|
||||||
|
assert.Equal(t, "12345678" , string(buf[:__i64toa(&buf[0], 12345678)]))
|
||||||
|
assert.Equal(t, "123456789" , string(buf[:__i64toa(&buf[0], 123456789)]))
|
||||||
|
assert.Equal(t, "1234567890" , string(buf[:__i64toa(&buf[0], 1234567890)]))
|
||||||
|
assert.Equal(t, "12345678901" , string(buf[:__i64toa(&buf[0], 12345678901)]))
|
||||||
|
assert.Equal(t, "123456789012" , string(buf[:__i64toa(&buf[0], 123456789012)]))
|
||||||
|
assert.Equal(t, "1234567890123" , string(buf[:__i64toa(&buf[0], 1234567890123)]))
|
||||||
|
assert.Equal(t, "12345678901234" , string(buf[:__i64toa(&buf[0], 12345678901234)]))
|
||||||
|
assert.Equal(t, "123456789012345" , string(buf[:__i64toa(&buf[0], 123456789012345)]))
|
||||||
|
assert.Equal(t, "1234567890123456" , string(buf[:__i64toa(&buf[0], 1234567890123456)]))
|
||||||
|
assert.Equal(t, "12345678901234567" , string(buf[:__i64toa(&buf[0], 12345678901234567)]))
|
||||||
|
assert.Equal(t, "123456789012345678" , string(buf[:__i64toa(&buf[0], 123456789012345678)]))
|
||||||
|
assert.Equal(t, "1234567890123456789" , string(buf[:__i64toa(&buf[0], 1234567890123456789)]))
|
||||||
|
assert.Equal(t, "9223372036854775807" , string(buf[:__i64toa(&buf[0], 9223372036854775807)]))
|
||||||
|
assert.Equal(t, "-1" , string(buf[:__i64toa(&buf[0], -1)]))
|
||||||
|
assert.Equal(t, "-12" , string(buf[:__i64toa(&buf[0], -12)]))
|
||||||
|
assert.Equal(t, "-123" , string(buf[:__i64toa(&buf[0], -123)]))
|
||||||
|
assert.Equal(t, "-1234" , string(buf[:__i64toa(&buf[0], -1234)]))
|
||||||
|
assert.Equal(t, "-12345" , string(buf[:__i64toa(&buf[0], -12345)]))
|
||||||
|
assert.Equal(t, "-123456" , string(buf[:__i64toa(&buf[0], -123456)]))
|
||||||
|
assert.Equal(t, "-1234567" , string(buf[:__i64toa(&buf[0], -1234567)]))
|
||||||
|
assert.Equal(t, "-12345678" , string(buf[:__i64toa(&buf[0], -12345678)]))
|
||||||
|
assert.Equal(t, "-123456789" , string(buf[:__i64toa(&buf[0], -123456789)]))
|
||||||
|
assert.Equal(t, "-1234567890" , string(buf[:__i64toa(&buf[0], -1234567890)]))
|
||||||
|
assert.Equal(t, "-12345678901" , string(buf[:__i64toa(&buf[0], -12345678901)]))
|
||||||
|
assert.Equal(t, "-123456789012" , string(buf[:__i64toa(&buf[0], -123456789012)]))
|
||||||
|
assert.Equal(t, "-1234567890123" , string(buf[:__i64toa(&buf[0], -1234567890123)]))
|
||||||
|
assert.Equal(t, "-12345678901234" , string(buf[:__i64toa(&buf[0], -12345678901234)]))
|
||||||
|
assert.Equal(t, "-123456789012345" , string(buf[:__i64toa(&buf[0], -123456789012345)]))
|
||||||
|
assert.Equal(t, "-1234567890123456" , string(buf[:__i64toa(&buf[0], -1234567890123456)]))
|
||||||
|
assert.Equal(t, "-12345678901234567" , string(buf[:__i64toa(&buf[0], -12345678901234567)]))
|
||||||
|
assert.Equal(t, "-123456789012345678" , string(buf[:__i64toa(&buf[0], -123456789012345678)]))
|
||||||
|
assert.Equal(t, "-1234567890123456789" , string(buf[:__i64toa(&buf[0], -1234567890123456789)]))
|
||||||
|
assert.Equal(t, "-9223372036854775808" , string(buf[:__i64toa(&buf[0], -9223372036854775808)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFastInt_UintToString(t *testing.T) {
|
||||||
|
var buf [32]byte
|
||||||
|
assert.Equal(t, "0" , string(buf[:__u64toa(&buf[0], 0)]))
|
||||||
|
assert.Equal(t, "1" , string(buf[:__u64toa(&buf[0], 1)]))
|
||||||
|
assert.Equal(t, "12" , string(buf[:__u64toa(&buf[0], 12)]))
|
||||||
|
assert.Equal(t, "123" , string(buf[:__u64toa(&buf[0], 123)]))
|
||||||
|
assert.Equal(t, "1234" , string(buf[:__u64toa(&buf[0], 1234)]))
|
||||||
|
assert.Equal(t, "12345" , string(buf[:__u64toa(&buf[0], 12345)]))
|
||||||
|
assert.Equal(t, "123456" , string(buf[:__u64toa(&buf[0], 123456)]))
|
||||||
|
assert.Equal(t, "1234567" , string(buf[:__u64toa(&buf[0], 1234567)]))
|
||||||
|
assert.Equal(t, "12345678" , string(buf[:__u64toa(&buf[0], 12345678)]))
|
||||||
|
assert.Equal(t, "123456789" , string(buf[:__u64toa(&buf[0], 123456789)]))
|
||||||
|
assert.Equal(t, "1234567890" , string(buf[:__u64toa(&buf[0], 1234567890)]))
|
||||||
|
assert.Equal(t, "12345678901" , string(buf[:__u64toa(&buf[0], 12345678901)]))
|
||||||
|
assert.Equal(t, "123456789012" , string(buf[:__u64toa(&buf[0], 123456789012)]))
|
||||||
|
assert.Equal(t, "1234567890123" , string(buf[:__u64toa(&buf[0], 1234567890123)]))
|
||||||
|
assert.Equal(t, "12345678901234" , string(buf[:__u64toa(&buf[0], 12345678901234)]))
|
||||||
|
assert.Equal(t, "123456789012345" , string(buf[:__u64toa(&buf[0], 123456789012345)]))
|
||||||
|
assert.Equal(t, "1234567890123456" , string(buf[:__u64toa(&buf[0], 1234567890123456)]))
|
||||||
|
assert.Equal(t, "12345678901234567" , string(buf[:__u64toa(&buf[0], 12345678901234567)]))
|
||||||
|
assert.Equal(t, "123456789012345678" , string(buf[:__u64toa(&buf[0], 123456789012345678)]))
|
||||||
|
assert.Equal(t, "1234567890123456789" , string(buf[:__u64toa(&buf[0], 1234567890123456789)]))
|
||||||
|
assert.Equal(t, "12345678901234567890" , string(buf[:__u64toa(&buf[0], 12345678901234567890)]))
|
||||||
|
assert.Equal(t, "18446744073709551615" , string(buf[:__u64toa(&buf[0], 18446744073709551615)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFastInt_IntToString(b *testing.B) {
|
||||||
|
benchmarks := []struct {
|
||||||
|
name string
|
||||||
|
test func(*testing.B)
|
||||||
|
}{{
|
||||||
|
name: "StdLib-Positive",
|
||||||
|
test: func(b *testing.B) { var buf [32]byte; for i := 0; i < b.N; i++ { strconv.AppendInt(buf[:0], int64(i), 10) }},
|
||||||
|
}, {
|
||||||
|
name: "StdLib-Negative",
|
||||||
|
test: func(b *testing.B) { var buf [32]byte; for i := 0; i < b.N; i++ { strconv.AppendInt(buf[:0], -int64(i), 10) }},
|
||||||
|
}, {
|
||||||
|
name: "FastInt-Positive",
|
||||||
|
test: func(b *testing.B) { var buf [32]byte; for i := 0; i < b.N; i++ { __i64toa(&buf[0], int64(i)) }},
|
||||||
|
}, {
|
||||||
|
name: "FastInt-Negative",
|
||||||
|
test: func(b *testing.B) { var buf [32]byte; for i := 0; i < b.N; i++ { __i64toa(&buf[0], -int64(i)) }},
|
||||||
|
}}
|
||||||
|
for _, bm := range benchmarks {
|
||||||
|
b.Run(bm.name, bm.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type utoaBench struct {
|
||||||
|
name string
|
||||||
|
num uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFastInt_UintToString(b *testing.B) {
|
||||||
|
maxUint := "18446744073709551615"
|
||||||
|
benchs := make([]utoaBench, len(maxUint) + 1)
|
||||||
|
benchs[0].name = "Zero"
|
||||||
|
benchs[0].num = 0
|
||||||
|
for i := 1; i <= len(maxUint); i++ {
|
||||||
|
benchs[i].name = strconv.FormatInt(int64(i), 10) + "-Digs"
|
||||||
|
benchs[i].num, _ = strconv.ParseUint(string(maxUint[:i]), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range(benchs) {
|
||||||
|
benchmarks := []struct {
|
||||||
|
name string
|
||||||
|
test func(*testing.B)
|
||||||
|
}{{
|
||||||
|
name: "StdLib",
|
||||||
|
test: func(b *testing.B) { var buf [32]byte; for i := 0; i < b.N; i++ { strconv.AppendUint(buf[:0], t.num, 10) }},
|
||||||
|
}, {
|
||||||
|
name: "FastInt",
|
||||||
|
test: func(b *testing.B) { var buf [32]byte; for i := 0; i < b.N; i++ { __u64toa(&buf[0], t.num) }},
|
||||||
|
}}
|
||||||
|
for _, bm := range benchmarks {
|
||||||
|
name := fmt.Sprintf("%s_%s", bm.name, t.name)
|
||||||
|
b.Run(name, bm.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue