diff --git a/go.mod b/go.mod index eaf722c..47d19e9 100644 --- a/go.mod +++ b/go.mod @@ -1,47 +1,49 @@ module git.huangwc.com/pig/pig-farm-controller -go 1.24.0 +go 1.23 require ( - github.com/gin-gonic/gin v1.10.1 + github.com/gin-gonic/gin v1.10.0 + github.com/golang-jwt/jwt/v5 v5.2.1 + golang.org/x/crypto v0.27.0 gopkg.in/yaml.v2 v2.4.0 - gorm.io/driver/postgres v1.5.7 - gorm.io/gorm v1.25.7 + gorm.io/driver/postgres v1.5.9 + gorm.io/gorm v1.25.10 ) require ( - github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.1 // indirect - github.com/bytedance/sonic/loader v0.3.0 // indirect - github.com/cloudwego/base64x v0.1.6 // indirect - github.com/gabriel-vasile/mimetype v1.4.10 // indirect - github.com/gin-contrib/sse v1.1.0 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.27.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.4.3 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/kr/pretty v0.3.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect - github.com/stretchr/testify v1.11.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.3.0 // indirect - golang.org/x/arch v0.21.0 // indirect - golang.org/x/crypto v0.41.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/text v0.28.0 // indirect - google.golang.org/protobuf v1.36.8 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c1dd291..d7e6764 100644 --- a/go.sum +++ b/go.sum @@ -1,50 +1,56 @@ -github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= -github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= -github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= -github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= -github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= -github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= -github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= -github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= -github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= -github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= +github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= @@ -56,41 +62,47 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= -github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= -golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw= -golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -99,7 +111,9 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= -gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= -gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= -gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= +gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/api/api.go b/internal/api/api.go index 9947898..6ff221f 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -10,7 +10,9 @@ import ( "time" "git.huangwc.com/pig/pig-farm-controller/internal/config" + "git.huangwc.com/pig/pig-farm-controller/internal/controller/user" "git.huangwc.com/pig/pig-farm-controller/internal/logs" + "git.huangwc.com/pig/pig-farm-controller/internal/storage/repository" "github.com/gin-gonic/gin" ) @@ -26,13 +28,16 @@ type API struct { // config 应用配置 config *config.Config + // userController 用户控制器 + userController *user.Controller + // logger 日志记录器 logger *logs.Logger } // NewAPI 创建并返回一个新的API实例 // 初始化Gin引擎和相关配置 -func NewAPI(cfg *config.Config) *API { +func NewAPI(cfg *config.Config, userRepo repository.UserRepo) *API { // 设置Gin为发布模式 gin.SetMode(gin.ReleaseMode) @@ -56,10 +61,14 @@ func NewAPI(cfg *config.Config) *API { engine.Use(gin.Recovery()) + // 创建用户控制器 + userController := user.NewController(userRepo) + return &API{ - engine: engine, - config: cfg, - logger: logs.NewLogger(), + engine: engine, + config: cfg, + userController: userController, + logger: logs.NewLogger(), } } @@ -79,11 +88,11 @@ func (a *API) Start() error { } // 启动HTTP服务器 - a.logger.Info(fmt.Sprintf("Starting HTTP server on %s:%d", a.config.Server.Host, a.config.Server.Port)) + a.logger.Info(fmt.Sprintf("正在启动HTTP服务器 %s:%d", a.config.Server.Host, a.config.Server.Port)) go func() { if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - a.logger.Error(fmt.Sprintf("HTTP server startup failed: %v", err)) + a.logger.Error(fmt.Sprintf("HTTP服务器启动失败: %v", err)) } }() @@ -92,7 +101,7 @@ func (a *API) Start() error { // Stop 停止HTTP服务器 func (a *API) Stop() error { - a.logger.Info("Stopping HTTP server") + a.logger.Info("正在停止HTTP服务器") // 创建一个5秒的超时上下文 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -100,11 +109,11 @@ func (a *API) Stop() error { // 优雅地关闭服务器 if err := a.server.Shutdown(ctx); err != nil { - a.logger.Error(fmt.Sprintf("HTTP server shutdown error: %v", err)) + a.logger.Error(fmt.Sprintf("HTTP服务器关闭错误: %v", err)) return err } - a.logger.Info("HTTP server stopped") + a.logger.Info("HTTP服务器已停止") return nil } @@ -113,6 +122,13 @@ func (a *API) setupRoutes() { // 基础路由示例 a.engine.GET("/health", a.healthHandler) + // 用户相关路由 + userGroup := a.engine.Group("/api/v1/user") + { + userGroup.POST("/register", a.userController.Register) + userGroup.POST("/login", a.userController.Login) + } + // TODO: 添加更多路由 } @@ -127,6 +143,6 @@ func (a *API) setupRoutes() { func (a *API) healthHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "ok", - "message": "Pig Farm Controller API is running", + "message": "猪场控制器API正在运行", }) } diff --git a/internal/api/middleware/auth.go b/internal/api/middleware/auth.go new file mode 100644 index 0000000..7d74156 --- /dev/null +++ b/internal/api/middleware/auth.go @@ -0,0 +1,148 @@ +// Package middleware 提供HTTP中间件功能 +// 包含鉴权、日志、恢复等中间件实现 +package middleware + +import ( + "net/http" + "os" + "strings" + "time" + + "git.huangwc.com/pig/pig-farm-controller/internal/logs" + "git.huangwc.com/pig/pig-farm-controller/internal/storage/repository" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + "gorm.io/gorm" +) + +// AuthMiddleware 鉴权中间件结构 +type AuthMiddleware struct { + userRepo repository.UserRepo + logger *logs.Logger +} + +// AuthUser 用于在上下文中存储的用户信息 +type AuthUser struct { + ID uint `json:"id"` + Username string `json:"username"` +} + +// JWTClaims 自定义JWT声明 +type JWTClaims struct { + UserID uint `json:"user_id"` + Username string `json:"username"` + jwt.RegisteredClaims +} + +// NewAuthMiddleware 创建鉴权中间件实例 +func NewAuthMiddleware(userRepo repository.UserRepo) *AuthMiddleware { + return &AuthMiddleware{ + userRepo: userRepo, + logger: logs.NewLogger(), + } +} + +// getJWTSecret 获取JWT密钥 +func (m *AuthMiddleware) getJWTSecret() []byte { + // 在实际项目中,应该从配置文件或环境变量中读取 + secret := os.Getenv("JWT_SECRET") + if secret == "" { + secret = "pig-farm-controller-secret-key" // 默认密钥 + } + return []byte(secret) +} + +// GenerateToken 为用户生成JWT token +func (m *AuthMiddleware) GenerateToken(userID uint, username string) (string, error) { + claims := JWTClaims{ + UserID: userID, + Username: username, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), // 24小时过期 + IssuedAt: jwt.NewNumericDate(time.Now()), + NotBefore: jwt.NewNumericDate(time.Now()), + Issuer: "pig-farm-controller", + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(m.getJWTSecret()) +} + +// Handle 鉴权中间件处理函数 +func (m *AuthMiddleware) Handle() gin.HandlerFunc { + return func(c *gin.Context) { + // 从请求头中获取认证信息 + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + c.JSON(http.StatusUnauthorized, gin.H{"error": "缺少认证信息"}) + c.Abort() + return + } + + // 检查Bearer token格式 + if !strings.HasPrefix(authHeader, "Bearer ") { + c.JSON(http.StatusUnauthorized, gin.H{"error": "认证信息格式错误"}) + c.Abort() + return + } + + // 解析token + tokenString := strings.TrimPrefix(authHeader, "Bearer ") + + // 验证token并获取用户信息 + user, err := m.getUserFromJWT(tokenString) + if err != nil { + if err == gorm.ErrRecordNotFound { + c.JSON(http.StatusUnauthorized, gin.H{"error": "用户不存在"}) + } else { + m.logger.Error("Token验证失败: " + err.Error()) + c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证令牌"}) + } + c.Abort() + return + } + + // 将用户信息保存到上下文中,供后续处理函数使用 + c.Set("user", user) + + // 继续处理请求 + c.Next() + } +} + +// getUserFromJWT 从JWT token中获取用户信息 +func (m *AuthMiddleware) getUserFromJWT(tokenString string) (*AuthUser, error) { + // 解析token + token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) { + return m.getJWTSecret(), nil + }) + + if err != nil { + return nil, err + } + + // 验证token + if !token.Valid { + return nil, gorm.ErrRecordNotFound + } + + // 获取声明 + claims, ok := token.Claims.(*JWTClaims) + if !ok { + return nil, gorm.ErrRecordNotFound + } + + // 根据用户ID查找用户 + userModel, err := m.userRepo.FindByID(claims.UserID) + if err != nil { + return nil, err + } + + user := &AuthUser{ + ID: userModel.ID, + Username: userModel.Username, + } + + return user, nil +} diff --git a/internal/config/config.go b/internal/config/config.go index f86cb7c..cea6aea 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -77,12 +77,12 @@ func (c *Config) Load(path string) error { // 读取配置文件 data, err := os.ReadFile(path) if err != nil { - return fmt.Errorf("failed to read config file: %v", err) + return fmt.Errorf("配置文件读取失败: %v", err) } // 解析YAML配置 if err := yaml.Unmarshal(data, c); err != nil { - return fmt.Errorf("failed to parse config file: %v", err) + return fmt.Errorf("配置文件解析失败: %v", err) } return nil diff --git a/internal/controller/user/user.go b/internal/controller/user/user.go new file mode 100644 index 0000000..5d90e9d --- /dev/null +++ b/internal/controller/user/user.go @@ -0,0 +1,126 @@ +// Package user 提供用户相关功能的控制器 +// 实现用户注册、登录等操作 +package user + +import ( + "net/http" + + "git.huangwc.com/pig/pig-farm-controller/internal/api/middleware" + "git.huangwc.com/pig/pig-farm-controller/internal/logs" + "git.huangwc.com/pig/pig-farm-controller/internal/storage/repository" + "github.com/gin-gonic/gin" + "golang.org/x/crypto/bcrypt" +) + +// Controller 用户控制器 +type Controller struct { + userRepo repository.UserRepo + logger *logs.Logger +} + +// NewController 创建用户控制器实例 +func NewController(userRepo repository.UserRepo) *Controller { + return &Controller{ + userRepo: userRepo, + logger: logs.NewLogger(), + } +} + +// RegisterRequest 注册请求结构体 +type RegisterRequest struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` +} + +// RegisterResponse 注册响应结构体 +type RegisterResponse struct { + ID uint `json:"id"` + Username string `json:"username"` + CreatedAt string `json:"created_at"` +} + +// Register 用户注册 +func (c *Controller) Register(ctx *gin.Context) { + var req RegisterRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"}) + return + } + + user, err := c.userRepo.CreateUser(req.Username, req.Password) + if err != nil { + c.logger.Error("创建用户失败: " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "创建用户失败"}) + return + } + + response := RegisterResponse{ + ID: user.ID, + Username: user.Username, + CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"), + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "用户创建成功", + "user": response, + }) +} + +// LoginRequest 登录请求结构体 +type LoginRequest struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` +} + +// LoginResponse 登录响应结构体 +type LoginResponse struct { + ID uint `json:"id"` + Username string `json:"username"` + Token string `json:"token"` + CreatedAt string `json:"created_at"` +} + +// Login 用户登录 +func (c *Controller) Login(ctx *gin.Context) { + var req LoginRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"}) + return + } + + // 查找用户 + user, err := c.userRepo.FindByUsername(req.Username) + if err != nil { + c.logger.Error("查找用户失败: " + err.Error()) + ctx.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"}) + return + } + + // 验证密码 + err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)) + if err != nil { + ctx.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"}) + return + } + + // 生成JWT访问令牌 + authMiddleware := middleware.NewAuthMiddleware(c.userRepo) + token, err := authMiddleware.GenerateToken(user.ID, user.Username) + if err != nil { + c.logger.Error("生成令牌失败: " + err.Error()) + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "登录失败"}) + return + } + + response := LoginResponse{ + ID: user.ID, + Username: user.Username, + Token: token, + CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"), + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "登录成功", + "user": response, + }) +} diff --git a/internal/core/application.go b/internal/core/application.go index 496367d..a908196 100644 --- a/internal/core/application.go +++ b/internal/core/application.go @@ -10,6 +10,7 @@ import ( "git.huangwc.com/pig/pig-farm-controller/internal/config" "git.huangwc.com/pig/pig-farm-controller/internal/logs" "git.huangwc.com/pig/pig-farm-controller/internal/storage/db" + "git.huangwc.com/pig/pig-farm-controller/internal/storage/repository" "git.huangwc.com/pig/pig-farm-controller/internal/task" ) @@ -25,6 +26,9 @@ type Application struct { // TaskExecutor 任务执行器组件实例 TaskExecutor *task.Executor + // UserRepo 用户仓库实例 + UserRepo repository.UserRepo + // Config 应用配置 Config *config.Config @@ -46,8 +50,11 @@ func NewApplication(cfg *config.Config) *Application { // 初始化存储组件 store := db.NewStorage(connectionString, maxOpenConns, maxIdleConns, connMaxLifetime) + // 初始化用户仓库 + userRepo := repository.NewUserRepo(store.GetDB()) + // 初始化API组件 - apiInstance := api.NewAPI(cfg) + apiInstance := api.NewAPI(cfg, userRepo) // 初始化任务执行器组件(使用5个工作协程) taskExecutor := task.NewExecutor(5) @@ -56,6 +63,7 @@ func NewApplication(cfg *config.Config) *Application { Storage: store, API: apiInstance, TaskExecutor: taskExecutor, + UserRepo: userRepo, Config: cfg, logger: logs.NewLogger(), } @@ -66,19 +74,19 @@ func NewApplication(cfg *config.Config) *Application { func (app *Application) Start() error { // 启动存储组件 if err := app.Storage.Connect(); err != nil { - return fmt.Errorf("failed to connect to storage: %v", err) + return fmt.Errorf("存储连接失败: %v", err) } - app.logger.Info("Storage connected successfully") + app.logger.Info("存储连接成功") // 启动API组件 if err := app.API.Start(); err != nil { - return fmt.Errorf("failed to start API: %v", err) + return fmt.Errorf("API启动失败: %v", err) } - app.logger.Info("API started successfully") + app.logger.Info("API启动成功") // 启动任务执行器组件 app.TaskExecutor.Start() - app.logger.Info("Task executor started successfully") + app.logger.Info("任务执行器启动成功") return nil } @@ -88,18 +96,18 @@ func (app *Application) Start() error { func (app *Application) Stop() error { // 停止API组件 if err := app.API.Stop(); err != nil { - app.logger.Error(fmt.Sprintf("Failed to stop API: %v", err)) + app.logger.Error(fmt.Sprintf("API停止失败: %v", err)) } // 停止任务执行器组件 app.TaskExecutor.Stop() - app.logger.Info("Task executor stopped successfully") + app.logger.Info("任务执行器已停止") // 停止存储组件 if err := app.Storage.Disconnect(); err != nil { - return fmt.Errorf("failed to disconnect from storage: %v", err) + return fmt.Errorf("存储断开连接失败: %v", err) } - app.logger.Info("Storage disconnected successfully") + app.logger.Info("存储断开连接成功") return nil } diff --git a/internal/logs/logs.go b/internal/logs/logs.go index 245bad6..86ed998 100644 --- a/internal/logs/logs.go +++ b/internal/logs/logs.go @@ -23,20 +23,20 @@ func NewLogger() *Logger { // Info 记录信息级别日志 func (l *Logger) Info(message string) { - l.logger.Printf("[INFO] %s %s", time.Now().Format(time.RFC3339), message) + l.logger.Printf("[信息] %s %s", time.Now().Format(time.RFC3339), message) } // Error 记录错误级别日志 func (l *Logger) Error(message string) { - l.logger.Printf("[ERROR] %s %s", time.Now().Format(time.RFC3339), message) + l.logger.Printf("[错误] %s %s", time.Now().Format(time.RFC3339), message) } // Debug 记录调试级别日志 func (l *Logger) Debug(message string) { - l.logger.Printf("[DEBUG] %s %s", time.Now().Format(time.RFC3339), message) + l.logger.Printf("[调试] %s %s", time.Now().Format(time.RFC3339), message) } // Warn 记录警告级别日志 func (l *Logger) Warn(message string) { - l.logger.Printf("[WARN] %s %s", time.Now().Format(time.RFC3339), message) + l.logger.Printf("[警告] %s %s", time.Now().Format(time.RFC3339), message) } diff --git a/internal/model/user.go b/internal/model/user.go new file mode 100644 index 0000000..f22cc4b --- /dev/null +++ b/internal/model/user.go @@ -0,0 +1,35 @@ +// Package model 提供数据模型定义 +// 包含用户、猪舍、饲料等相关数据结构 +package model + +import ( + "time" + + "gorm.io/gorm" +) + +// User 代表系统用户 +type User struct { + // ID 用户ID + ID uint `gorm:"primaryKey;column:id" json:"id"` + + // Username 用户名 + Username string `gorm:"uniqueIndex;not null;column:username" json:"username"` + + // PasswordHash 密码哈希值 + PasswordHash string `gorm:"not null;column:password_hash" json:"-"` + + // CreatedAt 创建时间 + CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + + // UpdatedAt 更新时间 + UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` + + // DeletedAt 删除时间(用于软删除) + DeletedAt gorm.DeletedAt `gorm:"index;column:deleted_at" json:"-"` +} + +// TableName 指定User模型对应的数据库表名 +func (User) TableName() string { + return "users" +} diff --git a/internal/storage/db/postgres.go b/internal/storage/db/postgres.go index 863269d..e5db9dc 100644 --- a/internal/storage/db/postgres.go +++ b/internal/storage/db/postgres.go @@ -49,25 +49,25 @@ func NewPostgresStorage(connectionString string, maxOpenConns, maxIdleConns, con // Connect 建立与PostgreSQL数据库的连接 // 使用GORM建立数据库连接 func (ps *PostgresStorage) Connect() error { - ps.logger.Info("Connecting to PostgreSQL database") + ps.logger.Info("正在连接PostgreSQL数据库") var err error ps.db, err = gorm.Open(postgres.Open(ps.connectionString), &gorm.Config{}) if err != nil { - ps.logger.Error(fmt.Sprintf("Failed to connect to database: %v", err)) - return fmt.Errorf("failed to connect to database: %v", err) + ps.logger.Error(fmt.Sprintf("数据库连接失败: %v", err)) + return fmt.Errorf("数据库连接失败: %v", err) } // 测试连接 sqlDB, err := ps.db.DB() if err != nil { - ps.logger.Error(fmt.Sprintf("Failed to get database instance: %v", err)) - return fmt.Errorf("failed to get database instance: %v", err) + ps.logger.Error(fmt.Sprintf("获取数据库实例失败: %v", err)) + return fmt.Errorf("获取数据库实例失败: %v", err) } if err = sqlDB.Ping(); err != nil { - ps.logger.Error(fmt.Sprintf("Failed to ping database: %v", err)) - return fmt.Errorf("failed to ping database: %v", err) + ps.logger.Error(fmt.Sprintf("数据库连接测试失败: %v", err)) + return fmt.Errorf("数据库连接测试失败: %v", err) } // 设置连接池参数 @@ -75,7 +75,7 @@ func (ps *PostgresStorage) Connect() error { sqlDB.SetMaxIdleConns(ps.maxIdleConns) sqlDB.SetConnMaxLifetime(time.Duration(ps.connMaxLifetime) * time.Second) - ps.logger.Info("Successfully connected to PostgreSQL database") + ps.logger.Info("PostgreSQL数据库连接成功") return nil } @@ -83,19 +83,19 @@ func (ps *PostgresStorage) Connect() error { // 安全地关闭所有数据库连接 func (ps *PostgresStorage) Disconnect() error { if ps.db != nil { - ps.logger.Info("Disconnecting from PostgreSQL database") + ps.logger.Info("正在断开PostgreSQL数据库连接") sqlDB, err := ps.db.DB() if err != nil { - ps.logger.Error(fmt.Sprintf("Failed to get database instance: %v", err)) - return fmt.Errorf("failed to get database instance: %v", err) + ps.logger.Error(fmt.Sprintf("获取数据库实例失败: %v", err)) + return fmt.Errorf("获取数据库实例失败: %v", err) } if err := sqlDB.Close(); err != nil { - ps.logger.Error(fmt.Sprintf("Failed to close database connection: %v", err)) - return fmt.Errorf("failed to close database connection: %v", err) + ps.logger.Error(fmt.Sprintf("关闭数据库连接失败: %v", err)) + return fmt.Errorf("关闭数据库连接失败: %v", err) } - ps.logger.Info("Successfully disconnected from PostgreSQL database") + ps.logger.Info("PostgreSQL数据库连接已断开") } return nil } diff --git a/internal/storage/repository/user.go b/internal/storage/repository/user.go new file mode 100644 index 0000000..9ea2dec --- /dev/null +++ b/internal/storage/repository/user.go @@ -0,0 +1,84 @@ +// Package repository 提供数据访问层实现 +// 包含各种数据实体的仓库接口和实现 +package repository + +import ( + "fmt" + + "git.huangwc.com/pig/pig-farm-controller/internal/model" + "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" +) + +// UserRepo 用户仓库接口 +type UserRepo interface { + // CreateUser 创建新用户 + CreateUser(username, password string) (*model.User, error) + + // FindByUsername 根据用户名查找用户 + FindByUsername(username string) (*model.User, error) + + // FindByID 根据ID查找用户 + FindByID(id uint) (*model.User, error) +} + +// userRepo 用户仓库实现 +type userRepo struct { + db *gorm.DB +} + +// NewUserRepo 创建用户仓库实例 +func NewUserRepo(db *gorm.DB) UserRepo { + return &userRepo{ + db: db, + } +} + +// CreateUser 创建新用户 +func (r *userRepo) CreateUser(username, password string) (*model.User, error) { + // 检查用户是否已存在 + var existingUser model.User + result := r.db.Where("username = ?", username).First(&existingUser) + if result.Error == nil { + return nil, fmt.Errorf("用户已存在") + } + + // 对密码进行哈希处理 + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return nil, fmt.Errorf("密码加密失败: %v", err) + } + + // 创建新用户 + user := &model.User{ + Username: username, + PasswordHash: string(hashedPassword), + } + + result = r.db.Create(user) + if result.Error != nil { + return nil, fmt.Errorf("用户创建失败: %v", result.Error) + } + + return user, nil +} + +// FindByUsername 根据用户名查找用户 +func (r *userRepo) FindByUsername(username string) (*model.User, error) { + var user model.User + result := r.db.Where("username = ?", username).First(&user) + if result.Error != nil { + return nil, result.Error + } + return &user, nil +} + +// FindByID 根据ID查找用户 +func (r *userRepo) FindByID(id uint) (*model.User, error) { + var user model.User + result := r.db.First(&user, id) + if result.Error != nil { + return nil, result.Error + } + return &user, nil +} diff --git a/internal/task/task.go b/internal/task/task.go index 5ecb5bb..44975b4 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -65,7 +65,7 @@ func (tq *TaskQueue) AddTask(task Task) { priority: task.GetPriority(), } heap.Push(tq.queue, item) - tq.logger.Info("Task added to queue: " + task.GetID()) + tq.logger.Info("任务已添加到队列: " + task.GetID()) } // GetNextTask 获取下一个要执行的任务(优先级最高的任务) @@ -79,7 +79,7 @@ func (tq *TaskQueue) GetNextTask() Task { // 获取优先级最高的任务 item := heap.Pop(tq.queue).(*taskItem) - tq.logger.Info("Task retrieved from queue: " + item.task.GetID()) + tq.logger.Info("从队列中获取任务: " + item.task.GetID()) return item.task } @@ -160,7 +160,7 @@ func NewExecutor(workers int) *Executor { // Start 启动任务执行器 func (e *Executor) Start() { - e.logger.Info(fmt.Sprintf("Starting task executor with %d workers", e.workers)) + e.logger.Info(fmt.Sprintf("正在启动任务执行器,工作协程数: %d", e.workers)) // 启动工作协程 for i := 0; i < e.workers; i++ { @@ -168,12 +168,12 @@ func (e *Executor) Start() { go e.worker(i) } - e.logger.Info("Task executor started successfully") + e.logger.Info("任务执行器启动成功") } // Stop 停止任务执行器 func (e *Executor) Stop() { - e.logger.Info("Stopping task executor") + e.logger.Info("正在停止任务执行器") // 取消上下文 e.cancel() @@ -181,37 +181,37 @@ func (e *Executor) Stop() { // 等待所有工作协程结束 e.wg.Wait() - e.logger.Info("Task executor stopped successfully") + e.logger.Info("任务执行器已停止") } // SubmitTask 提交任务到执行器 func (e *Executor) SubmitTask(task Task) { e.taskQueue.AddTask(task) - e.logger.Info("Task submitted: " + task.GetID()) + e.logger.Info("任务已提交: " + task.GetID()) } // worker 工作协程 func (e *Executor) worker(id int) { defer e.wg.Done() - e.logger.Info(fmt.Sprintf("Worker (id = %d) started", id)) + e.logger.Info(fmt.Sprintf("工作协程(id = %d)已启动", id)) for { select { case <-e.ctx.Done(): - e.logger.Info(fmt.Sprintf("Worker %d stopped", id)) + e.logger.Info(fmt.Sprintf("工作协程 %d 已停止", id)) return default: // 获取下一个任务 task := e.taskQueue.GetNextTask() if task != nil { - e.logger.Info(fmt.Sprintf("Worker %d executing task: %s", id, task.GetID())) + e.logger.Info(fmt.Sprintf("工作协程 %d 正在执行任务: %s", id, task.GetID())) // 执行任务 if err := task.Execute(); err != nil { - e.logger.Error("Task execution failed: " + task.GetID() + ", error: " + err.Error()) + e.logger.Error("任务执行失败: " + task.GetID() + ", 错误: " + err.Error()) } else { - e.logger.Info("Task executed successfully: " + task.GetID()) + e.logger.Info("任务执行成功: " + task.GetID()) } } else { // 没有任务时短暂休眠 diff --git a/main.go b/main.go index 84504d7..ac50dd7 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ func main() { // 加载配置 cfg := config.NewConfig() if err := cfg.Load("config.yml"); err != nil { - logger.Error("Failed to load config: " + err.Error()) + logger.Error("配置加载失败: " + err.Error()) os.Exit(1) } @@ -33,12 +33,12 @@ func main() { // 启动核心应用 if err := app.Start(); err != nil { - logger.Error("Failed to start application: " + err.Error()) + logger.Error("应用启动失败: " + err.Error()) os.Exit(1) } // 记录应用启动成功 - logger.Info("Application started successfully") + logger.Info("应用启动成功") // 等待中断信号以优雅地关闭应用 sigChan := make(chan os.Signal, 1) @@ -47,10 +47,10 @@ func main() { // 停止核心应用 if err := app.Stop(); err != nil { - logger.Error("Failed to stop application: " + err.Error()) + logger.Error("应用停止失败: " + err.Error()) os.Exit(1) } // 记录应用停止成功 - logger.Info("Application stopped successfully") + logger.Info("应用停止成功") } diff --git a/vendor/github.com/bytedance/sonic/.gitignore b/vendor/github.com/bytedance/sonic/.gitignore index fa60f43..0d88447 100644 --- a/vendor/github.com/bytedance/sonic/.gitignore +++ b/vendor/github.com/bytedance/sonic/.gitignore @@ -49,7 +49,4 @@ ast/bench.sh !testdata/*.json.gz fuzz/testdata -*__debug_bin* -*pprof -*coverage.txt -tools/venv/* \ No newline at end of file +*__debug_bin \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/.gitmodules b/vendor/github.com/bytedance/sonic/.gitmodules index 5a2d998..ea84b99 100644 --- a/vendor/github.com/bytedance/sonic/.gitmodules +++ b/vendor/github.com/bytedance/sonic/.gitmodules @@ -4,6 +4,3 @@ [submodule "tools/simde"] path = tools/simde url = https://github.com/simd-everywhere/simde.git -[submodule "fuzz/go-fuzz-corpus"] - path = fuzz/go-fuzz-corpus - url = https://github.com/dvyukov/go-fuzz-corpus.git diff --git a/vendor/github.com/bytedance/sonic/README.md b/vendor/github.com/bytedance/sonic/README.md index 6ada7f6..f3c7363 100644 --- a/vendor/github.com/bytedance/sonic/README.md +++ b/vendor/github.com/bytedance/sonic/README.md @@ -6,10 +6,9 @@ A blazingly fast JSON serializing & deserializing library, accelerated by JI ## Requirement -- Go: 1.18~1.25 - - Notice: Go1.24.0 is not supported due to the [issue](https://github.com/golang/go/issues/71672), please use higher go version or add build tag `--ldflags="-checklinkname=0"` -- OS: Linux / MacOS / Windows -- CPU: AMD64 / (ARM64, need go1.20 above) +- Go 1.16~1.22 +- Linux / MacOS / Windows(need go1.17 above) +- Amd64 ARCH ## Features @@ -212,7 +211,7 @@ 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 **DO NOT** compacting them for performance concerns. We provide the option `encoder.CompactMarshaler` to add compacting process. +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 @@ -283,22 +282,6 @@ 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. -#### SearchOption - -`Searcher` provides some options for user to meet different needs: - -```go -opts := ast.SearchOption{ CopyReturn: true ... } -val, err := sonic.GetWithOptions(JSON, opts, "key") -``` - -- CopyReturn -Indicate the searcher to copy the result JSON string instead of refer from the input. This can help to reduce memory usage if you cache the results -- ConcurentRead -Since `ast.Node` use `Lazy-Load` design, it doesn't support Concurrently-Read by default. If you want to read it concurrently, please specify it. -- ValidateJSON -Indicate the searcher to validate the entire JSON. This option is enabled by default, which slow down the search speed a little. - #### Set/Unset Modify the json content by Set()/Unset() @@ -385,12 +368,16 @@ See [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go ## Compatibility -For developers who want to use sonic to meet different scenarios, we provide some integrated configs as `sonic.API` +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: -- `ConfigDefault`: the sonic's default config (`EscapeHTML=false`,`SortKeys=false`...) to run sonic fast meanwhile ensure security. -- `ConfigStd`: the std-compatible config (`EscapeHTML=true`,`SortKeys=true`...) -- `ConfigFastest`: the fastest config (`NoQuoteTextMarshaler=true`) to run on sonic as fast as possible. -Sonic **DOES NOT** ensure to support all environments, due to the difficulty of developing high-performance codes. On non-sonic-supporting environment, the implementation will fall back to `encoding/json`. Thus below configs will all equal to `ConfigStd`. +- 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 @@ -479,23 +466,6 @@ For better performance, in previous case the `ast.Visitor` will be the better ch But `ast.Visitor` is not a very handy API. You might need to write a lot of code to implement your visitor and carefully maintain the tree hierarchy during decoding. Please read the comments in [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) carefully if you decide to use this API. -### Buffer Size - -Sonic use memory pool in many places like `encoder.Encode`, `ast.Node.MarshalJSON` to improve performance, which may produce more memory usage (in-use) when server's load is high. See [issue 614](https://github.com/bytedance/sonic/issues/614). Therefore, we introduce some options to let user control the behavior of memory pool. See [option](https://pkg.go.dev/github.com/bytedance/sonic@v1.11.9/option#pkg-variables) package. - -### Faster JSON Skip - -For security, sonic use [FSM](native/skip_one.c) algorithm to validate JSON when decoding raw JSON or encoding `json.Marshaler`, which is much slower (1~10x) than [SIMD-searching-pair](native/skip_one_fast.c) algorithm. If user has many redundant JSON value and DO NOT NEED to strictly validate JSON correctness, you can enable below options: - -- `Config.NoValidateSkipJSON`: for faster skipping JSON when decoding, such as unknown fields, json.Unmarshaler(json.RawMessage), mismatched values, and redundant array elements -- `Config.NoValidateJSONMarshaler`: avoid validating JSON when encoding `json.Marshaler` -- `SearchOption.ValidateJSON`: indicates if validate located JSON value when `Get` - -## JSON-Path Support (GJSON) - -[tidwall/gjson](https://github.com/tidwall/gjson) has provided a comprehensive and popular JSON-Path API, and - a lot of older codes heavily relies on it. Therefore, we provides a wrapper library, which combines gjson's API with sonic's SIMD algorithm to boost up the performance. See [cloudwego/gjson](https://github.com/cloudwego/gjson). - ## Community Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem. diff --git a/vendor/github.com/bytedance/sonic/README_ZH_CN.md b/vendor/github.com/bytedance/sonic/README_ZH_CN.md index ef4fc21..d0341ab 100644 --- a/vendor/github.com/bytedance/sonic/README_ZH_CN.md +++ b/vendor/github.com/bytedance/sonic/README_ZH_CN.md @@ -6,10 +6,9 @@ ## 依赖 -- Go: 1.18~1.25 - - 注意:Go1.24.0 由于 [issue](https://github.com/golang/go/issues/71672) 不可用,请升级到更高 Go 版本,或添加编译选项 `--ldflags="-checklinkname=0"` -- OS: Linux / MacOS / Windows -- CPU: AMD64 / (ARM64, 需要 Go1.20 以上) +- Go 1.16~1.22 +- Linux / MacOS / Windows(需要 Go1.17 以上) +- Amd64 架构 ## 接口 @@ -261,7 +260,7 @@ fmt.Printf("%+v", data) // {A:0 B:1} ### `Ast.Node` -Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改JSON数据的鲁棒的 API。 +Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改通用数据的鲁棒的 API。 #### 查找/索引 @@ -283,22 +282,6 @@ sub := root.Get("key3").Index(2).Int64() // == 3 **注意**:由于 `Index()` 使用偏移量来定位数据,比使用扫描的 `Get()` 要快的多,建议尽可能的使用 `Index` 。 Sonic 也提供了另一个 API, `IndexOrGet()` ,以偏移量为基础并且也确保键的匹配。 -#### 查找选项 - -`ast.Searcher`提供了一些选项,以满足用户的不同需求: - -```go -opts := ast.SearchOption{CopyReturn: true…} -val, err := sonic.GetWithOptions(JSON, opts, "key") -``` - -- CopyReturn -指示搜索器复制结果JSON字符串,而不是从输入引用。如果用户缓存结果,这有助于减少内存使用 -- ConcurentRead -因为`ast.Node`使用`Lazy-Load`设计,默认不支持并发读取。如果您想同时读取,请指定它。 -- ValidateJSON -指示搜索器来验证整个JSON。默认情况下启用该选项, 但是对于查找速度有一定影响。 - #### 修改 使用 `Set()` / `Unset()` 修改 json 的内容 @@ -385,12 +368,16 @@ type Visitor interface { ## 兼容性 -对于想要使用sonic来满足不同场景的开发人员,我们提供了一些集成配置: +由于开发高性能代码的困难性, Sonic **不**保证对所有环境的支持。对于在不同环境中使用 Sonic 构建应用程序的开发者,我们有以下建议: -- `ConfigDefault`: sonic的默认配置 (`EscapeHTML=false`, `SortKeys=false`…) 保证性能同时兼顾安全性。 -- `ConfigStd`: 与 `encoding/json` 保证完全兼容的配置 -- `ConfigFastest`: 最快的配置(`NoQuoteTextMarshaler=true...`) 保证性能最优但是会缺少一些安全性检查(validate UTF8 等) -Sonic **不**确保支持所有环境,由于开发高性能代码的困难。在不支持sonic的环境中,实现将回落到 `encoding/json`。因此上述配置将全部等于`ConfigStd`。 +- 在 **Mac M1** 上开发:确保在您的计算机上安装了 Rosetta 2,并在构建时设置 `GOARCH=amd64` 。 Rosetta 2 可以自动将 x86 二进制文件转换为 arm64 二进制文件,并在 Mac M1 上运行 x86 应用程序。 +- 在 **Linux arm64** 上开发:您可以安装 qemu 并使用 `qemu-x86_64 -cpu max` 命令来将 x86 二进制文件转换为 arm64 二进制文件。qemu可以实现与Mac M1上的Rosetta 2类似的转换效果。 + +对于希望在不使用 qemu 下使用 sonic 的开发者,或者希望处理 JSON 时与 `encoding/JSON` 严格保持一致的开发者,我们在 `sonic.API` 中提供了一些兼容性 API + +- `ConfigDefault`: 在支持 sonic 的环境下 sonic 的默认配置(`EscapeHTML=false`,`SortKeys=false`等)。行为与具有相应配置的 `encoding/json` 一致,一些选项,如 `SortKeys=false` 将无效。 +- `ConfigStd`: 在支持 sonic 的环境下与标准库兼容的配置(`EscapeHTML=true`,`SortKeys=true`等)。行为与 `encoding/json` 一致。 +- `ConfigFastest`: 在支持 sonic 的环境下运行最快的配置(`NoQuoteTextMarshaler=true`)。行为与具有相应配置的 `encoding/json` 一致,某些选项将无效。 ## 注意事项 @@ -477,18 +464,6 @@ go someFunc(user) 但是,`ast.Visitor` 并不是一个很易用的 API。你可能需要写大量的代码去实现自己的 `ast.Visitor`,并且需要在解析过程中仔细维护树的层级。如果你决定要使用这个 API,请先仔细阅读 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) 中的注释。 -### 缓冲区大小 - -Sonic在许多地方使用内存池,如`encoder.Encode`, `ast.Node.MarshalJSON`等来提高性能,这可能会在服务器负载高时产生更多的内存使用(in-use)。参见[issue 614](https://github.com/bytedance/sonic/issues/614)。因此,我们引入了一些选项来让用户配置内存池的行为。参见[option](https://pkg.go.dev/github.com/bytedance/sonic@v1.11.9/option#pkg-variables)包。 - -### 更快的 JSON Skip - -为了安全起见,在跳过原始JSON 时,sonic decoder 默认使用[FSM](native/skip_one.c)算法扫描来跳过同时校验 JSON。它相比[SIMD-searching-pair](native/skip_one_fast.c)算法跳过要慢得多(1~10倍)。如果用户有很多冗余的JSON值,并且不需要严格验证JSON的正确性,你可以启用以下选项: - -- `Config.NoValidateSkipJSON`: 用于在解码时更快地跳过JSON,例如未知字段,`json.RawMessage`,不匹配的值和冗余的数组元素等 -- `Config.NoValidateJSONMarshaler`: 编码JSON时避免验证JSON。封送拆收器 -- `SearchOption.ValidateJSON`: 指示当`Get`时是否验证定位的JSON值 - ## 社区 Sonic 是 [CloudWeGo](https://www.cloudwego.io/) 下的一个子项目。我们致力于构建云原生生态系统。 diff --git a/vendor/github.com/bytedance/sonic/api.go b/vendor/github.com/bytedance/sonic/api.go index 3858d9a..0933291 100644 --- a/vendor/github.com/bytedance/sonic/api.go +++ b/vendor/github.com/bytedance/sonic/api.go @@ -23,16 +23,6 @@ import ( `github.com/bytedance/sonic/internal/rt` ) -const ( - // UseStdJSON indicates you are using fallback implementation (encoding/json) - UseStdJSON = iota - // UseSonicJSON indicates you are using real sonic implementation - UseSonicJSON -) - -// APIKind is the kind of API, 0 is std json, 1 is sonic. -const APIKind = apiKind - // Config is a combination of sonic/encoder.Options and sonic/decoder.Options type Config struct { // EscapeHTML indicates encoder to escape all HTML characters @@ -77,26 +67,16 @@ type Config struct { // CopyString indicates decoder to decode string values by copying instead of referring. CopyString bool - // ValidateString indicates decoder and encoder to validate string values: decoder will return errors + // 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 // NoValidateJSONMarshaler indicates that the encoder should not validate the output string // after encoding the JSONMarshaler to JSON. NoValidateJSONMarshaler bool - - // NoValidateJSONSkip indicates the decoder should not validate the JSON value when skipping it, - // such as unknown-fields, mismatched-type, redundant elements.. - NoValidateJSONSkip bool // NoEncoderNewline indicates that the encoder should not add a newline after every message NoEncoderNewline bool - - // Encode Infinity or Nan float into `null`, instead of returning an error. - EncodeNullForInfOrNan bool - - // CaseSensitive indicates that the decoder should not ignore the case of object keys. - CaseSensitive bool } var ( @@ -114,15 +94,15 @@ var ( // ConfigFastest is the fastest config of APIs, aiming at speed. ConfigFastest = Config{ + NoQuoteTextMarshaler: true, NoValidateJSONMarshaler: true, - NoValidateJSONSkip: true, }.Froze() ) // API is a binding of specific config. // This interface is inspired by github.com/json-iterator/go, -// and has same behaviors under equivalent config. +// and has same behaviors under equavilent config. type API interface { // MarshalToString returns the JSON encoding string of v MarshalToString(v interface{}) (string, error) @@ -177,13 +157,6 @@ func Marshal(val interface{}) ([]byte, error) { return ConfigDefault.Marshal(val) } -// MarshalIndent is like Marshal 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 MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { - return ConfigDefault.MarshalIndent(v, prefix, indent) -} - // MarshalString returns the JSON encoding string of v. func MarshalString(val interface{}) (string, error) { return ConfigDefault.MarshalToString(val) @@ -216,14 +189,6 @@ func Get(src []byte, path ...interface{}) (ast.Node, error) { return GetCopyFromString(rt.Mem2Str(src), path...) } -//GetWithOptions searches and locates the given path from src json, -// with specific options of ast.Searcher -func GetWithOptions(src []byte, opts ast.SearchOptions, path ...interface{}) (ast.Node, error) { - s := ast.NewSearcher(rt.Mem2Str(src)) - s.SearchOptions = opts - return s.GetByPath(path...) -} - // GetFromString is same with Get except src is string. // // WARNING: The returned JSON is **Referenced** from the input. diff --git a/vendor/github.com/bytedance/sonic/ast/api_compat.go b/vendor/github.com/bytedance/sonic/ast/api_compat.go index c6a540c..82d1eac 100644 --- a/vendor/github.com/bytedance/sonic/ast/api_compat.go +++ b/vendor/github.com/bytedance/sonic/ast/api_compat.go @@ -1,4 +1,4 @@ -// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20 +// +build !amd64,!arm64 go1.23 !go1.16 arm64,!go1.20 /* * Copyright 2022 ByteDance Inc. @@ -23,17 +23,28 @@ import ( `unicode/utf8` `github.com/bytedance/sonic/internal/native/types` - `github.com/bytedance/sonic/internal/compat` + `github.com/bytedance/sonic/internal/rt` ) func init() { - compat.Warn("sonic/ast") + println("WARNING:(ast) sonic only supports Go1.16~1.22, but your environment is not suitable") } func quote(buf *[]byte, val string) { quoteString(buf, val) } +// unquote unescapes a internal JSON string (it doesn't count quotas at the begining and end) +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 (self *Parser) decodeValue() (val types.JsonState) { e, v := decodeValue(self.s, self.p, self.dbuf == nil) if e < 0 { diff --git a/vendor/github.com/bytedance/sonic/ast/decode.go b/vendor/github.com/bytedance/sonic/ast/decode.go index 45f5e2d..c521fb5 100644 --- a/vendor/github.com/bytedance/sonic/ast/decode.go +++ b/vendor/github.com/bytedance/sonic/ast/decode.go @@ -17,35 +17,36 @@ package ast import ( - "encoding/base64" - "runtime" - "strconv" - "unsafe" + `encoding/base64` + `runtime` + `strconv` + `unsafe` - "github.com/bytedance/sonic/internal/native/types" - "github.com/bytedance/sonic/internal/rt" - "github.com/bytedance/sonic/internal/utils" - "github.com/bytedance/sonic/unquote" + `github.com/bytedance/sonic/internal/native/types` + `github.com/bytedance/sonic/internal/rt` ) - -var bytesNull = []byte("null") +const _blankCharsMask = (1 << ' ') | (1 << '\t') | (1 << '\r') | (1 << '\n') const ( - strNull = "null" + bytesNull = "null" bytesTrue = "true" bytesFalse = "false" bytesObject = "{}" bytesArray = "[]" ) +func isSpace(c byte) bool { + return (int(1< len(src) { return -int(types.ERR_EOF) } - if src[pos:ret] == strNull { + if src[pos:ret] == bytesNull { return ret } else { return -int(types.ERR_INVALID_CHAR) @@ -102,13 +103,13 @@ func decodeString(src string, pos int) (ret int, v string) { return ret, v } - result, err := unquote.String(src[pos:ret]) - if err != 0 { + vv, ok := unquoteBytes(rt.Str2Mem(src[pos:ret])) + if !ok { return -int(types.ERR_INVALID_CHAR), "" } runtime.KeepAlive(src) - return ret, result + return ret, rt.Mem2Str(vv) } func decodeBinary(src string, pos int) (ret int, v []byte) { @@ -286,7 +287,67 @@ func decodeValue(src string, pos int, skipnum bool) (ret int, v types.JsonState) //go:nocheckptr func skipNumber(src string, pos int) (ret int) { - return utils.SkipNumber(src, pos) + 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 || exponent || 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)) } //go:nocheckptr @@ -544,7 +605,7 @@ func _DecodeString(src string, pos int, needEsc bool, validStr bool) (v string, return str, p.p, true } /* unquote the string */ - out, err := unquote.String(str) + out, err := unquote(str) /* check for errors */ if err != 0 { return "", -int(err), true diff --git a/vendor/github.com/bytedance/sonic/ast/encode.go b/vendor/github.com/bytedance/sonic/ast/encode.go index 9401a66..956809c 100644 --- a/vendor/github.com/bytedance/sonic/ast/encode.go +++ b/vendor/github.com/bytedance/sonic/ast/encode.go @@ -17,12 +17,12 @@ package ast import ( - "sync" - "unicode/utf8" + `sync` + `unicode/utf8` +) - "github.com/bytedance/gopkg/lang/dirtmake" - "github.com/bytedance/sonic/internal/rt" - "github.com/bytedance/sonic/option" +const ( + _MaxBuffer = 1024 // 1KB buffer size ) func quoteString(e *[]byte, s string) { @@ -30,7 +30,7 @@ func quoteString(e *[]byte, s string) { start := 0 for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { - if rt.SafeSet[b] { + if safeSet[b] { i++ continue } @@ -54,8 +54,8 @@ func quoteString(e *[]byte, s string) { // user-controlled strings are rendered into JSON // and served to some browsers. *e = append(*e, `u00`...) - *e = append(*e, rt.Hex[b>>4]) - *e = append(*e, rt.Hex[b&0xF]) + *e = append(*e, hex[b>>4]) + *e = append(*e, hex[b&0xF]) } i++ start = i @@ -76,7 +76,7 @@ func quoteString(e *[]byte, s string) { *e = append(*e, s[start:i]...) } *e = append(*e, `\u202`...) - *e = append(*e, rt.Hex[c&0xF]) + *e = append(*e, hex[c&0xF]) i += size start = i continue @@ -92,29 +92,16 @@ func quoteString(e *[]byte, s string) { var bytesPool = sync.Pool{} func (self *Node) MarshalJSON() ([]byte, error) { - if self == nil { - return bytesNull, nil - } - - // fast path for raw node - if self.isRaw() { - return rt.Str2Mem(self.toString()), nil - } - buf := newBuffer() err := self.encode(buf) if err != nil { freeBuffer(buf) return nil, err } - var ret []byte - if !rt.CanSizeResue(cap(*buf)) { - ret = *buf - } else { - ret = dirtmake.Bytes(len(*buf), len(*buf)) - copy(ret, *buf) - freeBuffer(buf) - } + + ret := make([]byte, len(*buf)) + copy(ret, *buf) + freeBuffer(buf) return ret, err } @@ -122,24 +109,21 @@ func newBuffer() *[]byte { if ret := bytesPool.Get(); ret != nil { return ret.(*[]byte) } else { - buf := make([]byte, 0, option.DefaultAstBufferSize) + buf := make([]byte, 0, _MaxBuffer) return &buf } } func freeBuffer(buf *[]byte) { - if !rt.CanSizeResue(cap(*buf)) { - return - } *buf = (*buf)[:0] bytesPool.Put(buf) } func (self *Node) encode(buf *[]byte) error { - if self.isRaw() { + if self.IsRaw() { return self.encodeRaw(buf) } - switch int(self.itype()) { + switch self.Type() { case V_NONE : return ErrNotExist case V_ERROR : return self.Check() case V_NULL : return self.encodeNull(buf) @@ -155,21 +139,16 @@ func (self *Node) encode(buf *[]byte) error { } func (self *Node) encodeRaw(buf *[]byte) error { - lock := self.rlock() - if !self.isRaw() { - self.runlock() - return self.encode(buf) - } - raw := self.toString() - if lock { - self.runlock() + 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, strNull...) + *buf = append(*buf, bytesNull...) return nil } diff --git a/vendor/github.com/bytedance/sonic/ast/error.go b/vendor/github.com/bytedance/sonic/ast/error.go index 3716e7a..00a0446 100644 --- a/vendor/github.com/bytedance/sonic/ast/error.go +++ b/vendor/github.com/bytedance/sonic/ast/error.go @@ -17,10 +17,6 @@ func newError(err types.ParsingError, msg string) *Node { } } -func newErrorPair(err SyntaxError) *Pair { - return &Pair{0, "", *newSyntaxError(err)} -} - // Error returns error message if the node is invalid func (self Node) Error() string { if self.t != V_ERROR { @@ -83,7 +79,7 @@ func (self SyntaxError) description() string { /* check for empty source */ if self.Src == "" { - return fmt.Sprintf("no sources available, the input json is empty: %#v", self) + return fmt.Sprintf("no sources available: %#v", self) } /* prevent slicing before the beginning */ diff --git a/vendor/github.com/bytedance/sonic/ast/iterator.go b/vendor/github.com/bytedance/sonic/ast/iterator.go index 978028a..64e1e5a 100644 --- a/vendor/github.com/bytedance/sonic/ast/iterator.go +++ b/vendor/github.com/bytedance/sonic/ast/iterator.go @@ -17,29 +17,19 @@ package ast import ( - "fmt" + `fmt` - "github.com/bytedance/sonic/internal/caching" - "github.com/bytedance/sonic/internal/native/types" + `github.com/bytedance/sonic/internal/native/types` ) type Pair struct { - hash uint64 Key string Value Node } -func NewPair(key string, val Node) Pair { - return Pair{ - hash: caching.StrHash(key), - Key: key, - Value: val, - } -} - // Values returns iterator for array's children traversal func (self *Node) Values() (ListIterator, error) { - if err := self.should(types.V_ARRAY); err != nil { + if err := self.should(types.V_ARRAY, "an array"); err != nil { return ListIterator{}, err } return self.values(), nil @@ -51,7 +41,7 @@ func (self *Node) values() ListIterator { // Properties returns iterator for object's children traversal func (self *Node) Properties() (ObjectIterator, error) { - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return ObjectIterator{}, err } return self.properties(), nil @@ -173,14 +163,11 @@ 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. // -// Especially, if the node is not V_ARRAY or V_OBJECT, +// Especailly, if the node is not V_ARRAY or V_OBJECT, // the node itself will be returned and Sequence.Index == -1. // -// NOTICE: An unset node WON'T trigger sc, but its index still counts into Path.Index +// NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index func (self *Node) ForEach(sc Scanner) error { - if err := self.checkRaw(); err != nil { - return err - } switch self.itype() { case types.V_ARRAY: iter, err := self.Values() diff --git a/vendor/github.com/bytedance/sonic/ast/node.go b/vendor/github.com/bytedance/sonic/ast/node.go index 6ea5f52..ac6d228 100644 --- a/vendor/github.com/bytedance/sonic/ast/node.go +++ b/vendor/github.com/bytedance/sonic/ast/node.go @@ -17,15 +17,13 @@ package ast import ( - "encoding/json" - "fmt" - "strconv" - "sync" - "sync/atomic" - "unsafe" - - "github.com/bytedance/sonic/internal/native/types" - "github.com/bytedance/sonic/internal/rt" + `encoding/json` + `fmt` + `strconv` + `unsafe` + + `github.com/bytedance/sonic/internal/native/types` + `github.com/bytedance/sonic/internal/rt` ) const ( @@ -38,7 +36,7 @@ const ( _V_ARRAY_LAZY = _V_LAZY | types.V_ARRAY _V_OBJECT_LAZY = _V_LAZY | types.V_OBJECT _MASK_LAZY = _V_LAZY - 1 - _MASK_RAW = _V_RAW - 1 + _MASK_RAW = _V_RAW - 1 ) const ( @@ -58,20 +56,19 @@ type Node struct { t types.ValueType l uint p unsafe.Pointer - m *sync.RWMutex } // UnmarshalJSON is just an adapter to json.Unmarshaler. // If you want better performance, use Searcher.GetByPath() directly func (self *Node) UnmarshalJSON(data []byte) (err error) { - *self = newRawNode(rt.Mem2Str(data), switchRawType(data[0]), false) - return nil + *self = NewRaw(string(data)) + return self.Check() } /** Node Type Accessor **/ // Type returns json type represented by the node -// It will be one of bellows: +// It will be one of belows: // V_NONE = 0 (empty node, key not exists) // V_ERROR = 1 (error node) // V_NULL = 2 (json value `null`, key exists) @@ -82,39 +79,17 @@ func (self *Node) UnmarshalJSON(data []byte) (err error) { // V_STRING = 7 (json value string) // V_NUMBER = 33 (json value number ) // V_ANY = 34 (golang interface{}) -// -// Deprecated: not concurrent safe. Use TypeSafe instead func (self Node) Type() int { return int(self.t & _MASK_LAZY & _MASK_RAW) } -// Type concurrently-safe returns json type represented by the node -// It will be one of bellows: -// V_NONE = 0 (empty node, key not exists) -// V_ERROR = 1 (error node) -// V_NULL = 2 (json value `null`, key exists) -// V_TRUE = 3 (json value `true`) -// V_FALSE = 4 (json value `false`) -// V_ARRAY = 5 (json value array) -// V_OBJECT = 6 (json value object) -// V_STRING = 7 (json value string) -// V_NUMBER = 33 (json value number ) -// V_ANY = 34 (golang interface{}) -func (self *Node) TypeSafe() int { - return int(self.loadt() & _MASK_LAZY & _MASK_RAW) -} - -func (self *Node) itype() types.ValueType { +func (self Node) itype() types.ValueType { return self.t & _MASK_LAZY & _MASK_RAW } // Exists returns false only if the self is nil or empty node V_NONE func (self *Node) Exists() bool { - if self == nil { - return false - } - t := self.loadt() - return t != V_ERROR && t != _V_NONE + return self.Valid() && self.t != _V_NONE } // Valid reports if self is NOT V_ERROR or nil @@ -122,7 +97,7 @@ func (self *Node) Valid() bool { if self == nil { return false } - return self.loadt() != V_ERROR + return self.t != V_ERROR } // Check checks if the node itself is valid, and return: @@ -131,31 +106,24 @@ func (self *Node) Valid() bool { func (self *Node) Check() error { if self == nil { return ErrNotExist - } else if self.loadt() != V_ERROR { + } else if self.t != V_ERROR { return nil } else { return self } } -// isRaw returns true if node's underlying value is raw json -// -// Deprecated: not concurrent safe -func (self Node) IsRaw() bool { - return self.t & _V_RAW != 0 -} - // IsRaw returns true if node's underlying value is raw json -func (self *Node) isRaw() bool { - return self.loadt() & _V_RAW != 0 +func (self Node) IsRaw() bool { + return self.t&_V_RAW != 0 } func (self *Node) isLazy() bool { - return self != nil && self.t & _V_LAZY != 0 + return self != nil && self.t&_V_LAZY != 0 } func (self *Node) isAny() bool { - return self != nil && self.loadt() == _V_ANY + return self != nil && self.t == _V_ANY } /** Simple Value Methods **/ @@ -165,26 +133,18 @@ func (self *Node) Raw() (string, error) { if self == nil { return "", ErrNotExist } - lock := self.rlock() - if !self.isRaw() { - if lock { - self.runlock() - } + if !self.IsRaw() { buf, err := self.MarshalJSON() return rt.Mem2Str(buf), err } - ret := self.toString() - if lock { - self.runlock() - } - return ret, nil + return self.toString(), nil } func (self *Node) checkRaw() error { if err := self.Check(); err != nil { return err } - if self.isRaw() { + if self.IsRaw() { self.parseRaw(false) } return self.Check() @@ -440,7 +400,7 @@ func (self *Node) String() (string, error) { } } -// StrictString returns string value (unescaped), including V_STRING, V_ANY of string. +// StrictString returns string value (unescaped), includeing V_STRING, V_ANY of string. // In other cases, it will return empty string. func (self *Node) StrictString() (string, error) { if err := self.checkRaw(); err != nil { @@ -509,24 +469,7 @@ func (self *Node) Float64() (float64, error) { } } -func (self *Node) StrictBool() (bool, error) { - if err := self.checkRaw(); err!= nil { - return false, err - } - switch self.t { - case types.V_TRUE : return true, nil - case types.V_FALSE : return false, nil - case _V_ANY : - any := self.packAny() - switch v := any.(type) { - case bool : return v, nil - default : return false, ErrUnsupportType - } - default : return false, ErrUnsupportType - } -} - -// Float64 exports underlying float64 value, including V_NUMBER, V_ANY +// Float64 exports underlying float64 value, includeing V_NUMBER, V_ANY func (self *Node) StrictFloat64() (float64, error) { if err := self.checkRaw(); err != nil { return 0.0, err @@ -544,7 +487,7 @@ func (self *Node) StrictFloat64() (float64, error) { } } -/** Sequential Value Methods **/ +/** Sequencial Value Methods **/ // Len returns children count of a array|object|string node // WARN: For partially loaded node, it also works but only counts the parsed children @@ -561,7 +504,7 @@ func (self *Node) Len() (int, error) { } } -func (self *Node) len() int { +func (self Node) len() int { return int(self.l) } @@ -584,7 +527,7 @@ func (self *Node) Cap() (int, error) { // // If self is V_NONE or V_NULL, it becomes V_OBJECT and sets the node at the key. func (self *Node) Set(key string, node Node) (bool, error) { - if err := self.checkRaw(); err != nil { + if err := self.Check(); err != nil { return false, err } if err := node.Check(); err != nil { @@ -592,7 +535,7 @@ func (self *Node) Set(key string, node Node) (bool, error) { } if self.t == _V_NONE || self.t == types.V_NULL { - *self = NewObject([]Pair{NewPair(key, node)}) + *self = NewObject([]Pair{{key, node}}) return false, nil } else if self.itype() != types.V_OBJECT { return false, ErrUnsupportType @@ -606,7 +549,7 @@ func (self *Node) Set(key string, node Node) (bool, error) { *self = newObject(new(linkedPairs)) } s := (*linkedPairs)(self.p) - s.Push(NewPair(key, node)) + s.Push(Pair{key, node}) self.l++ return false, nil @@ -625,10 +568,10 @@ func (self *Node) SetAny(key string, val interface{}) (bool, error) { // Unset REMOVE (soft) the node of given key under object parent, and reports if the key has existed. func (self *Node) Unset(key string) (bool, error) { - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return false, err } - // NOTICE: must get accurate length before deduct + // NOTICE: must get acurate length before deduct if err := self.skipAllKey(); err != nil { return false, err } @@ -646,7 +589,7 @@ func (self *Node) Unset(key string) (bool, error) { // // The index must be within self's children. func (self *Node) SetByIndex(index int, node Node) (bool, error) { - if err := self.checkRaw(); err != nil { + if err := self.Check(); err != nil { return false, err } if err := node.Check(); err != nil { @@ -674,7 +617,7 @@ func (self *Node) SetAnyByIndex(index int, val interface{}) (bool, error) { return self.SetByIndex(index, NewAny(val)) } -// UnsetByIndex REMOVE (softly) the node of given index. +// UnsetByIndex REOMVE (softly) the node of given index. // // WARN: this will change address of elements, which is a dangerous action. // Use Unset() for object or Pop() for array instead. @@ -726,7 +669,7 @@ func (self *Node) UnsetByIndex(index int) (bool, error) { // // If self is V_NONE or V_NULL, it becomes V_ARRAY and sets the node at index 0. func (self *Node) Add(node Node) error { - if err := self.checkRaw(); err != nil { + if err := self.Check(); err != nil { return err } @@ -734,7 +677,7 @@ func (self *Node) Add(node Node) error { *self = NewArray([]Node{node}) return nil } - if err := self.should(types.V_ARRAY); err != nil { + if err := self.should(types.V_ARRAY, "an array"); err != nil { return err } @@ -793,11 +736,11 @@ func (self *Node) Pop() error { } // Move moves the child at src index to dst index, -// meanwhile slides siblings from src+1 to dst. +// meanwhile slides sliblings from src+1 to dst. // // WARN: this will change address of elements, which is a dangerous action. func (self *Node) Move(dst, src int) error { - if err := self.should(types.V_ARRAY); err != nil { + if err := self.should(types.V_ARRAY, "an array"); err != nil { return err } @@ -833,7 +776,7 @@ func (self *Node) Move(dst, src int) error { return nil } -// AddAny wraps val with V_ANY node, and Add() the node. +// SetAny wraps val with V_ANY node, and Add() the node. func (self *Node) AddAny(val interface{}) error { return self.Add(NewAny(val)) } @@ -869,7 +812,7 @@ func (self *Node) GetByPath(path ...interface{}) *Node { // Get loads given key of an object node on demands func (self *Node) Get(key string) *Node { - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return unwrapError(err) } n, _ := self.skipKey(key) @@ -902,14 +845,14 @@ func (self *Node) Index(idx int) *Node { // IndexPair indexies pair at given idx, // node type MUST be either V_OBJECT func (self *Node) IndexPair(idx int) *Pair { - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return nil } return self.skipIndexPair(idx) } func (self *Node) indexOrGet(idx int, key string) (*Node, int) { - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return unwrapError(err), idx } @@ -946,16 +889,16 @@ func (self *Node) Map() (map[string]interface{}, error) { return nil, ErrUnsupportType } } - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return nil, err } - if err := self.loadAllKey(false); err != nil { + if err := self.loadAllKey(); err != nil { return nil, err } return self.toGenericObject() } -// MapUseNumber loads all keys of an object node, with numeric nodes cast to json.Number +// MapUseNumber loads all keys of an object node, with numeric nodes casted to json.Number func (self *Node) MapUseNumber() (map[string]interface{}, error) { if self.isAny() { any := self.packAny() @@ -965,16 +908,16 @@ func (self *Node) MapUseNumber() (map[string]interface{}, error) { return nil, ErrUnsupportType } } - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return nil, err } - if err := self.loadAllKey(false); err != nil { + if err := self.loadAllKey(); err != nil { return nil, err } return self.toGenericObjectUseNumber() } -// MapUseNode scans both parsed and non-parsed children nodes, +// MapUseNode scans both parsed and non-parsed chidren nodes, // and map them by their keys func (self *Node) MapUseNode() (map[string]Node, error) { if self.isAny() { @@ -985,7 +928,7 @@ func (self *Node) MapUseNode() (map[string]Node, error) { return nil, ErrUnsupportType } } - if err := self.should(types.V_OBJECT); err != nil { + if err := self.should(types.V_OBJECT, "an object"); err != nil { return nil, err } if err := self.skipAllKey(); err != nil { @@ -1091,16 +1034,16 @@ func (self *Node) Array() ([]interface{}, error) { return nil, ErrUnsupportType } } - if err := self.should(types.V_ARRAY); err != nil { + if err := self.should(types.V_ARRAY, "an array"); err != nil { return nil, err } - if err := self.loadAllIndex(false); err != nil { + if err := self.loadAllIndex(); err != nil { return nil, err } return self.toGenericArray() } -// ArrayUseNumber loads all indexes of an array node, with numeric nodes cast to json.Number +// ArrayUseNumber loads all indexes of an array node, with numeric nodes casted to json.Number func (self *Node) ArrayUseNumber() ([]interface{}, error) { if self.isAny() { any := self.packAny() @@ -1110,16 +1053,16 @@ func (self *Node) ArrayUseNumber() ([]interface{}, error) { return nil, ErrUnsupportType } } - if err := self.should(types.V_ARRAY); err != nil { + if err := self.should(types.V_ARRAY, "an array"); err != nil { return nil, err } - if err := self.loadAllIndex(false); err != nil { + if err := self.loadAllIndex(); err != nil { return nil, err } return self.toGenericArrayUseNumber() } -// ArrayUseNode copies both parsed and non-parsed children nodes, +// ArrayUseNode copys both parsed and non-parsed chidren nodes, // and indexes them by original order func (self *Node) ArrayUseNode() ([]Node, error) { if self.isAny() { @@ -1130,7 +1073,7 @@ func (self *Node) ArrayUseNode() ([]Node, error) { return nil, ErrUnsupportType } } - if err := self.should(types.V_ARRAY); err != nil { + if err := self.should(types.V_ARRAY, "an array"); err != nil { return nil, err } if err := self.skipAllIndex(); err != nil { @@ -1164,9 +1107,9 @@ func (self *Node) unsafeArray() (*linkedNodes, error) { return (*linkedNodes)(self.p), nil } -// Interface loads all children under all paths from this node, +// Interface loads all children under all pathes from this node, // and converts itself as generic type. -// WARN: all numeric nodes are cast to float64 +// WARN: all numberic nodes are casted to float64 func (self *Node) Interface() (interface{}, error) { if err := self.checkRaw(); err != nil { return nil, err @@ -1186,12 +1129,12 @@ func (self *Node) Interface() (interface{}, error) { } return v, nil case _V_ARRAY_LAZY : - if err := self.loadAllIndex(false); err != nil { + if err := self.loadAllIndex(); err != nil { return nil, err } return self.toGenericArray() case _V_OBJECT_LAZY : - if err := self.loadAllKey(false); err != nil { + if err := self.loadAllKey(); err != nil { return nil, err } return self.toGenericObject() @@ -1210,7 +1153,7 @@ func (self *Node) packAny() interface{} { } // InterfaceUseNumber works same with Interface() -// except numeric nodes are cast to json.Number +// except numberic nodes are casted to json.Number func (self *Node) InterfaceUseNumber() (interface{}, error) { if err := self.checkRaw(); err != nil { return nil, err @@ -1225,12 +1168,12 @@ func (self *Node) InterfaceUseNumber() (interface{}, error) { case types.V_STRING : return self.toString(), nil case _V_NUMBER : return self.toNumber(), nil case _V_ARRAY_LAZY : - if err := self.loadAllIndex(false); err != nil { + if err := self.loadAllIndex(); err != nil { return nil, err } return self.toGenericArrayUseNumber() case _V_OBJECT_LAZY : - if err := self.loadAllKey(false); err != nil { + if err := self.loadAllKey(); err != nil { return nil, err } return self.toGenericObjectUseNumber() @@ -1262,30 +1205,70 @@ func (self *Node) InterfaceUseNode() (interface{}, error) { } } -// LoadAll loads the node's children -// and ensure all its children can be READ concurrently (include its children's children) +// LoadAll loads all the node's children and children's children as parsed. +// After calling it, the node can be safely used on concurrency func (self *Node) LoadAll() error { - return self.Load() + if self.IsRaw() { + self.parseRaw(true) + return self.Check() + } + + switch self.itype() { + case types.V_ARRAY: + e := self.len() + if err := self.loadAllIndex(); err != nil { + return err + } + for i := 0; i < e; i++ { + n := self.nodeAt(i) + if n.IsRaw() { + n.parseRaw(true) + } + if err := n.Check(); err != nil { + return err + } + } + return nil + case types.V_OBJECT: + e := self.len() + if err := self.loadAllKey(); err != nil { + return err + } + for i := 0; i < e; i++ { + n := self.pairAt(i) + if n.Value.IsRaw() { + n.Value.parseRaw(true) + } + if err := n.Value.Check(); err != nil { + return err + } + } + return nil + default: + return self.Check() + } } // Load loads the node's children as parsed. -// and ensure all its children can be READ concurrently (include its children's children) +// After calling it, only the node itself can be used on concurrency (not include its children) func (self *Node) Load() error { + if err := self.checkRaw(); err != nil { + return err + } + switch self.t { - case _V_ARRAY_LAZY: self.loadAllIndex(true) - case _V_OBJECT_LAZY: self.loadAllKey(true) - case V_ERROR: return self - case V_NONE: return nil + case _V_ARRAY_LAZY: + return self.skipAllIndex() + case _V_OBJECT_LAZY: + return self.skipAllKey() + default: + return self.Check() } - if self.m == nil { - self.m = new(sync.RWMutex) - } - return self.checkRaw() } /**---------------------------------- Internal Helper Methods ----------------------------------**/ -func (self *Node) should(t types.ValueType) error { +func (self *Node) should(t types.ValueType, s string) error { if err := self.checkRaw(); err != nil { return err } @@ -1456,17 +1439,13 @@ func (self *Node) skipIndexPair(index int) *Pair { return nil } -func (self *Node) loadAllIndex(loadOnce bool) error { +func (self *Node) loadAllIndex() error { if !self.isLazy() { return nil } var err types.ParsingError parser, stack := self.getParserAndArrayStack() - if !loadOnce { - parser.noLazy = true - } else { - parser.loadOnce = true - } + parser.noLazy = true *self, err = parser.decodeArray(&stack.v) if err != 0 { return parser.ExportError(err) @@ -1474,19 +1453,14 @@ func (self *Node) loadAllIndex(loadOnce bool) error { return nil } -func (self *Node) loadAllKey(loadOnce bool) error { +func (self *Node) loadAllKey() error { if !self.isLazy() { return nil } var err types.ParsingError parser, stack := self.getParserAndObjectStack() - if !loadOnce { - parser.noLazy = true - *self, err = parser.decodeObject(&stack.v) - } else { - parser.loadOnce = true - *self, err = parser.decodeObject(&stack.v) - } + parser.noLazy = true + *self, err = parser.decodeObject(&stack.v) if err != 0 { return parser.ExportError(err) } @@ -1655,23 +1629,7 @@ func NewRaw(json string) Node { if it == _V_NONE { return Node{} } - return newRawNode(parser.s[start:parser.p], it, false) -} - -// NewRawConcurrentRead creates a node of raw json, which can be READ -// (GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON) concurrently. -// If the input json is invalid, NewRaw returns a error Node. -func NewRawConcurrentRead(json string) Node { - parser := NewParserObj(json) - start, err := parser.skip() - if err != 0 { - return *newError(err, err.Message()) - } - it := switchRawType(parser.s[start]) - if it == _V_NONE { - return Node{} - } - return newRawNode(parser.s[start:parser.p], it, true) + return newRawNode(parser.s[start:parser.p], it) } // NewAny creates a node of type V_ANY if any's type isn't Node or *Node, @@ -1695,7 +1653,7 @@ func NewBytes(src []byte) Node { if len(src) == 0 { panic("empty src bytes") } - out := rt.EncodeBase64ToString(src) + out := encodeBase64(src) return NewString(out) } @@ -1731,15 +1689,15 @@ func NewNumber(v string) Node { } } -func (node *Node) toNumber() json.Number { +func (node Node) toNumber() json.Number { return json.Number(rt.StrFrom(node.p, int64(node.l))) } -func (self *Node) toString() string { +func (self Node) toString() string { return rt.StrFrom(self.p, int64(self.l)) } -func (node *Node) toFloat64() (float64, error) { +func (node Node) toFloat64() (float64, error) { ret, err := node.toNumber().Float64() if err != nil { return 0, err @@ -1747,7 +1705,7 @@ func (node *Node) toFloat64() (float64, error) { return ret, nil } -func (node *Node) toInt64() (int64, error) { +func (node Node) toInt64() (int64, error) { ret,err := node.toNumber().Int64() if err != nil { return 0, err @@ -1783,8 +1741,6 @@ func NewArray(v []Node) Node { return newArray(s) } -const _Threshold_Index = 16 - func newArray(v *linkedNodes) Node { return Node{ t: types.V_ARRAY, @@ -1808,9 +1764,6 @@ func NewObject(v []Pair) Node { } func newObject(v *linkedPairs) Node { - if v.size > _Threshold_Index { - v.BuildIndex() - } return Node{ t: types.V_OBJECT, l: uint(v.Len()), @@ -1819,42 +1772,53 @@ func newObject(v *linkedPairs) Node { } func (self *Node) setObject(v *linkedPairs) { - if v.size > _Threshold_Index { - v.BuildIndex() - } self.t = types.V_OBJECT self.l = uint(v.Len()) self.p = unsafe.Pointer(v) } -func (self *Node) parseRaw(full bool) { - lock := self.lock() - defer self.unlock() - if !self.isRaw() { - return +func newRawNode(str string, typ types.ValueType) Node { + return Node{ + t: _V_RAW | typ, + p: rt.StrPtr(str), + l: uint(len(str)), } +} + +func (self *Node) parseRaw(full bool) { raw := self.toString() parser := NewParserObj(raw) - var e types.ParsingError if full { parser.noLazy = true - *self, e = parser.Parse() - } else if lock { - var n Node - parser.noLazy = true - parser.loadOnce = true - n, e = parser.Parse() - self.assign(n) - } else { - *self, e = parser.Parse() + parser.skipValue = false } + var e types.ParsingError + *self, e = parser.Parse() if e != 0 { *self = *newSyntaxError(parser.syntaxError(e)) } } -func (self *Node) assign(n Node) { - self.l = n.l - self.p = n.p - atomic.StoreInt64(&self.t, n.t) +var typeJumpTable = [256]types.ValueType{ + '"' : types.V_STRING, + '-' : _V_NUMBER, + '0' : _V_NUMBER, + '1' : _V_NUMBER, + '2' : _V_NUMBER, + '3' : _V_NUMBER, + '4' : _V_NUMBER, + '5' : _V_NUMBER, + '6' : _V_NUMBER, + '7' : _V_NUMBER, + '8' : _V_NUMBER, + '9' : _V_NUMBER, + '[' : types.V_ARRAY, + 'f' : types.V_FALSE, + 'n' : types.V_NULL, + 't' : types.V_TRUE, + '{' : types.V_OBJECT, +} + +func switchRawType(c byte) types.ValueType { + return typeJumpTable[c] } diff --git a/vendor/github.com/bytedance/sonic/ast/parser.go b/vendor/github.com/bytedance/sonic/ast/parser.go index f10b43e..a1f5826 100644 --- a/vendor/github.com/bytedance/sonic/ast/parser.go +++ b/vendor/github.com/bytedance/sonic/ast/parser.go @@ -17,18 +17,14 @@ package ast import ( - "fmt" - "sync" - "sync/atomic" + `fmt` - "github.com/bytedance/sonic/internal/native/types" - "github.com/bytedance/sonic/internal/rt" - "github.com/bytedance/sonic/internal/utils" - "github.com/bytedance/sonic/unquote" + `github.com/bytedance/sonic/internal/native/types` + `github.com/bytedance/sonic/internal/rt` ) const ( - _DEFAULT_NODE_CAP int = 16 + _DEFAULT_NODE_CAP int = 8 _APPEND_GROW_SHIFT = 1 ) @@ -49,7 +45,6 @@ type Parser struct { p int s string noLazy bool - loadOnce bool skipValue bool dbuf *byte } @@ -65,7 +60,7 @@ func (self *Parser) delim() types.ParsingError { return types.ERR_EOF } - /* check for the delimiter */ + /* check for the delimtier */ if self.s[p] != ':' { return types.ERR_INVALID_CHAR } @@ -84,7 +79,7 @@ func (self *Parser) object() types.ParsingError { return types.ERR_EOF } - /* check for the delimiter */ + /* check for the delimtier */ if self.s[p] != '{' { return types.ERR_INVALID_CHAR } @@ -103,7 +98,7 @@ func (self *Parser) array() types.ParsingError { return types.ERR_EOF } - /* check for the delimiter */ + /* check for the delimtier */ if self.s[p] != '[' { return types.ERR_INVALID_CHAR } @@ -115,15 +110,11 @@ func (self *Parser) array() types.ParsingError { func (self *Parser) lspace(sp int) int { ns := len(self.s) - for ; sp= 0 && utils.IsSpace(self.s[self.p]); self.p-=1 {} -} - func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) { sp := self.p ns := len(self.s) @@ -157,7 +148,7 @@ func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) { if t == _V_NONE { return Node{}, types.ERR_INVALID_CHAR } - val = newRawNode(self.s[start:self.p], t, false) + val = newRawNode(self.s[start:self.p], t) }else{ /* decode the value */ if val, err = self.Parse(); err != 0 { @@ -219,7 +210,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) { /* check for escape sequence */ if njs.Ep != -1 { - if key, err = unquote.String(key); err != 0 { + if key, err = unquote(key); err != 0 { return Node{}, err } } @@ -243,7 +234,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) { if t == _V_NONE { return Node{}, types.ERR_INVALID_CHAR } - val = newRawNode(self.s[start:self.p], t, false) + val = newRawNode(self.s[start:self.p], t) } else { /* decode the value */ if val, err = self.Parse(); err != 0 { @@ -253,7 +244,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) { /* add the value to result */ // FIXME: ret's address may change here, thus previous referred node in ret may be invalid !! - ret.Push(NewPair(key, val)) + ret.Push(Pair{Key: key, Value: val}) self.p = self.lspace(self.p) /* check for EOF */ @@ -284,7 +275,7 @@ func (self *Parser) decodeString(iv int64, ep int) (Node, types.ParsingError) { } /* unquote the string */ - out, err := unquote.String(s) + out, err := unquote(s) /* check for errors */ if err != 0 { @@ -300,10 +291,6 @@ func (self *Parser) Pos() int { return self.p } - -// Parse returns a ast.Node representing the parser's JSON. -// NOTICE: the specific parsing lazy dependens parser's option -// It only parse first layer and first child for Object or Array be default func (self *Parser) Parse() (Node, types.ParsingError) { switch val := self.decodeValue(); val.Vt { case types.V_EOF : return Node{}, types.ERR_EOF @@ -312,48 +299,22 @@ func (self *Parser) Parse() (Node, types.ParsingError) { case types.V_FALSE : return falseNode, 0 case types.V_STRING : return self.decodeString(val.Iv, val.Ep) case types.V_ARRAY: - s := self.p - 1; if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == ']' { self.p = p + 1 return Node{t: types.V_ARRAY}, 0 } if self.noLazy { - if self.loadOnce { - self.noLazy = false - } return self.decodeArray(new(linkedNodes)) } - // NOTICE: loadOnce always keep raw json for object or array - if self.loadOnce { - self.p = s - s, e := self.skipFast() - if e != 0 { - return Node{}, e - } - return newRawNode(self.s[s:self.p], types.V_ARRAY, true), 0 - } return newLazyArray(self), 0 case types.V_OBJECT: - s := self.p - 1; if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == '}' { self.p = p + 1 return Node{t: types.V_OBJECT}, 0 } - // NOTICE: loadOnce always keep raw json for object or array if self.noLazy { - if self.loadOnce { - self.noLazy = false - } return self.decodeObject(new(linkedPairs)) } - if self.loadOnce { - self.p = s - s, e := self.skipFast() - if e != 0 { - return Node{}, e - } - return newRawNode(self.s[s:self.p], types.V_OBJECT, true), 0 - } return newLazyObject(self), 0 case types.V_DOUBLE : return NewNumber(self.s[val.Ep:self.p]), 0 case types.V_INTEGER : return NewNumber(self.s[val.Ep:self.p]), 0 @@ -394,7 +355,7 @@ func (self *Parser) searchKey(match string) types.ParsingError { /* check for escape sequence */ if njs.Ep != -1 { - if key, err = unquote.String(key); err != 0 { + if key, err = unquote(key); err != 0 { return err } } @@ -510,7 +471,7 @@ func (self *Node) skipNextNode() *Node { if t == _V_NONE { return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR)) } - val = newRawNode(parser.s[start:parser.p], t, false) + val = newRawNode(parser.s[start:parser.p], t) } /* add the value to result */ @@ -549,7 +510,7 @@ func (self *Node) skipNextPair() (*Pair) { /* check for EOF */ if parser.p = parser.lspace(sp); parser.p >= ns { - return newErrorPair(parser.syntaxError(types.ERR_EOF)) + return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_EOF))} } /* check for empty object */ @@ -566,7 +527,7 @@ func (self *Node) skipNextPair() (*Pair) { /* decode the key */ if njs = parser.decodeValue(); njs.Vt != types.V_STRING { - return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR)) + return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))} } /* extract the key */ @@ -575,35 +536,35 @@ func (self *Node) skipNextPair() (*Pair) { /* check for escape sequence */ if njs.Ep != -1 { - if key, err = unquote.String(key); err != 0 { - return newErrorPair(parser.syntaxError(err)) + if key, err = unquote(key); err != 0 { + return &Pair{key, *newSyntaxError(parser.syntaxError(err))} } } /* expect a ':' delimiter */ if err = parser.delim(); err != 0 { - return newErrorPair(parser.syntaxError(err)) + return &Pair{key, *newSyntaxError(parser.syntaxError(err))} } /* skip the value */ if start, err := parser.skipFast(); err != 0 { - return newErrorPair(parser.syntaxError(err)) + return &Pair{key, *newSyntaxError(parser.syntaxError(err))} } else { t := switchRawType(parser.s[start]) if t == _V_NONE { - return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR)) + return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))} } - val = newRawNode(parser.s[start:parser.p], t, false) + val = newRawNode(parser.s[start:parser.p], t) } /* add the value to result */ - ret.Push(NewPair(key, val)) + ret.Push(Pair{Key: key, Value: val}) self.l++ parser.p = parser.lspace(parser.p) /* check for EOF */ if parser.p >= ns { - return newErrorPair(parser.syntaxError(types.ERR_EOF)) + return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_EOF))} } /* check for the next character */ @@ -616,7 +577,7 @@ func (self *Node) skipNextPair() (*Pair) { self.setObject(ret) return ret.At(ret.Len()-1) default: - return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR)) + return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))} } } @@ -640,7 +601,7 @@ func Loads(src string) (int, interface{}, error) { } } -// LoadsUseNumber parse all json into interface{}, with numeric nodes cast to json.Number +// 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() @@ -694,75 +655,6 @@ func (self *Parser) ExportError(err types.ParsingError) error { } func backward(src string, i int) int { - for ; i>=0 && utils.IsSpace(src[i]); i-- {} + for ; i>=0 && isSpace(src[i]); i-- {} return i } - - -func newRawNode(str string, typ types.ValueType, lock bool) Node { - ret := Node{ - t: typ | _V_RAW, - p: rt.StrPtr(str), - l: uint(len(str)), - } - if lock { - ret.m = new(sync.RWMutex) - } - return ret -} - -var typeJumpTable = [256]types.ValueType{ - '"' : types.V_STRING, - '-' : _V_NUMBER, - '0' : _V_NUMBER, - '1' : _V_NUMBER, - '2' : _V_NUMBER, - '3' : _V_NUMBER, - '4' : _V_NUMBER, - '5' : _V_NUMBER, - '6' : _V_NUMBER, - '7' : _V_NUMBER, - '8' : _V_NUMBER, - '9' : _V_NUMBER, - '[' : types.V_ARRAY, - 'f' : types.V_FALSE, - 'n' : types.V_NULL, - 't' : types.V_TRUE, - '{' : types.V_OBJECT, -} - -func switchRawType(c byte) types.ValueType { - return typeJumpTable[c] -} - -func (self *Node) loadt() types.ValueType { - return (types.ValueType)(atomic.LoadInt64(&self.t)) -} - -func (self *Node) lock() bool { - if m := self.m; m != nil { - m.Lock() - return true - } - return false -} - -func (self *Node) unlock() { - if m := self.m; m != nil { - m.Unlock() - } -} - -func (self *Node) rlock() bool { - if m := self.m; m != nil { - m.RLock() - return true - } - return false -} - -func (self *Node) runlock() { - if m := self.m; m != nil { - m.RUnlock() - } -} diff --git a/vendor/github.com/bytedance/sonic/ast/search.go b/vendor/github.com/bytedance/sonic/ast/search.go index 9a5fb94..a8d1e76 100644 --- a/vendor/github.com/bytedance/sonic/ast/search.go +++ b/vendor/github.com/bytedance/sonic/ast/search.go @@ -21,23 +21,8 @@ import ( `github.com/bytedance/sonic/internal/native/types` ) -// SearchOptions controls Searcher's behavior -type SearchOptions struct { - // ValidateJSON indicates the searcher to validate the entire JSON - ValidateJSON bool - - // CopyReturn indicates the searcher to copy the result JSON instead of refer from the input - // This can help to reduce memory usage if you cache the results - CopyReturn bool - - // ConcurrentRead indicates the searcher to return a concurrently-READ-safe node, - // including: GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON - ConcurrentRead bool -} - type Searcher struct { parser Parser - SearchOptions } func NewSearcher(str string) *Searcher { @@ -46,16 +31,12 @@ func NewSearcher(str string) *Searcher { s: str, noLazy: false, }, - SearchOptions: SearchOptions{ - ValidateJSON: true, - }, } } // GetByPathCopy search in depth from top json and returns a **Copied** json node at the path location func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) { - self.CopyReturn = true - return self.getByPath(path...) + return self.getByPath(true, true, path...) } // GetByPathNoCopy search in depth from top json and returns a **Referenced** json node at the path location @@ -63,15 +44,15 @@ func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) { // WARN: this search directly refer partial json from top json, which has faster speed, // may consumes more memory. func (self *Searcher) GetByPath(path ...interface{}) (Node, error) { - return self.getByPath(path...) + return self.getByPath(false, true, path...) } -func (self *Searcher) getByPath(path ...interface{}) (Node, error) { +func (self *Searcher) getByPath(copystring bool, validate bool, path ...interface{}) (Node, error) { var err types.ParsingError var start int self.parser.p = 0 - start, err = self.parser.getByPath(self.ValidateJSON, path...) + start, err = self.parser.getByPath(validate, path...) if err != 0 { // for compatibility with old version if err == types.ERR_NOT_FOUND { @@ -90,12 +71,12 @@ func (self *Searcher) getByPath(path ...interface{}) (Node, error) { // copy string to reducing memory usage var raw string - if self.CopyReturn { + if copystring { raw = rt.Mem2Str([]byte(self.parser.s[start:self.parser.p])) } else { raw = self.parser.s[start:self.parser.p] } - return newRawNode(raw, t, self.ConcurrentRead), nil + return newRawNode(raw, t), nil } // GetByPath searches a path and returns relaction and types of target diff --git a/vendor/github.com/bytedance/sonic/compat.go b/vendor/github.com/bytedance/sonic/compat.go index 1fa670a..728bc17 100644 --- a/vendor/github.com/bytedance/sonic/compat.go +++ b/vendor/github.com/bytedance/sonic/compat.go @@ -1,4 +1,4 @@ -// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20 +// +build !amd64 !go1.16 go1.23 /* * Copyright 2021 ByteDance Inc. @@ -27,8 +27,6 @@ import ( `github.com/bytedance/sonic/option` ) -const apiKind = UseStdJSON - type frozenConfig struct { Config } @@ -87,17 +85,7 @@ func (cfg frozenConfig) UnmarshalFromString(buf string, val interface{}) error { if cfg.DisallowUnknownFields { dec.DisallowUnknownFields() } - err := dec.Decode(val) - if err != nil { - return err - } - - // check the trailing chars - offset := dec.InputOffset() - if t, err := dec.Token(); !(t == nil && err == io.EOF) { - return &json.SyntaxError{ Offset: offset} - } - return nil + return dec.Decode(val) } // Unmarshal is implemented by sonic diff --git a/vendor/github.com/bytedance/sonic/decoder/decoder_compat.go b/vendor/github.com/bytedance/sonic/decoder/decoder_compat.go index 75b2174..7883862 100644 --- a/vendor/github.com/bytedance/sonic/decoder/decoder_compat.go +++ b/vendor/github.com/bytedance/sonic/decoder/decoder_compat.go @@ -1,5 +1,4 @@ -//go:build (!amd64 && !arm64) || go1.26 || !go1.17 || (arm64 && !go1.20) -// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20 +// +build !amd64 !go1.16 go1.23 /* * Copyright 2023 ByteDance Inc. @@ -20,33 +19,29 @@ package decoder import ( - "bytes" - "encoding/json" - "io" - "reflect" - "unsafe" + `bytes` + `encoding/json` + `io` + `reflect` + `unsafe` - "github.com/bytedance/sonic/internal/decoder/consts" - "github.com/bytedance/sonic/internal/native/types" - "github.com/bytedance/sonic/option" - "github.com/bytedance/sonic/internal/compat" + `github.com/bytedance/sonic/internal/native/types` + `github.com/bytedance/sonic/option` ) func init() { - compat.Warn("sonic/decoder") + println("WARNING: sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable") } const ( - _F_use_int64 = consts.F_use_int64 - _F_disable_urc = consts.F_disable_unknown - _F_disable_unknown = consts.F_disable_unknown - _F_copy_string = consts.F_copy_string + _F_use_int64 = 0 + _F_disable_urc = 2 + _F_disable_unknown = 3 + _F_copy_string = 4 - _F_use_number = consts.F_use_number - _F_validate_string = consts.F_validate_string - _F_allow_control = consts.F_allow_control - _F_no_validate_json = consts.F_no_validate_json - _F_case_sensitive = consts.F_case_sensitive + _F_use_number = types.B_USE_NUMBER + _F_validate_string = types.B_VALIDATE_STRING + _F_allow_control = types.B_ALLOW_CONTROL ) type Options uint64 @@ -58,8 +53,6 @@ const ( OptionDisableUnknown Options = 1 << _F_disable_unknown OptionCopyString Options = 1 << _F_copy_string OptionValidateString Options = 1 << _F_validate_string - OptionNoValidateJSON Options = 1 << _F_no_validate_json - OptionCaseSensitive Options = 1 << _F_case_sensitive ) func (self *Decoder) SetOptions(opts Options) { @@ -197,5 +190,5 @@ func (s SyntaxError) Error() string { return (*json.SyntaxError)(unsafe.Pointer(&s)).Error() } -// MismatchTypeError represents mismatching between json and object -type MismatchTypeError json.UnmarshalTypeError +// MismatchTypeError represents dismatching between json and object +type MismatchTypeError json.UnmarshalTypeError \ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/encoder/encoder_compat.go b/vendor/github.com/bytedance/sonic/encoder/encoder_compat.go index a735054..38761c4 100644 --- a/vendor/github.com/bytedance/sonic/encoder/encoder_compat.go +++ b/vendor/github.com/bytedance/sonic/encoder/encoder_compat.go @@ -1,4 +1,4 @@ -// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20 +// +build !amd64 !go1.16 go1.23 /* * Copyright 2023 ByteDance Inc. @@ -19,17 +19,16 @@ package encoder import ( - `io` + `io` `bytes` `encoding/json` `reflect` `github.com/bytedance/sonic/option` - `github.com/bytedance/sonic/internal/compat` ) func init() { - compat.Warn("sonic/encoder") + println("WARNING:(encoder) sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable") } // EnableFallback indicates if encoder use fallback diff --git a/vendor/github.com/bytedance/sonic/internal/caching/hashing.go b/vendor/github.com/bytedance/sonic/internal/caching/hashing.go index f185543..b8876a4 100644 --- a/vendor/github.com/bytedance/sonic/internal/caching/hashing.go +++ b/vendor/github.com/bytedance/sonic/internal/caching/hashing.go @@ -23,12 +23,16 @@ import ( ) var ( - V_strhash = rt.UnpackEface(rt.Strhash) + 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 := rt.Strhash(unsafe.Pointer(&s), 0); v == 0 { + if v := strhash(unsafe.Pointer(&s), 0); v == 0 { return 1 } else { return uint64(v) diff --git a/vendor/github.com/bytedance/sonic/internal/cpu/features.go b/vendor/github.com/bytedance/sonic/internal/cpu/features.go index fd4dbda..f9ee3b8 100644 --- a/vendor/github.com/bytedance/sonic/internal/cpu/features.go +++ b/vendor/github.com/bytedance/sonic/internal/cpu/features.go @@ -24,6 +24,7 @@ import ( ) var ( + HasAVX = cpuid.CPU.Has(cpuid.AVX) HasAVX2 = cpuid.CPU.Has(cpuid.AVX2) HasSSE = cpuid.CPU.Has(cpuid.SSE) ) @@ -32,8 +33,7 @@ func init() { switch v := os.Getenv("SONIC_MODE"); v { case "" : break case "auto" : break - case "noavx" : HasAVX2 = false - // will also disable avx, act as `noavx`, we remain it to make sure forward compatibility + case "noavx" : HasAVX = false; fallthrough case "noavx2" : HasAVX2 = false default : panic(fmt.Sprintf("invalid mode: '%s', should be one of 'auto', 'noavx', 'noavx2'", v)) } diff --git a/vendor/github.com/bytedance/sonic/internal/encoder/compiler.go b/vendor/github.com/bytedance/sonic/internal/encoder/compiler.go index eeeb119..ca0be8f 100644 --- a/vendor/github.com/bytedance/sonic/internal/encoder/compiler.go +++ b/vendor/github.com/bytedance/sonic/internal/encoder/compiler.go @@ -17,677 +17,869 @@ package encoder import ( - "reflect" - "unsafe" + `fmt` + `reflect` + `strconv` + `strings` + `unsafe` - "github.com/bytedance/sonic/internal/encoder/ir" - "github.com/bytedance/sonic/internal/encoder/vars" - "github.com/bytedance/sonic/internal/encoder/vm" - "github.com/bytedance/sonic/internal/resolver" - "github.com/bytedance/sonic/internal/rt" - "github.com/bytedance/sonic/option" + `github.com/bytedance/sonic/internal/resolver` + `github.com/bytedance/sonic/internal/rt` + `github.com/bytedance/sonic/option` ) -func ForceUseVM() { - vm.SetCompiler(makeEncoderVM) - pretouchType = pretouchTypeVM - encodeTypedPointer = vm.EncodeTypedPointer - vars.UseVM = true +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", } -var encodeTypedPointer func(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *vars.Stack, fv uint64) error - -func makeEncoderVM(vt *rt.GoType, ex ...interface{}) (interface{}, error) { - pp, err := NewCompiler().Compile(vt.Pack(), ex[0].(bool)) - if err != nil { - return nil, err - } - return &pp, nil +func (self _Op) String() string { + if ret := _OpNames[self]; ret != "" { + return ret + } else { + return "" + } } -var pretouchType func(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error) - -func pretouchTypeVM(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error) { - /* compile function */ - compiler := NewCompiler().apply(opts) - - /* find or compile */ - vt := rt.UnpackType(_vt) - if val := vars.GetProgram(vt); val != nil { - return nil, nil - } else if _, err := vars.ComputeProgram(vt, makeEncoderVM, v == 1); err == nil { - return compiler.rec, nil - } else { - return nil, err - } +func _OP_int() _Op { + switch _INT_SIZE { + case 32: return _OP_i32 + case 64: return _OP_i64 + default: panic("unsupported int size") + } } -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) +func _OP_uint() _Op { + switch _INT_SIZE { + case 32: return _OP_u32 + case 64: return _OP_u64 + default: panic("unsupported uint size") + } } -type Compiler struct { - opts option.CompileOptions - pv bool - tab map[reflect.Type]bool - rec map[reflect.Type]uint8 +func _OP_uintptr() _Op { + switch _PTR_SIZE { + case 32: return _OP_u32 + case 64: return _OP_u64 + default: panic("unsupported pointer size") + } } -func NewCompiler() *Compiler { - return &Compiler{ - opts: option.DefaultCompileOptions(), - tab: map[reflect.Type]bool{}, - rec: map[reflect.Type]uint8{}, - } +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") + } } -func (self *Compiler) apply(opts option.CompileOptions) *Compiler { - self.opts = opts - if self.opts.RecursiveDepth > 0 { - self.rec = map[reflect.Type]uint8{} - } - return self +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 (self *Compiler) rescue(ep *error) { - if val := recover(); val != nil { - if err, ok := val.(error); ok { - *ep = err - } else { - panic(val) - } - } +func packOp(op _Op) uint64 { + return uint64(op) << 56 } -func (self *Compiler) Compile(vt reflect.Type, pv bool) (ret ir.Program, err error) { - defer self.rescue(&err) - self.compileOne(&ret, 0, vt, pv) - return +func newInsOp(op _Op) _Instr { + return _Instr{u: packOp(op)} } -func (self *Compiler) compileOne(p *ir.Program, sp int, vt reflect.Type, pv bool) { - if self.tab[vt] { - p.Vp(ir.OP_recurse, vt, pv) - } else { - self.compileRec(p, sp, vt, pv) - } +func newInsVi(op _Op, vi int) _Instr { + return _Instr{u: packOp(op) | rt.PackInt(vi)} } -func (self *Compiler) tryCompileMarshaler(p *ir.Program, vt reflect.Type, pv bool) bool { - pt := reflect.PtrTo(vt) - - /* check for addressable `json.Marshaler` with pointer receiver */ - if pv && pt.Implements(vars.JsonMarshalerType) { - addMarshalerOp(p, ir.OP_marshal_p, pt, vars.JsonMarshalerType) - return true - } - - /* check for `json.Marshaler` */ - if vt.Implements(vars.JsonMarshalerType) { - self.compileMarshaler(p, ir.OP_marshal, vt, vars.JsonMarshalerType) - return true - } - - /* check for addressable `encoding.TextMarshaler` with pointer receiver */ - if pv && pt.Implements(vars.EncodingTextMarshalerType) { - addMarshalerOp(p, ir.OP_marshal_text_p, pt, vars.EncodingTextMarshalerType) - return true - } - - /* check for `encoding.TextMarshaler` */ - if vt.Implements(vars.EncodingTextMarshalerType) { - self.compileMarshaler(p, ir.OP_marshal_text, vt, vars.EncodingTextMarshalerType) - return true - } - - return false +func newInsVs(op _Op, vs string) _Instr { + return _Instr { + u: packOp(op) | rt.PackInt(len(vs)), + p: (*rt.GoString)(unsafe.Pointer(&vs)).Ptr, + } } -func (self *Compiler) compileRec(p *ir.Program, sp int, vt reflect.Type, pv bool) { - pr := self.pv - - if self.tryCompileMarshaler(p, vt, pv) { - 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 newInsVt(op _Op, vt reflect.Type) _Instr { + return _Instr { + u: packOp(op), + p: unsafe.Pointer(rt.UnpackType(vt)), + } } -func (self *Compiler) compileOps(p *ir.Program, sp int, vt reflect.Type) { - switch vt.Kind() { - case reflect.Bool: - p.Add(ir.OP_bool) - case reflect.Int: - p.Add(ir.OP_int()) - case reflect.Int8: - p.Add(ir.OP_i8) - case reflect.Int16: - p.Add(ir.OP_i16) - case reflect.Int32: - p.Add(ir.OP_i32) - case reflect.Int64: - p.Add(ir.OP_i64) - case reflect.Uint: - p.Add(ir.OP_uint()) - case reflect.Uint8: - p.Add(ir.OP_u8) - case reflect.Uint16: - p.Add(ir.OP_u16) - case reflect.Uint32: - p.Add(ir.OP_u32) - case reflect.Uint64: - p.Add(ir.OP_u64) - case reflect.Uintptr: - p.Add(ir.OP_uintptr()) - case reflect.Float32: - p.Add(ir.OP_f32) - case reflect.Float64: - p.Add(ir.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: - self.compileUnsupportedType(p, 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 *Compiler) compileNil(p *ir.Program, sp int, vt reflect.Type, nil_op ir.Op, fn func(*ir.Program, int, reflect.Type)) { - x := p.PC() - p.Add(ir.OP_is_nil) - fn(p, sp, vt) - e := p.PC() - p.Add(ir.OP_goto) - p.Pin(x) - p.Add(nil_op) - p.Pin(e) +func (self _Instr) op() _Op { + return _Op(self.u >> 56) } -func (self *Compiler) compilePtr(p *ir.Program, sp int, vt reflect.Type) { - self.compileNil(p, sp, vt, ir.OP_null, self.compilePtrBody) +func (self _Instr) vi() int { + return rt.UnpackInt(self.u) } -func (self *Compiler) compilePtrBody(p *ir.Program, sp int, vt reflect.Type) { - p.Tag(sp) - p.Add(ir.OP_save) - p.Add(ir.OP_deref) - self.compileOne(p, sp+1, vt, true) - p.Add(ir.OP_drop) +func (self _Instr) vf() uint8 { + return (*rt.GoType)(self.p).KindFlags } -func (self *Compiler) compileMap(p *ir.Program, sp int, vt reflect.Type) { - self.compileNil(p, sp, vt, ir.OP_empty_obj, self.compileMapBody) +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 *Compiler) compileMapBody(p *ir.Program, sp int, vt reflect.Type) { - p.Tag(sp + 1) - p.Int(ir.OP_byte, '{') - e := p.PC() - p.Add(ir.OP_is_zero_map) - p.Add(ir.OP_save) - p.Rtt(ir.OP_map_iter, vt) - p.Add(ir.OP_save) - i := p.PC() - p.Add(ir.OP_map_check_key) - u := p.PC() - p.Add(ir.OP_map_write_key) - self.compileMapBodyKey(p, vt.Key()) - p.Pin(u) - p.Int(ir.OP_byte, ':') - p.Add(ir.OP_map_value_next) - self.compileOne(p, sp+2, vt.Elem(), false) - j := p.PC() - p.Add(ir.OP_map_check_key) - p.Int(ir.OP_byte, ',') - v := p.PC() - p.Add(ir.OP_map_write_key) - self.compileMapBodyKey(p, vt.Key()) - p.Pin(v) - p.Int(ir.OP_byte, ':') - p.Add(ir.OP_map_value_next) - self.compileOne(p, sp+2, vt.Elem(), false) - p.Int(ir.OP_goto, j) - p.Pin(i) - p.Pin(j) - p.Add(ir.OP_map_stop) - p.Add(ir.OP_drop_2) - p.Pin(e) - p.Int(ir.OP_byte, '}') +func (self _Instr) vk() reflect.Kind { + return (*rt.GoType)(self.p).Kind() } -func (self *Compiler) compileMapBodyKey(p *ir.Program, vk reflect.Type) { - // followed as `encoding/json/emcode.go:resolveKeyName - if vk.Kind() == reflect.String { - self.compileString(p, vk) - return - } - - if !vk.Implements(vars.EncodingTextMarshalerType) { - self.compileMapBodyTextKey(p, vk) - } else { - self.compileMapBodyUtextKey(p, vk) - } +func (self _Instr) vt() reflect.Type { + return (*rt.GoType)(self.p).Pack() } -func (self *Compiler) compileMapBodyTextKey(p *ir.Program, vk reflect.Type) { - switch vk.Kind() { - case reflect.Invalid: - panic("map key is nil") - case reflect.Bool: - p.Key(ir.OP_bool) - case reflect.Int: - p.Key(ir.OP_int()) - case reflect.Int8: - p.Key(ir.OP_i8) - case reflect.Int16: - p.Key(ir.OP_i16) - case reflect.Int32: - p.Key(ir.OP_i32) - case reflect.Int64: - p.Key(ir.OP_i64) - case reflect.Uint: - p.Key(ir.OP_uint()) - case reflect.Uint8: - p.Key(ir.OP_u8) - case reflect.Uint16: - p.Key(ir.OP_u16) - case reflect.Uint32: - p.Key(ir.OP_u32) - case reflect.Uint64: - p.Key(ir.OP_u64) - case reflect.Uintptr: - p.Key(ir.OP_uintptr()) - case reflect.Float32: - p.Key(ir.OP_f32) - case reflect.Float64: - p.Key(ir.OP_f64) - case reflect.String: - self.compileString(p, vk) - default: - panic(vars.Error_type(vk)) - } +func (self _Instr) vp() (vt reflect.Type, pv bool) { + return (*rt.GoType)(self.p).Pack(), rt.UnpackInt(self.u) == 1 } -func (self *Compiler) compileMapBodyUtextKey(p *ir.Program, vk reflect.Type) { - if vk.Kind() != reflect.Ptr { - addMarshalerOp(p, ir.OP_marshal_text, vk, vars.EncodingTextMarshalerType) - } else { - self.compileMapBodyUtextPtr(p, vk) - } +func (self _Instr) i64() int64 { + return int64(self.vi()) } -func (self *Compiler) compileMapBodyUtextPtr(p *ir.Program, vk reflect.Type) { - i := p.PC() - p.Add(ir.OP_is_nil) - addMarshalerOp(p, ir.OP_marshal_text, vk, vars.EncodingTextMarshalerType) - j := p.PC() - p.Add(ir.OP_goto) - p.Pin(i) - p.Str(ir.OP_text, "\"\"") - p.Pin(j) +func (self _Instr) vlen() int { + return int((*rt.GoType)(self.p).Size) } -func (self *Compiler) compileSlice(p *ir.Program, sp int, vt reflect.Type) { - self.compileNil(p, sp, vt, ir.OP_empty_arr, self.compileSliceBody) +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 *Compiler) compileSliceBody(p *ir.Program, sp int, vt reflect.Type) { - if vars.IsSimpleByte(vt) { - p.Add(ir.OP_bin) - } else { - self.compileSliceArray(p, sp, vt) - } +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() + } } -func (self *Compiler) compileSliceArray(p *ir.Program, sp int, vt reflect.Type) { - p.Tag(sp) - p.Int(ir.OP_byte, '[') - e := p.PC() - p.Add(ir.OP_is_nil) - p.Add(ir.OP_save) - p.Add(ir.OP_slice_len) - i := p.PC() - p.Rtt(ir.OP_slice_next, vt) - self.compileOne(p, sp+1, vt, true) - j := p.PC() - p.Rtt(ir.OP_slice_next, vt) - p.Int(ir.OP_byte, ',') - self.compileOne(p, sp+1, vt, true) - p.Int(ir.OP_goto, j) - p.Pin(i) - p.Pin(j) - p.Add(ir.OP_drop) - p.Pin(e) - p.Int(ir.OP_byte, ']') +type ( + _Program []_Instr +) + +func (self _Program) pc() int { + return len(self) } -func (self *Compiler) compileArray(p *ir.Program, sp int, vt reflect.Type, nb int) { - p.Tag(sp) - p.Int(ir.OP_byte, '[') - p.Add(ir.OP_save) - - /* first item */ - if nb != 0 { - self.compileOne(p, sp+1, vt, self.pv) - p.Add(ir.OP_load) - } - - /* remaining items */ - for i := 1; i < nb; i++ { - p.Int(ir.OP_byte, ',') - p.Int(ir.OP_index, i*int(vt.Size())) - self.compileOne(p, sp+1, vt, self.pv) - p.Add(ir.OP_load) - } - - /* end of array */ - p.Add(ir.OP_drop) - p.Int(ir.OP_byte, ']') +func (self _Program) tag(n int) { + if n >= _MaxStack { + panic("type nesting too deep") + } } -func (self *Compiler) compileString(p *ir.Program, vt reflect.Type) { - if vt != vars.JsonNumberType { - p.Add(ir.OP_str) - } else { - p.Add(ir.OP_number) - } +func (self _Program) pin(i int) { + v := &self[i] + v.u &= 0xffff000000000000 + v.u |= rt.PackInt(self.pc()) } -func (self *Compiler) compileStruct(p *ir.Program, sp int, vt reflect.Type) { - if sp >= self.opts.MaxInlineDepth || p.PC() >= vars.MAX_ILBUF || (sp > 0 && vt.NumField() >= vars.MAX_FIELDS) { - p.Vp(ir.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 _Program) rel(v []int) { + for _, i := range v { + self.pin(i) + } } -func (self *Compiler) compileStructBody(p *ir.Program, sp int, vt reflect.Type) { - p.Tag(sp) - p.Int(ir.OP_byte, '{') - p.Add(ir.OP_save) - p.Add(ir.OP_cond_set) - - /* compile each field */ - fvs := resolver.ResolveStruct(vt) - for i, fv := range fvs { - 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(ir.OP_index, int(o.Size)); o.Kind == resolver.F_deref { - s = append(s, p.PC()) - p.Add(ir.OP_is_nil) - p.Add(ir.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.compileStructFieldEmpty(p, fv.Type) - } - /* check for "omitzero" option */ - if fv.Opts&resolver.F_omitzero != 0 { - s = append(s, p.PC()) - p.VField(ir.OP_is_zero, &fvs[i]) - } - - /* add the comma if not the first element */ - i := p.PC() - p.Add(ir.OP_cond_testc) - p.Int(ir.OP_byte, ',') - p.Pin(i) - - /* compile the key and value */ - ft := fv.Type - p.Str(ir.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(ir.OP_load) - } - - /* end of object */ - p.Add(ir.OP_drop) - p.Int(ir.OP_byte, '}') +func (self *_Program) add(op _Op) { + *self = append(*self, newInsOp(op)) } -func (self *Compiler) compileStructFieldStr(p *ir.Program, sp int, vt reflect.Type) { - // NOTICE: according to encoding/json, Marshaler type has higher priority than string option - // see issue: - if self.tryCompileMarshaler(p, vt, self.pv) { - return - } - - 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(ir.OP_is_nil) - p.Add(ir.OP_deref) - } - - /* special case of a double-quoted string */ - if ft != vars.JsonNumberType && ft.Kind() == reflect.String { - p.Add(ir.OP_quote) - } else { - self.compileStructFieldQuoted(p, sp, vt) - } - - /* the "null" case of the pointer */ - if pc != -1 { - e := p.PC() - p.Add(ir.OP_goto) - p.Pin(pc) - p.Add(ir.OP_null) - p.Pin(e) - } +func (self *_Program) key(op _Op) { + *self = append(*self, + newInsVi(_OP_byte, '"'), + newInsOp(op), + newInsVi(_OP_byte, '"'), + ) } -func (self *Compiler) compileStructFieldEmpty(p *ir.Program, vt reflect.Type) { - switch vt.Kind() { - case reflect.Bool: - p.Add(ir.OP_is_zero_1) - case reflect.Int: - p.Add(ir.OP_is_zero_ints()) - case reflect.Int8: - p.Add(ir.OP_is_zero_1) - case reflect.Int16: - p.Add(ir.OP_is_zero_2) - case reflect.Int32: - p.Add(ir.OP_is_zero_4) - case reflect.Int64: - p.Add(ir.OP_is_zero_8) - case reflect.Uint: - p.Add(ir.OP_is_zero_ints()) - case reflect.Uint8: - p.Add(ir.OP_is_zero_1) - case reflect.Uint16: - p.Add(ir.OP_is_zero_2) - case reflect.Uint32: - p.Add(ir.OP_is_zero_4) - case reflect.Uint64: - p.Add(ir.OP_is_zero_8) - case reflect.Uintptr: - p.Add(ir.OP_is_nil) - case reflect.Float32: - p.Add(ir.OP_is_zero_4) - case reflect.Float64: - p.Add(ir.OP_is_zero_8) - case reflect.String: - p.Add(ir.OP_is_nil_p1) - case reflect.Interface: - p.Add(ir.OP_is_nil) - case reflect.Map: - p.Add(ir.OP_is_zero_map) - case reflect.Ptr: - p.Add(ir.OP_is_nil) - case reflect.Slice: - p.Add(ir.OP_is_nil_p1) - default: - panic(vars.Error_type(vt)) - } +func (self *_Program) int(op _Op, vi int) { + *self = append(*self, newInsVi(op, vi)) } -func (self *Compiler) compileStructFieldQuoted(p *ir.Program, sp int, vt reflect.Type) { - p.Int(ir.OP_byte, '"') - self.compileOne(p, sp, vt, self.pv) - p.Int(ir.OP_byte, '"') +func (self *_Program) str(op _Op, vs string) { + *self = append(*self, newInsVs(op, vs)) } -func (self *Compiler) compileInterface(p *ir.Program, vt reflect.Type) { - /* iface and efaces are different */ - if vt.NumMethod() == 0 { - p.Add(ir.OP_eface) - return - } - - x := p.PC() - p.Add(ir.OP_is_nil_p1) - p.Add(ir.OP_iface) - - /* the "null" value */ - e := p.PC() - p.Add(ir.OP_goto) - p.Pin(x) - p.Add(ir.OP_null) - p.Pin(e) +func (self *_Program) rtt(op _Op, vt reflect.Type) { + *self = append(*self, newInsVt(op, vt)) } -func (self *Compiler) compileUnsupportedType(p *ir.Program, vt reflect.Type) { - p.Rtt(ir.OP_unsupported, 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) -func (self *Compiler) compileMarshaler(p *ir.Program, op ir.Op, vt reflect.Type, mt reflect.Type) { - pc := p.PC() - vk := vt.Kind() + /* prescan to get all the labels */ + for _, ins := range self { + if ins.isBranch() { + tab[ins.vi()] = true + } + } - /* direct receiver */ - if vk != reflect.Ptr { - addMarshalerOp(p, op, vt, mt) - return - } - /* value receiver with a pointer type, check for nil before calling the marshaler */ - p.Add(ir.OP_is_nil) + /* 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())) + } + } - addMarshalerOp(p, op, vt, mt) + /* add the last label, if needed */ + if tab[nb] { + ret = append(ret, fmt.Sprintf("L_%d:", nb)) + } - i := p.PC() - p.Add(ir.OP_goto) - p.Pin(pc) - p.Add(ir.OP_null) - p.Pin(i) + /* add an "end" indicator, and join all the strings */ + return strings.Join(append(ret, "\tend"), "\n") } -func addMarshalerOp(p *ir.Program, op ir.Op, vt reflect.Type, mt reflect.Type) { - if vars.UseVM { - itab := rt.GetItab(rt.IfaceType(rt.UnpackType(mt)), rt.UnpackType(vt), true) - p.Vtab(op, vt, itab) - } else { - // OPT: get itab here - p.Rtt(op, vt) - } +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) + 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) } diff --git a/vendor/github.com/bytedance/sonic/internal/encoder/encoder.go b/vendor/github.com/bytedance/sonic/internal/encoder/encoder.go index 9e675a5..d285c29 100644 --- a/vendor/github.com/bytedance/sonic/internal/encoder/encoder.go +++ b/vendor/github.com/bytedance/sonic/internal/encoder/encoder.go @@ -17,63 +17,72 @@ package encoder import ( - "bytes" - "encoding/json" - "reflect" - "runtime" - "unsafe" + `bytes` + `encoding/json` + `reflect` + `runtime` + `unsafe` - "github.com/bytedance/sonic/utf8" - "github.com/bytedance/sonic/internal/encoder/alg" - "github.com/bytedance/sonic/internal/encoder/vars" - "github.com/bytedance/sonic/internal/rt" - "github.com/bytedance/sonic/option" - "github.com/bytedance/gopkg/lang/dirtmake" + `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 + bitNoValidateJSONMarshaler + bitNoEncoderNewline + + // 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 << alg.BitSortMapKeys + 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 << alg.BitEscapeHTML + EscapeHTML Options = 1 << bitEscapeHTML // CompactMarshaler indicates that the output JSON from json.Marshaler // is always compact and needs no validation - CompactMarshaler Options = 1 << alg.BitCompactMarshaler + CompactMarshaler Options = 1 << bitCompactMarshaler // NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler // is always escaped string and needs no quoting - NoQuoteTextMarshaler Options = 1 << alg.BitNoQuoteTextMarshaler + NoQuoteTextMarshaler Options = 1 << bitNoQuoteTextMarshaler // NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}', - // instead of 'null'. - // NOTE: The priority of this option is lower than json tag `omitempty`. - NoNullSliceOrMap Options = 1 << alg.BitNoNullSliceOrMap + // instead of 'null' + NoNullSliceOrMap Options = 1 << bitNoNullSliceOrMap // ValidateString indicates that encoder should validate the input string // before encoding it into JSON. - ValidateString Options = 1 << alg.BitValidateString + ValidateString Options = 1 << bitValidateString // NoValidateJSONMarshaler indicates that the encoder should not validate the output string // after encoding the JSONMarshaler to JSON. - NoValidateJSONMarshaler Options = 1 << alg.BitNoValidateJSONMarshaler + NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler // NoEncoderNewline indicates that the encoder should not add a newline after every message - NoEncoderNewline Options = 1 << alg.BitNoEncoderNewline + NoEncoderNewline Options = 1 << bitNoEncoderNewline // CompatibleWithStd is used to be compatible with std encoder. CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler - - // Encode Infinity or Nan float into `null`, instead of returning an error. - EncodeNullForInfOrNan Options = 1 << alg.BitEncodeNullForInfOrNan ) // Encoder represents a specific set of encoder configurations. @@ -162,45 +171,53 @@ func (enc *Encoder) SetIndent(prefix, indent string) { // Quote returns the JSON-quoted version of s. func Quote(s string) string { - buf := make([]byte, 0, len(s)+2) - buf = alg.Quote(buf, s, false) - return rt.Mem2Str(buf) + 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) { var ret []byte - buf := vars.NewBytes() - err := encodeIntoCheckRace(buf, val, opts) + buf := newBytes() + err := encodeInto(&buf, val, opts) /* check for errors */ if err != nil { - vars.FreeBytes(buf) + freeBytes(buf) return nil, err } /* htmlescape or correct UTF-8 if opts enable */ old := buf - *buf = encodeFinish(*old, opts) - pbuf := ((*rt.GoSlice)(unsafe.Pointer(buf))).Ptr - pold := ((*rt.GoSlice)(unsafe.Pointer(old))).Ptr + buf = encodeFinish(old, opts) + pbuf := ((*rt.GoSlice)(unsafe.Pointer(&buf))).Ptr + pold := ((*rt.GoSlice)(unsafe.Pointer(&old))).Ptr /* return when allocated a new buffer */ if pbuf != pold { - vars.FreeBytes(old) - return *buf, nil + freeBytes(old) + return buf, nil } /* make a copy of the result */ - if rt.CanSizeResue(cap(*buf)) { - ret = dirtmake.Bytes(len(*buf), len(*buf)) - copy(ret, *buf) - vars.FreeBytes(buf) - } else { - ret = *buf - } - + ret = make([]byte, len(buf)) + copy(ret, buf) + + freeBytes(buf) /* return the buffer into pool */ return ret, nil } @@ -208,7 +225,7 @@ func Encode(val interface{}, opts Options) ([]byte, error) { // 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 { - err := encodeIntoCheckRace(buf, val, opts) + err := encodeInto(buf, val, opts) if err != nil { return err } @@ -217,15 +234,15 @@ func EncodeInto(buf *[]byte, val interface{}, opts Options) error { } func encodeInto(buf *[]byte, val interface{}, opts Options) error { - stk := vars.NewStack() + stk := newStack() efv := rt.UnpackEface(val) err := encodeTypedPointer(buf, efv.Type, &efv.Value, stk, uint64(opts)) /* return the stack into pool */ if err != nil { - vars.ResetStack(stk) + resetStack(stk) } - vars.FreeStack(stk) + freeStack(stk) /* avoid GC ahead */ runtime.KeepAlive(buf) @@ -237,12 +254,13 @@ func encodeFinish(buf []byte, opts Options) []byte { if opts & EscapeHTML != 0 { buf = HTMLEscape(nil, buf) } - if (opts & ValidateString != 0) && !utf8.Validate(buf) { + if opts & ValidateString != 0 && !utf8.Validate(buf) { buf = utf8.CorrectWith(nil, buf, `\ufffd`) } return buf } +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 @@ -251,7 +269,7 @@ func encodeFinish(buf []byte, opts Options) []byte { // escaping within