This commit is contained in:
xtaci
2023-02-07 12:17:25 +08:00
parent a3cc866b28
commit 968e76e5ae
320 changed files with 52945 additions and 10746 deletions
+20 -12
View File
@@ -1,19 +1,27 @@
module github.com/xtaci/kcptun
require (
github.com/coreos/go-iptables v0.4.2 // indirect
github.com/golang/snappy v0.0.1
github.com/google/gopacket v1.1.17 // indirect
github.com/klauspost/reedsolomon v1.10.0 // indirect
github.com/golang/snappy v0.0.4
github.com/pkg/errors v0.9.1
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/urfave/cli v1.21.0
github.com/xtaci/kcp-go/v5 v5.6.1
github.com/xtaci/smux v1.5.16
github.com/urfave/cli v1.22.12
github.com/xtaci/kcp-go/v5 v5.6.2
github.com/xtaci/smux v1.5.17
github.com/xtaci/tcpraw v1.2.25
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect
golang.org/x/crypto v0.5.0
)
go 1.14
require (
github.com/coreos/go-iptables v0.6.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
github.com/klauspost/reedsolomon v1.11.6 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/templexxx/cpu v0.1.0 // indirect
github.com/templexxx/xorsimd v0.4.2 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.5.0 // indirect
)
go 1.17
+59 -50
View File
@@ -1,12 +1,16 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
@@ -21,116 +25,122 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/klauspost/reedsolomon v1.10.0 h1:MonMtg979rxSHjwtsla5dZLhreS0Lu42AyQ20bhjIGg=
github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/reedsolomon v1.10.0/go.mod h1:qHMIzMkuZUWqIh8mS/GruPdo3u0qwX2jk/LH440ON7Y=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/klauspost/reedsolomon v1.11.6 h1:h0MUpEzmretucmlelC3EefQHKgk6vWpKz/ctB/tmaEs=
github.com/klauspost/reedsolomon v1.11.6/go.mod h1:cuXqklb3LNaurR5MVjy7WLXAEUqGz4I0Uc+rnQ7POUg=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
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/testify v1.6.1/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 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo=
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
github.com/templexxx/cpu v0.0.9/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE=
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/xtaci/kcp-go/v5 v5.6.2 h1:pSXMa5MOsb+EIZKe4sDBqlTExu2A/2Z+DFhoX2qtt2A=
github.com/xtaci/kcp-go/v5 v5.6.2/go.mod h1:LsinWoru+lWWJHb+EM9HeuqYxV6bb9rNcK12v67jYzQ=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/smux v1.5.17 h1:1V9FZ8kNProe0JqFj6EjttdAqNBzqANhzsDrBjN/L0k=
github.com/xtaci/smux v1.5.17/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -145,11 +155,10 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+135 -53
View File
@@ -31,7 +31,6 @@ type Error struct {
exec.ExitError
cmd exec.Cmd
msg string
proto Protocol
exitStatus *int //for overriding
}
@@ -48,9 +47,12 @@ func (e *Error) Error() string {
// IsNotExist returns true if the error is due to the chain or rule not existing
func (e *Error) IsNotExist() bool {
return e.ExitStatus() == 1 &&
(e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) ||
e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto)))
if e.ExitStatus() != 1 {
return false
}
msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n"
msgNoChainExist := "No chain/target/match by that name.\n"
return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist)
}
// Protocol to differentiate between IPv4 and IPv6
@@ -62,15 +64,17 @@ const (
)
type IPTables struct {
path string
proto Protocol
hasCheck bool
hasWait bool
hasRandomFully bool
v1 int
v2 int
v3 int
mode string // the underlying iptables operating mode, e.g. nf_tables
path string
proto Protocol
hasCheck bool
hasWait bool
waitSupportSecond bool
hasRandomFully bool
v1 int
v2 int
v3 int
mode string // the underlying iptables operating mode, e.g. nf_tables
timeout int // time to wait for the iptables lock, default waits forever
}
// Stat represents a structured statistic entry.
@@ -87,36 +91,68 @@ type Stat struct {
Options string `json:"options"`
}
// New creates a new IPTables.
// For backwards compatibility, this always uses IPv4, i.e. "iptables".
func New() (*IPTables, error) {
return NewWithProtocol(ProtocolIPv4)
type option func(*IPTables)
func IPFamily(proto Protocol) option {
return func(ipt *IPTables) {
ipt.proto = proto
}
}
func Timeout(timeout int) option {
return func(ipt *IPTables) {
ipt.timeout = timeout
}
}
// New creates a new IPTables configured with the options passed as parameter.
// For backwards compatibility, by default always uses IPv4 and timeout 0.
// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing
// the IPFamily and Timeout options as follow:
// ip6t := New(IPFamily(ProtocolIPv6), Timeout(5))
func New(opts ...option) (*IPTables, error) {
ipt := &IPTables{
proto: ProtocolIPv4,
timeout: 0,
}
for _, opt := range opts {
opt(ipt)
}
path, err := exec.LookPath(getIptablesCommand(ipt.proto))
if err != nil {
return nil, err
}
ipt.path = path
vstring, err := getIptablesVersionString(path)
if err != nil {
return nil, fmt.Errorf("could not get iptables version: %v", err)
}
v1, v2, v3, mode, err := extractIptablesVersion(vstring)
if err != nil {
return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err)
}
ipt.v1 = v1
ipt.v2 = v2
ipt.v3 = v3
ipt.mode = mode
checkPresent, waitPresent, waitSupportSecond, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
ipt.hasCheck = checkPresent
ipt.hasWait = waitPresent
ipt.waitSupportSecond = waitSupportSecond
ipt.hasRandomFully = randomFullyPresent
return ipt, nil
}
// New creates a new IPTables for the given proto.
// The proto will determine which command is used, either "iptables" or "ip6tables".
func NewWithProtocol(proto Protocol) (*IPTables, error) {
path, err := exec.LookPath(getIptablesCommand(proto))
if err != nil {
return nil, err
}
vstring, err := getIptablesVersionString(path)
v1, v2, v3, mode, err := extractIptablesVersion(vstring)
checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
ipt := IPTables{
path: path,
proto: proto,
hasCheck: checkPresent,
hasWait: waitPresent,
hasRandomFully: randomFullyPresent,
v1: v1,
v2: v2,
v3: v3,
mode: mode,
}
return &ipt, nil
return New(IPFamily(proto), Timeout(0))
}
// Proto returns the protocol used by this IPTables.
@@ -175,6 +211,14 @@ func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
return ipt.run(cmd...)
}
func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error {
exists, err := ipt.Exists(table, chain, rulespec...)
if err == nil && exists {
err = ipt.Delete(table, chain, rulespec...)
}
return err
}
// List rules in specified table/chain
func (ipt *IPTables) List(table, chain string) ([]string, error) {
args := []string{"-t", table, "-S", chain}
@@ -212,6 +256,21 @@ func (ipt *IPTables) ListChains(table string) ([]string, error) {
return chains, nil
}
// '-S' is fine with non existing rule index as long as the chain exists
// therefore pass index 1 to reduce overhead for large chains
func (ipt *IPTables) ChainExists(table, chain string) (bool, error) {
err := ipt.run("-t", table, "-S", chain, "1")
eerr, eok := err.(*Error)
switch {
case err == nil:
return true, nil
case eok && eerr.ExitStatus() == 1:
return false, nil
default:
return false, err
}
}
// Stats lists rules including the byte and packet counts
func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"}
@@ -348,18 +407,6 @@ func (ipt *IPTables) executeList(args []string) ([]string, error) {
rules = rules[:len(rules)-1]
}
// nftables mode doesn't return an error code when listing a non-existent
// chain. Patch that up.
if len(rules) == 0 && ipt.mode == "nf_tables" {
v := 1
return nil, &Error{
cmd: exec.Cmd{Args: args},
msg: fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(ipt.proto)),
proto: ipt.proto,
exitStatus: &v,
}
}
for i, rule := range rules {
rules[i] = filterRuleOutput(rule)
}
@@ -403,6 +450,26 @@ func (ipt *IPTables) DeleteChain(table, chain string) error {
return ipt.run("-t", table, "-X", chain)
}
func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error {
exists, err := ipt.ChainExists(table, chain)
if err != nil || !exists {
return err
}
err = ipt.run("-t", table, "-F", chain)
if err == nil {
err = ipt.run("-t", table, "-X", chain)
}
return err
}
func (ipt *IPTables) ClearAll() error {
return ipt.run("-F")
}
func (ipt *IPTables) DeleteAll() error {
return ipt.run("-X")
}
// ChangePolicy changes policy on chain to target
func (ipt *IPTables) ChangePolicy(table, chain, target string) error {
return ipt.run("-t", table, "-P", chain, target)
@@ -430,6 +497,9 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
args = append([]string{ipt.path}, args...)
if ipt.hasWait {
args = append(args, "--wait")
if ipt.timeout != 0 && ipt.waitSupportSecond {
args = append(args, strconv.Itoa(ipt.timeout))
}
} else {
fmu, err := newXtablesFileLock()
if err != nil {
@@ -437,6 +507,7 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
}
ul, err := fmu.tryLock()
if err != nil {
syscall.Close(fmu.fd)
return err
}
defer ul.Unlock()
@@ -453,7 +524,7 @@ func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
if err := cmd.Run(); err != nil {
switch e := err.(type) {
case *exec.ExitError:
return &Error{*e, cmd, stderr.String(), ipt.proto, nil}
return &Error{*e, cmd, stderr.String(), nil}
default:
return err
}
@@ -472,8 +543,8 @@ func getIptablesCommand(proto Protocol) string {
}
// Checks if iptables has the "-C" and "--wait" flag
func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool) {
return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3)
func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool, bool) {
return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesWaitSupportSecond(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3)
}
// getIptablesVersion returns the first three components of the iptables version
@@ -548,6 +619,17 @@ func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool {
return false
}
//Checks if an iptablse version is after 1.6.0, when --wait support second
func iptablesWaitSupportSecond(v1 int, v2 int, v3 int) bool {
if v1 > 1 {
return true
}
if v1 == 1 && v2 >= 6 {
return true
}
return false
}
// Checks if an iptables version is after 1.6.2, when --random-fully was added
func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool {
if v1 > 1 {
+3
View File
@@ -8,8 +8,11 @@
# Please keep the list sorted.
Amazon.com, Inc
Damian Gryski <dgryski@gmail.com>
Eric Buth <eric@topos.com>
Google Inc.
Jan Mercl <0xjnml@gmail.com>
Klaus Post <klauspost@gmail.com>
Rodolfo Carvalho <rhcarvalho@gmail.com>
Sebastien Binet <seb.binet@gmail.com>
+4
View File
@@ -26,9 +26,13 @@
# Please keep the list sorted.
Alex Legg <alexlegg@google.com>
Damian Gryski <dgryski@gmail.com>
Eric Buth <eric@topos.com>
Jan Mercl <0xjnml@gmail.com>
Jonathan Swinney <jswinney@amazon.com>
Kai Backman <kaib@golang.org>
Klaus Post <klauspost@gmail.com>
Marc-Antoine Ruel <maruel@chromium.org>
Nigel Tao <nigeltao@golang.org>
Rob Pike <r@golang.org>
+57 -30
View File
@@ -52,6 +52,8 @@ const (
// Otherwise, a newly allocated slice will be returned.
//
// The dst and src must not overlap. It is valid to pass a nil dst.
//
// Decode handles the Snappy block format, not the Snappy stream format.
func Decode(dst, src []byte) ([]byte, error) {
dLen, s, err := decodedLen(src)
if err != nil {
@@ -83,6 +85,8 @@ func NewReader(r io.Reader) *Reader {
}
// Reader is an io.Reader that can read Snappy-compressed bytes.
//
// Reader handles the Snappy stream format, not the Snappy block format.
type Reader struct {
r io.Reader
err error
@@ -114,32 +118,23 @@ func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
return true
}
// Read satisfies the io.Reader interface.
func (r *Reader) Read(p []byte) (int, error) {
if r.err != nil {
return 0, r.err
}
for {
if r.i < r.j {
n := copy(p, r.decoded[r.i:r.j])
r.i += n
return n, nil
}
func (r *Reader) fill() error {
for r.i >= r.j {
if !r.readFull(r.buf[:4], true) {
return 0, r.err
return r.err
}
chunkType := r.buf[0]
if !r.readHeader {
if chunkType != chunkTypeStreamIdentifier {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
r.readHeader = true
}
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
if chunkLen > len(r.buf) {
r.err = ErrUnsupported
return 0, r.err
return r.err
}
// The chunk types are specified at
@@ -149,11 +144,11 @@ func (r *Reader) Read(p []byte) (int, error) {
// Section 4.2. Compressed data (chunk type 0x00).
if chunkLen < checksumSize {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
buf := r.buf[:chunkLen]
if !r.readFull(buf, false) {
return 0, r.err
return r.err
}
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
buf = buf[checksumSize:]
@@ -161,19 +156,19 @@ func (r *Reader) Read(p []byte) (int, error) {
n, err := DecodedLen(buf)
if err != nil {
r.err = err
return 0, r.err
return r.err
}
if n > len(r.decoded) {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
if _, err := Decode(r.decoded, buf); err != nil {
r.err = err
return 0, r.err
return r.err
}
if crc(r.decoded[:n]) != checksum {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
r.i, r.j = 0, n
continue
@@ -182,25 +177,25 @@ func (r *Reader) Read(p []byte) (int, error) {
// Section 4.3. Uncompressed data (chunk type 0x01).
if chunkLen < checksumSize {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
buf := r.buf[:checksumSize]
if !r.readFull(buf, false) {
return 0, r.err
return r.err
}
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
// Read directly into r.decoded instead of via r.buf.
n := chunkLen - checksumSize
if n > len(r.decoded) {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
if !r.readFull(r.decoded[:n], false) {
return 0, r.err
return r.err
}
if crc(r.decoded[:n]) != checksum {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
r.i, r.j = 0, n
continue
@@ -209,15 +204,15 @@ func (r *Reader) Read(p []byte) (int, error) {
// Section 4.1. Stream identifier (chunk type 0xff).
if chunkLen != len(magicBody) {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
if !r.readFull(r.buf[:len(magicBody)], false) {
return 0, r.err
return r.err
}
for i := 0; i < len(magicBody); i++ {
if r.buf[i] != magicBody[i] {
r.err = ErrCorrupt
return 0, r.err
return r.err
}
}
continue
@@ -226,12 +221,44 @@ func (r *Reader) Read(p []byte) (int, error) {
if chunkType <= 0x7f {
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
r.err = ErrUnsupported
return 0, r.err
return r.err
}
// Section 4.4 Padding (chunk type 0xfe).
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
if !r.readFull(r.buf[:chunkLen], false) {
return 0, r.err
return r.err
}
}
return nil
}
// Read satisfies the io.Reader interface.
func (r *Reader) Read(p []byte) (int, error) {
if r.err != nil {
return 0, r.err
}
if err := r.fill(); err != nil {
return 0, err
}
n := copy(p, r.decoded[r.i:r.j])
r.i += n
return n, nil
}
// ReadByte satisfies the io.ByteReader interface.
func (r *Reader) ReadByte() (byte, error) {
if r.err != nil {
return 0, r.err
}
if err := r.fill(); err != nil {
return 0, err
}
c := r.decoded[r.i]
r.i++
return c, nil
}
-14
View File
@@ -1,14 +0,0 @@
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !appengine
// +build gc
// +build !noasm
package snappy
// decode has the same semantics as in decode_other.go.
//
//go:noescape
func decode(dst, src []byte) int
+19 -5
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !amd64 appengine !gc noasm
// +build !amd64,!arm64 appengine !gc noasm
package snappy
@@ -85,14 +85,28 @@ func decode(dst, src []byte) int {
if offset <= 0 || d < offset || length > len(dst)-d {
return decodeErrCodeCorrupt
}
// Copy from an earlier sub-slice of dst to a later sub-slice. Unlike
// the built-in copy function, this byte-by-byte copy always runs
// Copy from an earlier sub-slice of dst to a later sub-slice.
// If no overlap, use the built-in copy:
if offset >= length {
copy(dst[d:d+length], dst[d-offset:])
d += length
continue
}
// Unlike the built-in copy function, this byte-by-byte copy always runs
// forwards, even if the slices overlap. Conceptually, this is:
//
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
for end := d + length; d != end; d++ {
dst[d] = dst[d-offset]
//
// We align the slices into a and b and show the compiler they are the same size.
// This allows the loop to run without bounds checks.
a := dst[d : d+length]
b := dst[d-offset:]
b = b[:len(a)]
for i := range a {
a[i] = b[i]
}
d += length
}
if d != len(dst) {
return decodeErrCodeCorrupt
+4
View File
@@ -15,6 +15,8 @@ import (
// Otherwise, a newly allocated slice will be returned.
//
// The dst and src must not overlap. It is valid to pass a nil dst.
//
// Encode handles the Snappy block format, not the Snappy stream format.
func Encode(dst, src []byte) []byte {
if n := MaxEncodedLen(len(src)); n < 0 {
panic(ErrTooLarge)
@@ -139,6 +141,8 @@ func NewBufferedWriter(w io.Writer) *Writer {
}
// Writer is an io.Writer that can write Snappy-compressed bytes.
//
// Writer handles the Snappy stream format, not the Snappy block format.
type Writer struct {
w io.Writer
err error
-29
View File
@@ -1,29 +0,0 @@
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !appengine
// +build gc
// +build !noasm
package snappy
// emitLiteral has the same semantics as in encode_other.go.
//
//go:noescape
func emitLiteral(dst, lit []byte) int
// emitCopy has the same semantics as in encode_other.go.
//
//go:noescape
func emitCopy(dst []byte, offset, length int) int
// extendMatch has the same semantics as in encode_other.go.
//
//go:noescape
func extendMatch(src []byte, i, j int) int
// encodeBlock has the same semantics as in encode_other.go.
//
//go:noescape
func encodeBlock(dst, src []byte) (d int)
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !amd64 appengine !gc noasm
// +build !amd64,!arm64 appengine !gc noasm
package snappy
-1
View File
@@ -1 +0,0 @@
module github.com/golang/snappy
+6 -4
View File
@@ -2,6 +2,7 @@ language: go
go:
- 1.11.x
- 1.12.x
- 1.13.x
- master
addons:
@@ -38,10 +39,11 @@ jobs:
install: ./.travis.install.sh
- os: osx
go: 1.x
- os: windows
go: 1.x
# winpcap does not work on travis ci - so install nmap to get libpcap
before_install: choco install nmap
# windows doesn't work on travis (package installation just hangs and then errors out)
# - os: windows
# go: 1.x
# # We don't need nmap - but that's the only way to get npcap:
# before_install: choco install npcap --version 0.86 -y
- stage: style
name: "fmt/vet/lint"
go: 1.x
+2
View File
@@ -17,6 +17,7 @@ Christian Mäder <christian.maeder@nine.ch>
Gernot Vormayr <gvormayr@gmail.com>
Vitor Garcia Graveto <victor.graveto@gmail.com>
Elias Chavarria Reyes <elchavar@cisco.com>
Daniel Rittweiler <ripx80@protonmail.com>
CONTRIBUTORS:
Attila Oláh <attila@attilaolah.eu>
@@ -32,6 +33,7 @@ Jesse Ward <jesse@jesseward.com>
Kane Mathers <kane@kanemathers.name>
Jose Selvi <jselvi@pentester.es>
Yerden Zhumabekov <yerden.zhumabekov@gmail.com>
Jensen Hwa <jensenhwa@gmail.com>
-----------------------------------------------
FORKED FROM github.com/akrennmair/gopcap
+1 -1
View File
@@ -6,7 +6,7 @@ See [godoc](https://godoc.org/github.com/google/gopacket) for more details.
[![Build Status](https://travis-ci.org/google/gopacket.svg?branch=master)](https://travis-ci.org/google/gopacket)
[![GoDoc](https://godoc.org/github.com/google/gopacket?status.svg)](https://godoc.org/github.com/google/gopacket)
Minimum Go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, and bsdbpf which need at least 1.7 due to x/sys/unix dependencies.
Minimum Go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, and bsdbpf which need at least 1.9 due to x/sys/unix dependencies.
Originally forked from the gopcap project written by Andreas
Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).
+2 -2
View File
@@ -78,7 +78,7 @@ func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
func decodePayload(data []byte, p PacketBuilder) error {
payload := &Payload{}
if err := payload.DecodeFromBytes(data, p); err != nil {
return nil
return err
}
p.AddLayer(payload)
p.SetApplicationLayer(payload)
@@ -132,7 +132,7 @@ func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
func decodeFragment(data []byte, p PacketBuilder) error {
payload := &Fragment{}
if err := payload.DecodeFromBytes(data, p); err != nil {
return nil
return err
}
p.AddLayer(payload)
p.SetApplicationLayer(payload)
+62 -1
View File
@@ -208,7 +208,7 @@ based on endpoint criteria:
}
}
// Find all packets coming from UDP port 1000 to UDP port 500
interestingFlow := gopacket.NewFlow(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500))
interestingFlow := gopacket.FlowFromEndpoints(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500))
if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow {
fmt.Println("Found that UDP flow I was looking for!")
}
@@ -320,6 +320,67 @@ implementing the DecodingLayer interface are usable. Also, it's possible to
create DecodingLayers that are not themselves Layers... see
layers.IPv6ExtensionSkipper for an example of this.
Faster And Customized Decoding with DecodingLayerContainer
By default, DecodingLayerParser uses native map to store and search for a layer
to decode. Though being versatile, in some cases this solution may be not so
optimal. For example, if you have only few layers faster operations may be
provided by sparse array indexing or linear array scan.
To accomodate these scenarios, DecodingLayerContainer interface is introduced
along with its implementations: DecodingLayerSparse, DecodingLayerArray and
DecodingLayerMap. You can specify a container implementation to
DecodingLayerParser with SetDecodingLayerContainer method. Example:
dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet)
dlp.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil))
var eth layers.Ethernet
dlp.AddDecodingLayer(&eth)
// ... add layers and use DecodingLayerParser as usual...
To skip one level of indirection (though sacrificing some capabilities) you may
also use DecodingLayerContainer as a decoding tool as it is. In this case you have to
handle unknown layer types and layer panics by yourself. Example:
func main() {
var eth layers.Ethernet
var ip4 layers.IPv4
var ip6 layers.IPv6
var tcp layers.TCP
dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil))
dlc = dlc.Put(&eth)
dlc = dlc.Put(&ip4)
dlc = dlc.Put(&ip6)
dlc = dlc.Put(&tcp)
// you may specify some meaningful DecodeFeedback
decoder := dlc.LayersDecoder(LayerTypeEthernet, gopacket.NilDecodeFeedback)
decoded := make([]gopacket.LayerType, 0, 20)
for packetData := range somehowGetPacketData() {
lt, err := decoder(packetData, &decoded)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
continue
}
if lt != gopacket.LayerTypeZero {
fmt.Fprintf(os.Stderr, "unknown layer type: %v\n", lt)
continue
}
for _, layerType := range decoded {
// examine decoded layertypes just as already shown above
}
}
}
DecodingLayerSparse is the fastest but most effective when LayerType values
that layers in use can decode are not large because otherwise that would lead
to bigger memory footprint. DecodingLayerArray is very compact and primarily
usable if the number of decoding layers is not big (up to ~10-15, but please do
your own benchmarks). DecodingLayerMap is the most versatile one and used by
DecodingLayerParser by default. Please refer to tests and benchmarks in layers
subpackage to further examine usage examples and performance measurements.
You may also choose to implement your own DecodingLayerContainer if you want to
make use of your own internal packet decoding logic.
Creating Packet Data
-8
View File
@@ -1,8 +0,0 @@
module github.com/google/gopacket
go 1.12
require (
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67
)
-7
View File
@@ -1,7 +0,0 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+10 -1
View File
@@ -10,6 +10,7 @@ package layers
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
)
@@ -39,17 +40,25 @@ func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP }
// DecodeFromBytes decodes the given bytes into this layer.
func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return fmt.Errorf("ARP length %d too short", len(data))
}
arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2]))
arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
arp.HwAddressSize = data[4]
arp.ProtAddressSize = data[5]
arp.Operation = binary.BigEndian.Uint16(data[6:8])
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
if len(data) < int(arpLength) {
df.SetTruncated()
return fmt.Errorf("ARP length %d too short, %d expected", len(data), arpLength)
}
arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize]
arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
arp.Contents = data[:arpLength]
arp.Payload = data[arpLength:]
return nil
+11 -3
View File
@@ -13,6 +13,7 @@ package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
@@ -227,7 +228,7 @@ func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
}
var err error
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:])
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:], p)
if err != nil {
return err
}
@@ -242,8 +243,12 @@ func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
return LayerTypeCiscoDiscoveryInfo
}
func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err error) {
func decodeCiscoDiscoveryTLVs(data []byte, p gopacket.PacketBuilder) (values []CiscoDiscoveryValue, err error) {
for len(data) > 0 {
if len(data) < 4 {
p.SetTruncated()
return nil, errors.New("CDP TLV < 4 bytes")
}
val := CiscoDiscoveryValue{
Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])),
Length: binary.BigEndian.Uint16(data[2:4]),
@@ -251,6 +256,9 @@ func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err er
if val.Length < 4 {
err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length)
break
} else if len(data) < int(val.Length) {
p.SetTruncated()
return nil, fmt.Errorf("CDP TLV < length %d", val.Length)
}
val.Value = data[4:val.Length]
values = append(values, val)
@@ -263,7 +271,7 @@ func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
var err error
info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
p.AddLayer(info)
values, err := decodeCiscoDiscoveryTLVs(data)
values, err := decodeCiscoDiscoveryTLVs(data, p)
if err != nil { // Unlikely, as parent decode will fail, but better safe...
return err
}
+7
View File
@@ -124,6 +124,10 @@ func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
// DecodeFromBytes decodes the given bytes into this layer.
func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 240 {
df.SetTruncated()
return fmt.Errorf("DHCPv4 length %d too short", len(data))
}
d.Options = d.Options[:0]
d.Operation = DHCPOp(data[0])
d.HardwareType = LinkType(data[1])
@@ -168,6 +172,9 @@ func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error
start += int(o.Length) + 2
}
}
d.Contents = data
return nil
}
+21 -2
View File
@@ -8,7 +8,6 @@ package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
@@ -88,12 +87,20 @@ func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 }
// DecodeFromBytes decodes the given bytes into this layer.
func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("DHCPv6 length %d too short", len(data))
}
d.BaseLayer = BaseLayer{Contents: data}
d.Options = d.Options[:0]
d.MsgType = DHCPv6MsgType(data[0])
offset := 0
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
if len(data) < 34 {
df.SetTruncated()
return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType)
}
d.HopCount = data[1]
d.LinkAddr = net.IP(data[2:18])
d.PeerAddr = net.IP(data[18:34])
@@ -261,21 +268,33 @@ type DHCPv6DUID struct {
// DecodeFromBytes decodes the given bytes into a DHCPv6DUID
func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
if len(data) < 2 {
return errors.New("Not enough bytes to decode: " + string(len(data)))
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
if len(data) < 4 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.HardwareType = data[2:4]
}
if d.Type == DHCPv6DUIDTypeLLT {
if len(data) < 8 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.Time = data[4:8]
d.LinkLayerAddress = net.HardwareAddr(data[8:])
} else if d.Type == DHCPv6DUIDTypeEN {
if len(data) < 6 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.EnterpriseNumber = data[2:6]
d.Identifier = data[6:]
} else { // DHCPv6DUIDTypeLL
if len(data) < 4 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.LinkLayerAddress = net.HardwareAddr(data[4:])
}
+4 -4
View File
@@ -608,14 +608,14 @@ func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error {
}
func (o *DHCPv6Option) decode(data []byte) error {
if len(data) < 2 {
if len(data) < 4 {
return errors.New("not enough data to decode")
}
o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
if len(data) < 3 {
return errors.New("not enough data to decode")
}
o.Length = binary.BigEndian.Uint16(data[2:4])
if len(data) < 4+int(o.Length) {
return fmt.Errorf("dhcpv6 option size < length %d", 4+o.Length)
}
o.Data = data[4 : 4+o.Length]
return nil
}
+84 -39
View File
@@ -52,25 +52,26 @@ type DNSType uint16
// DNSType known values.
const (
DNSTypeA DNSType = 1 // a host address
DNSTypeNS DNSType = 2 // an authoritative name server
DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX)
DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX)
DNSTypeCNAME DNSType = 5 // the canonical name for an alias
DNSTypeSOA DNSType = 6 // marks the start of a zone of authority
DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL)
DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL)
DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL)
DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL)
DNSTypeWKS DNSType = 11 // a well known service description
DNSTypePTR DNSType = 12 // a domain name pointer
DNSTypeHINFO DNSType = 13 // host information
DNSTypeMINFO DNSType = 14 // mailbox or mail list information
DNSTypeMX DNSType = 15 // mail exchange
DNSTypeTXT DNSType = 16 // text strings
DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596]
DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195]
DNSTypeOPT DNSType = 41 // OPT Pseudo-RR [RFC6891]
DNSTypeA DNSType = 1 // a host address
DNSTypeNS DNSType = 2 // an authoritative name server
DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX)
DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX)
DNSTypeCNAME DNSType = 5 // the canonical name for an alias
DNSTypeSOA DNSType = 6 // marks the start of a zone of authority
DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL)
DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL)
DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL)
DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL)
DNSTypeWKS DNSType = 11 // a well known service description
DNSTypePTR DNSType = 12 // a domain name pointer
DNSTypeHINFO DNSType = 13 // host information
DNSTypeMINFO DNSType = 14 // mailbox or mail list information
DNSTypeMX DNSType = 15 // mail exchange
DNSTypeTXT DNSType = 16 // text strings
DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596]
DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195]
DNSTypeOPT DNSType = 41 // OPT Pseudo-RR [RFC6891]
DNSTypeURI DNSType = 256 // URI RR [RFC7553]
)
func (dt DNSType) String() string {
@@ -115,6 +116,8 @@ func (dt DNSType) String() string {
return "SRV"
case DNSTypeOPT:
return "OPT"
case DNSTypeURI:
return "URI"
}
}
@@ -123,25 +126,26 @@ type DNSResponseCode uint8
// DNSResponseCode known values.
const (
DNSResponseCodeNoErr DNSResponseCode = 0 // No error
DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035]
DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035]
DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035]
DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035]
DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035]
DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136]
DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136]
DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136]
DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136]
DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136]
DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671]
DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845]
DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845]
DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845]
DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930]
DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930]
DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930]
DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635]
DNSResponseCodeNoErr DNSResponseCode = 0 // No error
DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035]
DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035]
DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035]
DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035]
DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035]
DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136]
DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136]
DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136]
DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136]
DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136]
DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671]
DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845]
DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845]
DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845]
DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930]
DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930]
DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930]
DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635]
DNSResponseCodeBadCookie DNSResponseCode = 23 // Bad/missing Server Cookie [RFC7873]
)
func (drc DNSResponseCode) String() string {
@@ -184,6 +188,8 @@ func (drc DNSResponseCode) String() string {
return "Algorithm not supported"
case DNSResponseCodeBadTruc:
return "Bad Truncation"
case DNSResponseCodeBadCookie:
return "Bad Cookie"
}
}
@@ -367,6 +373,10 @@ func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
d.Additionals = d.Additionals[:i] // strip off erroneous value
return err
}
// extract extended RCODE from OPT RRs, RFC 6891 section 6.1.3
if d.Additionals[i].Type == DNSTypeOPT {
d.ResponseCode = DNSResponseCode(uint8(d.ResponseCode) | uint8(d.Additionals[i].TTL>>20&0xF0))
}
}
if uint16(len(d.Questions)) != d.QDCount {
@@ -427,6 +437,8 @@ func recSize(rr *DNSResourceRecord) int {
return l
case DNSTypeSRV:
return 6 + len(rr.SRV.Name) + 2
case DNSTypeURI:
return 4 + len(rr.URI.Target)
case DNSTypeOPT:
l := len(rr.OPT) * 4
for _, opt := range rr.OPT {
@@ -684,6 +696,7 @@ type DNSResourceRecord struct {
SRV DNSSRV
MX DNSMX
OPT []DNSOPT // See RFC 6891, section 6.1.2
URI DNSURI
// Undecoded TXT for backward compatibility
TXT []byte
@@ -707,7 +720,7 @@ func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeF
}
rr.Data = data[endq+10 : end]
if err = rr.decodeRData(data, endq+10, buffer); err != nil {
if err = rr.decodeRData(data[:end], endq+10, buffer); err != nil {
return 0, err
}
@@ -781,6 +794,10 @@ func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.Seria
binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight)
binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port)
encodeName(rr.SRV.Name, data, noff+16)
case DNSTypeURI:
binary.BigEndian.PutUint16(data[noff+10:], rr.URI.Priority)
binary.BigEndian.PutUint16(data[noff+12:], rr.URI.Weight)
copy(data[noff+14:], rr.URI.Target)
case DNSTypeOPT:
noff2 := noff + 10
for _, opt := range rr.OPT {
@@ -813,6 +830,9 @@ func (rr *DNSResourceRecord) String() string {
}
return "OPT " + strings.Join(opts, ",")
}
if rr.Type == DNSTypeURI {
return fmt.Sprintf("URI %d %d %s", rr.URI.Priority, rr.URI.Weight, string(rr.URI.Target))
}
if rr.Class == DNSClassIN {
switch rr.Type {
case DNSTypeA, DNSTypeAAAA:
@@ -858,6 +878,9 @@ func decodeOPTs(data []byte, offset int) ([]DNSOPT, error) {
for i := offset; i < end; {
opt := DNSOPT{}
if len(data) < i+4 {
return allOPT, fmt.Errorf("Malformed DNSOPT record. Length %d < %d", len(data), i+4)
}
opt.Code = DNSOptionCode(binary.BigEndian.Uint16(data[i : i+2]))
l := binary.BigEndian.Uint16(data[i+2 : i+4])
if i+4+int(l) > end {
@@ -911,6 +934,9 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte
if err != nil {
return err
}
if len(data) < endq+20 {
return errors.New("SOA too small")
}
rr.SOA.RName = name
rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4])
rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8])
@@ -918,13 +944,26 @@ func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte
rr.SOA.Expire = binary.BigEndian.Uint32(data[endq+12 : endq+16])
rr.SOA.Minimum = binary.BigEndian.Uint32(data[endq+16 : endq+20])
case DNSTypeMX:
if len(data) < offset+2 {
return errors.New("MX too small")
}
rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2])
name, _, err := decodeName(data, offset+2, buffer, 1)
if err != nil {
return err
}
rr.MX.Name = name
case DNSTypeURI:
if len(rr.Data) < 4 {
return errors.New("URI too small")
}
rr.URI.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
rr.URI.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
rr.URI.Target = rr.Data[4:]
case DNSTypeSRV:
if len(data) < offset+6 {
return errors.New("SRV too small")
}
rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6])
@@ -964,6 +1003,12 @@ type DNSMX struct {
Name []byte
}
// DNSURI is a URI record, defining a target (URI) of a server/service
type DNSURI struct {
Priority, Weight uint16
Target []byte
}
// DNSOptionCode represents the code of a DNS Option, see RFC6891, section 6.1.2
type DNSOptionCode uint16
+18 -5
View File
@@ -922,6 +922,7 @@ func (m *Dot11) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
}
m.Type = Dot11Type((data[0])&0xFC) >> 2
m.DataLayer = nil
m.Proto = uint8(data[0]) & 0x0003
m.Flags = Dot11Flags(data[1])
m.DurationID = binary.LittleEndian.Uint16(data[2:4])
@@ -1064,7 +1065,11 @@ func (m *Dot11) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
}
if mainType == Dot11TypeData {
l := dataDecodeMap[m.Type]()
d := dataDecodeMap[m.Type]
if d == nil {
return fmt.Errorf("unsupported type: %v", m.Type)
}
l := d()
err := l.DecodeFromBytes(m.BaseLayer.Payload, df)
if err != nil {
return err
@@ -1279,8 +1284,10 @@ func decodeDot11DataCFPollNoData(data []byte, p gopacket.PacketBuilder) error {
return decodingLayerDecoder(d, data, p)
}
func (m *Dot11DataCFPollNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPollNoData }
func (m *Dot11DataCFPollNoData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFPollNoData }
func (m *Dot11DataCFPollNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPollNoData }
func (m *Dot11DataCFPollNoData) CanDecode() gopacket.LayerClass {
return LayerTypeDot11DataCFPollNoData
}
func (m *Dot11DataCFPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
return m.Dot11Data.DecodeFromBytes(data, df)
}
@@ -1338,8 +1345,10 @@ func decodeDot11DataQOSDataCFAck(data []byte, p gopacket.PacketBuilder) error {
return decodingLayerDecoder(d, data, p)
}
func (m *Dot11DataQOSDataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSDataCFAck }
func (m *Dot11DataQOSDataCFAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSDataCFAck }
func (m *Dot11DataQOSDataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSDataCFAck }
func (m *Dot11DataQOSDataCFAck) CanDecode() gopacket.LayerClass {
return LayerTypeDot11DataQOSDataCFAck
}
func (m *Dot11DataQOSDataCFAck) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFAck }
type Dot11DataQOSDataCFPoll struct {
@@ -1461,6 +1470,10 @@ func (m *Dot11InformationElement) DecodeFromBytes(data []byte, df gopacket.Decod
df.SetTruncated()
return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), offset+int(m.Length))
}
if len(data) < offset+4 {
df.SetTruncated()
return fmt.Errorf("vendor extension size < %d", offset+int(m.Length))
}
if m.ID == 221 {
// Vendor extension
m.OUI = data[offset : offset+4]
+4
View File
@@ -27,6 +27,10 @@ func (d *Dot1Q) LayerType() gopacket.LayerType { return LayerTypeDot1Q }
// DecodeFromBytes decodes the given bytes into this layer.
func (d *Dot1Q) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("802.1Q tag length %d too short", len(data))
}
d.Priority = (data[0] & 0xE0) >> 5
d.DropEligible = data[0]&0x10 != 0
d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
+8
View File
@@ -47,9 +47,17 @@ func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP }
// DecodeFromBytes decodes the given bytes into this layer.
func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("EAP length %d too short", len(data))
}
e.Code = EAPCode(data[0])
e.Id = data[1]
e.Length = binary.BigEndian.Uint16(data[2:4])
if len(data) < int(e.Length) {
df.SetTruncated()
return fmt.Errorf("EAP length %d too short, %d expected", len(data), e.Length)
}
switch {
case e.Length > 4:
e.Type = EAPType(data[4])
+4
View File
@@ -25,6 +25,10 @@ func (e *EAPOL) LayerType() gopacket.LayerType { return LayerTypeEAPOL }
// DecodeFromBytes decodes the given bytes into this layer.
func (e *EAPOL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("EAPOL length %d too short", len(data))
}
e.Version = data[0]
e.Type = EAPOLType(data[1])
e.Length = binary.BigEndian.Uint16(data[2:4])
+4 -9
View File
@@ -8,7 +8,6 @@
package layers
import (
"errors"
"fmt"
"runtime"
@@ -26,14 +25,6 @@ type EnumMetadata struct {
LayerType gopacket.LayerType
}
// errorFunc returns a decoder that spits out a specific error message.
func errorFunc(msg string) gopacket.Decoder {
var e = errors.New(msg)
return gopacket.DecodeFunc(func([]byte, gopacket.PacketBuilder) error {
return e
})
}
// EthernetType is an enumeration of ethernet type values, and acts as a decoder
// for any type it supports.
type EthernetType uint16
@@ -56,6 +47,7 @@ const (
EthernetTypeMPLSUnicast EthernetType = 0x8847
EthernetTypeMPLSMulticast EthernetType = 0x8848
EthernetTypeEAPOL EthernetType = 0x888e
EthernetTypeERSPAN EthernetType = 0x88be
EthernetTypeQinQ EthernetType = 0x88a8
EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc
EthernetTypeEthernetCTP EthernetType = 0x9000
@@ -130,6 +122,8 @@ const (
LinkTypeLinuxIRDA LinkType = 144
LinkTypeLinuxLAPD LinkType = 177
LinkTypeLinuxUSB LinkType = 220
LinkTypeFC2 LinkType = 224
LinkTypeFC2Framed LinkType = 225
LinkTypeIPv4 LinkType = 228
LinkTypeIPv6 LinkType = 229
)
@@ -324,6 +318,7 @@ func initActualTypeData() {
EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL}
EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet}
EthernetTypeMetadata[EthernetTypeERSPAN] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeERSPANII), Name: "ERSPAN Type II", LayerType: LayerTypeERSPANII}
IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP}
+14 -3
View File
@@ -50,7 +50,11 @@ type GeneveOption struct {
// LayerType returns LayerTypeGeneve
func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }
func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
func decodeGeneveOption(data []byte, gn *Geneve, df gopacket.DecodeFeedback) (*GeneveOption, uint8, error) {
if len(data) < 3 {
df.SetTruncated()
return nil, 0, errors.New("geneve option too small")
}
opt := &GeneveOption{}
opt.Class = binary.BigEndian.Uint16(data[0:2])
@@ -58,10 +62,14 @@ func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
opt.Flags = data[3] >> 4
opt.Length = (data[3]&0xf)*4 + 4
if len(data) < int(opt.Length) {
df.SetTruncated()
return nil, 0, errors.New("geneve option too small")
}
opt.Data = make([]byte, opt.Length-4)
copy(opt.Data, data[4:opt.Length])
return opt, opt.Length
return opt, opt.Length, nil
}
func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
@@ -88,7 +96,10 @@ func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error
}
for length > 0 {
opt, len := decodeGeneveOption(data[offset:], gn)
opt, len, err := decodeGeneveOption(data[offset:], gn, df)
if err != nil {
return err
}
gn.Options = append(gn.Options, opt)
length -= int32(len)
+3
View File
@@ -160,6 +160,9 @@ func (g *GTPv1U) CanDecode() gopacket.LayerClass {
// NextLayerType specifies the next layer that GoPacket should attempt to
func (g *GTPv1U) NextLayerType() gopacket.LayerType {
if len(g.LayerPayload()) == 0 {
return gopacket.LayerTypeZero
}
version := uint8(g.LayerPayload()[0]) >> 4
if version == 4 {
return LayerTypeIPv4
+18 -3
View File
@@ -324,7 +324,11 @@ func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun b
return length
}
func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) {
func decodeIPv6HeaderTLVOption(data []byte, df gopacket.DecodeFeedback) (h *ipv6HeaderTLVOption, _ error) {
if len(data) < 2 {
df.SetTruncated()
return nil, errors.New("IPv6 header option too small")
}
h = &ipv6HeaderTLVOption{}
if data[0] == 0 {
h.ActualLength = 1
@@ -333,6 +337,10 @@ func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) {
h.OptionType = data[0]
h.OptionLength = data[1]
h.ActualLength = int(h.OptionLength) + 2
if len(data) < h.ActualLength {
df.SetTruncated()
return nil, errors.New("IPv6 header TLV option too small")
}
h.OptionData = data[2:h.ActualLength]
return
}
@@ -504,9 +512,13 @@ func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback)
if err != nil {
return err
}
i.Options = i.Options[:0]
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])
opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
if err != nil {
return err
}
i.Options = append(i.Options, (*IPv6HopByHopOption)(opt))
offset += opt.ActualLength
}
@@ -633,7 +645,10 @@ func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedbac
}
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])
opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
if err != nil {
return err
}
i.Options = append(i.Options, (*IPv6DestinationOption)(opt))
offset += opt.ActualLength
}
+9
View File
@@ -8,6 +8,7 @@ package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
@@ -27,6 +28,10 @@ type IPSecAH struct {
func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH }
func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
if len(data) < 12 {
p.SetTruncated()
return errors.New("IPSec AH packet less than 12 bytes")
}
i := &IPSecAH{
ipv6ExtensionBase: ipv6ExtensionBase{
NextHeader: IPProtocol(data[0]),
@@ -37,6 +42,10 @@ func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
Seq: binary.BigEndian.Uint32(data[8:12]),
}
i.ActualLength = (int(i.HeaderLength) + 2) * 4
if len(data) < i.ActualLength {
p.SetTruncated()
return errors.New("Truncated AH packet < ActualLength")
}
i.AuthenticationData = data[12:i.ActualLength]
i.Contents = data[:i.ActualLength]
i.Payload = data[i.ActualLength:]
+5
View File
@@ -143,6 +143,11 @@ var (
LayerTypeMLDv2MulticastListenerQuery = gopacket.RegisterLayerType(139, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerQuery)})
LayerTypeTLS = gopacket.RegisterLayerType(140, gopacket.LayerTypeMetadata{Name: "TLS", Decoder: gopacket.DecodeFunc(decodeTLS)})
LayerTypeModbusTCP = gopacket.RegisterLayerType(141, gopacket.LayerTypeMetadata{Name: "ModbusTCP", Decoder: gopacket.DecodeFunc(decodeModbusTCP)})
LayerTypeRMCP = gopacket.RegisterLayerType(142, gopacket.LayerTypeMetadata{Name: "RMCP", Decoder: gopacket.DecodeFunc(decodeRMCP)})
LayerTypeASF = gopacket.RegisterLayerType(143, gopacket.LayerTypeMetadata{Name: "ASF", Decoder: gopacket.DecodeFunc(decodeASF)})
LayerTypeASFPresencePong = gopacket.RegisterLayerType(144, gopacket.LayerTypeMetadata{Name: "ASFPresencePong", Decoder: gopacket.DecodeFunc(decodeASFPresencePong)})
LayerTypeERSPANII = gopacket.RegisterLayerType(145, gopacket.LayerTypeMetadata{Name: "ERSPAN Type II", Decoder: gopacket.DecodeFunc(decodeERSPANII)})
LayerTypeRADIUS = gopacket.RegisterLayerType(146, gopacket.LayerTypeMetadata{Name: "RADIUS", Decoder: gopacket.DecodeFunc(decodeRADIUS)})
)
var (
+5
View File
@@ -8,6 +8,7 @@ package layers
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
@@ -114,6 +115,10 @@ func decodeLCM(data []byte, p gopacket.PacketBuilder) error {
// DecodeFromBytes decodes the given bytes into this layer.
func (lcm *LCM) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return errors.New("LCM < 8 bytes")
}
offset := 0
lcm.Magic = binary.BigEndian.Uint32(data[offset:4])
+20 -2
View File
@@ -779,6 +779,16 @@ func (c *LinkLayerDiscovery) SerializeTo(b gopacket.SerializeBuffer, opts gopack
binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen:], ttlIDLen)
binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen+2:], c.TTL)
for _, v := range c.Values {
vb, err := b.AppendBytes(int(v.Length) + 2) // +2 for TLV type and length; 1 byte for subtype is included in v.Value
if err != nil {
return err
}
idLen := ((uint16(v.Type) << 9) | v.Length)
binary.BigEndian.PutUint16(vb[0:2], idLen)
copy(vb[2:], v.Value)
}
vb, err = b.AppendBytes(2) // End Tlv, 2 bytes
if err != nil {
return err
@@ -792,10 +802,18 @@ func decodeLinkLayerDiscovery(data []byte, p gopacket.PacketBuilder) error {
var vals []LinkLayerDiscoveryValue
vData := data[0:]
for len(vData) > 0 {
if len(vData) < 2 {
p.SetTruncated()
return errors.New("LLDP vdata < 2 bytes")
}
nbit := vData[0] & 0x01
t := LLDPTLVType(vData[0] >> 1)
val := LinkLayerDiscoveryValue{Type: t, Length: uint16(nbit)<<8 + uint16(vData[1])}
if val.Length > 0 {
if len(vData) < int(val.Length+2) {
p.SetTruncated()
return fmt.Errorf("LLDP VData < %d bytes", val.Length+2)
}
val.Value = vData[2 : val.Length+2]
}
vals = append(vals, val)
@@ -872,10 +890,10 @@ func decodeLinkLayerDiscovery(data []byte, p gopacket.PacketBuilder) error {
info.MgmtAddress.InterfaceSubtype = LLDPInterfaceSubtype(v.Value[mlen+1])
info.MgmtAddress.InterfaceNumber = binary.BigEndian.Uint32(v.Value[mlen+2 : mlen+6])
olen := v.Value[mlen+6]
if err := checkLLDPTLVLen(v, int(mlen+6+olen)); err != nil {
if err := checkLLDPTLVLen(v, int(mlen+7+olen)); err != nil {
return err
}
info.MgmtAddress.OID = string(v.Value[mlen+9 : mlen+9+olen])
info.MgmtAddress.OID = string(v.Value[mlen+7 : mlen+7+olen])
case LLDPTLVOrgSpecific:
if err := checkLLDPTLVLen(v, 4); err != nil {
return err
+1 -1
View File
@@ -471,7 +471,7 @@ type MLDv2MulticastAddressRecord struct {
// decodes a multicast address record from bytes
func (m *MLDv2MulticastAddressRecord) decode(data []byte, df gopacket.DecodeFeedback) (int, error) {
if len(data) < 4 {
if len(data) < 20 {
df.SetTruncated()
return 0, errors.New(
"Multicast Listener Report Message V2 layer less than 4 bytes for Multicast Address Record")
+36 -1
View File
@@ -8,6 +8,7 @@ package layers
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
@@ -38,6 +39,7 @@ const (
ASExternalLSAtypeV2 = 0x5
ASExternalLSAtype = 0x4005
NSSALSAtype = 0x2007
NSSALSAtypeV2 = 0x7
LinkLSAtype = 0x0008
IntraAreaPrefixLSAtype = 0x2009
)
@@ -129,6 +131,12 @@ type NetworkLSA struct {
AttachedRouter []uint32
}
// NetworkLSAV2 is the struct from RFC 2328 A.4.3.
type NetworkLSAV2 struct {
NetworkMask uint32
AttachedRouter []uint32
}
// RouterV2 extends RouterLSAV2
type RouterV2 struct {
Type uint8
@@ -288,12 +296,30 @@ func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{},
switch lstype {
case RouterLSAtypeV2:
var routers []RouterV2
var j uint32
for j = 24; j < uint32(lsalength); j += 12 {
if len(data) < int(j+12) {
return nil, errors.New("LSAtypeV2 too small")
}
router := RouterV2{
LinkID: binary.BigEndian.Uint32(data[j : j+4]),
LinkData: binary.BigEndian.Uint32(data[j+4 : j+8]),
Type: uint8(data[j+8]),
Metric: binary.BigEndian.Uint16(data[j+10 : j+12]),
}
routers = append(routers, router)
}
if len(data) < 24 {
return nil, errors.New("LSAtypeV2 too small")
}
links := binary.BigEndian.Uint16(data[22:24])
content = RouterLSAV2{
Flags: data[20],
Links: links,
Routers: routers,
}
case NSSALSAtypeV2:
fallthrough
case ASExternalLSAtypeV2:
content = ASExternalLSAV2{
NetworkMask: binary.BigEndian.Uint32(data[20:24]),
@@ -302,6 +328,16 @@ func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{},
ForwardingAddress: binary.BigEndian.Uint32(data[28:32]),
ExternalRouteTag: binary.BigEndian.Uint32(data[32:36]),
}
case NetworkLSAtypeV2:
var routers []uint32
var j uint32
for j = 24; j < uint32(lsalength); j += 4 {
routers = append(routers, binary.BigEndian.Uint32(data[j:j+4]))
}
content = NetworkLSAV2{
NetworkMask: binary.BigEndian.Uint32(data[20:24]),
AttachedRouter: routers,
}
case RouterLSAtype:
var routers []Router
var j uint32
@@ -346,7 +382,6 @@ func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{},
case ASExternalLSAtype:
fallthrough
case NSSALSAtype:
flags := uint8(data[20])
prefixLen := uint8(data[24]) / 8
var forwardingAddress []byte
+8
View File
@@ -9,6 +9,7 @@ package layers
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
)
@@ -39,6 +40,10 @@ type PFLog struct {
}
func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 60 {
df.SetTruncated()
return errors.New("PFLog data less than 60 bytes")
}
pf.Length = data[0]
pf.Family = ProtocolFamily(data[1])
pf.Action = data[2]
@@ -56,6 +61,9 @@ func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error
return errors.New("PFLog header length should be 3 less than multiple of 4")
}
actualLength := int(pf.Length) + 3
if len(data) < actualLength {
return fmt.Errorf("PFLog data size < %d", actualLength)
}
pf.Contents = data[:actualLength]
pf.Payload = data[actualLength:]
return nil
+2
View File
@@ -115,6 +115,8 @@ var udpPortLayerType = [65536]gopacket.LayerType{
6081: LayerTypeGeneve,
3784: LayerTypeBFD,
2152: LayerTypeGTPv1U,
623: LayerTypeRMCP,
1812: LayerTypeRADIUS,
}
// RegisterUDPPortLayerType creates a new mapping between a UDPPort
+12 -5
View File
@@ -9,6 +9,7 @@ package layers
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"strings"
@@ -468,11 +469,13 @@ const (
func (self RadioTapAMPDUStatusFlags) ReportZerolen() bool {
return self&RadioTapAMPDUStatusFlagsReportZerolen != 0
}
func (self RadioTapAMPDUStatusFlags) IsZerolen() bool { return self&RadioTapAMPDUIsZerolen != 0 }
func (self RadioTapAMPDUStatusFlags) LastKnown() bool { return self&RadioTapAMPDULastKnown != 0 }
func (self RadioTapAMPDUStatusFlags) IsLast() bool { return self&RadioTapAMPDUIsLast != 0 }
func (self RadioTapAMPDUStatusFlags) DelimCRCErr() bool { return self&RadioTapAMPDUDelimCRCErr != 0 }
func (self RadioTapAMPDUStatusFlags) DelimCRCKnown() bool { return self&RadioTapAMPDUDelimCRCKnown != 0 }
func (self RadioTapAMPDUStatusFlags) IsZerolen() bool { return self&RadioTapAMPDUIsZerolen != 0 }
func (self RadioTapAMPDUStatusFlags) LastKnown() bool { return self&RadioTapAMPDULastKnown != 0 }
func (self RadioTapAMPDUStatusFlags) IsLast() bool { return self&RadioTapAMPDUIsLast != 0 }
func (self RadioTapAMPDUStatusFlags) DelimCRCErr() bool { return self&RadioTapAMPDUDelimCRCErr != 0 }
func (self RadioTapAMPDUStatusFlags) DelimCRCKnown() bool {
return self&RadioTapAMPDUDelimCRCKnown != 0
}
type RadioTapVHT struct {
Known RadioTapVHTKnown
@@ -727,6 +730,10 @@ type RadioTap struct {
func (m *RadioTap) LayerType() gopacket.LayerType { return LayerTypeRadioTap }
func (m *RadioTap) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return errors.New("RadioTap too small")
}
m.Version = uint8(data[0])
m.Length = binary.LittleEndian.Uint16(data[2:4])
m.Present = RadioTapPresent(binary.LittleEndian.Uint32(data[4:8]))
+88 -1
View File
@@ -25,7 +25,7 @@ Specification has this to say:
be used for all interfaces.
This decoder only supports the compact form, because that is the only
one for which data was avaialble.
one for which data was available.
The datagram is composed of one or more samples of type flow or counter,
and each sample is composed of one or more records describing the sample.
@@ -460,28 +460,73 @@ func decodeFlowSample(data *[]byte, expanded bool) (SFlowFlowSample, error) {
var sdc SFlowDataSource
s.EnterpriseID, s.Format = sdf.decode()
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if expanded {
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.SourceIDClass = (*data)[4:], SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4]))
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.SourceIDIndex = (*data)[4:], SFlowSourceValue(binary.BigEndian.Uint32((*data)[:4]))
} else {
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4]))
s.SourceIDClass, s.SourceIDIndex = sdc.decode()
}
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.SamplingRate = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.SamplePool = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.Dropped = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if expanded {
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.InputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.OutputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
} else {
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
}
if len(*data) < 4 {
return SFlowFlowSample{}, errors.New("ethernet counters too small")
}
*data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
for i := uint32(0); i < s.RecordCount; i++ {
@@ -2184,19 +2229,61 @@ func decodeEthernetCounters(data *[]byte) (SFlowEthernetCounters, error) {
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
ec.EnterpriseID, ec.Format = cdf.decode()
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.AlignmentErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.FCSErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.SingleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.MultipleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.SQETestErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.DeferredTransmissions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.LateCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.ExcessiveCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.InternalMacTransmitErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.CarrierSenseErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.FrameTooLongs = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.InternalMacReceiveErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
if len(*data) < 4 {
return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
}
*data, ec.SymbolErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
return ec, nil
}
+14 -18
View File
@@ -245,14 +245,11 @@ func (s *SIP) NextLayerType() gopacket.LayerType {
// DecodeFromBytes decodes the slice into the SIP struct.
func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
// Init some vars for parsing follow-up
var countLines int
var line []byte
var err error
// Clean leading new line
data = bytes.Trim(data, "\n")
var offset int
// Iterate on all lines of the SIP Headers
// and stop when we reach the SDP (aka when the new line
@@ -265,19 +262,21 @@ func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
line, err = buffer.ReadBytes(byte('\n'))
if err != nil {
if err == io.EOF {
if len(bytes.Trim(line, "\r\n")) > 0 {
df.SetTruncated()
}
break
} else {
return err
}
}
offset += len(line)
// Trim the new line delimiters
line = bytes.Trim(line, "\r\n")
// Empty line, we hit Body
// Putting packet remain in Paypload
if len(line) == 0 {
s.BaseLayer.Payload = buffer.Bytes()
break
}
@@ -298,6 +297,7 @@ func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
countLines++
}
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}
return nil
}
@@ -469,11 +469,11 @@ func (s *SIP) GetHeader(headerName string) []string {
headerName = strings.ToLower(headerName)
h := make([]string, 0)
if _, ok := s.Headers[headerName]; ok {
if len(s.Headers[headerName]) > 0 {
return s.Headers[headerName]
} else if len(s.Headers[compactSipHeadersCorrespondance[headerName]]) > 0 {
return s.Headers[compactSipHeadersCorrespondance[headerName]]
}
return s.Headers[headerName]
}
compactHeader := compactSipHeadersCorrespondance[headerName]
if _, ok := s.Headers[compactHeader]; ok {
return s.Headers[compactHeader]
}
return h
}
@@ -482,13 +482,9 @@ func (s *SIP) GetHeader(headerName string) []string {
// the specified name. If the current SIP packet has multiple
// headers with the same name, it returns the first.
func (s *SIP) GetFirstHeader(headerName string) string {
headerName = strings.ToLower(headerName)
if _, ok := s.Headers[headerName]; ok {
if len(s.Headers[headerName]) > 0 {
return s.Headers[headerName][0]
} else if len(s.Headers[compactSipHeadersCorrespondance[headerName]]) > 0 {
return s.Headers[compactSipHeadersCorrespondance[headerName]][0]
}
headers := s.GetHeader(headerName)
if len(headers) > 0 {
return headers[0]
}
return ""
}
+9 -5
View File
@@ -108,10 +108,12 @@ func (t TCPOption) String() string {
}
switch t.OptionType {
case TCPOptionKindMSS:
return fmt.Sprintf("TCPOption(%s:%v%s)",
t.OptionType,
binary.BigEndian.Uint16(t.OptionData),
hd)
if len(t.OptionData) >= 2 {
return fmt.Sprintf("TCPOption(%s:%v%s)",
t.OptionType,
binary.BigEndian.Uint16(t.OptionData),
hd)
}
case TCPOptionKindTimestamps:
if len(t.OptionData) == 8 {
@@ -254,6 +256,7 @@ func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
} else {
tcp.Options = tcp.Options[:0]
}
tcp.Padding = tcp.Padding[:0]
if tcp.DataOffset < 5 {
return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
}
@@ -268,6 +271,7 @@ func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
tcp.Payload = data[dataStart:]
// From here on, data points just to the header options.
data = data[20:dataStart]
OPTIONS:
for len(data) > 0 {
tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
opt := &tcp.Options[len(tcp.Options)-1]
@@ -275,7 +279,7 @@ func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
case TCPOptionKindEndList: // End of options
opt.OptionLength = 1
tcp.Padding = data[1:]
break
break OPTIONS
case TCPOptionKindNop: // 1 byte padding
opt.OptionLength = 1
default:
+75
View File
@@ -135,6 +135,7 @@ func (t *TLS) decodeTLSRecords(data []byte, df gopacket.DecodeFeedback) error {
// since there are no further layers, the baselayer's content is
// pointing to this layer
// TODO: Consider removing this
t.BaseLayer = BaseLayer{Contents: data[:len(data)]}
var h TLSRecordHeader
@@ -206,3 +207,77 @@ func (t *TLS) NextLayerType() gopacket.LayerType {
func (t *TLS) Payload() []byte {
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
func (t *TLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
totalLength := 0
for _, record := range t.ChangeCipherSpec {
if opts.FixLengths {
record.Length = 1
}
totalLength += 5 + 1 // length of header + record
}
for range t.Handshake {
totalLength += 5
// TODO
}
for _, record := range t.AppData {
if opts.FixLengths {
record.Length = uint16(len(record.Payload))
}
totalLength += 5 + len(record.Payload)
}
for _, record := range t.Alert {
if len(record.EncryptedMsg) == 0 {
if opts.FixLengths {
record.Length = 2
}
totalLength += 5 + 2
} else {
if opts.FixLengths {
record.Length = uint16(len(record.EncryptedMsg))
}
totalLength += 5 + len(record.EncryptedMsg)
}
}
data, err := b.PrependBytes(totalLength)
if err != nil {
return err
}
off := 0
for _, record := range t.ChangeCipherSpec {
off = encodeHeader(record.TLSRecordHeader, data, off)
data[off] = byte(record.Message)
off++
}
for _, record := range t.Handshake {
off = encodeHeader(record.TLSRecordHeader, data, off)
// TODO
}
for _, record := range t.AppData {
off = encodeHeader(record.TLSRecordHeader, data, off)
copy(data[off:], record.Payload)
off += len(record.Payload)
}
for _, record := range t.Alert {
off = encodeHeader(record.TLSRecordHeader, data, off)
if len(record.EncryptedMsg) == 0 {
data[off] = byte(record.Level)
data[off+1] = byte(record.Description)
off += 2
} else {
copy(data[off:], record.EncryptedMsg)
off += len(record.EncryptedMsg)
}
}
return nil
}
func encodeHeader(header TLSRecordHeader, data []byte, offset int) int {
data[offset] = byte(header.ContentType)
binary.BigEndian.PutUint16(data[offset+1:], uint16(header.Version))
binary.BigEndian.PutUint16(data[offset+3:], header.Length)
return offset + 5
}
+5
View File
@@ -8,6 +8,7 @@ package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
@@ -142,6 +143,10 @@ func decodeUSB(data []byte, p gopacket.PacketBuilder) error {
}
func (m *USB) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 40 {
df.SetTruncated()
return errors.New("USB < 40 bytes")
}
m.ID = binary.LittleEndian.Uint64(data[0:8])
m.EventType = USBEventType(data[8])
m.TransferType = USBTransportType(data[9])
+27 -2
View File
@@ -8,7 +8,9 @@ package layers
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
)
@@ -37,9 +39,21 @@ type VXLAN struct {
// LayerType returns LayerTypeVXLAN
func (vx *VXLAN) LayerType() gopacket.LayerType { return LayerTypeVXLAN }
func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error {
vx := &VXLAN{}
// CanDecode returns the layer type this DecodingLayer can decode
func (vx *VXLAN) CanDecode() gopacket.LayerClass {
return LayerTypeVXLAN
}
// NextLayerType retuns the next layer we should see after vxlan
func (vx *VXLAN) NextLayerType() gopacket.LayerType {
return LayerTypeEthernet
}
// DecodeFromBytes takes a byte buffer and decodes
func (vx *VXLAN) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
return errors.New("vxlan packet too small")
}
// VNI is a 24bit number, Uint32 requires 32 bits
var buf [4]byte
copy(buf[1:], data[4:7])
@@ -59,6 +73,17 @@ func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error {
vx.Contents = data[:vxlanLength]
vx.Payload = data[vxlanLength:]
return nil
}
func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error {
vx := &VXLAN{}
err := vx.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(vx)
return p.NextDecoder(LinkTypeEthernet)
}
+169 -26
View File
@@ -10,6 +10,12 @@ import (
"fmt"
)
// A container for single LayerType->DecodingLayer mapping.
type decodingLayerElem struct {
typ LayerType
dec DecodingLayer
}
// DecodingLayer is an interface for packet layers that can decode themselves.
//
// The important part of DecodingLayer is that they decode themselves in-place.
@@ -39,15 +45,150 @@ type DecodingLayer interface {
LayerPayload() []byte
}
// DecodingLayerFunc decodes given packet and stores decoded LayerType
// values into specified slice. Returns either first encountered
// unsupported LayerType value or decoding error. In case of success,
// returns (LayerTypeZero, nil).
type DecodingLayerFunc func([]byte, *[]LayerType) (LayerType, error)
// DecodingLayerContainer stores all DecodingLayer-s and serves as a
// searching tool for DecodingLayerParser.
type DecodingLayerContainer interface {
// Put adds new DecodingLayer to container. The new instance of
// the same DecodingLayerContainer is returned so it may be
// implemented as a value receiver.
Put(DecodingLayer) DecodingLayerContainer
// Decoder returns DecodingLayer to decode given LayerType and
// true if it was found. If no decoder found, return false.
Decoder(LayerType) (DecodingLayer, bool)
// LayersDecoder returns DecodingLayerFunc which decodes given
// packet, starting with specified LayerType and DecodeFeedback.
LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc
}
// DecodingLayerSparse is a sparse array-based implementation of
// DecodingLayerContainer. Each DecodingLayer is addressed in an
// allocated slice by LayerType value itself. Though this is the
// fastest container it may be memory-consuming if used with big
// LayerType values.
type DecodingLayerSparse []DecodingLayer
// Put implements DecodingLayerContainer interface.
func (dl DecodingLayerSparse) Put(d DecodingLayer) DecodingLayerContainer {
maxLayerType := LayerType(len(dl) - 1)
for _, typ := range d.CanDecode().LayerTypes() {
if typ > maxLayerType {
maxLayerType = typ
}
}
if extra := maxLayerType - LayerType(len(dl)) + 1; extra > 0 {
dl = append(dl, make([]DecodingLayer, extra)...)
}
for _, typ := range d.CanDecode().LayerTypes() {
dl[typ] = d
}
return dl
}
// LayersDecoder implements DecodingLayerContainer interface.
func (dl DecodingLayerSparse) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
return LayersDecoder(dl, first, df)
}
// Decoder implements DecodingLayerContainer interface.
func (dl DecodingLayerSparse) Decoder(typ LayerType) (DecodingLayer, bool) {
if int64(typ) < int64(len(dl)) {
decoder := dl[typ]
return decoder, decoder != nil
}
return nil, false
}
// DecodingLayerArray is an array-based implementation of
// DecodingLayerContainer. Each DecodingLayer is searched linearly in
// an allocated slice in one-by-one fashion.
type DecodingLayerArray []decodingLayerElem
// Put implements DecodingLayerContainer interface.
func (dl DecodingLayerArray) Put(d DecodingLayer) DecodingLayerContainer {
TYPES:
for _, typ := range d.CanDecode().LayerTypes() {
for i := range dl {
if dl[i].typ == typ {
dl[i].dec = d
continue TYPES
}
}
dl = append(dl, decodingLayerElem{typ, d})
}
return dl
}
// Decoder implements DecodingLayerContainer interface.
func (dl DecodingLayerArray) Decoder(typ LayerType) (DecodingLayer, bool) {
for i := range dl {
if dl[i].typ == typ {
return dl[i].dec, true
}
}
return nil, false
}
// LayersDecoder implements DecodingLayerContainer interface.
func (dl DecodingLayerArray) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
return LayersDecoder(dl, first, df)
}
// DecodingLayerMap is an map-based implementation of
// DecodingLayerContainer. Each DecodingLayer is searched in a map
// hashed by LayerType value.
type DecodingLayerMap map[LayerType]DecodingLayer
// Put implements DecodingLayerContainer interface.
func (dl DecodingLayerMap) Put(d DecodingLayer) DecodingLayerContainer {
for _, typ := range d.CanDecode().LayerTypes() {
if dl == nil {
dl = make(map[LayerType]DecodingLayer)
}
dl[typ] = d
}
return dl
}
// Decoder implements DecodingLayerContainer interface.
func (dl DecodingLayerMap) Decoder(typ LayerType) (DecodingLayer, bool) {
d, ok := dl[typ]
return d, ok
}
// LayersDecoder implements DecodingLayerContainer interface.
func (dl DecodingLayerMap) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
return LayersDecoder(dl, first, df)
}
// Static code check.
var (
_ = []DecodingLayerContainer{
DecodingLayerSparse(nil),
DecodingLayerMap(nil),
DecodingLayerArray(nil),
}
)
// DecodingLayerParser parses a given set of layer types. See DecodeLayers for
// more information on how DecodingLayerParser should be used.
type DecodingLayerParser struct {
// DecodingLayerParserOptions is the set of options available to the
// user to define the parser's behavior.
DecodingLayerParserOptions
first LayerType
decoders map[LayerType]DecodingLayer
df DecodeFeedback
dlc DecodingLayerContainer
first LayerType
df DecodeFeedback
decodeFunc DecodingLayerFunc
// Truncated is set when a decode layer detects that the packet has been
// truncated.
Truncated bool
@@ -57,9 +198,7 @@ type DecodingLayerParser struct {
// the decoding layer's CanDecode layers to the parser... should they be
// encountered, they'll be parsed.
func (l *DecodingLayerParser) AddDecodingLayer(d DecodingLayer) {
for _, typ := range d.CanDecode().LayerTypes() {
l.decoders[typ] = d
}
l.SetDecodingLayerContainer(l.dlc.Put(d))
}
// SetTruncated is used by DecodingLayers to set the Truncated boolean in the
@@ -77,18 +216,30 @@ func (l *DecodingLayerParser) SetTruncated() {
// subsequently decoded layers to find the next relevant decoder. Should a
// deoder not be available for the layer type returned by NextLayerType,
// decoding will stop.
//
// NewDecodingLayerParser uses DecodingLayerMap container by
// default.
func NewDecodingLayerParser(first LayerType, decoders ...DecodingLayer) *DecodingLayerParser {
dlp := &DecodingLayerParser{
decoders: make(map[LayerType]DecodingLayer),
first: first,
}
dlp := &DecodingLayerParser{first: first}
dlp.df = dlp // Cast this once to the interface
// default container
dlc := DecodingLayerContainer(DecodingLayerMap(make(map[LayerType]DecodingLayer)))
for _, d := range decoders {
dlp.AddDecodingLayer(d)
dlc = dlc.Put(d)
}
dlp.SetDecodingLayerContainer(dlc)
return dlp
}
// SetDecodingLayerContainer specifies container with decoders. This
// call replaces all decoders already registered in given instance of
// DecodingLayerParser.
func (l *DecodingLayerParser) SetDecodingLayerContainer(dlc DecodingLayerContainer) {
l.dlc = dlc
l.decodeFunc = l.dlc.LayersDecoder(l.first, l.df)
}
// DecodeLayers decodes as many layers as possible from the given data. It
// initially treats the data as layer type 'typ', then uses NextLayerType on
// each subsequent decoded layer until it gets to a layer type it doesn't know
@@ -153,23 +304,15 @@ func (l *DecodingLayerParser) DecodeLayers(data []byte, decoded *[]LayerType) (e
if !l.IgnorePanic {
defer panicToError(&err)
}
typ := l.first
*decoded = (*decoded)[:0] // Truncated decoded layers.
for len(data) > 0 {
decoder, ok := l.decoders[typ]
if !ok {
if l.IgnoreUnsupported {
return nil
}
return UnsupportedLayerType(typ)
} else if err = decoder.DecodeFromBytes(data, l.df); err != nil {
return err
typ, err := l.decodeFunc(data, decoded)
if typ != LayerTypeZero {
// no decoder
if l.IgnoreUnsupported {
return nil
}
*decoded = append(*decoded, typ)
typ = decoder.NextLayerType()
data = decoder.LayerPayload()
return UnsupportedLayerType(typ)
}
return nil
return err
}
// UnsupportedLayerType is returned by DecodingLayerParser if DecodeLayers
+226 -3
View File
@@ -16,10 +16,17 @@ Package home: https://github.com/klauspost/cpuid
## installing
`go get -u github.com/klauspost/cpuid/v2` using modules.
`go get -u github.com/klauspost/cpuid/v2` using modules.
Drop `v2` for others.
### Homebrew
For macOS/Linux users, you can install via [brew](https://brew.sh/)
```sh
$ brew install cpuid
```
## example
```Go
@@ -77,10 +84,14 @@ We have Streaming SIMD 2 Extensions
The `cpuid.CPU` provides access to CPU features. Use `cpuid.CPU.Supports()` to check for CPU features.
A faster `cpuid.CPU.Has()` is provided which will usually be inlined by the gc compiler.
To test a larger number of features, they can be combined using `f := CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SYSCALL, SSE, SSE2)`, etc.
This can be using with `cpuid.CPU.HasAll(f)` to quickly test if all features are supported.
Note that for some cpu/os combinations some features will not be detected.
`amd64` has rather good support and should work reliably on all platforms.
Note that hypervisors may not pass through all CPU features.
Note that hypervisors may not pass through all CPU features through to the guest OS,
so even if your host supports a feature it may not be visible on guests.
## arm64 feature detection
@@ -253,6 +264,218 @@ Exit Code 0
Exit Code 1
```
## Available flags
### x86 & amd64
| Feature Flag | Description |
|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ADX | Intel ADX (Multi-Precision Add-Carry Instruction Extensions) |
| AESNI | Advanced Encryption Standard New Instructions |
| AMD3DNOW | AMD 3DNOW |
| AMD3DNOWEXT | AMD 3DNowExt |
| AMXBF16 | Tile computational operations on BFLOAT16 numbers |
| AMXINT8 | Tile computational operations on 8-bit integers |
| AMXFP16 | Tile computational operations on FP16 numbers |
| AMXTILE | Tile architecture |
| AVX | AVX functions |
| AVX2 | AVX2 functions |
| AVX512BF16 | AVX-512 BFLOAT16 Instructions |
| AVX512BITALG | AVX-512 Bit Algorithms |
| AVX512BW | AVX-512 Byte and Word Instructions |
| AVX512CD | AVX-512 Conflict Detection Instructions |
| AVX512DQ | AVX-512 Doubleword and Quadword Instructions |
| AVX512ER | AVX-512 Exponential and Reciprocal Instructions |
| AVX512F | AVX-512 Foundation |
| AVX512FP16 | AVX-512 FP16 Instructions |
| AVX512IFMA | AVX-512 Integer Fused Multiply-Add Instructions |
| AVX512PF | AVX-512 Prefetch Instructions |
| AVX512VBMI | AVX-512 Vector Bit Manipulation Instructions |
| AVX512VBMI2 | AVX-512 Vector Bit Manipulation Instructions, Version 2 |
| AVX512VL | AVX-512 Vector Length Extensions |
| AVX512VNNI | AVX-512 Vector Neural Network Instructions |
| AVX512VP2INTERSECT | AVX-512 Intersect for D/Q |
| AVX512VPOPCNTDQ | AVX-512 Vector Population Count Doubleword and Quadword |
| AVXIFMA | AVX-IFMA instructions |
| AVXNECONVERT | AVX-NE-CONVERT instructions |
| AVXSLOW | Indicates the CPU performs 2 128 bit operations instead of one |
| AVXVNNI | AVX (VEX encoded) VNNI neural network instructions |
| AVXVNNIINT8 | AVX-VNNI-INT8 instructions |
| BMI1 | Bit Manipulation Instruction Set 1 |
| BMI2 | Bit Manipulation Instruction Set 2 |
| CETIBT | Intel CET Indirect Branch Tracking |
| CETSS | Intel CET Shadow Stack |
| CLDEMOTE | Cache Line Demote |
| CLMUL | Carry-less Multiplication |
| CLZERO | CLZERO instruction supported |
| CMOV | i686 CMOV |
| CMPCCXADD | CMPCCXADD instructions |
| CMPSB_SCADBS_SHORT | Fast short CMPSB and SCASB |
| CMPXCHG8 | CMPXCHG8 instruction |
| CPBOOST | Core Performance Boost |
| CPPC | AMD: Collaborative Processor Performance Control |
| CX16 | CMPXCHG16B Instruction |
| EFER_LMSLE_UNS | AMD: =Core::X86::Msr::EFER[LMSLE] is not supported, and MBZ |
| ENQCMD | Enqueue Command |
| ERMS | Enhanced REP MOVSB/STOSB |
| F16C | Half-precision floating-point conversion |
| FLUSH_L1D | Flush L1D cache |
| FMA3 | Intel FMA 3. Does not imply AVX. |
| FMA4 | Bulldozer FMA4 functions |
| FP128 | AMD: When set, the internal FP/SIMD execution datapath is 128-bits wide |
| FP256 | AMD: When set, the internal FP/SIMD execution datapath is 256-bits wide |
| FSRM | Fast Short Rep Mov |
| FXSR | FXSAVE, FXRESTOR instructions, CR4 bit 9 |
| FXSROPT | FXSAVE/FXRSTOR optimizations |
| GFNI | Galois Field New Instructions. May require other features (AVX, AVX512VL,AVX512F) based on usage. |
| HLE | Hardware Lock Elision |
| HRESET | If set CPU supports history reset and the IA32_HRESET_ENABLE MSR |
| HTT | Hyperthreading (enabled) |
| HWA | Hardware assert supported. Indicates support for MSRC001_10 |
| HYBRID_CPU | This part has CPUs of more than one type. |
| HYPERVISOR | This bit has been reserved by Intel & AMD for use by hypervisors |
| IA32_ARCH_CAP | IA32_ARCH_CAPABILITIES MSR (Intel) |
| IA32_CORE_CAP | IA32_CORE_CAPABILITIES MSR |
| IBPB | Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) |
| IBRS | AMD: Indirect Branch Restricted Speculation |
| IBRS_PREFERRED | AMD: IBRS is preferred over software solution |
| IBRS_PROVIDES_SMP | AMD: IBRS provides Same Mode Protection |
| IBS | Instruction Based Sampling (AMD) |
| IBSBRNTRGT | Instruction Based Sampling Feature (AMD) |
| IBSFETCHSAM | Instruction Based Sampling Feature (AMD) |
| IBSFFV | Instruction Based Sampling Feature (AMD) |
| IBSOPCNT | Instruction Based Sampling Feature (AMD) |
| IBSOPCNTEXT | Instruction Based Sampling Feature (AMD) |
| IBSOPSAM | Instruction Based Sampling Feature (AMD) |
| IBSRDWROPCNT | Instruction Based Sampling Feature (AMD) |
| IBSRIPINVALIDCHK | Instruction Based Sampling Feature (AMD) |
| IBS_FETCH_CTLX | AMD: IBS fetch control extended MSR supported |
| IBS_OPDATA4 | AMD: IBS op data 4 MSR supported |
| IBS_OPFUSE | AMD: Indicates support for IbsOpFuse |
| IBS_PREVENTHOST | Disallowing IBS use by the host supported |
| IBS_ZEN4 | Fetch and Op IBS support IBS extensions added with Zen4 |
| INT_WBINVD | WBINVD/WBNOINVD are interruptible. |
| INVLPGB | NVLPGB and TLBSYNC instruction supported |
| LAHF | LAHF/SAHF in long mode |
| LAM | If set, CPU supports Linear Address Masking |
| LBRVIRT | LBR virtualization |
| LZCNT | LZCNT instruction |
| MCAOVERFLOW | MCA overflow recovery support. |
| MCDT_NO | Processor do not exhibit MXCSR Configuration Dependent Timing behavior and do not need to mitigate it. |
| MCOMMIT | MCOMMIT instruction supported |
| MD_CLEAR | VERW clears CPU buffers |
| MMX | standard MMX |
| MMXEXT | SSE integer functions or AMD MMX ext |
| MOVBE | MOVBE instruction (big-endian) |
| MOVDIR64B | Move 64 Bytes as Direct Store |
| MOVDIRI | Move Doubleword as Direct Store |
| MOVSB_ZL | Fast Zero-Length MOVSB |
| MPX | Intel MPX (Memory Protection Extensions) |
| MOVU | MOVU SSE instructions are more efficient and should be preferred to SSE MOVL/MOVH. MOVUPS is more efficient than MOVLPS/MOVHPS. MOVUPD is more efficient than MOVLPD/MOVHPD |
| MSRIRC | Instruction Retired Counter MSR available |
| MSR_PAGEFLUSH | Page Flush MSR available |
| NRIPS | Indicates support for NRIP save on VMEXIT |
| NX | NX (No-Execute) bit |
| OSXSAVE | XSAVE enabled by OS |
| PCONFIG | PCONFIG for Intel Multi-Key Total Memory Encryption |
| POPCNT | POPCNT instruction |
| PPIN | AMD: Protected Processor Inventory Number support. Indicates that Protected Processor Inventory Number (PPIN) capability can be enabled |
| PREFETCHI | PREFETCHIT0/1 instructions |
| PSFD | AMD: Predictive Store Forward Disable |
| RDPRU | RDPRU instruction supported |
| RDRAND | RDRAND instruction is available |
| RDSEED | RDSEED instruction is available |
| RDTSCP | RDTSCP Instruction |
| RTM | Restricted Transactional Memory |
| RTM_ALWAYS_ABORT | Indicates that the loaded microcode is forcing RTM abort. |
| SERIALIZE | Serialize Instruction Execution |
| SEV | AMD Secure Encrypted Virtualization supported |
| SEV_64BIT | AMD SEV guest execution only allowed from a 64-bit host |
| SEV_ALTERNATIVE | AMD SEV Alternate Injection supported |
| SEV_DEBUGSWAP | Full debug state swap supported for SEV-ES guests |
| SEV_ES | AMD SEV Encrypted State supported |
| SEV_RESTRICTED | AMD SEV Restricted Injection supported |
| SEV_SNP | AMD SEV Secure Nested Paging supported |
| SGX | Software Guard Extensions |
| SGXLC | Software Guard Extensions Launch Control |
| SHA | Intel SHA Extensions |
| SME | AMD Secure Memory Encryption supported |
| SME_COHERENT | AMD Hardware cache coherency across encryption domains enforced |
| SPEC_CTRL_SSBD | Speculative Store Bypass Disable |
| SRBDS_CTRL | SRBDS mitigation MSR available |
| SSE | SSE functions |
| SSE2 | P4 SSE functions |
| SSE3 | Prescott SSE3 functions |
| SSE4 | Penryn SSE4.1 functions |
| SSE42 | Nehalem SSE4.2 functions |
| SSE4A | AMD Barcelona microarchitecture SSE4a instructions |
| SSSE3 | Conroe SSSE3 functions |
| STIBP | Single Thread Indirect Branch Predictors |
| STIBP_ALWAYSON | AMD: Single Thread Indirect Branch Prediction Mode has Enhanced Performance and may be left Always On |
| STOSB_SHORT | Fast short STOSB |
| SUCCOR | Software uncorrectable error containment and recovery capability. |
| SVM | AMD Secure Virtual Machine |
| SVMDA | Indicates support for the SVM decode assists. |
| SVMFBASID | SVM, Indicates that TLB flush events, including CR3 writes and CR4.PGE toggles, flush only the current ASID's TLB entries. Also indicates support for the extended VMCBTLB_Control |
| SVML | AMD SVM lock. Indicates support for SVM-Lock. |
| SVMNP | AMD SVM nested paging |
| SVMPF | SVM pause intercept filter. Indicates support for the pause intercept filter |
| SVMPFT | SVM PAUSE filter threshold. Indicates support for the PAUSE filter cycle count threshold |
| SYSCALL | System-Call Extension (SCE): SYSCALL and SYSRET instructions. |
| SYSEE | SYSENTER and SYSEXIT instructions |
| TBM | AMD Trailing Bit Manipulation |
| TLB_FLUSH_NESTED | AMD: Flushing includes all the nested translations for guest translations |
| TME | Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. |
| TOPEXT | TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. |
| TSCRATEMSR | MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104 |
| TSXLDTRK | Intel TSX Suspend Load Address Tracking |
| VAES | Vector AES. AVX(512) versions requires additional checks. |
| VMCBCLEAN | VMCB clean bits. Indicates support for VMCB clean bits. |
| VMPL | AMD VM Permission Levels supported |
| VMSA_REGPROT | AMD VMSA Register Protection supported |
| VMX | Virtual Machine Extensions |
| VPCLMULQDQ | Carry-Less Multiplication Quadword. Requires AVX for 3 register versions. |
| VTE | AMD Virtual Transparent Encryption supported |
| WAITPKG | TPAUSE, UMONITOR, UMWAIT |
| WBNOINVD | Write Back and Do Not Invalidate Cache |
| X87 | FPU |
| XGETBV1 | Supports XGETBV with ECX = 1 |
| XOP | Bulldozer XOP functions |
| XSAVE | XSAVE, XRESTOR, XSETBV, XGETBV |
| XSAVEC | Supports XSAVEC and the compacted form of XRSTOR. |
| XSAVEOPT | XSAVEOPT available |
| XSAVES | Supports XSAVES/XRSTORS and IA32_XSS |
# ARM features:
| Feature Flag | Description |
|--------------|------------------------------------------------------------------|
| AESARM | AES instructions |
| ARMCPUID | Some CPU ID registers readable at user-level |
| ASIMD | Advanced SIMD |
| ASIMDDP | SIMD Dot Product |
| ASIMDHP | Advanced SIMD half-precision floating point |
| ASIMDRDM | Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH) |
| ATOMICS | Large System Extensions (LSE) |
| CRC32 | CRC32/CRC32C instructions |
| DCPOP | Data cache clean to Point of Persistence (DC CVAP) |
| EVTSTRM | Generic timer |
| FCMA | Floatin point complex number addition and multiplication |
| FP | Single-precision and double-precision floating point |
| FPHP | Half-precision floating point |
| GPA | Generic Pointer Authentication |
| JSCVT | Javascript-style double->int convert (FJCVTZS) |
| LRCPC | Weaker release consistency (LDAPR, etc) |
| PMULL | Polynomial Multiply instructions (PMULL/PMULL2) |
| SHA1 | SHA-1 instructions (SHA1C, etc) |
| SHA2 | SHA-2 instructions (SHA256H, etc) |
| SHA3 | SHA-3 instructions (EOR3, RAXI, XAR, BCAX) |
| SHA512 | SHA512 instructions |
| SM3 | SM3 instructions |
| SM4 | SM4 instructions |
| SVE | Scalable Vector Extension |
# license
This code is published under an MIT license. See LICENSE file for more information.
+233 -44
View File
@@ -73,6 +73,7 @@ const (
AMD3DNOW // AMD 3DNOW
AMD3DNOWEXT // AMD 3DNowExt
AMXBF16 // Tile computational operations on BFLOAT16 numbers
AMXFP16 // Tile computational operations on FP16 numbers
AMXINT8 // Tile computational operations on 8-bit integers
AMXTILE // Tile architecture
AVX // AVX functions
@@ -93,7 +94,11 @@ const (
AVX512VNNI // AVX-512 Vector Neural Network Instructions
AVX512VP2INTERSECT // AVX-512 Intersect for D/Q
AVX512VPOPCNTDQ // AVX-512 Vector Population Count Doubleword and Quadword
AVXSLOW // Indicates the CPU performs 2 128 bit operations instead of one.
AVXIFMA // AVX-IFMA instructions
AVXNECONVERT // AVX-NE-CONVERT instructions
AVXSLOW // Indicates the CPU performs 2 128 bit operations instead of one
AVXVNNI // AVX (VEX encoded) VNNI neural network instructions
AVXVNNIINT8 // AVX-VNNI-INT8 instructions
BMI1 // Bit Manipulation Instruction Set 1
BMI2 // Bit Manipulation Instruction Set 2
CETIBT // Intel CET Indirect Branch Tracking
@@ -102,22 +107,37 @@ const (
CLMUL // Carry-less Multiplication
CLZERO // CLZERO instruction supported
CMOV // i686 CMOV
CMPCCXADD // CMPCCXADD instructions
CMPSB_SCADBS_SHORT // Fast short CMPSB and SCASB
CMPXCHG8 // CMPXCHG8 instruction
CPBOOST // Core Performance Boost
CPPC // AMD: Collaborative Processor Performance Control
CX16 // CMPXCHG16B Instruction
EFER_LMSLE_UNS // AMD: =Core::X86::Msr::EFER[LMSLE] is not supported, and MBZ
ENQCMD // Enqueue Command
ERMS // Enhanced REP MOVSB/STOSB
F16C // Half-precision floating-point conversion
FLUSH_L1D // Flush L1D cache
FMA3 // Intel FMA 3. Does not imply AVX.
FMA4 // Bulldozer FMA4 functions
FP128 // AMD: When set, the internal FP/SIMD execution datapath is no more than 128-bits wide
FP256 // AMD: When set, the internal FP/SIMD execution datapath is no more than 256-bits wide
FSRM // Fast Short Rep Mov
FXSR // FXSAVE, FXRESTOR instructions, CR4 bit 9
FXSROPT // FXSAVE/FXRSTOR optimizations
GFNI // Galois Field New Instructions
GFNI // Galois Field New Instructions. May require other features (AVX, AVX512VL,AVX512F) based on usage.
HLE // Hardware Lock Elision
HRESET // If set CPU supports history reset and the IA32_HRESET_ENABLE MSR
HTT // Hyperthreading (enabled)
HWA // Hardware assert supported. Indicates support for MSRC001_10
HYBRID_CPU // This part has CPUs of more than one type.
HYPERVISOR // This bit has been reserved by Intel & AMD for use by hypervisors
IA32_ARCH_CAP // IA32_ARCH_CAPABILITIES MSR (Intel)
IA32_CORE_CAP // IA32_CORE_CAPABILITIES MSR
IBPB // Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB)
IBRS // AMD: Indirect Branch Restricted Speculation
IBRS_PREFERRED // AMD: IBRS is preferred over software solution
IBRS_PROVIDES_SMP // AMD: IBRS provides Same Mode Protection
IBS // Instruction Based Sampling (AMD)
IBSBRNTRGT // Instruction Based Sampling Feature (AMD)
IBSFETCHSAM // Instruction Based Sampling Feature (AMD)
@@ -127,32 +147,45 @@ const (
IBSOPSAM // Instruction Based Sampling Feature (AMD)
IBSRDWROPCNT // Instruction Based Sampling Feature (AMD)
IBSRIPINVALIDCHK // Instruction Based Sampling Feature (AMD)
IBS_FETCH_CTLX // AMD: IBS fetch control extended MSR supported
IBS_OPDATA4 // AMD: IBS op data 4 MSR supported
IBS_OPFUSE // AMD: Indicates support for IbsOpFuse
IBS_PREVENTHOST // Disallowing IBS use by the host supported
IBS_ZEN4 // AMD: Fetch and Op IBS support IBS extensions added with Zen4
INT_WBINVD // WBINVD/WBNOINVD are interruptible.
INVLPGB // NVLPGB and TLBSYNC instruction supported
LAHF // LAHF/SAHF in long mode
LAM // If set, CPU supports Linear Address Masking
LBRVIRT // LBR virtualization
LZCNT // LZCNT instruction
MCAOVERFLOW // MCA overflow recovery support.
MCDT_NO // Processor do not exhibit MXCSR Configuration Dependent Timing behavior and do not need to mitigate it.
MCOMMIT // MCOMMIT instruction supported
MD_CLEAR // VERW clears CPU buffers
MMX // standard MMX
MMXEXT // SSE integer functions or AMD MMX ext
MOVBE // MOVBE instruction (big-endian)
MOVDIR64B // Move 64 Bytes as Direct Store
MOVDIRI // Move Doubleword as Direct Store
MOVSB_ZL // Fast Zero-Length MOVSB
MOVU // AMD: MOVU SSE instructions are more efficient and should be preferred to SSE MOVL/MOVH. MOVUPS is more efficient than MOVLPS/MOVHPS. MOVUPD is more efficient than MOVLPD/MOVHPD
MPX // Intel MPX (Memory Protection Extensions)
MSR_PAGEFLUSH // Page Flush MSR available
MSRIRC // Instruction Retired Counter MSR available
MSR_PAGEFLUSH // Page Flush MSR available
NRIPS // Indicates support for NRIP save on VMEXIT
NX // NX (No-Execute) bit
OSXSAVE // XSAVE enabled by OS
PCONFIG // PCONFIG for Intel Multi-Key Total Memory Encryption
POPCNT // POPCNT instruction
PPIN // AMD: Protected Processor Inventory Number support. Indicates that Protected Processor Inventory Number (PPIN) capability can be enabled
PREFETCHI // PREFETCHIT0/1 instructions
PSFD // AMD: Predictive Store Forward Disable
RDPRU // RDPRU instruction supported
RDRAND // RDRAND instruction is available
RDSEED // RDSEED instruction is available
RDTSCP // RDTSCP Instruction
RTM // Restricted Transactional Memory
RTM_ALWAYS_ABORT // Indicates that the loaded microcode is forcing RTM abort.
SCE // SYSENTER and SYSEXIT instructions
SERIALIZE // Serialize Instruction Execution
SEV // AMD Secure Encrypted Virtualization supported
SEV_64BIT // AMD SEV guest execution only allowed from a 64-bit host
@@ -166,6 +199,8 @@ const (
SHA // Intel SHA Extensions
SME // AMD Secure Memory Encryption supported
SME_COHERENT // AMD Hardware cache coherency across encryption domains enforced
SPEC_CTRL_SSBD // Speculative Store Bypass Disable
SRBDS_CTRL // SRBDS mitigation MSR available
SSE // SSE functions
SSE2 // P4 SSE functions
SSE3 // Prescott SSE3 functions
@@ -174,15 +209,30 @@ const (
SSE4A // AMD Barcelona microarchitecture SSE4a instructions
SSSE3 // Conroe SSSE3 functions
STIBP // Single Thread Indirect Branch Predictors
STIBP_ALWAYSON // AMD: Single Thread Indirect Branch Prediction Mode has Enhanced Performance and may be left Always On
STOSB_SHORT // Fast short STOSB
SUCCOR // Software uncorrectable error containment and recovery capability.
SVM // AMD Secure Virtual Machine
SVMDA // Indicates support for the SVM decode assists.
SVMFBASID // SVM, Indicates that TLB flush events, including CR3 writes and CR4.PGE toggles, flush only the current ASID's TLB entries. Also indicates support for the extended VMCBTLB_Control
SVML // AMD SVM lock. Indicates support for SVM-Lock.
SVMNP // AMD SVM nested paging
SVMPF // SVM pause intercept filter. Indicates support for the pause intercept filter
SVMPFT // SVM PAUSE filter threshold. Indicates support for the PAUSE filter cycle count threshold
SYSCALL // System-Call Extension (SCE): SYSCALL and SYSRET instructions.
SYSEE // SYSENTER and SYSEXIT instructions
TBM // AMD Trailing Bit Manipulation
TLB_FLUSH_NESTED // AMD: Flushing includes all the nested translations for guest translations
TME // Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE.
TOPEXT // TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX.
TSCRATEMSR // MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104
TSXLDTRK // Intel TSX Suspend Load Address Tracking
VAES // Vector AES
VAES // Vector AES. AVX(512) versions requires additional checks.
VMCBCLEAN // VMCB clean bits. Indicates support for VMCB clean bits.
VMPL // AMD VM Permission Levels supported
VMSA_REGPROT // AMD VMSA Register Protection supported
VMX // Virtual Machine Extensions
VPCLMULQDQ // Carry-Less Multiplication Quadword
VPCLMULQDQ // Carry-Less Multiplication Quadword. Requires AVX for 3 register versions.
VTE // AMD Virtual Transparent Encryption supported
WAITPKG // TPAUSE, UMONITOR, UMWAIT
WBNOINVD // Write Back and Do Not Invalidate Cache
@@ -219,7 +269,6 @@ const (
SM3 // SM3 instructions
SM4 // SM4 instructions
SVE // Scalable Vector Extension
// Keep it last. It automatically defines the size of []flagSet
lastID
@@ -237,6 +286,7 @@ type CPUInfo struct {
LogicalCores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable.
Family int // CPU family number
Model int // CPU model number
Stepping int // CPU stepping info
CacheLine int // Cache line size in bytes. Will be 0 if undetectable.
Hz int64 // Clock speed, if known, 0 otherwise. Will attempt to contain base clock speed.
BoostFreq int64 // Max clock speed, if known, 0 otherwise
@@ -339,30 +389,61 @@ func (c CPUInfo) Supports(ids ...FeatureID) bool {
// Has allows for checking a single feature.
// Should be inlined by the compiler.
func (c CPUInfo) Has(id FeatureID) bool {
func (c *CPUInfo) Has(id FeatureID) bool {
return c.featureSet.inSet(id)
}
// AnyOf returns whether the CPU supports one or more of the requested features.
func (c CPUInfo) AnyOf(ids ...FeatureID) bool {
for _, id := range ids {
if c.featureSet.inSet(id) {
return true
}
}
return false
}
// Features contains several features combined for a fast check using
// CpuInfo.HasAll
type Features *flagSet
// CombineFeatures allows to combine several features for a close to constant time lookup.
func CombineFeatures(ids ...FeatureID) Features {
var v flagSet
for _, id := range ids {
v.set(id)
}
return &v
}
func (c *CPUInfo) HasAll(f Features) bool {
return c.featureSet.hasSetP(f)
}
// https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels
var level1Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2)
var level2Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3)
var level3Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE)
var level4Features = flagSetWith(CMOV, CMPXCHG8, X87, FXSR, MMX, SCE, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE, AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL)
var oneOfLevel = CombineFeatures(SYSEE, SYSCALL)
var level1Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2)
var level2Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3)
var level3Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE)
var level4Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE, AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL)
// X64Level returns the microarchitecture level detected on the CPU.
// If features are lacking or non x64 mode, 0 is returned.
// See https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels
func (c CPUInfo) X64Level() int {
if c.featureSet.hasSet(level4Features) {
if !c.featureSet.hasOneOf(oneOfLevel) {
return 0
}
if c.featureSet.hasSetP(level4Features) {
return 4
}
if c.featureSet.hasSet(level3Features) {
if c.featureSet.hasSetP(level3Features) {
return 3
}
if c.featureSet.hasSet(level2Features) {
if c.featureSet.hasSetP(level2Features) {
return 2
}
if c.featureSet.hasSet(level1Features) {
if c.featureSet.hasSetP(level1Features) {
return 1
}
return 0
@@ -526,7 +607,7 @@ const flagMask = flagBits - 1
// flagSet contains detected cpu features and characteristics in an array of flags
type flagSet [(lastID + flagMask) / flagBits]flags
func (s flagSet) inSet(feat FeatureID) bool {
func (s *flagSet) inSet(feat FeatureID) bool {
return s[feat>>flagBitsLog2]&(1<<(feat&flagMask)) != 0
}
@@ -556,7 +637,7 @@ func (s *flagSet) or(other flagSet) {
}
// hasSet returns whether all features are present.
func (s flagSet) hasSet(other flagSet) bool {
func (s *flagSet) hasSet(other flagSet) bool {
for i, v := range other[:] {
if s[i]&v != v {
return false
@@ -565,8 +646,28 @@ func (s flagSet) hasSet(other flagSet) bool {
return true
}
// hasSet returns whether all features are present.
func (s *flagSet) hasSetP(other *flagSet) bool {
for i, v := range other[:] {
if s[i]&v != v {
return false
}
}
return true
}
// hasOneOf returns whether one or more features are present.
func (s *flagSet) hasOneOf(other *flagSet) bool {
for i, v := range other[:] {
if s[i]&v != 0 {
return true
}
}
return false
}
// nEnabled will return the number of enabled flags.
func (s flagSet) nEnabled() (n int) {
func (s *flagSet) nEnabled() (n int) {
for _, v := range s[:] {
n += bits.OnesCount64(uint64(v))
}
@@ -661,7 +762,7 @@ func threadsPerCore() int {
if vend == AMD {
// Workaround for AMD returning 0, assume 2 if >= Zen 2
// It will be more correct than not.
fam, _ := familyModel()
fam, _, _ := familyModel()
_, _, _, d := cpuid(1)
if (d&(1<<28)) != 0 && fam >= 23 {
return 2
@@ -699,14 +800,27 @@ func logicalCores() int {
}
}
func familyModel() (int, int) {
func familyModel() (family, model, stepping int) {
if maxFunctionID() < 0x1 {
return 0, 0
return 0, 0, 0
}
eax, _, _, _ := cpuid(1)
family := ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff)
model := ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0)
return int(family), int(model)
// If BaseFamily[3:0] is less than Fh then ExtendedFamily[7:0] is reserved and Family is equal to BaseFamily[3:0].
family = int((eax >> 8) & 0xf)
extFam := family == 0x6 // Intel is 0x6, needs extended model.
if family == 0xf {
// Add ExtFamily
family += int((eax >> 20) & 0xff)
extFam = true
}
// If BaseFamily[3:0] is less than 0Fh then ExtendedModel[3:0] is reserved and Model is equal to BaseModel[3:0].
model = int((eax >> 4) & 0xf)
if extFam {
// Add ExtModel
model += int((eax >> 12) & 0xf0)
}
stepping = int(eax & 0xf)
return family, model, stepping
}
func physicalCores() int {
@@ -841,7 +955,7 @@ func (c *CPUInfo) cacheSize() {
c.Cache.L2 = int(((ecx >> 16) & 0xFFFF) * 1024)
// CPUID Fn8000_001D_EAX_x[N:0] Cache Properties
if maxExtendedFunction() < 0x8000001D {
if maxExtendedFunction() < 0x8000001D || !c.Has(TOPEXT) {
return
}
@@ -958,14 +1072,13 @@ func support() flagSet {
if mfi < 0x1 {
return fs
}
family, model := familyModel()
family, model, _ := familyModel()
_, _, c, d := cpuid(1)
fs.setIf((d&(1<<0)) != 0, X87)
fs.setIf((d&(1<<8)) != 0, CMPXCHG8)
fs.setIf((d&(1<<11)) != 0, SCE)
fs.setIf((d&(1<<11)) != 0, SYSEE)
fs.setIf((d&(1<<15)) != 0, CMOV)
fs.setIf((d&(1<<22)) != 0, MMXEXT)
fs.setIf((d&(1<<23)) != 0, MMX)
fs.setIf((d&(1<<24)) != 0, FXSR)
fs.setIf((d&(1<<25)) != 0, FXSROPT)
@@ -973,9 +1086,9 @@ func support() flagSet {
fs.setIf((d&(1<<26)) != 0, SSE2)
fs.setIf((c&1) != 0, SSE3)
fs.setIf((c&(1<<5)) != 0, VMX)
fs.setIf((c&0x00000200) != 0, SSSE3)
fs.setIf((c&0x00080000) != 0, SSE4)
fs.setIf((c&0x00100000) != 0, SSE42)
fs.setIf((c&(1<<9)) != 0, SSSE3)
fs.setIf((c&(1<<19)) != 0, SSE4)
fs.setIf((c&(1<<20)) != 0, SSE42)
fs.setIf((c&(1<<25)) != 0, AESNI)
fs.setIf((c&(1<<1)) != 0, CLMUL)
fs.setIf(c&(1<<22) != 0, MOVBE)
@@ -1021,7 +1134,6 @@ func support() flagSet {
// Check AVX2, AVX2 requires OS support, but BMI1/2 don't.
if mfi >= 7 {
_, ebx, ecx, edx := cpuidex(7, 0)
eax1, _, _, _ := cpuidex(7, 1)
if fs.inSet(AVX) && (ebx&0x00000020) != 0 {
fs.set(AVX2)
}
@@ -1038,23 +1150,52 @@ func support() flagSet {
fs.setIf(ebx&(1<<18) != 0, RDSEED)
fs.setIf(ebx&(1<<19) != 0, ADX)
fs.setIf(ebx&(1<<29) != 0, SHA)
// CPUID.(EAX=7, ECX=0).ECX
fs.setIf(ecx&(1<<5) != 0, WAITPKG)
fs.setIf(ecx&(1<<7) != 0, CETSS)
fs.setIf(ecx&(1<<8) != 0, GFNI)
fs.setIf(ecx&(1<<9) != 0, VAES)
fs.setIf(ecx&(1<<10) != 0, VPCLMULQDQ)
fs.setIf(ecx&(1<<13) != 0, TME)
fs.setIf(ecx&(1<<25) != 0, CLDEMOTE)
fs.setIf(ecx&(1<<27) != 0, MOVDIRI)
fs.setIf(ecx&(1<<28) != 0, MOVDIR64B)
fs.setIf(ecx&(1<<29) != 0, ENQCMD)
fs.setIf(ecx&(1<<30) != 0, SGXLC)
// CPUID.(EAX=7, ECX=0).EDX
fs.setIf(edx&(1<<4) != 0, FSRM)
fs.setIf(edx&(1<<9) != 0, SRBDS_CTRL)
fs.setIf(edx&(1<<10) != 0, MD_CLEAR)
fs.setIf(edx&(1<<11) != 0, RTM_ALWAYS_ABORT)
fs.setIf(edx&(1<<14) != 0, SERIALIZE)
fs.setIf(edx&(1<<15) != 0, HYBRID_CPU)
fs.setIf(edx&(1<<16) != 0, TSXLDTRK)
fs.setIf(edx&(1<<18) != 0, PCONFIG)
fs.setIf(edx&(1<<20) != 0, CETIBT)
fs.setIf(edx&(1<<26) != 0, IBPB)
fs.setIf(edx&(1<<27) != 0, STIBP)
fs.setIf(edx&(1<<28) != 0, FLUSH_L1D)
fs.setIf(edx&(1<<29) != 0, IA32_ARCH_CAP)
fs.setIf(edx&(1<<30) != 0, IA32_CORE_CAP)
fs.setIf(edx&(1<<31) != 0, SPEC_CTRL_SSBD)
// CPUID.(EAX=7, ECX=1).EDX
fs.setIf(edx&(1<<4) != 0, AVXVNNIINT8)
fs.setIf(edx&(1<<5) != 0, AVXNECONVERT)
fs.setIf(edx&(1<<14) != 0, PREFETCHI)
// CPUID.(EAX=7, ECX=1).EAX
eax1, _, _, _ := cpuidex(7, 1)
fs.setIf(fs.inSet(AVX) && eax1&(1<<4) != 0, AVXVNNI)
fs.setIf(eax1&(1<<7) != 0, CMPCCXADD)
fs.setIf(eax1&(1<<10) != 0, MOVSB_ZL)
fs.setIf(eax1&(1<<11) != 0, STOSB_SHORT)
fs.setIf(eax1&(1<<12) != 0, CMPSB_SCADBS_SHORT)
fs.setIf(eax1&(1<<22) != 0, HRESET)
fs.setIf(eax1&(1<<23) != 0, AVXIFMA)
fs.setIf(eax1&(1<<26) != 0, LAM)
// Only detect AVX-512 features if XGETBV is supported
if c&((1<<26)|(1<<27)) == (1<<26)|(1<<27) {
@@ -1080,9 +1221,6 @@ func support() flagSet {
// ecx
fs.setIf(ecx&(1<<1) != 0, AVX512VBMI)
fs.setIf(ecx&(1<<6) != 0, AVX512VBMI2)
fs.setIf(ecx&(1<<8) != 0, GFNI)
fs.setIf(ecx&(1<<9) != 0, VAES)
fs.setIf(ecx&(1<<10) != 0, VPCLMULQDQ)
fs.setIf(ecx&(1<<11) != 0, AVX512VNNI)
fs.setIf(ecx&(1<<12) != 0, AVX512BITALG)
fs.setIf(ecx&(1<<14) != 0, AVX512VPOPCNTDQ)
@@ -1094,9 +1232,15 @@ func support() flagSet {
fs.setIf(edx&(1<<25) != 0, AMXINT8)
// eax1 = CPUID.(EAX=7, ECX=1).EAX
fs.setIf(eax1&(1<<5) != 0, AVX512BF16)
fs.setIf(eax1&(1<<21) != 0, AMXFP16)
}
}
// CPUID.(EAX=7, ECX=2)
_, _, _, edx = cpuidex(7, 2)
fs.setIf(edx&(1<<5) != 0, MCDT_NO)
}
// Processor Extended State Enumeration Sub-leaf (EAX = 0DH, ECX = 1)
// EAX
// Bit 00: XSAVEOPT is available.
@@ -1125,21 +1269,29 @@ func support() flagSet {
fs.set(LZCNT)
fs.set(POPCNT)
}
// ECX
fs.setIf((c&(1<<0)) != 0, LAHF)
fs.setIf((c&(1<<10)) != 0, IBS)
fs.setIf((d&(1<<31)) != 0, AMD3DNOW)
fs.setIf((d&(1<<30)) != 0, AMD3DNOWEXT)
fs.setIf((d&(1<<23)) != 0, MMX)
fs.setIf((d&(1<<22)) != 0, MMXEXT)
fs.setIf((c&(1<<2)) != 0, SVM)
fs.setIf((c&(1<<6)) != 0, SSE4A)
fs.setIf((c&(1<<10)) != 0, IBS)
fs.setIf((c&(1<<22)) != 0, TOPEXT)
// EDX
fs.setIf(d&(1<<11) != 0, SYSCALL)
fs.setIf(d&(1<<20) != 0, NX)
fs.setIf(d&(1<<22) != 0, MMXEXT)
fs.setIf(d&(1<<23) != 0, MMX)
fs.setIf(d&(1<<24) != 0, FXSR)
fs.setIf(d&(1<<25) != 0, FXSROPT)
fs.setIf(d&(1<<27) != 0, RDTSCP)
fs.setIf(d&(1<<30) != 0, AMD3DNOWEXT)
fs.setIf(d&(1<<31) != 0, AMD3DNOW)
/* XOP and FMA4 use the AVX instruction coding scheme, so they can't be
* used unless the OS has AVX support. */
if fs.inSet(AVX) {
fs.setIf((c&0x00000800) != 0, XOP)
fs.setIf((c&0x00010000) != 0, FMA4)
fs.setIf((c&(1<<11)) != 0, XOP)
fs.setIf((c&(1<<16)) != 0, FMA4)
}
}
@@ -1153,15 +1305,48 @@ func support() flagSet {
if maxExtendedFunction() >= 0x80000008 {
_, b, _, _ := cpuid(0x80000008)
fs.setIf(b&(1<<28) != 0, PSFD)
fs.setIf(b&(1<<27) != 0, CPPC)
fs.setIf(b&(1<<24) != 0, SPEC_CTRL_SSBD)
fs.setIf(b&(1<<23) != 0, PPIN)
fs.setIf(b&(1<<21) != 0, TLB_FLUSH_NESTED)
fs.setIf(b&(1<<20) != 0, EFER_LMSLE_UNS)
fs.setIf(b&(1<<19) != 0, IBRS_PROVIDES_SMP)
fs.setIf(b&(1<<18) != 0, IBRS_PREFERRED)
fs.setIf(b&(1<<17) != 0, STIBP_ALWAYSON)
fs.setIf(b&(1<<15) != 0, STIBP)
fs.setIf(b&(1<<14) != 0, IBRS)
fs.setIf((b&(1<<13)) != 0, INT_WBINVD)
fs.setIf(b&(1<<12) != 0, IBPB)
fs.setIf((b&(1<<9)) != 0, WBNOINVD)
fs.setIf((b&(1<<8)) != 0, MCOMMIT)
fs.setIf((b&(1<<13)) != 0, INT_WBINVD)
fs.setIf((b&(1<<4)) != 0, RDPRU)
fs.setIf((b&(1<<3)) != 0, INVLPGB)
fs.setIf((b&(1<<1)) != 0, MSRIRC)
fs.setIf((b&(1<<0)) != 0, CLZERO)
}
if fs.inSet(SVM) && maxExtendedFunction() >= 0x8000000A {
_, _, _, edx := cpuid(0x8000000A)
fs.setIf((edx>>0)&1 == 1, SVMNP)
fs.setIf((edx>>1)&1 == 1, LBRVIRT)
fs.setIf((edx>>2)&1 == 1, SVML)
fs.setIf((edx>>3)&1 == 1, NRIPS)
fs.setIf((edx>>4)&1 == 1, TSCRATEMSR)
fs.setIf((edx>>5)&1 == 1, VMCBCLEAN)
fs.setIf((edx>>6)&1 == 1, SVMFBASID)
fs.setIf((edx>>7)&1 == 1, SVMDA)
fs.setIf((edx>>10)&1 == 1, SVMPF)
fs.setIf((edx>>12)&1 == 1, SVMPFT)
}
if maxExtendedFunction() >= 0x8000001a {
eax, _, _, _ := cpuid(0x8000001a)
fs.setIf((eax>>0)&1 == 1, FP128)
fs.setIf((eax>>1)&1 == 1, MOVU)
fs.setIf((eax>>2)&1 == 1, FP256)
}
if maxExtendedFunction() >= 0x8000001b && fs.inSet(IBS) {
eax, _, _, _ := cpuid(0x8000001b)
fs.setIf((eax>>0)&1 == 1, IBSFFV)
@@ -1172,6 +1357,10 @@ func support() flagSet {
fs.setIf((eax>>5)&1 == 1, IBSBRNTRGT)
fs.setIf((eax>>6)&1 == 1, IBSOPCNTEXT)
fs.setIf((eax>>7)&1 == 1, IBSRIPINVALIDCHK)
fs.setIf((eax>>8)&1 == 1, IBS_OPFUSE)
fs.setIf((eax>>9)&1 == 1, IBS_FETCH_CTLX)
fs.setIf((eax>>10)&1 == 1, IBS_OPDATA4) // Doc says "Fixed,0. IBS op data 4 MSR supported", but assuming they mean 1.
fs.setIf((eax>>11)&1 == 1, IBS_ZEN4)
}
if maxExtendedFunction() >= 0x8000001f && vend == AMD {
+1 -1
View File
@@ -24,7 +24,7 @@ func addInfo(c *CPUInfo, safe bool) {
c.maxExFunc = maxExtendedFunction()
c.BrandName = brandName()
c.CacheLine = cacheLine()
c.Family, c.Model = familyModel()
c.Family, c.Model, c.Stepping = familyModel()
c.featureSet = support()
c.SGX = hasSGX(c.featureSet.inSet(SGX), c.featureSet.inSet(SGXLC))
c.ThreadsPerCore = threadsPerCore()
+197 -147
View File
@@ -13,157 +13,207 @@ func _() {
_ = x[AMD3DNOW-3]
_ = x[AMD3DNOWEXT-4]
_ = x[AMXBF16-5]
_ = x[AMXINT8-6]
_ = x[AMXTILE-7]
_ = x[AVX-8]
_ = x[AVX2-9]
_ = x[AVX512BF16-10]
_ = x[AVX512BITALG-11]
_ = x[AVX512BW-12]
_ = x[AVX512CD-13]
_ = x[AVX512DQ-14]
_ = x[AVX512ER-15]
_ = x[AVX512F-16]
_ = x[AVX512FP16-17]
_ = x[AVX512IFMA-18]
_ = x[AVX512PF-19]
_ = x[AVX512VBMI-20]
_ = x[AVX512VBMI2-21]
_ = x[AVX512VL-22]
_ = x[AVX512VNNI-23]
_ = x[AVX512VP2INTERSECT-24]
_ = x[AVX512VPOPCNTDQ-25]
_ = x[AVXSLOW-26]
_ = x[BMI1-27]
_ = x[BMI2-28]
_ = x[CETIBT-29]
_ = x[CETSS-30]
_ = x[CLDEMOTE-31]
_ = x[CLMUL-32]
_ = x[CLZERO-33]
_ = x[CMOV-34]
_ = x[CMPXCHG8-35]
_ = x[CPBOOST-36]
_ = x[CX16-37]
_ = x[ENQCMD-38]
_ = x[ERMS-39]
_ = x[F16C-40]
_ = x[FMA3-41]
_ = x[FMA4-42]
_ = x[FXSR-43]
_ = x[FXSROPT-44]
_ = x[GFNI-45]
_ = x[HLE-46]
_ = x[HTT-47]
_ = x[HWA-48]
_ = x[HYPERVISOR-49]
_ = x[IBPB-50]
_ = x[IBS-51]
_ = x[IBSBRNTRGT-52]
_ = x[IBSFETCHSAM-53]
_ = x[IBSFFV-54]
_ = x[IBSOPCNT-55]
_ = x[IBSOPCNTEXT-56]
_ = x[IBSOPSAM-57]
_ = x[IBSRDWROPCNT-58]
_ = x[IBSRIPINVALIDCHK-59]
_ = x[IBS_PREVENTHOST-60]
_ = x[INT_WBINVD-61]
_ = x[INVLPGB-62]
_ = x[LAHF-63]
_ = x[LZCNT-64]
_ = x[MCAOVERFLOW-65]
_ = x[MCOMMIT-66]
_ = x[MMX-67]
_ = x[MMXEXT-68]
_ = x[MOVBE-69]
_ = x[MOVDIR64B-70]
_ = x[MOVDIRI-71]
_ = x[MPX-72]
_ = x[MSR_PAGEFLUSH-73]
_ = x[MSRIRC-74]
_ = x[NX-75]
_ = x[OSXSAVE-76]
_ = x[PCONFIG-77]
_ = x[POPCNT-78]
_ = x[RDPRU-79]
_ = x[RDRAND-80]
_ = x[RDSEED-81]
_ = x[RDTSCP-82]
_ = x[RTM-83]
_ = x[RTM_ALWAYS_ABORT-84]
_ = x[SCE-85]
_ = x[SERIALIZE-86]
_ = x[SEV-87]
_ = x[SEV_64BIT-88]
_ = x[SEV_ALTERNATIVE-89]
_ = x[SEV_DEBUGSWAP-90]
_ = x[SEV_ES-91]
_ = x[SEV_RESTRICTED-92]
_ = x[SEV_SNP-93]
_ = x[SGX-94]
_ = x[SGXLC-95]
_ = x[SHA-96]
_ = x[SME-97]
_ = x[SME_COHERENT-98]
_ = x[SSE-99]
_ = x[SSE2-100]
_ = x[SSE3-101]
_ = x[SSE4-102]
_ = x[SSE42-103]
_ = x[SSE4A-104]
_ = x[SSSE3-105]
_ = x[STIBP-106]
_ = x[SUCCOR-107]
_ = x[TBM-108]
_ = x[TME-109]
_ = x[TSXLDTRK-110]
_ = x[VAES-111]
_ = x[VMPL-112]
_ = x[VMSA_REGPROT-113]
_ = x[VMX-114]
_ = x[VPCLMULQDQ-115]
_ = x[VTE-116]
_ = x[WAITPKG-117]
_ = x[WBNOINVD-118]
_ = x[X87-119]
_ = x[XGETBV1-120]
_ = x[XOP-121]
_ = x[XSAVE-122]
_ = x[XSAVEC-123]
_ = x[XSAVEOPT-124]
_ = x[XSAVES-125]
_ = x[AESARM-126]
_ = x[ARMCPUID-127]
_ = x[ASIMD-128]
_ = x[ASIMDDP-129]
_ = x[ASIMDHP-130]
_ = x[ASIMDRDM-131]
_ = x[ATOMICS-132]
_ = x[CRC32-133]
_ = x[DCPOP-134]
_ = x[EVTSTRM-135]
_ = x[FCMA-136]
_ = x[FP-137]
_ = x[FPHP-138]
_ = x[GPA-139]
_ = x[JSCVT-140]
_ = x[LRCPC-141]
_ = x[PMULL-142]
_ = x[SHA1-143]
_ = x[SHA2-144]
_ = x[SHA3-145]
_ = x[SHA512-146]
_ = x[SM3-147]
_ = x[SM4-148]
_ = x[SVE-149]
_ = x[lastID-150]
_ = x[AMXFP16-6]
_ = x[AMXINT8-7]
_ = x[AMXTILE-8]
_ = x[AVX-9]
_ = x[AVX2-10]
_ = x[AVX512BF16-11]
_ = x[AVX512BITALG-12]
_ = x[AVX512BW-13]
_ = x[AVX512CD-14]
_ = x[AVX512DQ-15]
_ = x[AVX512ER-16]
_ = x[AVX512F-17]
_ = x[AVX512FP16-18]
_ = x[AVX512IFMA-19]
_ = x[AVX512PF-20]
_ = x[AVX512VBMI-21]
_ = x[AVX512VBMI2-22]
_ = x[AVX512VL-23]
_ = x[AVX512VNNI-24]
_ = x[AVX512VP2INTERSECT-25]
_ = x[AVX512VPOPCNTDQ-26]
_ = x[AVXIFMA-27]
_ = x[AVXNECONVERT-28]
_ = x[AVXSLOW-29]
_ = x[AVXVNNI-30]
_ = x[AVXVNNIINT8-31]
_ = x[BMI1-32]
_ = x[BMI2-33]
_ = x[CETIBT-34]
_ = x[CETSS-35]
_ = x[CLDEMOTE-36]
_ = x[CLMUL-37]
_ = x[CLZERO-38]
_ = x[CMOV-39]
_ = x[CMPCCXADD-40]
_ = x[CMPSB_SCADBS_SHORT-41]
_ = x[CMPXCHG8-42]
_ = x[CPBOOST-43]
_ = x[CPPC-44]
_ = x[CX16-45]
_ = x[EFER_LMSLE_UNS-46]
_ = x[ENQCMD-47]
_ = x[ERMS-48]
_ = x[F16C-49]
_ = x[FLUSH_L1D-50]
_ = x[FMA3-51]
_ = x[FMA4-52]
_ = x[FP128-53]
_ = x[FP256-54]
_ = x[FSRM-55]
_ = x[FXSR-56]
_ = x[FXSROPT-57]
_ = x[GFNI-58]
_ = x[HLE-59]
_ = x[HRESET-60]
_ = x[HTT-61]
_ = x[HWA-62]
_ = x[HYBRID_CPU-63]
_ = x[HYPERVISOR-64]
_ = x[IA32_ARCH_CAP-65]
_ = x[IA32_CORE_CAP-66]
_ = x[IBPB-67]
_ = x[IBRS-68]
_ = x[IBRS_PREFERRED-69]
_ = x[IBRS_PROVIDES_SMP-70]
_ = x[IBS-71]
_ = x[IBSBRNTRGT-72]
_ = x[IBSFETCHSAM-73]
_ = x[IBSFFV-74]
_ = x[IBSOPCNT-75]
_ = x[IBSOPCNTEXT-76]
_ = x[IBSOPSAM-77]
_ = x[IBSRDWROPCNT-78]
_ = x[IBSRIPINVALIDCHK-79]
_ = x[IBS_FETCH_CTLX-80]
_ = x[IBS_OPDATA4-81]
_ = x[IBS_OPFUSE-82]
_ = x[IBS_PREVENTHOST-83]
_ = x[IBS_ZEN4-84]
_ = x[INT_WBINVD-85]
_ = x[INVLPGB-86]
_ = x[LAHF-87]
_ = x[LAM-88]
_ = x[LBRVIRT-89]
_ = x[LZCNT-90]
_ = x[MCAOVERFLOW-91]
_ = x[MCDT_NO-92]
_ = x[MCOMMIT-93]
_ = x[MD_CLEAR-94]
_ = x[MMX-95]
_ = x[MMXEXT-96]
_ = x[MOVBE-97]
_ = x[MOVDIR64B-98]
_ = x[MOVDIRI-99]
_ = x[MOVSB_ZL-100]
_ = x[MOVU-101]
_ = x[MPX-102]
_ = x[MSRIRC-103]
_ = x[MSR_PAGEFLUSH-104]
_ = x[NRIPS-105]
_ = x[NX-106]
_ = x[OSXSAVE-107]
_ = x[PCONFIG-108]
_ = x[POPCNT-109]
_ = x[PPIN-110]
_ = x[PREFETCHI-111]
_ = x[PSFD-112]
_ = x[RDPRU-113]
_ = x[RDRAND-114]
_ = x[RDSEED-115]
_ = x[RDTSCP-116]
_ = x[RTM-117]
_ = x[RTM_ALWAYS_ABORT-118]
_ = x[SERIALIZE-119]
_ = x[SEV-120]
_ = x[SEV_64BIT-121]
_ = x[SEV_ALTERNATIVE-122]
_ = x[SEV_DEBUGSWAP-123]
_ = x[SEV_ES-124]
_ = x[SEV_RESTRICTED-125]
_ = x[SEV_SNP-126]
_ = x[SGX-127]
_ = x[SGXLC-128]
_ = x[SHA-129]
_ = x[SME-130]
_ = x[SME_COHERENT-131]
_ = x[SPEC_CTRL_SSBD-132]
_ = x[SRBDS_CTRL-133]
_ = x[SSE-134]
_ = x[SSE2-135]
_ = x[SSE3-136]
_ = x[SSE4-137]
_ = x[SSE42-138]
_ = x[SSE4A-139]
_ = x[SSSE3-140]
_ = x[STIBP-141]
_ = x[STIBP_ALWAYSON-142]
_ = x[STOSB_SHORT-143]
_ = x[SUCCOR-144]
_ = x[SVM-145]
_ = x[SVMDA-146]
_ = x[SVMFBASID-147]
_ = x[SVML-148]
_ = x[SVMNP-149]
_ = x[SVMPF-150]
_ = x[SVMPFT-151]
_ = x[SYSCALL-152]
_ = x[SYSEE-153]
_ = x[TBM-154]
_ = x[TLB_FLUSH_NESTED-155]
_ = x[TME-156]
_ = x[TOPEXT-157]
_ = x[TSCRATEMSR-158]
_ = x[TSXLDTRK-159]
_ = x[VAES-160]
_ = x[VMCBCLEAN-161]
_ = x[VMPL-162]
_ = x[VMSA_REGPROT-163]
_ = x[VMX-164]
_ = x[VPCLMULQDQ-165]
_ = x[VTE-166]
_ = x[WAITPKG-167]
_ = x[WBNOINVD-168]
_ = x[X87-169]
_ = x[XGETBV1-170]
_ = x[XOP-171]
_ = x[XSAVE-172]
_ = x[XSAVEC-173]
_ = x[XSAVEOPT-174]
_ = x[XSAVES-175]
_ = x[AESARM-176]
_ = x[ARMCPUID-177]
_ = x[ASIMD-178]
_ = x[ASIMDDP-179]
_ = x[ASIMDHP-180]
_ = x[ASIMDRDM-181]
_ = x[ATOMICS-182]
_ = x[CRC32-183]
_ = x[DCPOP-184]
_ = x[EVTSTRM-185]
_ = x[FCMA-186]
_ = x[FP-187]
_ = x[FPHP-188]
_ = x[GPA-189]
_ = x[JSCVT-190]
_ = x[LRCPC-191]
_ = x[PMULL-192]
_ = x[SHA1-193]
_ = x[SHA2-194]
_ = x[SHA3-195]
_ = x[SHA512-196]
_ = x[SM3-197]
_ = x[SM4-198]
_ = x[SVE-199]
_ = x[lastID-200]
_ = x[firstID-0]
}
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXINT8AMXTILEAVXAVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXSLOWBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPXCHG8CPBOOSTCX16ENQCMDERMSF16CFMA3FMA4FXSRFXSROPTGFNIHLEHTTHWAHYPERVISORIBPBIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_PREVENTHOSTINT_WBINVDINVLPGBLAHFLZCNTMCAOVERFLOWMCOMMITMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMPXMSR_PAGEFLUSHMSRIRCNXOSXSAVEPCONFIGPOPCNTRDPRURDRANDRDSEEDRDTSCPRTMRTM_ALWAYS_ABORTSCESERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSUCCORTBMTMETSXLDTRKVAESVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXTILEAVXAVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8BMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4INT_WBINVDINVLPGBLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRTMRTM_ALWAYS_ABORTSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSPEC_CTRL_SSBDSRBDS_CTRLSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTLB_FLUSH_NESTEDTMETOPEXTTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFPFPHPGPAJSCVTLRCPCPMULLSHA1SHA2SHA3SHA512SM3SM4SVElastID"
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 58, 62, 72, 84, 92, 100, 108, 116, 123, 133, 143, 151, 161, 172, 180, 190, 208, 223, 230, 234, 238, 244, 249, 257, 262, 268, 272, 280, 287, 291, 297, 301, 305, 309, 313, 317, 324, 328, 331, 334, 337, 347, 351, 354, 364, 375, 381, 389, 400, 408, 420, 436, 451, 461, 468, 472, 477, 488, 495, 498, 504, 509, 518, 525, 528, 541, 547, 549, 556, 563, 569, 574, 580, 586, 592, 595, 611, 614, 623, 626, 635, 650, 663, 669, 683, 690, 693, 698, 701, 704, 716, 719, 723, 727, 731, 736, 741, 746, 751, 757, 760, 763, 771, 775, 779, 791, 794, 804, 807, 814, 822, 825, 832, 835, 840, 846, 854, 860, 866, 874, 879, 886, 893, 901, 908, 913, 918, 925, 929, 931, 935, 938, 943, 948, 953, 957, 961, 965, 971, 974, 977, 980, 986}
var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 62, 65, 69, 79, 91, 99, 107, 115, 123, 130, 140, 150, 158, 168, 179, 187, 197, 215, 230, 237, 249, 256, 263, 274, 278, 282, 288, 293, 301, 306, 312, 316, 325, 343, 351, 358, 362, 366, 380, 386, 390, 394, 403, 407, 411, 416, 421, 425, 429, 436, 440, 443, 449, 452, 455, 465, 475, 488, 501, 505, 509, 523, 540, 543, 553, 564, 570, 578, 589, 597, 609, 625, 639, 650, 660, 675, 683, 693, 700, 704, 707, 714, 719, 730, 737, 744, 752, 755, 761, 766, 775, 782, 790, 794, 797, 803, 816, 821, 823, 830, 837, 843, 847, 856, 860, 865, 871, 877, 883, 886, 902, 911, 914, 923, 938, 951, 957, 971, 978, 981, 986, 989, 992, 1004, 1018, 1028, 1031, 1035, 1039, 1043, 1048, 1053, 1058, 1063, 1077, 1088, 1094, 1097, 1102, 1111, 1115, 1120, 1125, 1131, 1138, 1143, 1146, 1162, 1165, 1171, 1181, 1189, 1193, 1202, 1206, 1218, 1221, 1231, 1234, 1241, 1249, 1252, 1259, 1262, 1267, 1273, 1281, 1287, 1293, 1301, 1306, 1313, 1320, 1328, 1335, 1340, 1345, 1352, 1356, 1358, 1362, 1365, 1370, 1375, 1380, 1384, 1388, 1392, 1398, 1401, 1404, 1407, 1413}
func (i FeatureID) String() string {
if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) {
-3
View File
@@ -1,3 +0,0 @@
module github.com/klauspost/cpuid/v2
go 1.15
+107 -5
View File
@@ -2,18 +2,120 @@
package cpuid
import "runtime"
import (
"runtime"
"strings"
"golang.org/x/sys/unix"
)
func detectOS(c *CPUInfo) bool {
if runtime.GOOS != "ios" {
tryToFillCPUInfoFomSysctl(c)
}
// There are no hw.optional sysctl values for the below features on Mac OS 11.0
// to detect their supported state dynamically. Assume the CPU features that
// Apple Silicon M1 supports to be available as a minimal set of features
// to all Go programs running on darwin/arm64.
// TODO: Add more if we know them.
c.featureSet.setIf(runtime.GOOS != "ios", AESARM, PMULL, SHA1, SHA2)
c.PhysicalCores = runtime.NumCPU()
// For now assuming 1 thread per core...
c.ThreadsPerCore = 1
c.LogicalCores = c.PhysicalCores
return true
}
func sysctlGetBool(name string) bool {
value, err := unix.SysctlUint32(name)
if err != nil {
return false
}
return value != 0
}
func sysctlGetString(name string) string {
value, err := unix.Sysctl(name)
if err != nil {
return ""
}
return value
}
func sysctlGetInt(unknown int, names ...string) int {
for _, name := range names {
value, err := unix.SysctlUint32(name)
if err != nil {
continue
}
if value != 0 {
return int(value)
}
}
return unknown
}
func sysctlGetInt64(unknown int, names ...string) int {
for _, name := range names {
value64, err := unix.SysctlUint64(name)
if err != nil {
continue
}
if int(value64) != unknown {
return int(value64)
}
}
return unknown
}
func setFeature(c *CPUInfo, name string, feature FeatureID) {
c.featureSet.setIf(sysctlGetBool(name), feature)
}
func tryToFillCPUInfoFomSysctl(c *CPUInfo) {
c.BrandName = sysctlGetString("machdep.cpu.brand_string")
if len(c.BrandName) != 0 {
c.VendorString = strings.Fields(c.BrandName)[0]
}
c.PhysicalCores = sysctlGetInt(runtime.NumCPU(), "hw.physicalcpu")
c.ThreadsPerCore = sysctlGetInt(1, "machdep.cpu.thread_count", "kern.num_threads") /
sysctlGetInt(1, "hw.physicalcpu")
c.LogicalCores = sysctlGetInt(runtime.NumCPU(), "machdep.cpu.core_count")
c.Family = sysctlGetInt(0, "machdep.cpu.family", "hw.cpufamily")
c.Model = sysctlGetInt(0, "machdep.cpu.model")
c.CacheLine = sysctlGetInt64(0, "hw.cachelinesize")
c.Cache.L1I = sysctlGetInt64(-1, "hw.l1icachesize")
c.Cache.L1D = sysctlGetInt64(-1, "hw.l1dcachesize")
c.Cache.L2 = sysctlGetInt64(-1, "hw.l2cachesize")
c.Cache.L3 = sysctlGetInt64(-1, "hw.l3cachesize")
// from https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile
setFeature(c, "hw.optional.arm.FEAT_AES", AESARM)
setFeature(c, "hw.optional.AdvSIMD", ASIMD)
setFeature(c, "hw.optional.arm.FEAT_DotProd", ASIMDDP)
setFeature(c, "hw.optional.arm.FEAT_RDM", ASIMDRDM)
setFeature(c, "hw.optional.FEAT_CRC32", CRC32)
setFeature(c, "hw.optional.arm.FEAT_DPB", DCPOP)
// setFeature(c, "", EVTSTRM)
setFeature(c, "hw.optional.arm.FEAT_FCMA", FCMA)
setFeature(c, "hw.optional.arm.FEAT_FP", FP)
setFeature(c, "hw.optional.arm.FEAT_FP16", FPHP)
setFeature(c, "hw.optional.arm.FEAT_PAuth", GPA)
setFeature(c, "hw.optional.arm.FEAT_JSCVT", JSCVT)
setFeature(c, "hw.optional.arm.FEAT_LRCPC", LRCPC)
setFeature(c, "hw.optional.arm.FEAT_PMULL", PMULL)
setFeature(c, "hw.optional.arm.FEAT_SHA1", SHA1)
setFeature(c, "hw.optional.arm.FEAT_SHA256", SHA2)
setFeature(c, "hw.optional.arm.FEAT_SHA3", SHA3)
setFeature(c, "hw.optional.arm.FEAT_SHA512", SHA512)
// setFeature(c, "", SM3)
// setFeature(c, "", SM4)
setFeature(c, "hw.optional.arm.FEAT_SVE", SVE)
// from empirical observation
setFeature(c, "hw.optional.AdvSIMD_HPFPCvt", ASIMDHP)
setFeature(c, "hw.optional.armv8_1_atomics", ATOMICS)
setFeature(c, "hw.optional.floatingpoint", FP)
setFeature(c, "hw.optional.armv8_2_sha3", SHA3)
setFeature(c, "hw.optional.armv8_2_sha512", SHA512)
setFeature(c, "hw.optional.armv8_3_compnum", FCMA)
setFeature(c, "hw.optional.armv8_crc32", CRC32)
}
+136 -46
View File
@@ -1,8 +1,5 @@
# Reed-Solomon
[![Go Reference](https://pkg.go.dev/badge/github.com/klauspost/reedsolomon.svg)](https://pkg.go.dev/github.com/klauspost/reedsolomon) [![Build Status][3]][4]
[3]: https://travis-ci.org/klauspost/reedsolomon.svg?branch=master
[4]: https://travis-ci.org/klauspost/reedsolomon
[![Go Reference](https://pkg.go.dev/badge/github.com/klauspost/reedsolomon.svg)](https://pkg.go.dev/github.com/klauspost/reedsolomon) [![Go](https://github.com/klauspost/reedsolomon/actions/workflows/go.yml/badge.svg)](https://github.com/klauspost/reedsolomon/actions/workflows/go.yml)
Reed-Solomon Erasure Coding in Go, with speeds exceeding 1GB/s/cpu core implemented in pure Go.
@@ -11,9 +8,12 @@ This is a Go port of the [JavaReedSolomon](https://github.com/Backblaze/JavaReed
For an introduction on erasure coding, see the post on the [Backblaze blog](https://www.backblaze.com/blog/reed-solomon/).
For encoding high shard counts (>256) a Leopard implementation is used.
For most platforms this performs close to the original Leopard implementation in terms of speed.
Package home: https://github.com/klauspost/reedsolomon
Godoc: https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc
Godoc: https://pkg.go.dev/github.com/klauspost/reedsolomon
# Installation
To get the package use the standard:
@@ -21,9 +21,17 @@ To get the package use the standard:
go get -u github.com/klauspost/reedsolomon
```
Using Go modules recommended.
Using Go modules is recommended.
# Changes
## 2022
* [GFNI](https://github.com/klauspost/reedsolomon/pull/224) support for amd64, for up to 3x faster processing.
* [Leopard GF8](https://github.com/klauspost/reedsolomon#leopard-gf8) mode added, for faster processing of medium shard counts.
* [Leopard GF16](https://github.com/klauspost/reedsolomon#leopard-compatible-gf16) mode added, for up to 65536 shards.
* [WithJerasureMatrix](https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc#WithJerasureMatrix) allows constructing a [Jerasure](https://github.com/tsuraan/Jerasure) compatible matrix.
## 2021
* Use `GOAMD64=v4` to enable faster AVX2.
@@ -34,6 +42,8 @@ Using Go modules recommended.
* Allow disabling inversion cache.
* Faster AVX2 encoding.
<details>
<summary>See older changes</summary>
## May 2020
@@ -97,6 +107,8 @@ The [`StreamEncoder`](https://godoc.org/github.com/klauspost/reedsolomon#StreamE
handles this without modifying the interface.
This is a good lesson on why returning interfaces is not a good design.
</details>
# Usage
This section assumes you know the basics of Reed-Solomon encoding.
@@ -106,23 +118,19 @@ This package performs the calculation of the parity sets. The usage is therefore
First of all, you need to choose your distribution of data and parity shards.
A 'good' distribution is very subjective, and will depend a lot on your usage scenario.
A good starting point is above 5 and below 257 data shards (the maximum supported number),
and the number of parity shards to be 2 or above, and below the number of data shards.
To create an encoder with 10 data shards (where your data goes) and 3 parity shards (calculated):
```Go
enc, err := reedsolomon.New(10, 3)
```
This encoder will work for all parity sets with this distribution of data and parity shards.
The error will only be set if you specify 0 or negative values in any of the parameters,
or if you specify more than 256 data shards.
If you will primarily be using it with one shard size it is recommended to use
[`WithAutoGoroutines(shardSize)`](https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc#WithAutoGoroutines)
as an additional parameter. This will attempt to calculate the optimal number of goroutines to use for the best speed.
It is not required that all shards are this size.
The you send and receive data is a simple slice of byte slices; `[][]byte`.
Then you send and receive data that is a simple slice of byte slices; `[][]byte`.
In the example above, the top slice must have a length of 13.
```Go
@@ -138,8 +146,10 @@ but you could for instance also use [mmap](https://github.com/edsrzf/mmap-go) to
data[i] := make([]byte, 50000)
}
// The above allocations can also be done by the encoder:
// data := enc.(reedsolomon.Extended).AllocAligned(50000)
// Fill some data into the data shards
// Fill some data into the data shards
for i, in := range data[:10] {
for j:= range in {
in[j] = byte((i+j)&0xff)
@@ -230,6 +240,29 @@ To join a data set, use the `Join()` function, which will join the shards and wr
err = enc.Join(io.Discard, data, len(bigfile))
```
## Aligned Allocations
For AMD64 aligned inputs can make a big speed difference.
This is an example of the speed difference when inputs are unaligned/aligned:
```
BenchmarkEncode100x20x10000-32 7058 172648 ns/op 6950.57 MB/s
BenchmarkEncode100x20x10000-32 8406 137911 ns/op 8701.24 MB/s
```
This is mostly the case when dealing with odd-sized shards.
To facilitate this the package provides an `AllocAligned(shards, each int) [][]byte`.
This will allocate a number of shards, each with the size `each`.
Each shard will then be aligned to a 64 byte boundary.
Each encoder also has a `AllocAligned(each int) [][]byte` as an extended interface which will return the same,
but with the shard count configured in the encoder.
It is not possible to re-aligned already allocated slices, for example when using `Split`.
When it is not possible to write to aligned shards, you should not copy to them.
# Progressive encoding
It is possible to encode individual shards using EncodeIdx:
@@ -345,6 +378,8 @@ There is no buffering or timeouts/retry specified. If you want to add that, you
For complete examples of a streaming encoder and decoder see the
[examples folder](https://github.com/klauspost/reedsolomon/tree/master/examples).
GF16 (more than 256 shards) is not supported by the streaming interface.
# Advanced Options
You can modify internal options which affects how jobs are split between and processed by goroutines.
@@ -358,8 +393,88 @@ Example of how to supply options:
enc, err := reedsolomon.New(10, 3, WithMaxGoroutines(25))
```
# Leopard Compatible GF16
When you encode more than 256 shards the library will switch to a [Leopard-RS](https://github.com/catid/leopard) implementation.
This allows encoding up to 65536 shards (data+parity) with the following limitations, similar to leopard:
* The original and recovery data must not exceed 65536 pieces.
* The shard size *must* each be a multiple of 64 bytes.
* Each buffer should have the same number of bytes.
* Even the last shard must be rounded up to the block size.
| | Regular | Leopard |
|-----------------|---------|---------|
| Encode | ✓ | ✓ |
| EncodeIdx | ✓ | - |
| Verify | ✓ | ✓ |
| Reconstruct | ✓ | ✓ |
| ReconstructData | ✓ | ✓ |
| ReconstructSome | ✓ | ✓ (+) |
| Update | ✓ | - |
| Split | ✓ | ✓ |
| Join | ✓ | ✓ |
* (+) Same as calling `ReconstructData`.
The Split/Join functions will help to split an input to the proper sizes.
Speed can be expected to be `O(N*log(N))`, compared to the `O(N*N)`.
Reconstruction matrix calculation is more time-consuming,
so be sure to include that as part of any benchmark you run.
For now SSSE3, AVX2 and AVX512 assembly are available on AMD64 platforms.
Leopard mode currently always runs as a single goroutine, since multiple
goroutines doesn't provide any worthwhile speedup.
## Leopard GF8
It is possible to replace the default reed-solomon encoder with a leopard compatible one.
This will typically be faster when dealing with more than 20-30 shards.
Note that the limitations listed above also applies to this mode.
See table below for speed with different number of shards.
To enable Leopard GF8 mode use `WithLeopardGF(true)`.
Benchmark Encoding and Reconstructing *1KB* shards with variable number of shards.
All implementation use inversion cache when available.
Speed is total shard size for each operation. Data shard throughput is speed/2.
AVX2 is used.
| Encoder | Shards | Encode | Recover All | Recover One |
|--------------|-------------|----------------|--------------|----------------|
| Cauchy | 4+4 | 23076.83 MB/s | 5444.02 MB/s | 10834.67 MB/s |
| Cauchy | 8+8 | 15206.87 MB/s | 4223.42 MB/s | 16181.62 MB/s |
| Cauchy | 16+16 | 7427.47 MB/s | 3305.84 MB/s | 22480.41 MB/s |
| Cauchy | 32+32 | 3785.64 MB/s | 2300.07 MB/s | 26181.31 MB/s |
| Cauchy | 64+64 | 1911.93 MB/s | 1368.51 MB/s | 27992.93 MB/s |
| Cauchy | 128+128 | 963.83 MB/s | 1327.56 MB/s | 32866.86 MB/s |
| Leopard GF8 | 4+4 | 17061.28 MB/s | 3099.06 MB/s | 4096.78 MB/s |
| Leopard GF8 | 8+8 | 10546.67 MB/s | 2925.92 MB/s | 3964.00 MB/s |
| Leopard GF8 | 16+16 | 10961.37 MB/s | 2328.40 MB/s | 3110.22 MB/s |
| Leopard GF8 | 32+32 | 7111.47 MB/s | 2374.61 MB/s | 3220.75 MB/s |
| Leopard GF8 | 64+64 | 7468.57 MB/s | 2055.41 MB/s | 3061.81 MB/s |
| Leopard GF8 | 128+128 | 5479.99 MB/s | 1953.21 MB/s | 2815.15 MB/s |
| Leopard GF16 | 256+256 | 6158.66 MB/s | 454.14 MB/s | 506.70 MB/s |
| Leopard GF16 | 512+512 | 4418.58 MB/s | 685.75 MB/s | 801.63 MB/s |
| Leopard GF16 | 1024+1024 | 4778.05 MB/s | 814.51 MB/s | 1080.19 MB/s |
| Leopard GF16 | 2048+2048 | 3417.05 MB/s | 911.64 MB/s | 1179.48 MB/s |
| Leopard GF16 | 4096+4096 | 3209.41 MB/s | 729.13 MB/s | 1135.06 MB/s |
| Leopard GF16 | 8192+8192 | 2034.11 MB/s | 604.52 MB/s | 842.13 MB/s |
| Leopard GF16 | 16384+16384 | 1525.88 MB/s | 486.74 MB/s | 750.01 MB/s |
| Leopard GF16 | 32768+32768 | 1138.67 MB/s | 482.81 MB/s | 712.73 MB/s |
"Traditional" encoding is faster until somewhere between 16 and 32 shards.
Leopard provides fast encoding in all cases, but shows a significant overhead for reconstruction.
Calculating the reconstruction matrix takes a significant amount of computation.
With bigger shards that will be smaller. Arguably, fewer shards typically also means bigger shards.
Due to the high shard count caching reconstruction matrices generally isn't feasible for Leopard.
# Performance
Performance depends mainly on the number of parity shards.
In rough terms, doubling the number of parity shards will double the encoding time.
@@ -368,27 +483,16 @@ For reference each shard is 1MB random data, and 16 CPU cores are used for encod
| Data | Parity | Go MB/s | SSSE3 MB/s | AVX2 MB/s |
|------|--------|---------|------------|-----------|
| 5 | 2 | 14287 | 66355 | 108755 |
| 8 | 8 | 5569 | 34298 | 70516 |
| 10 | 4 | 6766 | 48237 | 93875 |
| 50 | 20 | 1540 | 12130 | 22090 |
| 5 | 2 | 20,772 | 66,355 | 108,755 |
| 8 | 8 | 6,815 | 38,338 | 70,516 |
| 10 | 4 | 9,245 | 48,237 | 93,875 |
| 50 | 20 | 2,063 | 12,130 | 22,828 |
The throughput numbers here is the size of the encoded data and parity shards.
If `runtime.GOMAXPROCS()` is set to a value higher than 1,
the encoder will use multiple goroutines to perform the calculations in `Verify`, `Encode` and `Reconstruct`.
Example of performance scaling on AMD Ryzen 3950X - 16 physical cores, 32 logical cores, AVX 2.
The example uses 10 blocks with 1MB data each and 4 parity blocks.
| Threads | Speed |
|---------|------------|
| 1 | 9979 MB/s |
| 2 | 18870 MB/s |
| 4 | 33697 MB/s |
| 8 | 51531 MB/s |
| 16 | 59204 MB/s |
Benchmarking `Reconstruct()` followed by a `Verify()` (=`all`) versus just calling `ReconstructData()` (=`data`) gives the following result:
```
@@ -402,22 +506,10 @@ BenchmarkReconstruct50x20x1M-8 1364.35 4189.79 3.07x
BenchmarkReconstruct10x4x16M-8 1484.35 5779.53 3.89x
```
# Performance on AVX512
The package will use [GFNI](https://en.wikipedia.org/wiki/AVX-512#GFNI) instructions combined with AVX512 when these are available.
This further improves speed by up to 3x over AVX2 code paths.
The performance on AVX512 has been accelerated for Intel CPUs.
This gives speedups on a per-core basis typically up to 2x compared to
AVX2 as can be seen in the following table:
```
[...]
```
This speedup has been achieved by computing multiple parity blocks in parallel as opposed to one after the other.
In doing so it is possible to minimize the memory bandwidth required for loading all data shards.
At the same time the calculations are performed in the 512-bit wide ZMM registers and the surplus of ZMM
registers (32 in total) is used to keep more data around (most notably the matrix coefficients).
# Performance on ARM64 NEON
## ARM64 NEON
By exploiting NEON instructions the performance for ARM has been accelerated.
Below are the performance numbers for a single core on an EC2 m6g.16xlarge (Graviton2) instance (Amazon Linux 2):
@@ -432,7 +524,7 @@ BenchmarkGaloisXor1M-64 10000 100322 ns/op 10452.13 MB/s
# Performance on ppc64le
The performance for ppc64le has been accelerated.
This gives roughly a 10x performance improvement on this architecture as can been seen below:
This gives roughly a 10x performance improvement on this architecture as can be seen below:
```
benchmark old MB/s new MB/s speedup
@@ -442,9 +534,6 @@ BenchmarkGaloisXor128K-160 862.02 7905.00 9.17x
BenchmarkGaloisXor1M-160 784.60 6296.65 8.03x
```
# asm2plan9s
[asm2plan9s](https://github.com/fwessels/asm2plan9s) is used for assembling the AVX2 instructions into their BYTE/WORD/LONG equivalents.
# Links
* [Backblaze Open Sources Reed-Solomon Erasure Coding Source Code](https://www.backblaze.com/blog/reed-solomon/).
@@ -455,6 +544,7 @@ BenchmarkGaloisXor1M-160 784.60 6296.65 8.03x
* [reed-solomon-erasure](https://github.com/darrenldl/reed-solomon-erasure). Compatible Rust implementation.
* [go-erasure](https://github.com/somethingnew2-0/go-erasure). A similar library using cgo, slower in my tests.
* [Screaming Fast Galois Field Arithmetic](http://www.snia.org/sites/default/files2/SDC2013/presentations/NewThinking/EthanMiller_Screaming_Fast_Galois_Field%20Arithmetic_SIMD%20Instructions.pdf). Basis for SSE3 optimizations.
* [Leopard-RS](https://github.com/catid/leopard) C library used as basis for GF16 implementation.
# License
+42 -1
View File
File diff suppressed because one or more lines are too long
-339
View File
@@ -1,339 +0,0 @@
//go:build !noasm && !appengine && !gccgo
// +build !noasm,!appengine,!gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2019, Minio, Inc.
package reedsolomon
import (
"sync"
)
//go:noescape
func _galMulAVX512Parallel81(in, out [][]byte, matrix *[matrixSize81]byte, addTo bool)
//go:noescape
func _galMulAVX512Parallel82(in, out [][]byte, matrix *[matrixSize82]byte, addTo bool)
//go:noescape
func _galMulAVX512Parallel84(in, out [][]byte, matrix *[matrixSize84]byte, addTo bool)
const (
dimIn = 8 // Number of input rows processed simultaneously
dimOut81 = 1 // Number of output rows processed simultaneously for x1 routine
dimOut82 = 2 // Number of output rows processed simultaneously for x2 routine
dimOut84 = 4 // Number of output rows processed simultaneously for x4 routine
matrixSize81 = (16 + 16) * dimIn * dimOut81 // Dimension of slice of matrix coefficient passed into x1 routine
matrixSize82 = (16 + 16) * dimIn * dimOut82 // Dimension of slice of matrix coefficient passed into x2 routine
matrixSize84 = (16 + 16) * dimIn * dimOut84 // Dimension of slice of matrix coefficient passed into x4 routine
)
// Construct block of matrix coefficients for single output row in parallel
func setupMatrix81(matrixRows [][]byte, inputOffset, outputOffset int, matrix *[matrixSize81]byte) {
offset := 0
for c := inputOffset; c < inputOffset+dimIn; c++ {
for iRow := outputOffset; iRow < outputOffset+dimOut81; iRow++ {
if c < len(matrixRows[iRow]) {
coeff := matrixRows[iRow][c]
copy(matrix[offset*32:], mulTableLow[coeff][:])
copy(matrix[offset*32+16:], mulTableHigh[coeff][:])
} else {
// coefficients not used for this input shard (so null out)
v := matrix[offset*32 : offset*32+32]
for i := range v {
v[i] = 0
}
}
offset += dimIn
if offset >= dimIn*dimOut81 {
offset -= dimIn*dimOut81 - 1
}
}
}
}
// Construct block of matrix coefficients for 2 output rows in parallel
func setupMatrix82(matrixRows [][]byte, inputOffset, outputOffset int, matrix *[matrixSize82]byte) {
offset := 0
for c := inputOffset; c < inputOffset+dimIn; c++ {
for iRow := outputOffset; iRow < outputOffset+dimOut82; iRow++ {
if c < len(matrixRows[iRow]) {
coeff := matrixRows[iRow][c]
copy(matrix[offset*32:], mulTableLow[coeff][:])
copy(matrix[offset*32+16:], mulTableHigh[coeff][:])
} else {
// coefficients not used for this input shard (so null out)
v := matrix[offset*32 : offset*32+32]
for i := range v {
v[i] = 0
}
}
offset += dimIn
if offset >= dimIn*dimOut82 {
offset -= dimIn*dimOut82 - 1
}
}
}
}
// Construct block of matrix coefficients for 4 output rows in parallel
func setupMatrix84(matrixRows [][]byte, inputOffset, outputOffset int, matrix *[matrixSize84]byte) {
offset := 0
for c := inputOffset; c < inputOffset+dimIn; c++ {
for iRow := outputOffset; iRow < outputOffset+dimOut84; iRow++ {
if c < len(matrixRows[iRow]) {
coeff := matrixRows[iRow][c]
copy(matrix[offset*32:], mulTableLow[coeff][:])
copy(matrix[offset*32+16:], mulTableHigh[coeff][:])
} else {
// coefficients not used for this input shard (so null out)
v := matrix[offset*32 : offset*32+32]
for i := range v {
v[i] = 0
}
}
offset += dimIn
if offset >= dimIn*dimOut84 {
offset -= dimIn*dimOut84 - 1
}
}
}
}
// Invoke AVX512 routine for single output row in parallel
func galMulAVX512Parallel81(in, out [][]byte, matrixRows [][]byte, inputOffset, outputOffset, start, stop int, matrix81 *[matrixSize81]byte) {
done := stop - start
if done <= 0 || len(in) == 0 || len(out) == 0 {
return
}
inputEnd := inputOffset + dimIn
if inputEnd > len(in) {
inputEnd = len(in)
}
outputEnd := outputOffset + dimOut81
if outputEnd > len(out) {
outputEnd = len(out)
}
// We know the max size, alloc temp array.
var inTmp [dimIn][]byte
for i, v := range in[inputOffset:inputEnd] {
inTmp[i] = v[start:stop]
}
var outTmp [dimOut81][]byte
for i, v := range out[outputOffset:outputEnd] {
outTmp[i] = v[start:stop]
}
addTo := inputOffset != 0 // Except for the first input column, add to previous results
_galMulAVX512Parallel81(inTmp[:inputEnd-inputOffset], outTmp[:outputEnd-outputOffset], matrix81, addTo)
done = start + ((done >> 6) << 6)
if done < stop {
galMulAVX512LastInput(inputOffset, inputEnd, outputOffset, outputEnd, matrixRows, done, stop, out, in)
}
}
// Invoke AVX512 routine for 2 output rows in parallel
func galMulAVX512Parallel82(in, out [][]byte, matrixRows [][]byte, inputOffset, outputOffset, start, stop int, matrix82 *[matrixSize82]byte) {
done := stop - start
if done <= 0 || len(in) == 0 || len(out) == 0 {
return
}
inputEnd := inputOffset + dimIn
if inputEnd > len(in) {
inputEnd = len(in)
}
outputEnd := outputOffset + dimOut82
if outputEnd > len(out) {
outputEnd = len(out)
}
// We know the max size, alloc temp array.
var inTmp [dimIn][]byte
for i, v := range in[inputOffset:inputEnd] {
inTmp[i] = v[start:stop]
}
var outTmp [dimOut82][]byte
for i, v := range out[outputOffset:outputEnd] {
outTmp[i] = v[start:stop]
}
addTo := inputOffset != 0 // Except for the first input column, add to previous results
_galMulAVX512Parallel82(inTmp[:inputEnd-inputOffset], outTmp[:outputEnd-outputOffset], matrix82, addTo)
done = start + ((done >> 6) << 6)
if done < stop {
galMulAVX512LastInput(inputOffset, inputEnd, outputOffset, outputEnd, matrixRows, done, stop, out, in)
}
}
// Invoke AVX512 routine for 4 output rows in parallel
func galMulAVX512Parallel84(in, out [][]byte, matrixRows [][]byte, inputOffset, outputOffset, start, stop int, matrix84 *[matrixSize84]byte) {
done := stop - start
if done <= 0 || len(in) == 0 || len(out) == 0 {
return
}
inputEnd := inputOffset + dimIn
if inputEnd > len(in) {
inputEnd = len(in)
}
outputEnd := outputOffset + dimOut84
if outputEnd > len(out) {
outputEnd = len(out)
}
// We know the max size, alloc temp array.
var inTmp [dimIn][]byte
for i, v := range in[inputOffset:inputEnd] {
inTmp[i] = v[start:stop]
}
var outTmp [dimOut84][]byte
for i, v := range out[outputOffset:outputEnd] {
outTmp[i] = v[start:stop]
}
addTo := inputOffset != 0 // Except for the first input column, add to previous results
_galMulAVX512Parallel84(inTmp[:inputEnd-inputOffset], outTmp[:outputEnd-outputOffset], matrix84, addTo)
done = start + ((done >> 6) << 6)
if done < stop {
galMulAVX512LastInput(inputOffset, inputEnd, outputOffset, outputEnd, matrixRows, done, stop, out, in)
}
}
func galMulAVX512LastInput(inputOffset int, inputEnd int, outputOffset int, outputEnd int, matrixRows [][]byte, done int, stop int, out [][]byte, in [][]byte) {
for c := inputOffset; c < inputEnd; c++ {
for iRow := outputOffset; iRow < outputEnd; iRow++ {
if c < len(matrixRows[iRow]) {
mt := mulTable[matrixRows[iRow][c]][:256]
for i := done; i < stop; i++ {
if c == 0 { // only set value for first input column
out[iRow][i] = mt[in[c][i]]
} else { // and add for all others
out[iRow][i] ^= mt[in[c][i]]
}
}
}
}
}
}
// Perform the same as codeSomeShards, but taking advantage of
// AVX512 parallelism for up to 4x faster execution as compared to AVX2
func (r *reedSolomon) codeSomeShardsAvx512(matrixRows, inputs, outputs [][]byte, byteCount int) {
// Process using no goroutines
outputCount := len(outputs)
start, end := 0, r.o.perRound
if end > byteCount {
end = byteCount
}
for start < byteCount {
matrix84 := [matrixSize84]byte{}
matrix82 := [matrixSize82]byte{}
matrix81 := [matrixSize81]byte{}
outputRow := 0
// First process (multiple) batches of 4 output rows in parallel
if outputRow+dimOut84 <= outputCount {
for ; outputRow+dimOut84 <= outputCount; outputRow += dimOut84 {
for inputRow := 0; inputRow < len(inputs); inputRow += dimIn {
setupMatrix84(matrixRows, inputRow, outputRow, &matrix84)
galMulAVX512Parallel84(inputs, outputs, matrixRows, inputRow, outputRow, start, end, &matrix84)
}
}
}
// Then process a (single) batch of 2 output rows in parallel
if outputRow+dimOut82 <= outputCount {
for inputRow := 0; inputRow < len(inputs); inputRow += dimIn {
setupMatrix82(matrixRows, inputRow, outputRow, &matrix82)
galMulAVX512Parallel82(inputs, outputs, matrixRows, inputRow, outputRow, start, end, &matrix82)
}
outputRow += dimOut82
}
// Lastly, we may have a single output row left (for uneven parity)
if outputRow < outputCount {
for inputRow := 0; inputRow < len(inputs); inputRow += dimIn {
setupMatrix81(matrixRows, inputRow, outputRow, &matrix81)
galMulAVX512Parallel81(inputs, outputs, matrixRows, inputRow, outputRow, start, end, &matrix81)
}
}
start = end
end += r.o.perRound
if end > byteCount {
end = byteCount
}
}
}
// Perform the same as codeSomeShards, but taking advantage of
// AVX512 parallelism for up to 4x faster execution as compared to AVX2
func (r *reedSolomon) codeSomeShardsAvx512P(matrixRows, inputs, outputs [][]byte, byteCount int) {
outputCount := len(outputs)
var wg sync.WaitGroup
do := byteCount / r.o.maxGoroutines
if do < r.o.minSplitSize {
do = r.o.minSplitSize
}
// Make sizes divisible by 64
do = (do + 63) & (^63)
start := 0
for start < byteCount {
if start+do > byteCount {
do = byteCount - start
}
wg.Add(1)
go func(grStart, grStop int) {
start, stop := grStart, grStart+r.o.perRound
if stop > grStop {
stop = grStop
}
// Loop for each round.
matrix84 := [matrixSize84]byte{}
matrix82 := [matrixSize82]byte{}
matrix81 := [matrixSize81]byte{}
for start < grStop {
outputRow := 0
// First process (multiple) batches of 4 output rows in parallel
if outputRow+dimOut84 <= outputCount {
// 1K matrix buffer
for ; outputRow+dimOut84 <= outputCount; outputRow += dimOut84 {
for inputRow := 0; inputRow < len(inputs); inputRow += dimIn {
setupMatrix84(matrixRows, inputRow, outputRow, &matrix84)
galMulAVX512Parallel84(inputs, outputs, matrixRows, inputRow, outputRow, start, stop, &matrix84)
}
}
}
// Then process a (single) batch of 2 output rows in parallel
if outputRow+dimOut82 <= outputCount {
// 512B matrix buffer
for inputRow := 0; inputRow < len(inputs); inputRow += dimIn {
setupMatrix82(matrixRows, inputRow, outputRow, &matrix82)
galMulAVX512Parallel82(inputs, outputs, matrixRows, inputRow, outputRow, start, stop, &matrix82)
}
outputRow += dimOut82
}
// Lastly, we may have a single output row left (for uneven parity)
if outputRow < outputCount {
for inputRow := 0; inputRow < len(inputs); inputRow += dimIn {
setupMatrix81(matrixRows, inputRow, outputRow, &matrix81)
galMulAVX512Parallel81(inputs, outputs, matrixRows, inputRow, outputRow, start, stop, &matrix81)
}
}
start = stop
stop += r.o.perRound
if stop > grStop {
stop = grStop
}
}
wg.Done()
}(start, start+do)
start += do
}
wg.Wait()
}
-402
View File
@@ -1,402 +0,0 @@
//+build !noasm
//+build !appengine
//+build !gccgo
// Copyright 2015, Klaus Post, see LICENSE for details.
// Copyright 2019, Minio, Inc.
#define LOAD(OFFSET) \
MOVQ OFFSET(SI), BX \
VMOVDQU64 (BX)(R11*1), Z0 \
VPSRLQ $4, Z0, Z1 \ // high input
VPANDQ Z2, Z0, Z0 \ // low input
VPANDQ Z2, Z1, Z1 // high input
#define GALOIS_MUL(MUL_LO, MUL_HI, LO, HI, OUT) \
VPSHUFB Z0, MUL_LO, LO \ // mul low part
VPSHUFB Z1, MUL_HI, HI \ // mul high part
VPTERNLOGD $0x96, LO, HI, OUT
#define GALOIS(C1, C2, IN, LO, HI, OUT) \
VSHUFI64X2 $C1, IN, IN, LO \
VSHUFI64X2 $C2, IN, IN, HI \
GALOIS_MUL(LO, HI, LO, HI, OUT)
//
// Process single output row from a total of 8 input rows
//
// func _galMulAVX512Parallel81(in, out [][]byte, matrix *[matrixSize81]byte, addTo bool)
TEXT ·_galMulAVX512Parallel81(SB), 7, $0
MOVQ in+0(FP), SI
MOVQ 8(SI), R9 // R9: len(in)
SHRQ $6, R9 // len(in) / 64
TESTQ R9, R9
JZ done_avx512_parallel81
MOVQ matrix+48(FP), SI
VMOVDQU64 0x000(SI), Z16
VMOVDQU64 0x040(SI), Z17
VMOVDQU64 0x080(SI), Z18
VMOVDQU64 0x0c0(SI), Z19
// Initialize multiplication constants
VSHUFI64X2 $0x55, Z16, Z16, Z20
VSHUFI64X2 $0xaa, Z16, Z16, Z24
VSHUFI64X2 $0xff, Z16, Z16, Z28
VSHUFI64X2 $0x00, Z16, Z16, Z16
VSHUFI64X2 $0x55, Z17, Z17, Z21
VSHUFI64X2 $0xaa, Z17, Z17, Z25
VSHUFI64X2 $0xff, Z17, Z17, Z29
VSHUFI64X2 $0x00, Z17, Z17, Z17
VSHUFI64X2 $0x55, Z18, Z18, Z22
VSHUFI64X2 $0xaa, Z18, Z18, Z26
VSHUFI64X2 $0xff, Z18, Z18, Z30
VSHUFI64X2 $0x00, Z18, Z18, Z18
VSHUFI64X2 $0x55, Z19, Z19, Z23
VSHUFI64X2 $0xaa, Z19, Z19, Z27
VSHUFI64X2 $0xff, Z19, Z19, Z31
VSHUFI64X2 $0x00, Z19, Z19, Z19
MOVQ $15, BX
VPBROADCASTB BX, Z2
MOVB addTo+56(FP), AX
IMULQ $-0x1, AX
KMOVQ AX, K1
MOVQ in+0(FP), SI // SI: &in
MOVQ in_len+8(FP), AX // number of inputs
XORQ R11, R11
MOVQ out+24(FP), DX
MOVQ (DX), DX // DX: &out[0][0]
loopback_avx512_parallel81:
VMOVDQU64.Z (DX), K1, Z4
LOAD(0x00) // &in[0][0]
GALOIS_MUL(Z16, Z20, Z14, Z15, Z4)
CMPQ AX, $1
JE skip_avx512_parallel81
LOAD(0x18) // &in[1][0]
GALOIS_MUL(Z24, Z28, Z14, Z15, Z4)
CMPQ AX, $2
JE skip_avx512_parallel81
LOAD(0x30) // &in[2][0]
GALOIS_MUL(Z17, Z21, Z14, Z15, Z4)
CMPQ AX, $3
JE skip_avx512_parallel81
LOAD(0x48) // &in[3][0]
GALOIS_MUL(Z25, Z29, Z14, Z15, Z4)
CMPQ AX, $4
JE skip_avx512_parallel81
LOAD(0x60) // &in[4][0]
GALOIS_MUL(Z18, Z22, Z14, Z15, Z4)
CMPQ AX, $5
JE skip_avx512_parallel81
LOAD(0x78) // &in[5][0]
GALOIS_MUL(Z26, Z30, Z14, Z15, Z4)
CMPQ AX, $6
JE skip_avx512_parallel81
LOAD(0x90) // &in[6][0]
GALOIS_MUL(Z19, Z23, Z14, Z15, Z4)
CMPQ AX, $7
JE skip_avx512_parallel81
LOAD(0xa8) // &in[7][0]
GALOIS_MUL(Z27, Z31, Z14, Z15, Z4)
skip_avx512_parallel81:
VMOVDQU64 Z4, (DX)
ADDQ $64, R11 // in4+=64
ADDQ $64, DX // out+=64
SUBQ $1, R9
JNZ loopback_avx512_parallel81
done_avx512_parallel81:
VZEROUPPER
RET
//
// Process 2 output rows in parallel from a total of 8 input rows
//
// func _galMulAVX512Parallel82(in, out [][]byte, matrix *[matrixSize82]byte, addTo bool)
TEXT ·_galMulAVX512Parallel82(SB), 7, $0
MOVQ in+0(FP), SI
MOVQ 8(SI), R9 // R9: len(in)
SHRQ $6, R9 // len(in) / 64
TESTQ R9, R9
JZ done_avx512_parallel82
MOVQ matrix+48(FP), SI
VMOVDQU64 0x000(SI), Z16
VMOVDQU64 0x040(SI), Z17
VMOVDQU64 0x080(SI), Z18
VMOVDQU64 0x0c0(SI), Z19
VMOVDQU64 0x100(SI), Z20
VMOVDQU64 0x140(SI), Z21
VMOVDQU64 0x180(SI), Z22
VMOVDQU64 0x1c0(SI), Z23
// Initialize multiplication constants
VSHUFI64X2 $0x55, Z16, Z16, Z24
VSHUFI64X2 $0xaa, Z16, Z16, Z25
VSHUFI64X2 $0xff, Z16, Z16, Z26
VSHUFI64X2 $0x00, Z16, Z16, Z16
VSHUFI64X2 $0x55, Z20, Z20, Z27
VSHUFI64X2 $0xaa, Z20, Z20, Z28
VSHUFI64X2 $0xff, Z20, Z20, Z29
VSHUFI64X2 $0x00, Z20, Z20, Z20
VSHUFI64X2 $0x55, Z17, Z17, Z30
VSHUFI64X2 $0xaa, Z17, Z17, Z31
VSHUFI64X2 $0xff, Z17, Z17, Z11
VSHUFI64X2 $0x00, Z17, Z17, Z17
VSHUFI64X2 $0x55, Z21, Z21, Z8
VSHUFI64X2 $0xaa, Z21, Z21, Z9
VSHUFI64X2 $0xff, Z21, Z21, Z10
VSHUFI64X2 $0x00, Z21, Z21, Z21
MOVQ $15, BX
VPBROADCASTB BX, Z2
MOVB addTo+56(FP), AX
IMULQ $-0x1, AX
KMOVQ AX, K1
MOVQ in+0(FP), SI // SI: &in
MOVQ in_len+8(FP), AX // number of inputs
XORQ R11, R11
MOVQ out+24(FP), DX
MOVQ 24(DX), CX // CX: &out[1][0]
MOVQ (DX), DX // DX: &out[0][0]
loopback_avx512_parallel82:
VMOVDQU64.Z (DX), K1, Z4
VMOVDQU64.Z (CX), K1, Z5
LOAD(0x00) // &in[0][0]
GALOIS_MUL(Z16, Z24, Z14, Z15, Z4)
GALOIS_MUL(Z20, Z27, Z12, Z13, Z5)
CMPQ AX, $1
JE skip_avx512_parallel82
LOAD(0x18) // &in[1][0]
GALOIS_MUL(Z25, Z26, Z14, Z15, Z4)
GALOIS_MUL(Z28, Z29, Z12, Z13, Z5)
CMPQ AX, $2
JE skip_avx512_parallel82
LOAD(0x30) // &in[2][0]
GALOIS_MUL(Z17, Z30, Z14, Z15, Z4)
GALOIS_MUL(Z21, Z8, Z12, Z13, Z5)
CMPQ AX, $3
JE skip_avx512_parallel82
LOAD(0x48) // &in[3][0]
GALOIS_MUL(Z31, Z11, Z14, Z15, Z4)
GALOIS_MUL(Z9, Z10, Z12, Z13, Z5)
CMPQ AX, $4
JE skip_avx512_parallel82
LOAD(0x60) // &in[4][0]
GALOIS(0x00, 0x55, Z18, Z14, Z15, Z4)
GALOIS(0x00, 0x55, Z22, Z12, Z13, Z5)
CMPQ AX, $5
JE skip_avx512_parallel82
LOAD(0x78) // &in[5][0]
GALOIS(0xaa, 0xff, Z18, Z14, Z15, Z4)
GALOIS(0xaa, 0xff, Z22, Z12, Z13, Z5)
CMPQ AX, $6
JE skip_avx512_parallel82
LOAD(0x90) // &in[6][0]
GALOIS(0x00, 0x55, Z19, Z14, Z15, Z4)
GALOIS(0x00, 0x55, Z23, Z12, Z13, Z5)
CMPQ AX, $7
JE skip_avx512_parallel82
LOAD(0xa8) // &in[7][0]
GALOIS(0xaa, 0xff, Z19, Z14, Z15, Z4)
GALOIS(0xaa, 0xff, Z23, Z12, Z13, Z5)
skip_avx512_parallel82:
VMOVDQU64 Z4, (DX)
VMOVDQU64 Z5, (CX)
ADDQ $64, R11 // in4+=64
ADDQ $64, DX // out+=64
ADDQ $64, CX // out2+=64
SUBQ $1, R9
JNZ loopback_avx512_parallel82
done_avx512_parallel82:
VZEROUPPER
RET
//
// Process 4 output rows in parallel from a total of 8 input rows
//
// func _galMulAVX512Parallel84(in, out [][]byte, matrix *[matrixSize84]byte, addTo bool)
TEXT ·_galMulAVX512Parallel84(SB), 7, $0
MOVQ in+0(FP), SI
MOVQ 8(SI), R9 // R9: len(in)
SHRQ $6, R9 // len(in) / 64
TESTQ R9, R9
JZ done_avx512_parallel84
MOVQ matrix+48(FP), SI
VMOVDQU64 0x000(SI), Z16
VMOVDQU64 0x040(SI), Z17
VMOVDQU64 0x080(SI), Z18
VMOVDQU64 0x0c0(SI), Z19
VMOVDQU64 0x100(SI), Z20
VMOVDQU64 0x140(SI), Z21
VMOVDQU64 0x180(SI), Z22
VMOVDQU64 0x1c0(SI), Z23
VMOVDQU64 0x200(SI), Z24
VMOVDQU64 0x240(SI), Z25
VMOVDQU64 0x280(SI), Z26
VMOVDQU64 0x2c0(SI), Z27
VMOVDQU64 0x300(SI), Z28
VMOVDQU64 0x340(SI), Z29
VMOVDQU64 0x380(SI), Z30
VMOVDQU64 0x3c0(SI), Z31
MOVQ $15, BX
VPBROADCASTB BX, Z2
MOVB addTo+56(FP), AX
IMULQ $-0x1, AX
KMOVQ AX, K1
MOVQ in+0(FP), SI // SI: &in
MOVQ in_len+8(FP), AX // number of inputs
XORQ R11, R11
MOVQ out+24(FP), DX
MOVQ 24(DX), CX // CX: &out[1][0]
MOVQ 48(DX), R10 // R10: &out[2][0]
MOVQ 72(DX), R12 // R12: &out[3][0]
MOVQ (DX), DX // DX: &out[0][0]
loopback_avx512_parallel84:
VMOVDQU64.Z (DX), K1, Z4
VMOVDQU64.Z (CX), K1, Z5
VMOVDQU64.Z (R10), K1, Z6
VMOVDQU64.Z (R12), K1, Z7
LOAD(0x00) // &in[0][0]
GALOIS(0x00, 0x55, Z16, Z14, Z15, Z4)
GALOIS(0x00, 0x55, Z20, Z12, Z13, Z5)
GALOIS(0x00, 0x55, Z24, Z10, Z11, Z6)
GALOIS(0x00, 0x55, Z28, Z8, Z9, Z7)
CMPQ AX, $1
JE skip_avx512_parallel84
LOAD(0x18) // &in[1][0]
GALOIS(0xaa, 0xff, Z16, Z14, Z15, Z4)
GALOIS(0xaa, 0xff, Z20, Z12, Z13, Z5)
GALOIS(0xaa, 0xff, Z24, Z10, Z11, Z6)
GALOIS(0xaa, 0xff, Z28, Z8, Z9, Z7)
CMPQ AX, $2
JE skip_avx512_parallel84
LOAD(0x30) // &in[2][0]
GALOIS(0x00, 0x55, Z17, Z14, Z15, Z4)
GALOIS(0x00, 0x55, Z21, Z12, Z13, Z5)
GALOIS(0x00, 0x55, Z25, Z10, Z11, Z6)
GALOIS(0x00, 0x55, Z29, Z8, Z9, Z7)
CMPQ AX, $3
JE skip_avx512_parallel84
LOAD(0x48) // &in[3][0]
GALOIS(0xaa, 0xff, Z17, Z14, Z15, Z4)
GALOIS(0xaa, 0xff, Z21, Z12, Z13, Z5)
GALOIS(0xaa, 0xff, Z25, Z10, Z11, Z6)
GALOIS(0xaa, 0xff, Z29, Z8, Z9, Z7)
CMPQ AX, $4
JE skip_avx512_parallel84
LOAD(0x60) // &in[4][0]
GALOIS(0x00, 0x55, Z18, Z14, Z15, Z4)
GALOIS(0x00, 0x55, Z22, Z12, Z13, Z5)
GALOIS(0x00, 0x55, Z26, Z10, Z11, Z6)
GALOIS(0x00, 0x55, Z30, Z8, Z9, Z7)
CMPQ AX, $5
JE skip_avx512_parallel84
LOAD(0x78) // &in[5][0]
GALOIS(0xaa, 0xff, Z18, Z14, Z15, Z4)
GALOIS(0xaa, 0xff, Z22, Z12, Z13, Z5)
GALOIS(0xaa, 0xff, Z26, Z10, Z11, Z6)
GALOIS(0xaa, 0xff, Z30, Z8, Z9, Z7)
CMPQ AX, $6
JE skip_avx512_parallel84
LOAD(0x90) // &in[6][0]
GALOIS(0x00, 0x55, Z19, Z14, Z15, Z4)
GALOIS(0x00, 0x55, Z23, Z12, Z13, Z5)
GALOIS(0x00, 0x55, Z27, Z10, Z11, Z6)
GALOIS(0x00, 0x55, Z31, Z8, Z9, Z7)
CMPQ AX, $7
JE skip_avx512_parallel84
LOAD(0xa8) // &in[7][0]
GALOIS(0xaa, 0xff, Z19, Z14, Z15, Z4)
GALOIS(0xaa, 0xff, Z23, Z12, Z13, Z5)
GALOIS(0xaa, 0xff, Z27, Z10, Z11, Z6)
GALOIS(0xaa, 0xff, Z31, Z8, Z9, Z7)
skip_avx512_parallel84:
VMOVDQU64 Z4, (DX)
VMOVDQU64 Z5, (CX)
VMOVDQU64 Z6, (R10)
VMOVDQU64 Z7, (R12)
ADDQ $64, R11 // in4+=64
ADDQ $64, DX // out+=64
ADDQ $64, CX // out2+=64
ADDQ $64, R10 // out3+=64
ADDQ $64, R12 // out4+=64
SUBQ $1, R9
JNZ loopback_avx512_parallel84
done_avx512_parallel84:
VZEROUPPER
RET
+455 -4
View File
@@ -29,6 +29,9 @@ func galMulAVX2_64(low, high, in, out []byte)
//go:noescape
func sSE2XorSlice_64(in, out []byte)
//go:noescape
func avx2XorSlice_64(in, out []byte)
// This is what the assembler routines do in blocks of 16 bytes:
/*
func galMulSSSE3(low, high, in, out []byte) {
@@ -121,10 +124,17 @@ func galMulSliceXor(c byte, in, out []byte, o *options) {
func sliceXor(in, out []byte, o *options) {
if o.useSSE2 {
if len(in) >= bigSwitchover {
sSE2XorSlice_64(in, out)
done := (len(in) >> 6) << 6
in = in[done:]
out = out[done:]
if o.useAVX2 {
avx2XorSlice_64(in, out)
done := (len(in) >> 6) << 6
in = in[done:]
out = out[done:]
} else {
sSE2XorSlice_64(in, out)
done := (len(in) >> 6) << 6
in = in[done:]
out = out[done:]
}
}
if len(in) >= 16 {
sSE2XorSlice(in, out)
@@ -132,9 +142,450 @@ func sliceXor(in, out []byte, o *options) {
in = in[done:]
out = out[done:]
}
} else {
sliceXorGo(in, out, o)
return
}
out = out[:len(in)]
for i := range in {
out[i] ^= in[i]
}
}
// 4-way butterfly
func ifftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
if len(work[0]) == 0 {
return
}
t01 := &multiply256LUT[log_m01]
t23 := &multiply256LUT[log_m23]
t02 := &multiply256LUT[log_m02]
if o.useAVX512 {
if log_m01 == modulus {
if log_m23 == modulus {
if log_m02 == modulus {
ifftDIT4_avx512_7(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx512_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus {
ifftDIT4_avx512_5(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx512_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m23 == modulus {
if log_m02 == modulus {
ifftDIT4_avx512_6(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx512_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus {
ifftDIT4_avx512_4(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx512_0(work, dist*24, t01, t23, t02)
}
}
}
return
} else if o.useAVX2 {
if log_m01 == modulus {
if log_m23 == modulus {
if log_m02 == modulus {
ifftDIT4_avx2_7(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx2_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus {
ifftDIT4_avx2_5(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx2_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m23 == modulus {
if log_m02 == modulus {
ifftDIT4_avx2_6(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx2_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus {
ifftDIT4_avx2_4(work, dist*24, t01, t23, t02)
} else {
ifftDIT4_avx2_0(work, dist*24, t01, t23, t02)
}
}
}
return
}
ifftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func ifftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
if len(work[0]) == 0 {
return
}
if false && o.useGFNI {
// Note that these currently require that length is multiple of 64.
t01 := gf2p811dMulMatrices[log_m01]
t23 := gf2p811dMulMatrices[log_m23]
t02 := gf2p811dMulMatrices[log_m02]
if log_m01 == modulus8 {
if log_m23 == modulus8 {
if log_m02 == modulus8 {
ifftDIT48_gfni_7(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_gfni_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus8 {
ifftDIT48_gfni_5(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_gfni_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m23 == modulus8 {
if log_m02 == modulus8 {
ifftDIT48_gfni_6(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_gfni_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus8 {
ifftDIT48_gfni_4(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_gfni_0(work, dist*24, t01, t23, t02)
}
}
}
return
}
if o.useAVX2 {
// Note that these currently require that length is multiple of 64.
t01 := &multiply256LUT8[log_m01]
t23 := &multiply256LUT8[log_m23]
t02 := &multiply256LUT8[log_m02]
if log_m01 == modulus8 {
if log_m23 == modulus8 {
if log_m02 == modulus8 {
ifftDIT48_avx2_7(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_avx2_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus8 {
ifftDIT48_avx2_5(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_avx2_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m23 == modulus8 {
if log_m02 == modulus8 {
ifftDIT48_avx2_6(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_avx2_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m02 == modulus8 {
ifftDIT48_avx2_4(work, dist*24, t01, t23, t02)
} else {
ifftDIT48_avx2_0(work, dist*24, t01, t23, t02)
}
}
}
return
}
ifftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
func fftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
if len(work[0]) == 0 {
return
}
t01 := &multiply256LUT[log_m01]
t23 := &multiply256LUT[log_m23]
t02 := &multiply256LUT[log_m02]
if o.useAVX512 {
if log_m02 == modulus {
if log_m01 == modulus {
if log_m23 == modulus {
fftDIT4_avx512_7(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx512_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus {
fftDIT4_avx512_5(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx512_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m01 == modulus {
if log_m23 == modulus {
fftDIT4_avx512_6(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx512_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus {
fftDIT4_avx512_4(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx512_0(work, dist*24, t01, t23, t02)
}
}
}
return
} else if o.useAVX2 {
if log_m02 == modulus {
if log_m01 == modulus {
if log_m23 == modulus {
fftDIT4_avx2_7(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx2_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus {
fftDIT4_avx2_5(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx2_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m01 == modulus {
if log_m23 == modulus {
fftDIT4_avx2_6(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx2_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus {
fftDIT4_avx2_4(work, dist*24, t01, t23, t02)
} else {
fftDIT4_avx2_0(work, dist*24, t01, t23, t02)
}
}
}
return
}
fftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func fftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
if len(work[0]) == 0 {
return
}
if false && o.useGFNI {
t01 := gf2p811dMulMatrices[log_m01]
t23 := gf2p811dMulMatrices[log_m23]
t02 := gf2p811dMulMatrices[log_m02]
// Note that these currently require that length is multiple of 64.
if log_m02 == modulus8 {
if log_m01 == modulus8 {
if log_m23 == modulus8 {
fftDIT48_gfni_7(work, dist*24, t01, t23, t02)
} else {
fftDIT48_gfni_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus8 {
fftDIT48_gfni_5(work, dist*24, t01, t23, t02)
} else {
fftDIT48_gfni_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m01 == modulus8 {
if log_m23 == modulus8 {
fftDIT48_gfni_6(work, dist*24, t01, t23, t02)
} else {
fftDIT48_gfni_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus8 {
fftDIT48_gfni_4(work, dist*24, t01, t23, t02)
} else {
fftDIT48_gfni_0(work, dist*24, t01, t23, t02)
}
}
}
return
}
if o.useAVX2 {
t01 := &multiply256LUT8[log_m01]
t23 := &multiply256LUT8[log_m23]
t02 := &multiply256LUT8[log_m02]
// Note that these currently require that length is multiple of 64.
if log_m02 == modulus8 {
if log_m01 == modulus8 {
if log_m23 == modulus8 {
fftDIT48_avx2_7(work, dist*24, t01, t23, t02)
} else {
fftDIT48_avx2_3(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus8 {
fftDIT48_avx2_5(work, dist*24, t01, t23, t02)
} else {
fftDIT48_avx2_1(work, dist*24, t01, t23, t02)
}
}
} else {
if log_m01 == modulus8 {
if log_m23 == modulus8 {
fftDIT48_avx2_6(work, dist*24, t01, t23, t02)
} else {
fftDIT48_avx2_2(work, dist*24, t01, t23, t02)
}
} else {
if log_m23 == modulus8 {
fftDIT48_avx2_4(work, dist*24, t01, t23, t02)
} else {
fftDIT48_avx2_0(work, dist*24, t01, t23, t02)
}
}
}
return
}
fftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
// 2-way butterfly forward
func fftDIT2(x, y []byte, log_m ffe, o *options) {
if len(x) == 0 {
return
}
if o.useAVX2 {
tmp := &multiply256LUT[log_m]
fftDIT2_avx2(x, y, tmp)
} else if o.useSSSE3 {
tmp := &multiply256LUT[log_m]
fftDIT2_ssse3(x, y, tmp)
} else {
// Reference version:
refMulAdd(x, y, log_m)
sliceXor(x, y, o)
}
}
// 2-way butterfly forward
func fftDIT28(x, y []byte, log_m ffe8, o *options) {
if len(x) == 0 {
return
}
if o.useAVX2 {
fftDIT28_avx2(x, y, &multiply256LUT8[log_m])
if len(x)&63 == 0 {
return
}
done := (len(y) >> 6) << 6
y = y[done:]
x = x[done:]
}
mulAdd8(x, y, log_m, o)
sliceXor(x, y, o)
}
// 2-way butterfly inverse
func ifftDIT28(x, y []byte, log_m ffe8, o *options) {
if len(x) == 0 {
return
}
if o.useAVX2 {
ifftDIT28_avx2(x, y, &multiply256LUT8[log_m])
if len(x)&63 == 0 {
return
}
done := (len(y) >> 6) << 6
y = y[done:]
x = x[done:]
}
sliceXor(x, y, o)
mulAdd8(x, y, log_m, o)
}
func mulAdd8(x, y []byte, log_m ffe8, o *options) {
if o.useAVX2 {
t := &multiply256LUT8[log_m]
galMulAVX2Xor_64(t[:16], t[16:32], y, x)
done := (len(y) >> 6) << 6
y = y[done:]
x = x[done:]
} else if o.useSSSE3 {
t := &multiply256LUT8[log_m]
galMulSSSE3Xor(t[:16], t[16:32], y, x)
done := (len(y) >> 4) << 4
y = y[done:]
x = x[done:]
}
refMulAdd8(x, y, log_m)
}
// 2-way butterfly
func ifftDIT2(x, y []byte, log_m ffe, o *options) {
if len(x) == 0 {
return
}
if o.useAVX2 {
tmp := &multiply256LUT[log_m]
ifftDIT2_avx2(x, y, tmp)
} else if o.useSSSE3 {
tmp := &multiply256LUT[log_m]
ifftDIT2_ssse3(x, y, tmp)
} else {
// Reference version:
sliceXor(x, y, o)
refMulAdd(x, y, log_m)
}
}
func mulgf16(x, y []byte, log_m ffe, o *options) {
if len(x) == 0 {
return
}
if o.useAVX2 {
tmp := &multiply256LUT[log_m]
mulgf16_avx2(x, y, tmp)
} else if o.useSSSE3 {
tmp := &multiply256LUT[log_m]
mulgf16_ssse3(x, y, tmp)
} else {
refMul(x, y, log_m)
}
}
func mulgf8(out, in []byte, log_m ffe8, o *options) {
if o.useAVX2 {
t := &multiply256LUT8[log_m]
galMulAVX2_64(t[:16], t[16:32], in, out)
done := (len(in) >> 6) << 6
in = in[done:]
out = out[done:]
} else if o.useSSSE3 {
t := &multiply256LUT8[log_m]
galMulSSSE3(t[:16], t[16:32], in, out)
done := (len(in) >> 4) << 4
in = in[done:]
out = out[done:]
}
out = out[:len(in)]
mt := mul8LUTs[log_m].Value[:]
for i := range in {
out[i] = byte(mt[in[i]])
}
}
+45 -21
View File
@@ -239,17 +239,15 @@ done_xor_sse2:
// func galMulAVX2Xor_64(low, high, in, out []byte)
TEXT ·galMulAVX2Xor_64(SB), 7, $0
MOVQ low+0(FP), SI // SI: &low
MOVQ high+24(FP), DX // DX: &high
MOVQ $15, BX // BX: low mask
MOVQ BX, X5
MOVOU (SI), X6 // X6: low
MOVOU (DX), X7 // X7: high
MOVQ in_len+56(FP), R9 // R9: len(in)
MOVQ low+0(FP), SI // SI: &low
MOVQ high+24(FP), DX // DX: &high
MOVQ $15, BX // BX: low mask
MOVQ BX, X5
MOVQ in_len+56(FP), R9 // R9: len(in)
VINSERTI128 $1, X6, Y6, Y6 // low
VINSERTI128 $1, X7, Y7, Y7 // high
VPBROADCASTB X5, Y8 // Y8: lomask (unpacked)
VBROADCASTI128 (SI), Y6 // low table
VBROADCASTI128 (DX), Y7 // high high table
VPBROADCASTB X5, Y8 // Y8: lomask (unpacked)
SHRQ $6, R9 // len(in) / 64
MOVQ out+72(FP), DX // DX: &out
@@ -290,17 +288,14 @@ done_xor_avx2_64:
// func galMulAVX2_64(low, high, in, out []byte)
TEXT ·galMulAVX2_64(SB), 7, $0
MOVQ low+0(FP), SI // SI: &low
MOVQ high+24(FP), DX // DX: &high
MOVQ $15, BX // BX: low mask
MOVQ BX, X5
MOVOU (SI), X6 // X6: low
MOVOU (DX), X7 // X7: high
MOVQ in_len+56(FP), R9 // R9: len(in)
VINSERTI128 $1, X6, Y6, Y6 // low
VINSERTI128 $1, X7, Y7, Y7 // high
VPBROADCASTB X5, Y8 // Y8: lomask (unpacked)
MOVQ low+0(FP), SI // SI: &low
MOVQ high+24(FP), DX // DX: &high
MOVQ $15, BX // BX: low mask
MOVQ BX, X5
MOVQ in_len+56(FP), R9 // R9: len(in)
VBROADCASTI128 (SI), Y6 // low table
VBROADCASTI128 (DX), Y7 // high high table
VPBROADCASTB X5, Y8 // Y8: lomask (unpacked)
SHRQ $6, R9 // len(in) / 64
MOVQ out+72(FP), DX // DX: &out
@@ -368,3 +363,32 @@ loopback_xor_sse2_64:
done_xor_sse2_64:
RET
// func avx2XorSlice_64(in, out []byte)
TEXT ·avx2XorSlice_64(SB), 7, $0
MOVQ in+0(FP), SI // SI: &in
MOVQ in_len+8(FP), R9 // R9: len(in)
MOVQ out+24(FP), DX // DX: &out
SHRQ $6, R9 // len(in) / 64
CMPQ R9, $0
JEQ done_xor_avx2_64
loopback_xor_avx2_64:
VMOVDQU (SI), Y0
VMOVDQU 32(SI), Y2
VMOVDQU (DX), Y1
VMOVDQU 32(DX), Y3
VPXOR Y0, Y1, Y1
VPXOR Y2, Y3, Y3
VMOVDQU Y1, (DX)
VMOVDQU Y3, 32(DX)
ADDQ $64, SI // in+=64
ADDQ $64, DX // out+=64
SUBQ $1, R9
JNZ loopback_xor_avx2_64
VZEROUPPER
done_xor_avx2_64:
RET
+80
View File
@@ -64,3 +64,83 @@ func sliceXor(in, out []byte, o *options) {
}
}
}
// 4-way butterfly
func ifftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
ifftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func ifftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
ifftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func fftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
fftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func fftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
fftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
// 2-way butterfly forward
func fftDIT2(x, y []byte, log_m ffe, o *options) {
// Reference version:
refMulAdd(x, y, log_m)
// 64 byte aligned, always full.
galXorNEON(x, y)
}
// 2-way butterfly forward
func fftDIT28(x, y []byte, log_m ffe8, o *options) {
// Reference version:
mulAdd8(x, y, log_m, o)
sliceXor(x, y, o)
}
// 2-way butterfly
func ifftDIT2(x, y []byte, log_m ffe, o *options) {
// 64 byte aligned, always full.
galXorNEON(x, y)
// Reference version:
refMulAdd(x, y, log_m)
}
// 2-way butterfly inverse
func ifftDIT28(x, y []byte, log_m ffe8, o *options) {
// Reference version:
sliceXor(x, y, o)
mulAdd8(x, y, log_m, o)
}
func mulgf16(x, y []byte, log_m ffe, o *options) {
refMul(x, y, log_m)
}
func mulAdd8(out, in []byte, log_m ffe8, o *options) {
t := &multiply256LUT8[log_m]
galMulXorNEON(t[:16], t[16:32], in, out)
done := (len(in) >> 5) << 5
in = in[done:]
if len(in) > 0 {
out = out[done:]
refMulAdd8(in, out, log_m)
}
}
func mulgf8(out, in []byte, log_m ffe8, o *options) {
var done int
t := &multiply256LUT8[log_m]
galMulNEON(t[:16], t[16:32], in, out)
done = (len(in) >> 5) << 5
remain := len(in) - done
if remain > 0 {
mt := mul8LUTs[log_m].Value[:]
for i := done; i < len(in); i++ {
out[i] ^= byte(mt[in[i]])
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+10 -2
View File
@@ -10,9 +10,17 @@ const avxSizeMask = 0
const avx2CodeGen = false
func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
panic("avx2 codegen not available")
panic("codegen not available")
}
func galMulSlicesAvx2Xor(matrix []byte, in, out [][]byte, start, stop int) int {
panic("avx2 codegen not available")
panic("codegen not available")
}
func galMulSlicesGFNI(matrix []uint64, in, out [][]byte, start, stop int) int {
panic("codegen not available")
}
func galMulSlicesGFNIXor(matrix []uint64, in, out [][]byte, start, stop int) int {
panic("codegen not available")
}
+676
View File
@@ -692,3 +692,679 @@ func galMulSlicesAvx2Xor(matrix []byte, in, out [][]byte, start, stop int) int {
}
panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
}
func galMulSlicesGFNI(matrix []uint64, in, out [][]byte, start, stop int) int {
n := (stop - start) & avxSizeMask
switch len(in) {
case 1:
switch len(out) {
case 1:
mulGFNI_1x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_1x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_1x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_1x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_1x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_1x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_1x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_1x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_1x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_1x10_64(matrix, in, out, start, n)
return n
}
case 2:
switch len(out) {
case 1:
mulGFNI_2x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_2x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_2x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_2x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_2x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_2x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_2x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_2x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_2x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_2x10_64(matrix, in, out, start, n)
return n
}
case 3:
switch len(out) {
case 1:
mulGFNI_3x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_3x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_3x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_3x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_3x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_3x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_3x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_3x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_3x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_3x10_64(matrix, in, out, start, n)
return n
}
case 4:
switch len(out) {
case 1:
mulGFNI_4x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_4x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_4x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_4x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_4x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_4x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_4x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_4x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_4x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_4x10_64(matrix, in, out, start, n)
return n
}
case 5:
switch len(out) {
case 1:
mulGFNI_5x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_5x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_5x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_5x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_5x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_5x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_5x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_5x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_5x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_5x10_64(matrix, in, out, start, n)
return n
}
case 6:
switch len(out) {
case 1:
mulGFNI_6x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_6x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_6x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_6x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_6x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_6x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_6x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_6x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_6x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_6x10_64(matrix, in, out, start, n)
return n
}
case 7:
switch len(out) {
case 1:
mulGFNI_7x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_7x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_7x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_7x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_7x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_7x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_7x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_7x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_7x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_7x10_64(matrix, in, out, start, n)
return n
}
case 8:
switch len(out) {
case 1:
mulGFNI_8x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_8x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_8x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_8x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_8x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_8x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_8x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_8x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_8x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_8x10_64(matrix, in, out, start, n)
return n
}
case 9:
switch len(out) {
case 1:
mulGFNI_9x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_9x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_9x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_9x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_9x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_9x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_9x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_9x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_9x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_9x10_64(matrix, in, out, start, n)
return n
}
case 10:
switch len(out) {
case 1:
mulGFNI_10x1_64(matrix, in, out, start, n)
return n
case 2:
mulGFNI_10x2_64(matrix, in, out, start, n)
return n
case 3:
mulGFNI_10x3_64(matrix, in, out, start, n)
return n
case 4:
mulGFNI_10x4_64(matrix, in, out, start, n)
return n
case 5:
mulGFNI_10x5_64(matrix, in, out, start, n)
return n
case 6:
mulGFNI_10x6_64(matrix, in, out, start, n)
return n
case 7:
mulGFNI_10x7_64(matrix, in, out, start, n)
return n
case 8:
mulGFNI_10x8_64(matrix, in, out, start, n)
return n
case 9:
mulGFNI_10x9_64(matrix, in, out, start, n)
return n
case 10:
mulGFNI_10x10_64(matrix, in, out, start, n)
return n
}
}
panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
}
func galMulSlicesGFNIXor(matrix []uint64, in, out [][]byte, start, stop int) int {
n := (stop - start) & avxSizeMask
switch len(in) {
case 1:
switch len(out) {
case 1:
mulGFNI_1x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_1x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_1x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_1x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_1x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_1x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_1x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_1x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_1x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_1x10_64Xor(matrix, in, out, start, n)
return n
}
case 2:
switch len(out) {
case 1:
mulGFNI_2x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_2x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_2x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_2x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_2x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_2x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_2x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_2x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_2x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_2x10_64Xor(matrix, in, out, start, n)
return n
}
case 3:
switch len(out) {
case 1:
mulGFNI_3x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_3x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_3x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_3x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_3x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_3x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_3x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_3x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_3x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_3x10_64Xor(matrix, in, out, start, n)
return n
}
case 4:
switch len(out) {
case 1:
mulGFNI_4x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_4x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_4x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_4x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_4x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_4x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_4x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_4x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_4x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_4x10_64Xor(matrix, in, out, start, n)
return n
}
case 5:
switch len(out) {
case 1:
mulGFNI_5x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_5x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_5x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_5x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_5x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_5x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_5x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_5x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_5x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_5x10_64Xor(matrix, in, out, start, n)
return n
}
case 6:
switch len(out) {
case 1:
mulGFNI_6x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_6x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_6x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_6x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_6x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_6x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_6x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_6x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_6x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_6x10_64Xor(matrix, in, out, start, n)
return n
}
case 7:
switch len(out) {
case 1:
mulGFNI_7x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_7x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_7x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_7x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_7x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_7x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_7x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_7x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_7x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_7x10_64Xor(matrix, in, out, start, n)
return n
}
case 8:
switch len(out) {
case 1:
mulGFNI_8x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_8x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_8x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_8x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_8x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_8x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_8x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_8x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_8x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_8x10_64Xor(matrix, in, out, start, n)
return n
}
case 9:
switch len(out) {
case 1:
mulGFNI_9x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_9x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_9x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_9x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_9x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_9x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_9x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_9x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_9x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_9x10_64Xor(matrix, in, out, start, n)
return n
}
case 10:
switch len(out) {
case 1:
mulGFNI_10x1_64Xor(matrix, in, out, start, n)
return n
case 2:
mulGFNI_10x2_64Xor(matrix, in, out, start, n)
return n
case 3:
mulGFNI_10x3_64Xor(matrix, in, out, start, n)
return n
case 4:
mulGFNI_10x4_64Xor(matrix, in, out, start, n)
return n
case 5:
mulGFNI_10x5_64Xor(matrix, in, out, start, n)
return n
case 6:
mulGFNI_10x6_64Xor(matrix, in, out, start, n)
return n
case 7:
mulGFNI_10x7_64Xor(matrix, in, out, start, n)
return n
case 8:
mulGFNI_10x8_64Xor(matrix, in, out, start, n)
return n
case 9:
mulGFNI_10x9_64Xor(matrix, in, out, start, n)
return n
case 10:
mulGFNI_10x10_64Xor(matrix, in, out, start, n)
return n
}
}
panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
}
+58 -19
View File
@@ -7,8 +7,6 @@
package reedsolomon
import "encoding/binary"
func galMulSlice(c byte, in, out []byte, o *options) {
out = out[:len(in)]
if c == 1 {
@@ -34,25 +32,66 @@ func galMulSliceXor(c byte, in, out []byte, o *options) {
}
// simple slice xor
func sliceXor(in, out []byte, _ *options) {
for len(out) >= 32 {
inS := in[:32]
v0 := binary.LittleEndian.Uint64(out[:]) ^ binary.LittleEndian.Uint64(inS[:])
v1 := binary.LittleEndian.Uint64(out[8:]) ^ binary.LittleEndian.Uint64(inS[8:])
v2 := binary.LittleEndian.Uint64(out[16:]) ^ binary.LittleEndian.Uint64(inS[16:])
v3 := binary.LittleEndian.Uint64(out[24:]) ^ binary.LittleEndian.Uint64(inS[24:])
binary.LittleEndian.PutUint64(out[:], v0)
binary.LittleEndian.PutUint64(out[8:], v1)
binary.LittleEndian.PutUint64(out[16:], v2)
binary.LittleEndian.PutUint64(out[24:], v3)
out = out[32:]
in = in[32:]
}
for n, input := range in {
out[n] ^= input
}
func sliceXor(in, out []byte, o *options) {
sliceXorGo(in, out, o)
}
func init() {
defaultOptions.useAVX512 = false
}
// 4-way butterfly
func ifftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
ifftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func ifftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
ifftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func fftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
fftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func fftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
fftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
// 2-way butterfly forward
func fftDIT2(x, y []byte, log_m ffe, o *options) {
// Reference version:
refMulAdd(x, y, log_m)
sliceXorGo(x, y, o)
}
// 2-way butterfly forward
func fftDIT28(x, y []byte, log_m ffe8, o *options) {
// Reference version:
refMulAdd8(x, y, log_m)
sliceXorGo(x, y, o)
}
// 2-way butterfly inverse
func ifftDIT2(x, y []byte, log_m ffe, o *options) {
// Reference version:
sliceXorGo(x, y, o)
refMulAdd(x, y, log_m)
}
// 2-way butterfly inverse
func ifftDIT28(x, y []byte, log_m ffe8, o *options) {
// Reference version:
sliceXorGo(x, y, o)
refMulAdd8(x, y, log_m)
}
func mulgf16(x, y []byte, log_m ffe, o *options) {
refMul(x, y, log_m)
}
func mulgf8(x, y []byte, log_m ffe8, o *options) {
refMul8(x, y, log_m)
}
+78 -2
View File
@@ -68,7 +68,83 @@ func galMulSliceXor(c byte, in, out []byte, o *options) {
// slice galois add
func sliceXor(in, out []byte, o *options) {
for n, input := range in {
out[n] ^= input
sliceXorGo(in, out, o)
}
// 4-way butterfly
func ifftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
ifftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func ifftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
ifftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func fftDIT4(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe, o *options) {
fftDIT4Ref(work, dist, log_m01, log_m23, log_m02, o)
}
// 4-way butterfly
func fftDIT48(work [][]byte, dist int, log_m01, log_m23, log_m02 ffe8, o *options) {
fftDIT4Ref8(work, dist, log_m01, log_m23, log_m02, o)
}
// 2-way butterfly forward
func fftDIT2(x, y []byte, log_m ffe, o *options) {
// Reference version:
refMulAdd(x, y, log_m)
sliceXorGo(x, y, o)
}
// 2-way butterfly forward
func fftDIT28(x, y []byte, log_m ffe8, o *options) {
// Reference version:
mulAdd8(x, y, log_m, o)
sliceXorGo(x, y, o)
}
// 2-way butterfly inverse
func ifftDIT2(x, y []byte, log_m ffe, o *options) {
// Reference version:
sliceXorGo(x, y, o)
refMulAdd(x, y, log_m)
}
// 2-way butterfly inverse
func ifftDIT28(x, y []byte, log_m ffe8, o *options) {
// Reference version:
sliceXorGo(x, y, o)
mulAdd8(x, y, log_m, o)
}
func mulgf16(x, y []byte, log_m ffe, o *options) {
refMul(x, y, log_m)
}
func mulAdd8(out, in []byte, log_m ffe8, o *options) {
t := &multiply256LUT8[log_m]
galMulPpcXor(t[:16], t[16:32], in, out)
done := (len(in) >> 4) << 4
in = in[done:]
if len(in) > 0 {
out = out[done:]
refMulAdd8(in, out, log_m)
}
}
func mulgf8(out, in []byte, log_m ffe8, o *options) {
var done int
t := &multiply256LUT8[log_m]
galMulPpc(t[:16], t[16:32], in, out)
done = (len(in) >> 4) << 4
remain := len(in) - done
if remain > 0 {
mt := mul8LUTs[log_m].Value[:]
for i := done; i < len(in); i++ {
out[i] ^= byte(mt[in[i]])
}
}
}
-5
View File
@@ -1,5 +0,0 @@
module github.com/klauspost/reedsolomon
go 1.15
require github.com/klauspost/cpuid/v2 v2.0.14
-2
View File
@@ -1,2 +0,0 @@
github.com/klauspost/cpuid/v2 v2.0.14 h1:QRqdp6bb9M9S5yyKeYteXKuoKE4p0tGlra81fKOpWH8=
github.com/klauspost/cpuid/v2 v2.0.14/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
+81 -11
View File
@@ -15,12 +15,15 @@ type options struct {
shardSize int
perRound int
useAVX512, useAVX2, useSSSE3, useSSE2 bool
usePAR1Matrix bool
useCauchy bool
fastOneParity bool
inversionCache bool
customMatrix [][]byte
useGFNI, useAVX512, useAVX2, useSSSE3, useSSE2 bool
useJerasureMatrix bool
usePAR1Matrix bool
useCauchy bool
fastOneParity bool
inversionCache bool
forcedInversionCache bool
customMatrix [][]byte
withLeopard leopardMode
// stream options
concReads bool
@@ -38,9 +41,24 @@ var defaultOptions = options{
useSSSE3: cpuid.CPU.Supports(cpuid.SSSE3),
useSSE2: cpuid.CPU.Supports(cpuid.SSE2),
useAVX2: cpuid.CPU.Supports(cpuid.AVX2),
useAVX512: cpuid.CPU.Supports(cpuid.AVX512F, cpuid.AVX512BW),
useAVX512: cpuid.CPU.Supports(cpuid.AVX512F, cpuid.AVX512BW, cpuid.AVX512VL),
useGFNI: cpuid.CPU.Supports(cpuid.AVX512F, cpuid.GFNI, cpuid.AVX512DQ),
}
// leopardMode controls the use of leopard GF in encoding and decoding.
type leopardMode int
const (
// leopardAsNeeded only switches to leopard 16-bit when there are more than
// 256 shards.
leopardAsNeeded leopardMode = iota
// leopardGF16 uses leopard in 16-bit mode for all shard counts.
leopardGF16
// leopardAlways uses 8-bit leopard for shards less than or equal to 256,
// 16-bit leopard otherwise.
leopardAlways
)
func init() {
if runtime.GOMAXPROCS(0) <= 1 {
defaultOptions.maxGoroutines = 1
@@ -114,10 +132,11 @@ func WithConcurrentStreamWrites(enabled bool) Option {
// WithInversionCache allows to control the inversion cache.
// This will cache reconstruction matrices so they can be reused.
// Enabled by default.
// Enabled by default, or <= 64 shards for Leopard encoding.
func WithInversionCache(enabled bool) Option {
return func(o *options) {
o.inversionCache = enabled
o.forcedInversionCache = true
}
}
@@ -155,11 +174,31 @@ func WithSSE2(enabled bool) Option {
}
}
// WithAVX512 allows to enable/disable AVX512 instructions.
// If not set, AVX512 will be turned on or off automatically based on CPU ID information.
// WithAVX512 allows to enable/disable AVX512 (and GFNI) instructions.
func WithAVX512(enabled bool) Option {
return func(o *options) {
o.useAVX512 = enabled
o.useGFNI = enabled
}
}
// WithGFNI allows to enable/disable AVX512+GFNI instructions.
// If not set, GFNI will be turned on or off automatically based on CPU ID information.
func WithGFNI(enabled bool) Option {
return func(o *options) {
o.useGFNI = enabled
}
}
// WithJerasureMatrix causes the encoder to build the Reed-Solomon-Vandermonde
// matrix in the same way as done by the Jerasure library.
// The first row and column of the coding matrix only contains 1's in this method
// so the first parity chunk is always equal to XOR of all data chunks.
func WithJerasureMatrix() Option {
return func(o *options) {
o.useJerasureMatrix = true
o.usePAR1Matrix = false
o.useCauchy = false
}
}
@@ -169,6 +208,7 @@ func WithAVX512(enabled bool) Option {
// shards.
func WithPAR1Matrix() Option {
return func(o *options) {
o.useJerasureMatrix = false
o.usePAR1Matrix = true
o.useCauchy = false
}
@@ -180,8 +220,9 @@ func WithPAR1Matrix() Option {
// but will result in slightly faster start-up time.
func WithCauchyMatrix() Option {
return func(o *options) {
o.useCauchy = true
o.useJerasureMatrix = false
o.usePAR1Matrix = false
o.useCauchy = true
}
}
@@ -205,3 +246,32 @@ func WithCustomMatrix(customMatrix [][]byte) Option {
o.customMatrix = customMatrix
}
}
// WithLeopardGF16 will always use leopard GF16 for encoding,
// even when there is less than 256 shards.
// This will likely improve reconstruction time for some setups.
// This is not compatible with Leopard output for <= 256 shards.
// Note that Leopard places certain restrictions on use see other documentation.
func WithLeopardGF16(enabled bool) Option {
return func(o *options) {
if enabled {
o.withLeopard = leopardGF16
} else {
o.withLeopard = leopardAsNeeded
}
}
}
// WithLeopardGF will use leopard GF for encoding, even when there are fewer than
// 256 shards.
// This will likely improve reconstruction time for some setups.
// Note that Leopard places certain restrictions on use see other documentation.
func WithLeopardGF(enabled bool) Option {
return func(o *options) {
if enabled {
o.withLeopard = leopardAlways
} else {
o.withLeopard = leopardAsNeeded
}
}
}
+453 -106
View File
@@ -8,7 +8,6 @@
// Package reedsolomon enables Erasure Coding in Go
//
// For usage and examples, see https://github.com/klauspost/reedsolomon
//
package reedsolomon
import (
@@ -104,12 +103,16 @@ type Encoder interface {
Update(shards [][]byte, newDatashards [][]byte) error
// Split a data slice into the number of shards given to the encoder,
// and create empty parity shards.
// and create empty parity shards if necessary.
//
// The data will be split into equally sized shards.
// If the data size isn't dividable by the number of shards,
// If the data size isn't divisible by the number of shards,
// the last shard will contain extra zeros.
//
// If there is extra capacity on the provided data slice
// it will be used instead of allocating parity shards.
// It will be zeroed out.
//
// There must be at least 1 byte otherwise ErrShortData will be
// returned.
//
@@ -126,10 +129,32 @@ type Encoder interface {
Join(dst io.Writer, shards [][]byte, outSize int) error
}
// Extensions is an optional interface.
// All returned instances will support this interface.
type Extensions interface {
// ShardSizeMultiple will return the size the shard sizes must be a multiple of.
ShardSizeMultiple() int
// DataShards will return the number of data shards.
DataShards() int
// ParityShards will return the number of parity shards.
ParityShards() int
// TotalShards will return the total number of shards.
TotalShards() int
// AllocAligned will allocate TotalShards number of slices,
// aligned to reasonable memory sizes.
// Provide the size of each shard.
AllocAligned(each int) [][]byte
}
const (
avx2CodeGenMinSize = 64
avx2CodeGenMinShards = 3
avx2CodeGenMaxGoroutines = 8
gfniCodeGenMaxGoroutines = 4
intSize = 32 << (^uint(0) >> 63) // 32 or 64
maxInt = 1<<(intSize-1) - 1
@@ -139,9 +164,9 @@ const (
// distribution of datashards and parity shards.
// Construct if using New()
type reedSolomon struct {
DataShards int // Number of data shards, should not be modified.
ParityShards int // Number of parity shards, should not be modified.
Shards int // Total number of shards. Calculated, and should not be modified.
dataShards int // Number of data shards, should not be modified.
parityShards int // Number of parity shards, should not be modified.
totalShards int // Total number of shards. Calculated, and should not be modified.
m matrix
tree *inversionTree
parity [][]byte
@@ -149,6 +174,28 @@ type reedSolomon struct {
mPool sync.Pool
}
var _ = Extensions(&reedSolomon{})
func (r *reedSolomon) ShardSizeMultiple() int {
return 1
}
func (r *reedSolomon) DataShards() int {
return r.dataShards
}
func (r *reedSolomon) ParityShards() int {
return r.parityShards
}
func (r *reedSolomon) TotalShards() int {
return r.totalShards
}
func (r *reedSolomon) AllocAligned(each int) [][]byte {
return AllocAligned(r.totalShards, each)
}
// ErrInvShardNum will be returned by New, if you attempt to create
// an Encoder with less than one data shard or less than zero parity
// shards.
@@ -159,6 +206,9 @@ var ErrInvShardNum = errors.New("cannot create Encoder with less than one data s
// GF(2^8).
var ErrMaxShardNum = errors.New("cannot create Encoder with more than 256 data+parity shards")
// ErrNotSupported is returned when an operation is not supported.
var ErrNotSupported = errors.New("operation not supported")
// buildMatrix creates the matrix to use for encoding, given the
// number of data shards and the number of total shards.
//
@@ -191,6 +241,87 @@ func buildMatrix(dataShards, totalShards int) (matrix, error) {
return vm.Multiply(topInv)
}
// buildMatrixJerasure creates the same encoding matrix as Jerasure library
//
// The top square of the matrix is guaranteed to be an identity
// matrix, which means that the data shards are unchanged after
// encoding.
func buildMatrixJerasure(dataShards, totalShards int) (matrix, error) {
// Start with a Vandermonde matrix. This matrix would work,
// in theory, but doesn't have the property that the data
// shards are unchanged after encoding.
vm, err := vandermonde(totalShards, dataShards)
if err != nil {
return nil, err
}
// Jerasure does this:
// first row is always 100..00
vm[0][0] = 1
for i := 1; i < dataShards; i++ {
vm[0][i] = 0
}
// last row is always 000..01
for i := 0; i < dataShards-1; i++ {
vm[totalShards-1][i] = 0
}
vm[totalShards-1][dataShards-1] = 1
for i := 0; i < dataShards; i++ {
// Find the row where i'th col is not 0
r := i
for ; r < totalShards && vm[r][i] == 0; r++ {
}
if r != i {
// Swap it with i'th row if not already
t := vm[r]
vm[r] = vm[i]
vm[i] = t
}
// Multiply by the inverted matrix (same as vm.Multiply(vm[0:dataShards].Invert()))
if vm[i][i] != 1 {
// Make vm[i][i] = 1 by dividing the column by vm[i][i]
tmp := galDivide(1, vm[i][i])
for j := 0; j < totalShards; j++ {
vm[j][i] = galMultiply(vm[j][i], tmp)
}
}
for j := 0; j < dataShards; j++ {
// Make vm[i][j] = 0 where j != i by adding vm[i][j]*vm[.][i] to each column
tmp := vm[i][j]
if j != i && tmp != 0 {
for r := 0; r < totalShards; r++ {
vm[r][j] = galAdd(vm[r][j], galMultiply(tmp, vm[r][i]))
}
}
}
}
// Make vm[dataShards] row all ones - divide each column j by vm[dataShards][j]
for j := 0; j < dataShards; j++ {
tmp := vm[dataShards][j]
if tmp != 1 {
tmp = galDivide(1, tmp)
for i := dataShards; i < totalShards; i++ {
vm[i][j] = galMultiply(vm[i][j], tmp)
}
}
}
// Make vm[dataShards...totalShards-1][0] column all ones - divide each row
for i := dataShards + 1; i < totalShards; i++ {
tmp := vm[i][0]
if tmp != 1 {
tmp = galDivide(1, tmp)
for j := 0; j < dataShards; j++ {
vm[i][j] = galMultiply(vm[i][j], tmp)
}
}
}
return vm, nil
}
// buildMatrixPAR1 creates the matrix to use for encoding according to
// the PARv1 spec, given the number of data shards and the number of
// total shards. Note that the method they use is buggy, and may lead
@@ -270,27 +401,41 @@ func buildXorMatrix(dataShards, totalShards int) (matrix, error) {
// New creates a new encoder and initializes it to
// the number of data shards and parity shards that
// you want to use. You can reuse this encoder.
// Note that the maximum number of total shards is 256.
// Note that the maximum number of total shards is 65536, with some
// restrictions for a total larger than 256:
//
// - Shard sizes must be multiple of 64
// - The methods Join/Split/Update/EncodeIdx are not supported
//
// If no options are supplied, default options are used.
func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
r := reedSolomon{
DataShards: dataShards,
ParityShards: parityShards,
Shards: dataShards + parityShards,
o: defaultOptions,
o := defaultOptions
for _, opt := range opts {
opt(&o)
}
for _, opt := range opts {
opt(&r.o)
totShards := dataShards + parityShards
switch {
case o.withLeopard == leopardGF16 && parityShards > 0 || totShards > 256:
return newFF16(dataShards, parityShards, o)
case o.withLeopard == leopardAlways && parityShards > 0:
return newFF8(dataShards, parityShards, o)
}
if totShards > 256 {
return nil, ErrMaxShardNum
}
r := reedSolomon{
dataShards: dataShards,
parityShards: parityShards,
totalShards: dataShards + parityShards,
o: o,
}
if dataShards <= 0 || parityShards < 0 {
return nil, ErrInvShardNum
}
if dataShards+parityShards > 256 {
return nil, ErrMaxShardNum
}
if parityShards == 0 {
return &r, nil
}
@@ -301,7 +446,7 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
if len(r.o.customMatrix) < parityShards {
return nil, errors.New("coding matrix must contain at least parityShards rows")
}
r.m = make([][]byte, r.Shards)
r.m = make([][]byte, r.totalShards)
for i := 0; i < dataShards; i++ {
r.m[i] = make([]byte, dataShards)
r.m[i][i] = 1
@@ -314,13 +459,15 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
copy(r.m[dataShards+k], row)
}
case r.o.fastOneParity && parityShards == 1:
r.m, err = buildXorMatrix(dataShards, r.Shards)
r.m, err = buildXorMatrix(dataShards, r.totalShards)
case r.o.useCauchy:
r.m, err = buildMatrixCauchy(dataShards, r.Shards)
r.m, err = buildMatrixCauchy(dataShards, r.totalShards)
case r.o.usePAR1Matrix:
r.m, err = buildMatrixPAR1(dataShards, r.Shards)
r.m, err = buildMatrixPAR1(dataShards, r.totalShards)
case r.o.useJerasureMatrix:
r.m, err = buildMatrixJerasure(dataShards, r.totalShards)
default:
r.m, err = buildMatrix(dataShards, r.Shards)
r.m, err = buildMatrix(dataShards, r.totalShards)
}
if err != nil {
return nil, err
@@ -403,6 +550,10 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
r.o.maxGoroutines = avx2CodeGenMaxGoroutines
}
if r.canGFNI(avx2CodeGenMinSize, maxAvx2Inputs, maxAvx2Outputs) && r.o.maxGoroutines > gfniCodeGenMaxGoroutines {
r.o.maxGoroutines = gfniCodeGenMaxGoroutines
}
// Inverted matrices are cached in a tree keyed by the indices
// of the invalid rows of the data to reconstruct.
// The inversion root node will have the identity matrix as
@@ -418,7 +569,7 @@ func New(dataShards, parityShards int, opts ...Option) (Encoder, error) {
}
if avx2CodeGen && r.o.useAVX2 {
sz := r.DataShards * r.ParityShards * 2 * 32
sz := r.dataShards * r.parityShards * 2 * 32
r.mPool.New = func() interface{} {
return make([]byte, sz)
}
@@ -438,7 +589,7 @@ var ErrTooFewShards = errors.New("too few shards given")
// The parity shards will always be overwritten and the data shards
// will remain the same.
func (r *reedSolomon) Encode(shards [][]byte) error {
if len(shards) != r.Shards {
if len(shards) != r.totalShards {
return ErrTooFewShards
}
@@ -448,10 +599,10 @@ func (r *reedSolomon) Encode(shards [][]byte) error {
}
// Get the slice of output buffers.
output := shards[r.DataShards:]
output := shards[r.dataShards:]
// Do the coding.
r.codeSomeShards(r.parity, shards[0:r.DataShards], output[:r.ParityShards], len(shards[0]))
r.codeSomeShards(r.parity, shards[0:r.dataShards], output[:r.parityShards], len(shards[0]))
return nil
}
@@ -460,13 +611,13 @@ func (r *reedSolomon) Encode(shards [][]byte) error {
// Data shards should only be delivered once. There is no check for this.
// The parity shards will always be updated and the data shards will remain the unchanged.
func (r *reedSolomon) EncodeIdx(dataShard []byte, idx int, parity [][]byte) error {
if len(parity) != r.ParityShards {
if len(parity) != r.parityShards {
return ErrTooFewShards
}
if len(parity) == 0 {
return nil
}
if idx < 0 || idx >= r.DataShards {
if idx < 0 || idx >= r.dataShards {
return ErrInvShardNum
}
err := checkShards(parity, false)
@@ -485,7 +636,7 @@ func (r *reedSolomon) EncodeIdx(dataShard []byte, idx int, parity [][]byte) erro
for start < len(dataShard) {
in := dataShard[start:end]
for iRow := 0; iRow < r.ParityShards; iRow++ {
for iRow := 0; iRow < r.parityShards; iRow++ {
galMulSliceXor(r.parity[iRow][idx], in, parity[iRow][start:end], &r.o)
}
start = end
@@ -501,11 +652,11 @@ func (r *reedSolomon) EncodeIdx(dataShard []byte, idx int, parity [][]byte) erro
var ErrInvalidInput = errors.New("invalid input")
func (r *reedSolomon) Update(shards [][]byte, newDatashards [][]byte) error {
if len(shards) != r.Shards {
if len(shards) != r.totalShards {
return ErrTooFewShards
}
if len(newDatashards) != r.DataShards {
if len(newDatashards) != r.dataShards {
return ErrTooFewShards
}
@@ -524,7 +675,7 @@ func (r *reedSolomon) Update(shards [][]byte, newDatashards [][]byte) error {
return ErrInvalidInput
}
}
for _, p := range shards[r.DataShards:] {
for _, p := range shards[r.dataShards:] {
if p == nil {
return ErrInvalidInput
}
@@ -533,10 +684,10 @@ func (r *reedSolomon) Update(shards [][]byte, newDatashards [][]byte) error {
shardSize := shardSize(shards)
// Get the slice of output buffers.
output := shards[r.DataShards:]
output := shards[r.dataShards:]
// Do the coding.
r.updateParityShards(r.parity, shards[0:r.DataShards], newDatashards[0:r.DataShards], output, r.ParityShards, shardSize)
r.updateParityShards(r.parity, shards[0:r.dataShards], newDatashards[0:r.dataShards], output, r.parityShards, shardSize)
return nil
}
@@ -550,7 +701,7 @@ func (r *reedSolomon) updateParityShards(matrixRows, oldinputs, newinputs, outpu
return
}
for c := 0; c < r.DataShards; c++ {
for c := 0; c < r.dataShards; c++ {
in := newinputs[c]
if in == nil {
continue
@@ -577,7 +728,7 @@ func (r *reedSolomon) updateParityShardsP(matrixRows, oldinputs, newinputs, outp
}
wg.Add(1)
go func(start, stop int) {
for c := 0; c < r.DataShards; c++ {
for c := 0; c < r.dataShards; c++ {
in := newinputs[c]
if in == nil {
continue
@@ -599,7 +750,7 @@ func (r *reedSolomon) updateParityShardsP(matrixRows, oldinputs, newinputs, outp
// Verify returns true if the parity shards contain the right data.
// The data is the same format as Encode. No data is modified.
func (r *reedSolomon) Verify(shards [][]byte) (bool, error) {
if len(shards) != r.Shards {
if len(shards) != r.totalShards {
return false, ErrTooFewShards
}
err := checkShards(shards, false)
@@ -608,10 +759,10 @@ func (r *reedSolomon) Verify(shards [][]byte) (bool, error) {
}
// Slice of buffers being checked.
toCheck := shards[r.DataShards:]
toCheck := shards[r.dataShards:]
// Do the checking.
return r.checkSomeShards(r.parity, shards[:r.DataShards], toCheck[:r.ParityShards], len(shards[0])), nil
return r.checkSomeShards(r.parity, shards[:r.dataShards], toCheck[:r.parityShards], len(shards[0])), nil
}
func (r *reedSolomon) canAVX2C(byteCount int, inputs, outputs int) bool {
@@ -620,12 +771,18 @@ func (r *reedSolomon) canAVX2C(byteCount int, inputs, outputs int) bool {
inputs <= maxAvx2Inputs && outputs <= maxAvx2Outputs
}
func (r *reedSolomon) canGFNI(byteCount int, inputs, outputs int) bool {
return avx2CodeGen && r.o.useGFNI &&
byteCount >= avx2CodeGenMinSize && inputs+outputs >= avx2CodeGenMinShards &&
inputs <= maxAvx2Inputs && outputs <= maxAvx2Outputs
}
// Multiplies a subset of rows from a coding matrix by a full set of
// input shards to produce some output shards.
// input totalShards to produce some output totalShards.
// 'matrixRows' is The rows from the matrix to use.
// 'inputs' An array of byte arrays, each of which is one input shard.
// The number of inputs used is determined by the length of each matrix row.
// outputs Byte arrays where the computed shards are stored.
// outputs Byte arrays where the computed totalShards are stored.
// The number of outputs computed, and the
// number of matrix rows used, is determined by
// outputCount, which is the number of outputs to compute.
@@ -633,14 +790,7 @@ func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, byteC
if len(outputs) == 0 {
return
}
switch {
case r.o.useAVX512 && r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize && len(inputs) >= 4 && len(outputs) >= 2:
r.codeSomeShardsAvx512P(matrixRows, inputs, outputs, byteCount)
return
case r.o.useAVX512 && len(inputs) >= 4 && len(outputs) >= 2:
r.codeSomeShardsAvx512(matrixRows, inputs, outputs, byteCount)
return
case byteCount > r.o.minSplitSize:
if byteCount > r.o.minSplitSize {
r.codeSomeShardsP(matrixRows, inputs, outputs, byteCount)
return
}
@@ -650,12 +800,18 @@ func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, byteC
if end > len(inputs[0]) {
end = len(inputs[0])
}
if r.canAVX2C(byteCount, len(inputs), len(outputs)) {
if r.canGFNI(byteCount, len(inputs), len(outputs)) {
var gfni [maxAvx2Inputs * maxAvx2Outputs]uint64
m := genGFNIMatrix(matrixRows, len(inputs), 0, len(outputs), gfni[:])
start += galMulSlicesGFNI(m, inputs, outputs, 0, byteCount)
end = len(inputs[0])
} else if r.canAVX2C(byteCount, len(inputs), len(outputs)) {
m := genAvx2Matrix(matrixRows, len(inputs), 0, len(outputs), r.mPool.Get().([]byte))
start += galMulSlicesAvx2(m, inputs, outputs, 0, byteCount)
r.mPool.Put(m)
end = len(inputs[0])
} else if len(inputs)+len(outputs) > avx2CodeGenMinShards && r.canAVX2C(byteCount, maxAvx2Inputs, maxAvx2Outputs) {
var gfni [maxAvx2Inputs * maxAvx2Outputs]uint64
end = len(inputs[0])
inIdx := 0
m := r.mPool.Get().([]byte)
@@ -673,11 +829,20 @@ func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, byteC
if len(outPer) > maxAvx2Outputs {
outPer = outPer[:maxAvx2Outputs]
}
m = genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), m)
if inIdx == 0 {
galMulSlicesAvx2(m, inPer, outPer, 0, byteCount)
if r.o.useGFNI {
m := genGFNIMatrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), gfni[:])
if inIdx == 0 {
galMulSlicesGFNI(m, inPer, outPer, 0, byteCount)
} else {
galMulSlicesGFNIXor(m, inPer, outPer, 0, byteCount)
}
} else {
galMulSlicesAvx2Xor(m, inPer, outPer, 0, byteCount)
m = genAvx2Matrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), m)
if inIdx == 0 {
galMulSlicesAvx2(m, inPer, outPer, 0, byteCount)
} else {
galMulSlicesAvx2Xor(m, inPer, outPer, 0, byteCount)
}
}
start = byteCount & avxSizeMask
outIdx += len(outPer)
@@ -716,11 +881,22 @@ func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, byte
gor := r.o.maxGoroutines
var avx2Matrix []byte
var gfniMatrix []uint64
useAvx2 := r.canAVX2C(byteCount, len(inputs), len(outputs))
if useAvx2 {
useGFNI := r.canGFNI(byteCount, len(inputs), len(outputs))
if useGFNI {
var tmp [maxAvx2Inputs * maxAvx2Outputs]uint64
gfniMatrix = genGFNIMatrix(matrixRows, len(inputs), 0, len(outputs), tmp[:])
} else if useAvx2 {
avx2Matrix = genAvx2Matrix(matrixRows, len(inputs), 0, len(outputs), r.mPool.Get().([]byte))
defer r.mPool.Put(avx2Matrix)
} else if byteCount < 10<<20 && len(inputs)+len(outputs) > avx2CodeGenMinShards &&
} else if r.o.useGFNI && byteCount < 10<<20 && len(inputs)+len(outputs) > avx2CodeGenMinShards &&
r.canAVX2C(byteCount/4, maxAvx2Inputs, maxAvx2Outputs) {
// It appears there is a switchover point at around 10MB where
// Regular processing is faster...
r.codeSomeShardsAVXP(matrixRows, inputs, outputs, byteCount)
return
} else if r.o.useAVX2 && byteCount < 10<<20 && len(inputs)+len(outputs) > avx2CodeGenMinShards &&
r.canAVX2C(byteCount/4, maxAvx2Inputs, maxAvx2Outputs) {
// It appears there is a switchover point at around 10MB where
// Regular processing is faster...
@@ -734,8 +910,12 @@ func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, byte
}
exec := func(start, stop int) {
if useAvx2 && stop-start >= 64 {
start += galMulSlicesAvx2(avx2Matrix, inputs, outputs, start, stop)
if stop-start >= 64 {
if useGFNI {
start += galMulSlicesGFNI(gfniMatrix, inputs, outputs, start, stop)
} else if useAvx2 {
start += galMulSlicesAvx2(avx2Matrix, inputs, outputs, start, stop)
}
}
lstart, lstop := start, start+r.o.perRound
@@ -937,6 +1117,154 @@ func (r *reedSolomon) codeSomeShardsAVXP(matrixRows, inputs, outputs [][]byte, b
wg.Wait()
}
// Perform the same as codeSomeShards, but split the workload into
// several goroutines.
func (r *reedSolomon) codeSomeShardsGFNI(matrixRows, inputs, outputs [][]byte, byteCount int) {
var wg sync.WaitGroup
gor := r.o.maxGoroutines
type state struct {
input [][]byte
output [][]byte
m []uint64
first bool
}
// Make a plan...
plan := make([]state, 0, ((len(inputs)+maxAvx2Inputs-1)/maxAvx2Inputs)*((len(outputs)+maxAvx2Outputs-1)/maxAvx2Outputs))
// Flips between input first to output first.
// We put the smallest data load in the inner loop.
if len(inputs) > len(outputs) {
inIdx := 0
ins := inputs
for len(ins) > 0 {
inPer := ins
if len(inPer) > maxAvx2Inputs {
inPer = inPer[:maxAvx2Inputs]
}
outs := outputs
outIdx := 0
for len(outs) > 0 {
outPer := outs
if len(outPer) > maxAvx2Outputs {
outPer = outPer[:maxAvx2Outputs]
}
// Generate local matrix
m := genGFNIMatrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), make([]uint64, len(inPer)*len(outPer)))
plan = append(plan, state{
input: inPer,
output: outPer,
m: m,
first: inIdx == 0,
})
outIdx += len(outPer)
outs = outs[len(outPer):]
}
inIdx += len(inPer)
ins = ins[len(inPer):]
}
} else {
outs := outputs
outIdx := 0
for len(outs) > 0 {
outPer := outs
if len(outPer) > maxAvx2Outputs {
outPer = outPer[:maxAvx2Outputs]
}
inIdx := 0
ins := inputs
for len(ins) > 0 {
inPer := ins
if len(inPer) > maxAvx2Inputs {
inPer = inPer[:maxAvx2Inputs]
}
// Generate local matrix
m := genGFNIMatrix(matrixRows[outIdx:], len(inPer), inIdx, len(outPer), make([]uint64, len(inPer)*len(outPer)))
//fmt.Println("bytes:", len(inPer)*r.o.perRound, "out:", len(outPer)*r.o.perRound)
plan = append(plan, state{
input: inPer,
output: outPer,
m: m,
first: inIdx == 0,
})
inIdx += len(inPer)
ins = ins[len(inPer):]
}
outIdx += len(outPer)
outs = outs[len(outPer):]
}
}
do := byteCount / gor
if do < r.o.minSplitSize {
do = r.o.minSplitSize
}
exec := func(start, stop int) {
lstart, lstop := start, start+r.o.perRound
if lstop > stop {
lstop = stop
}
for lstart < stop {
if lstop-lstart >= minAvx2Size {
// Execute plan...
for _, p := range plan {
if p.first {
galMulSlicesGFNI(p.m, p.input, p.output, lstart, lstop)
} else {
galMulSlicesGFNIXor(p.m, p.input, p.output, lstart, lstop)
}
}
lstart += (lstop - lstart) & avxSizeMask
if lstart == lstop {
lstop += r.o.perRound
if lstop > stop {
lstop = stop
}
continue
}
}
for c := range inputs {
in := inputs[c][lstart:lstop]
for iRow := 0; iRow < len(outputs); iRow++ {
if c == 0 {
galMulSlice(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
} else {
galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][lstart:lstop], &r.o)
}
}
}
lstart = lstop
lstop += r.o.perRound
if lstop > stop {
lstop = stop
}
}
wg.Done()
}
if gor == 1 {
wg.Add(1)
exec(0, byteCount)
return
}
// Make sizes divisible by 64
do = (do + 63) & (^63)
start := 0
for start < byteCount {
if start+do > byteCount {
do = byteCount - start
}
wg.Add(1)
go exec(start, start+do)
start += do
}
wg.Wait()
}
// checkSomeShards is mostly the same as codeSomeShards,
// except this will check values and return
// as soon as a difference is found.
@@ -945,10 +1273,7 @@ func (r *reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, byte
return true
}
outputs := make([][]byte, len(toCheck))
for i := range outputs {
outputs[i] = make([]byte, byteCount)
}
outputs := AllocAligned(len(toCheck), byteCount)
r.codeSomeShards(matrixRows, inputs, outputs, byteCount)
for i, calc := range outputs {
@@ -1002,7 +1327,7 @@ func shardSize(shards [][]byte) int {
// Given a list of shards, some of which contain data, fills in the
// ones that don't have data.
//
// The length of the array must be equal to Shards.
// The length of the array must be equal to shards.
// You indicate that a shard is missing by setting it to nil or zero-length.
// If a shard is zero-length but has sufficient capacity, that memory will
// be used, otherwise a new []byte will be allocated.
@@ -1021,7 +1346,7 @@ func (r *reedSolomon) Reconstruct(shards [][]byte) error {
// Given a list of shards, some of which contain data, fills in the
// data shards that don't have data.
//
// The length of the array must be equal to Shards.
// The length of the array must be equal to shards.
// You indicate that a shard is missing by setting it to nil or zero-length.
// If a shard is zero-length but has sufficient capacity, that memory will
// be used, otherwise a new []byte will be allocated.
@@ -1039,9 +1364,9 @@ func (r *reedSolomon) ReconstructData(shards [][]byte) error {
//
// Given a list of shards, some of which contain data, fills in the
// data shards indicated by true values in the "required" parameter.
// The length of "required" array must be equal to DataShards.
// The length of "required" array must be equal to dataShards.
//
// The length of "shards" array must be equal to Shards.
// The length of "shards" array must be equal to shards.
// You indicate that a shard is missing by setting it to nil or zero-length.
// If a shard is zero-length but has sufficient capacity, that memory will
// be used, otherwise a new []byte will be allocated.
@@ -1055,16 +1380,16 @@ func (r *reedSolomon) ReconstructSome(shards [][]byte, required []bool) error {
return r.reconstruct(shards, true, required)
}
// reconstruct will recreate the missing data shards, and unless
// dataOnly is true, also the missing parity shards
// reconstruct will recreate the missing data totalShards, and unless
// dataOnly is true, also the missing parity totalShards
//
// The length of "shards" array must be equal to Shards.
// The length of "shards" array must be equal to totalShards.
// You indicate that a shard is missing by setting it to nil.
//
// If there are too few shards to reconstruct the missing
// If there are too few totalShards to reconstruct the missing
// ones, ErrTooFewShards will be returned.
func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []bool) error {
if len(shards) != r.Shards || required != nil && len(required) < r.DataShards {
if len(shards) != r.totalShards || required != nil && len(required) < r.dataShards {
return ErrTooFewShards
}
// Check arguments.
@@ -1080,25 +1405,25 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []boo
numberPresent := 0
dataPresent := 0
missingRequired := 0
for i := 0; i < r.Shards; i++ {
for i := 0; i < r.totalShards; i++ {
if len(shards[i]) != 0 {
numberPresent++
if i < r.DataShards {
if i < r.dataShards {
dataPresent++
}
} else if required != nil && required[i] {
missingRequired++
}
}
if numberPresent == r.Shards || dataOnly && dataPresent == r.DataShards ||
if numberPresent == r.totalShards || dataOnly && dataPresent == r.dataShards ||
required != nil && missingRequired == 0 {
// Cool. All of the shards data data. We don't
// Cool. All of the shards have data. We don't
// need to do anything.
return nil
}
// More complete sanity check
if numberPresent < r.DataShards {
if numberPresent < r.dataShards {
return ErrTooFewShards
}
@@ -1109,11 +1434,11 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []boo
//
// Also, create an array of indices of the valid rows we do have
// and the invalid rows we don't have up until we have enough valid rows.
subShards := make([][]byte, r.DataShards)
validIndices := make([]int, r.DataShards)
subShards := make([][]byte, r.dataShards)
validIndices := make([]int, r.dataShards)
invalidIndices := make([]int, 0)
subMatrixRow := 0
for matrixRow := 0; matrixRow < r.Shards && subMatrixRow < r.DataShards; matrixRow++ {
for matrixRow := 0; matrixRow < r.totalShards && subMatrixRow < r.dataShards; matrixRow++ {
if len(shards[matrixRow]) != 0 {
subShards[subMatrixRow] = shards[matrixRow]
validIndices[subMatrixRow] = matrixRow
@@ -1135,9 +1460,9 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []boo
// shards that we have and build a square matrix. This
// matrix could be used to generate the shards that we have
// from the original data.
subMatrix, _ := newMatrix(r.DataShards, r.DataShards)
subMatrix, _ := newMatrix(r.dataShards, r.dataShards)
for subMatrixRow, validIndex := range validIndices {
for c := 0; c < r.DataShards; c++ {
for c := 0; c < r.dataShards; c++ {
subMatrix[subMatrixRow][c] = r.m[validIndex][c]
}
}
@@ -1153,7 +1478,7 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []boo
// Cache the inverted matrix in the tree for future use keyed on the
// indices of the invalid rows.
err = r.tree.InsertInvertedMatrix(invalidIndices, dataDecodeMatrix, r.Shards)
err = r.tree.InsertInvertedMatrix(invalidIndices, dataDecodeMatrix, r.totalShards)
if err != nil {
return err
}
@@ -1164,16 +1489,16 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []boo
// The input to the coding is all of the shards we actually
// have, and the output is the missing data shards. The computation
// is done using the special decode matrix we just built.
outputs := make([][]byte, r.ParityShards)
matrixRows := make([][]byte, r.ParityShards)
outputs := make([][]byte, r.parityShards)
matrixRows := make([][]byte, r.parityShards)
outputCount := 0
for iShard := 0; iShard < r.DataShards; iShard++ {
for iShard := 0; iShard < r.dataShards; iShard++ {
if len(shards[iShard]) == 0 && (required == nil || required[iShard]) {
if cap(shards[iShard]) >= shardSize {
shards[iShard] = shards[iShard][0:shardSize]
} else {
shards[iShard] = make([]byte, shardSize)
shards[iShard] = AllocAligned(1, shardSize)[0]
}
outputs[outputCount] = shards[iShard]
matrixRows[outputCount] = dataDecodeMatrix[iShard]
@@ -1194,19 +1519,19 @@ func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool, required []boo
// any that we just calculated. The output is whichever of the
// data shards were missing.
outputCount = 0
for iShard := r.DataShards; iShard < r.Shards; iShard++ {
for iShard := r.dataShards; iShard < r.totalShards; iShard++ {
if len(shards[iShard]) == 0 && (required == nil || required[iShard]) {
if cap(shards[iShard]) >= shardSize {
shards[iShard] = shards[iShard][0:shardSize]
} else {
shards[iShard] = make([]byte, shardSize)
shards[iShard] = AllocAligned(1, shardSize)[0]
}
outputs[outputCount] = shards[iShard]
matrixRows[outputCount] = r.parity[iShard-r.DataShards]
matrixRows[outputCount] = r.parity[iShard-r.dataShards]
outputCount++
}
}
r.codeSomeShards(matrixRows, shards[:r.DataShards], outputs[:outputCount], shardSize)
r.codeSomeShards(matrixRows, shards[:r.dataShards], outputs[:outputCount], shardSize)
return nil
}
@@ -1221,6 +1546,10 @@ var ErrShortData = errors.New("not enough data to fill the number of requested s
// If the data size isn't divisible by the number of shards,
// the last shard will contain extra zeros.
//
// If there is extra capacity on the provided data slice
// it will be used instead of allocating parity shards.
// It will be zeroed out.
//
// There must be at least 1 byte otherwise ErrShortData will be
// returned.
//
@@ -1230,30 +1559,48 @@ func (r *reedSolomon) Split(data []byte) ([][]byte, error) {
if len(data) == 0 {
return nil, ErrShortData
}
if r.totalShards == 1 {
return [][]byte{data}, nil
}
dataLen := len(data)
// Calculate number of bytes per data shard.
perShard := (len(data) + r.DataShards - 1) / r.DataShards
perShard := (len(data) + r.dataShards - 1) / r.dataShards
needTotal := r.totalShards * perShard
if cap(data) > len(data) {
data = data[:cap(data)]
if cap(data) > needTotal {
data = data[:needTotal]
} else {
data = data[:cap(data)]
}
clear := data[dataLen:]
for i := range clear {
clear[i] = 0
}
}
// Only allocate memory if necessary
var padding []byte
if len(data) < (r.Shards * perShard) {
var padding [][]byte
if len(data) < needTotal {
// calculate maximum number of full shards in `data` slice
fullShards := len(data) / perShard
padding = make([]byte, r.Shards*perShard-perShard*fullShards)
copy(padding, data[perShard*fullShards:])
data = data[0 : perShard*fullShards]
} else {
for i := dataLen; i < dataLen+r.DataShards; i++ {
data[i] = 0
padding = AllocAligned(r.totalShards-fullShards, perShard)
if dataLen > perShard*fullShards {
// Copy partial shards
copyFrom := data[perShard*fullShards : dataLen]
for i := range padding {
if len(copyFrom) <= 0 {
break
}
copyFrom = copyFrom[copy(padding[i], copyFrom):]
}
}
}
// Split into equal-length shards.
dst := make([][]byte, r.Shards)
dst := make([][]byte, r.totalShards)
i := 0
for ; i < len(dst) && len(data) >= perShard; i++ {
dst[i] = data[:perShard:perShard]
@@ -1261,8 +1608,8 @@ func (r *reedSolomon) Split(data []byte) ([][]byte, error) {
}
for j := 0; i+j < len(dst); j++ {
dst[i+j] = padding[:perShard:perShard]
padding = padding[perShard:]
dst[i+j] = padding[0]
padding = padding[1:]
}
return dst, nil
@@ -1282,10 +1629,10 @@ var ErrReconstructRequired = errors.New("reconstruction required as one or more
// If one or more required data shards are nil, ErrReconstructRequired will be returned.
func (r *reedSolomon) Join(dst io.Writer, shards [][]byte, outSize int) error {
// Do we have enough shards?
if len(shards) < r.DataShards {
if len(shards) < r.dataShards {
return ErrTooFewShards
}
shards = shards[:r.DataShards]
shards = shards[:r.dataShards]
// Do we have enough data?
size := 0
+31 -20
View File
@@ -8,7 +8,6 @@
package reedsolomon
import (
"bytes"
"errors"
"fmt"
"io"
@@ -147,6 +146,10 @@ type rsStream struct {
// you want to use. You can reuse this encoder.
// Note that the maximum number of data shards is 256.
func NewStream(dataShards, parityShards int, o ...Option) (StreamEncoder, error) {
if dataShards+parityShards > 256 {
return nil, ErrMaxShardNum
}
r := rsStream{o: defaultOptions}
for _, opt := range o {
opt(&r.o)
@@ -169,11 +172,7 @@ func NewStream(dataShards, parityShards int, o ...Option) (StreamEncoder, error)
r.r = enc.(*reedSolomon)
r.blockPool.New = func() interface{} {
out := make([][]byte, dataShards+parityShards)
for i := range out {
out[i] = make([]byte, r.o.streamBS)
}
return out
return AllocAligned(dataShards+parityShards, r.o.streamBS)
}
r.readShards = readShards
r.writeShards = writeShards
@@ -219,18 +218,18 @@ func (r *rsStream) createSlice() [][]byte {
// will be returned. If a parity writer returns an error, a
// StreamWriteError will be returned.
func (r *rsStream) Encode(data []io.Reader, parity []io.Writer) error {
if len(data) != r.r.DataShards {
if len(data) != r.r.dataShards {
return ErrTooFewShards
}
if len(parity) != r.r.ParityShards {
if len(parity) != r.r.parityShards {
return ErrTooFewShards
}
all := r.createSlice()
defer r.blockPool.Put(all)
in := all[:r.r.DataShards]
out := all[r.r.DataShards:]
in := all[:r.r.dataShards]
out := all[r.r.dataShards:]
read := 0
for {
@@ -425,7 +424,7 @@ func cWriteShards(out []io.Writer, in [][]byte) error {
// If a shard stream returns an error, a StreamReadError type error
// will be returned.
func (r *rsStream) Verify(shards []io.Reader) (bool, error) {
if len(shards) != r.r.Shards {
if len(shards) != r.r.totalShards {
return false, ErrTooFewShards
}
@@ -472,10 +471,10 @@ var ErrReconstructMismatch = errors.New("valid shards and fill shards are mutual
// However its integrity is not automatically verified.
// Use the Verify function to check in case the data set is complete.
func (r *rsStream) Reconstruct(valid []io.Reader, fill []io.Writer) error {
if len(valid) != r.r.Shards {
if len(valid) != r.r.totalShards {
return ErrTooFewShards
}
if len(fill) != r.r.Shards {
if len(fill) != r.r.totalShards {
return ErrTooFewShards
}
@@ -486,7 +485,7 @@ func (r *rsStream) Reconstruct(valid []io.Reader, fill []io.Writer) error {
if valid[i] != nil && fill[i] != nil {
return ErrReconstructMismatch
}
if i >= r.r.DataShards && fill[i] != nil {
if i >= r.r.dataShards && fill[i] != nil {
reconDataOnly = false
}
}
@@ -530,12 +529,12 @@ func (r *rsStream) Reconstruct(valid []io.Reader, fill []io.Writer) error {
// If the total data size is less than outSize, ErrShortData will be returned.
func (r *rsStream) Join(dst io.Writer, shards []io.Reader, outSize int64) error {
// Do we have enough shards?
if len(shards) < r.r.DataShards {
if len(shards) < r.r.dataShards {
return ErrTooFewShards
}
// Trim off parity shards if any
shards = shards[:r.r.DataShards]
shards = shards[:r.r.dataShards]
for i := range shards {
if shards[i] == nil {
return StreamReadError{Err: ErrShardNoData, Stream: i}
@@ -571,7 +570,7 @@ func (r *rsStream) Split(data io.Reader, dst []io.Writer, size int64) error {
if size == 0 {
return ErrShortData
}
if len(dst) != r.r.DataShards {
if len(dst) != r.r.dataShards {
return ErrInvShardNum
}
@@ -582,11 +581,11 @@ func (r *rsStream) Split(data io.Reader, dst []io.Writer, size int64) error {
}
// Calculate number of bytes per shard.
perShard := (size + int64(r.r.DataShards) - 1) / int64(r.r.DataShards)
perShard := (size + int64(r.r.dataShards) - 1) / int64(r.r.dataShards)
// Pad data to r.Shards*perShard.
padding := make([]byte, (int64(r.r.Shards)*perShard)-size)
data = io.MultiReader(data, bytes.NewBuffer(padding))
paddingSize := (int64(r.r.totalShards) * perShard) - size
data = io.MultiReader(data, io.LimitReader(zeroPaddingReader{}, paddingSize))
// Split into equal-length shards and copy.
for i := range dst {
@@ -601,3 +600,15 @@ func (r *rsStream) Split(data io.Reader, dst []io.Writer, size int64) error {
return nil
}
type zeroPaddingReader struct{}
var _ io.Reader = &zeroPaddingReader{}
func (t zeroPaddingReader) Read(p []byte) (n int, err error) {
n = len(p)
for i := 0; i < n; i++ {
p[i] = 0
}
return n, nil
}
+1
View File
@@ -10,3 +10,4 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.idea/
+7 -6
View File
@@ -27,6 +27,7 @@ const X86FalseSharingRange = 128
// The struct is padded to avoid false sharing.
type x86 struct {
_ [X86FalseSharingRange]byte
HasCMPXCHG16B bool
HasAES bool
HasADX bool
HasAVX bool
@@ -58,14 +59,14 @@ type x86 struct {
// Unit: Hz.
//
// Warn:
// 1. If it's 0, means can't get it. Don't use it.
// 2. Don't use it if you want "100%" precise timestamp.
// 1. If it's 0, means failed to get it from frequency table provided by Intel manual.
TSCFrequency uint64
Name string
Signature string // DisplayFamily_DisplayModel.
Family uint32 // CPU family number.
Model uint32 // CPU model number.
Name string
Signature string // DisplayFamily_DisplayModel.
Family uint32 // CPU family number.
Model uint32 // CPU model number.
SteppingID uint32
_ [X86FalseSharingRange]byte
}
+1
View File
@@ -8,6 +8,7 @@
// +build !arm64
// +build !ppc64
// +build !ppc64le
// +build !riscv64
// +build !s390x
package cpu
+7 -9
View File
@@ -2,10 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "textflag.h"
// func stfle() facilityList
TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32
TEXT ·stfle(SB), 4|512, $0-32
MOVD $ret+0(FP), R1
MOVD $3, R0 // last doubleword index to store
XC $32, (R1), (R1) // clear 4 doublewords (32 bytes)
@@ -13,42 +11,42 @@ TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32
RET
// func kmQuery() queryResult
TEXT ·kmQuery(SB), NOSPLIT|NOFRAME, $0-16
TEXT ·kmQuery(SB), 4|512, $0-16
MOVD $0, R0 // set function code to 0 (KM-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB92E0024 // cipher message (KM)
RET
// func kmcQuery() queryResult
TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16
TEXT ·kmcQuery(SB), 4|512, $0-16
MOVD $0, R0 // set function code to 0 (KMC-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB92F0024 // cipher message with chaining (KMC)
RET
// func kmctrQuery() queryResult
TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16
TEXT ·kmctrQuery(SB), 4|512, $0-16
MOVD $0, R0 // set function code to 0 (KMCTR-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB92D4024 // cipher message with counter (KMCTR)
RET
// func kmaQuery() queryResult
TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16
TEXT ·kmaQuery(SB), 4|512, $0-16
MOVD $0, R0 // set function code to 0 (KMA-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xb9296024 // cipher message with authentication (KMA)
RET
// func kimdQuery() queryResult
TEXT ·kimdQuery(SB), NOSPLIT|NOFRAME, $0-16
TEXT ·kimdQuery(SB), 4|512, $0-16
MOVD $0, R0 // set function code to 0 (KIMD-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB93E0024 // compute intermediate message digest (KIMD)
RET
// func klmdQuery() queryResult
TEXT ·klmdQuery(SB), NOSPLIT|NOFRAME, $0-16
TEXT ·klmdQuery(SB), 4|512, $0-16
MOVD $0, R0 // set function code to 0 (KLMD-Query)
MOVD $ret+0(FP), R1 // address of 16-byte return value
WORD $0xB93F0024 // compute last message digest (KLMD)
+24 -16
View File
@@ -34,6 +34,7 @@ const (
cpuid_AES = 1 << 25
cpuid_OSXSAVE = 1 << 27
cpuid_AVX = 1 << 28
cpuid_CMPXCHG16B = 1 << 13
// ebx bits
cpuid_BMI1 = 1 << 3
@@ -98,6 +99,7 @@ func doinit() {
X86.HasSSE42 = isSet(ecx1, cpuid_SSE42)
X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT)
X86.HasAES = isSet(ecx1, cpuid_AES)
X86.HasCMPXCHG16B = isSet(ecx1, cpuid_CMPXCHG16B)
X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE)
osSupportsAVX := false
@@ -132,13 +134,13 @@ func doinit() {
X86.HasInvariantTSC = hasInvariantTSC()
X86.Family, X86.Model = getFamilyModel()
X86.Family, X86.Model, X86.SteppingID = getVersionInfo()
X86.Signature = makeSignature(X86.Family, X86.Model)
X86.Name = getName()
X86.TSCFrequency = getNativeTSCFrequency(X86.Name, X86.Signature)
X86.TSCFrequency = getNativeTSCFrequency(X86.Name, X86.Signature, X86.SteppingID)
}
func isSet(hwc uint32, value uint32) bool {
@@ -168,7 +170,7 @@ func getName() string {
// getNativeTSCFrequency gets TSC frequency from CPUID,
// only supports Intel (Skylake or later microarchitecture) & key information is from Intel manual & kernel codes
// (especially this commit: https://github.com/torvalds/linux/commit/604dc9170f2435d27da5039a3efd757dceadc684).
func getNativeTSCFrequency(name, sign string) uint64 {
func getNativeTSCFrequency(name, sign string, steppingID uint32) uint64 {
if vendorID() != Intel {
return 0
@@ -192,7 +194,7 @@ func getNativeTSCFrequency(name, sign string) uint64 {
// Skylake, Kabylake and all variants of those two chipsets report a
// crystal frequency of zero.
if ecx == 0 { // Crystal clock frequency is not enumerated.
ecx = getCrystalClockFrequency(sign)
ecx = getCrystalClockFrequency(sign, steppingID)
}
// TSC frequency = “core crystal clock frequency” * EBX/EAX.
@@ -204,11 +206,11 @@ func getNativeTSCFrequency(name, sign string) uint64 {
// Volume 4: Model-Specific Registers
// & https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/intel-family.h
const (
IntelFam6SkylakeL = "06_4EH"
IntelFam6Skylake = "06_5EH"
IntelFam6SkylakeX = "06_55H"
IntelFam6KabylakeL = "06_8EH"
IntelFam6Kabylake = "06_9EH"
IntelFam6SkylakeL = "06_4EH"
IntelFam6Skylake = "06_5EH"
IntelFam6XeonScalable = "06_55H"
IntelFam6KabylakeL = "06_8EH"
IntelFam6Kabylake = "06_9EH"
)
// getCrystalClockFrequency gets crystal clock frequency
@@ -223,10 +225,11 @@ const (
// With this report, I set a coefficient (0.9975) for IntelFam6SkyLakeX.
//
// Unlike the kernel way (mentioned in https://github.com/torvalds/linux/commit/604dc9170f2435d27da5039a3efd757dceadc684),
// I prefer the Intel hardcoded tables,
// I prefer the Intel hardcoded tables, (in <Intel® 64 and IA-32 Architectures Software Developers Manual, Volume 3>
// 18.7.3 Determining the Processor Base Frequency, Table 18-85. Nominal Core Crystal Clock Frequency)
// because after some testing (comparing with wall clock, see https://github.com/templexxx/tsc/tsc_test.go for more details),
// I found hardcoded tables are more accurate.
func getCrystalClockFrequency(sign string) uint32 {
func getCrystalClockFrequency(sign string, steppingID uint32) uint32 {
if maxFunctionID() < 0x16 {
return 0
@@ -237,8 +240,13 @@ func getCrystalClockFrequency(sign string) uint32 {
return 24 * 1000 * 1000
case IntelFam6Skylake:
return 24 * 1000 * 1000
case IntelFam6SkylakeX:
return 25 * 1000 * 1000 * 0.9975
case IntelFam6XeonScalable:
// SKL-SP.
// see: https://community.intel.com/t5/Software-Tuning-Performance/How-to-detect-microarchitecture-on-Xeon-Scalable/m-p/1205162#M7633.
if steppingID == 0x2 || steppingID == 0x3 || steppingID == 0x4 {
return 25 * 1000 * 1000 * 0.9975
}
return 25 * 1000 * 1000 // TODO check other Xeon Scalable has no slow down issue.
case IntelFam6KabylakeL:
return 24 * 1000 * 1000
case IntelFam6Kabylake:
@@ -248,9 +256,9 @@ func getCrystalClockFrequency(sign string) uint32 {
return 0
}
func getFamilyModel() (uint32, uint32) {
func getVersionInfo() (uint32, uint32, uint32) {
if maxFunctionID() < 0x1 {
return 0, 0
return 0, 0, 0
}
eax, _, _, _ := cpuid(1, 0)
family := (eax >> 8) & 0xf
@@ -263,7 +271,7 @@ func getFamilyModel() (uint32, uint32) {
if family == 0x6 || family == 0xf {
displayModel = ((eax >> 12) & 0xf0) + model
}
return displayFamily, displayModel
return displayFamily, displayModel, eax & 0x7
}
// signature format: XX_XXH
+2 -4
View File
@@ -4,10 +4,8 @@
// +build 386 amd64 amd64p32
#include "textflag.h"
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
TEXT ·cpuid(SB), NOSPLIT, $0-24
TEXT ·cpuid(SB), 4, $0-24
MOVL eaxArg+0(FP), AX
MOVL ecxArg+4(FP), CX
CPUID
@@ -18,7 +16,7 @@ TEXT ·cpuid(SB), NOSPLIT, $0-24
RET
// func xgetbv() (eax, edx uint32)
TEXT ·xgetbv(SB),NOSPLIT,$0-8
TEXT ·xgetbv(SB),4,$0-8
#ifdef GOOS_nacl
// nacl does not support XGETBV.
MOVL $0, eax+0(FP)
-5
View File
@@ -1,5 +0,0 @@
module github.com/templexxx/xorsimd
require github.com/templexxx/cpu v0.0.1
go 1.13
-2
View File
@@ -1,2 +0,0 @@
github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
+1 -3
View File
@@ -3,8 +3,6 @@
// Use of this source code is governed by the MIT License
// that can be found in the LICENSE file.
#include "textflag.h"
#define dst BX // parity's address
#define d2src SI // two-dimension src_slice's address
#define csrc CX // cnt of src
@@ -19,7 +17,7 @@
#define src_val1 R14
// func encodeAVX2(dst []byte, src [][]byte)
TEXT ·encodeAVX2(SB), NOSPLIT, $0
TEXT ·encodeAVX2(SB), 4, $0
MOVQ d+0(FP), dst
MOVQ s+24(FP), d2src
MOVQ c+32(FP), csrc
+1 -3
View File
@@ -3,8 +3,6 @@
// Use of this source code is governed by the MIT License
// that can be found in the LICENSE file.
#include "textflag.h"
#define dst BX // parity's address
#define d2src SI // two-dimension src_slice's address
#define csrc CX // cnt of src
@@ -19,7 +17,7 @@
#define src_val1 R14
// func encodeAVX512(dst []byte, src [][]byte)
TEXT ·encodeAVX512(SB), NOSPLIT, $0
TEXT ·encodeAVX512(SB), 4, $0
MOVQ d+0(FP), dst
MOVQ src+24(FP), d2src
MOVQ c+32(FP), csrc
+3 -4
View File
@@ -1,7 +1,6 @@
#include "textflag.h"
// func bytesN(dst, a, b *byte, n int)
TEXT ·bytesN(SB), NOSPLIT, $0
TEXT ·bytesN(SB), 4, $0
MOVQ d+0(FP), BX
MOVQ a+8(FP), SI
MOVQ b+16(FP), CX
@@ -50,7 +49,7 @@ ret:
RET
// func bytes8(dst, a, b *byte)
TEXT ·bytes8(SB), NOSPLIT, $0
TEXT ·bytes8(SB), 4, $0
MOVQ d+0(FP), BX
MOVQ a+8(FP), SI
MOVQ b+16(FP), CX
@@ -61,7 +60,7 @@ TEXT ·bytes8(SB), NOSPLIT, $0
RET
// func bytes16(dst, a, b *byte)
TEXT ·bytes16(SB), NOSPLIT, $0
TEXT ·bytes16(SB), 4, $0
MOVQ d+0(FP), BX
MOVQ a+8(FP), SI
MOVQ b+16(FP), CX
+1 -2
View File
@@ -3,7 +3,6 @@
// Use of this source code is governed by the MIT License
// that can be found in the LICENSE file.
#include "textflag.h"
#define dst BX // parity's address
#define d2src SI // two-dimension src_slice's address
@@ -19,7 +18,7 @@
#define src_val1 R14
// func encodeSSE2(dst []byte, src [][]byte)
TEXT ·encodeSSE2(SB), NOSPLIT, $0
TEXT ·encodeSSE2(SB), 4, $0
MOVQ d+0(FP), dst
MOVQ src+24(FP), d2src
MOVQ c+32(FP), csrc
+8
View File
@@ -1,2 +1,10 @@
*.coverprofile
coverage.txt
node_modules/
vendor
.idea
/.local/
/internal/
/site/
package.json
package-lock.json
-27
View File
@@ -1,27 +0,0 @@
language: go
sudo: false
dist: trusty
osx_image: xcode8.3
go: 1.11.x
os:
- linux
- osx
cache:
directories:
- node_modules
before_script:
- go get github.com/urfave/gfmrun/... || true
- go get golang.org/x/tools/cmd/goimports
- if [ ! -f node_modules/.bin/markdown-toc ] ; then
npm install markdown-toc ;
fi
script:
- ./runtests gen
- ./runtests vet
- ./runtests test
- ./runtests gfmrun
- ./runtests toc
-460
View File
@@ -1,460 +0,0 @@
# Change Log
**ATTN**: This project uses [semantic versioning](http://semver.org/).
## [Unreleased]
## [1.21.0] - 2019-08-02
### Fixed
* Fix using "slice" flag types with `EnvVar` in [urfave/cli/pull/687](https://github.com/urfave/cli/pull/687) via [@joshuarubin](https://github.com/joshuarubin)
* Fix regression of `SkipFlagParsing` behavior in [urfave/cli/pull/697](https://github.com/urfave/cli/pull/697) via [@jszwedko](https://github.com/jszwedko)
* Fix handling `ShortOptions` and `SkipArgReorder` in [urfave/cli/pull/686](https://github.com/urfave/cli/pull/686) via [@baude](https://github.com/baude)
* Fix args reordering when bool flags are present in [urfave/cli/pull/712](https://github.com/urfave/cli/pull/712) via [@windler](https://github.com/windler)
* Fix parsing of short options in [urfave/cli/pull/758](https://github.com/urfave/cli/pull/758) via [@vrothberg](https://github.com/vrothberg)
### Added / Changed
* Added _"required flags"_ support in [urfave/cli/pull/819](https://github.com/urfave/cli/pull/819) via [@lynncyrin](https://github.com/lynncyrin/)
* Cleaned up help output in [urfave/cli/pull/664](https://github.com/urfave/cli/pull/664) via [@maguro](https://github.com/maguro)
* Case is now considered when sorting strings in [urfave/cli/pull/676](https://github.com/urfave/cli/pull/676) via [@rliebz](https://github.com/rliebz)
* Backport JSON `InputSource` to v1 in [urfave/cli/pull/598](https://github.com/urfave/cli/pull/598) via [@jszwedko](https://github.com/jszwedko)
* Allow more customization of flag help strings in [urfave/cli/pull/661](https://github.com/urfave/cli/pull/661) via [@rliebz](https://github.com/rliebz)
* Allow custom `ExitError` handler function in [urfave/cli/pull/628](https://github.com/urfave/cli/pull/628) via [@phinnaeus](https://github.com/phinnaeus)
* Allow loading a variable from a file in [urfave/cli/pull/675](https://github.com/urfave/cli/pull/675) via [@jmccann](https://github.com/jmccann)
* Allow combining short bool names in [urfave/cli/pull/684](https://github.com/urfave/cli/pull/684) via [@baude](https://github.com/baude)
## [1.20.0] - 2017-08-10
### Fixed
* `HandleExitCoder` is now correctly iterates over all errors in
a `MultiError`. The exit code is the exit code of the last error or `1` if
there are no `ExitCoder`s in the `MultiError`.
* Fixed YAML file loading on Windows (previously would fail validate the file path)
* Subcommand `Usage`, `Description`, `ArgsUsage`, `OnUsageError` correctly
propogated
* `ErrWriter` is now passed downwards through command structure to avoid the
need to redefine it
* Pass `Command` context into `OnUsageError` rather than parent context so that
all fields are avaiable
* Errors occuring in `Before` funcs are no longer double printed
* Use `UsageText` in the help templates for commands and subcommands if
defined; otherwise build the usage as before (was previously ignoring this
field)
* `IsSet` and `GlobalIsSet` now correctly return whether a flag is set if
a program calls `Set` or `GlobalSet` directly after flag parsing (would
previously only return `true` if the flag was set during parsing)
### Changed
* No longer exit the program on command/subcommand error if the error raised is
not an `OsExiter`. This exiting behavior was introduced in 1.19.0, but was
determined to be a regression in functionality. See [the
PR](https://github.com/urfave/cli/pull/595) for discussion.
### Added
* `CommandsByName` type was added to make it easy to sort `Command`s by name,
alphabetically
* `altsrc` now handles loading of string and int arrays from TOML
* Support for definition of custom help templates for `App` via
`CustomAppHelpTemplate`
* Support for arbitrary key/value fields on `App` to be used with
`CustomAppHelpTemplate` via `ExtraInfo`
* `HelpFlag`, `VersionFlag`, and `BashCompletionFlag` changed to explictly be
`cli.Flag`s allowing for the use of custom flags satisfying the `cli.Flag`
interface to be used.
## [1.19.1] - 2016-11-21
### Fixed
- Fixes regression introduced in 1.19.0 where using an `ActionFunc` as
the `Action` for a command would cause it to error rather than calling the
function. Should not have a affected declarative cases using `func(c
*cli.Context) err)`.
- Shell completion now handles the case where the user specifies
`--generate-bash-completion` immediately after a flag that takes an argument.
Previously it call the application with `--generate-bash-completion` as the
flag value.
## [1.19.0] - 2016-11-19
### Added
- `FlagsByName` was added to make it easy to sort flags (e.g. `sort.Sort(cli.FlagsByName(app.Flags))`)
- A `Description` field was added to `App` for a more detailed description of
the application (similar to the existing `Description` field on `Command`)
- Flag type code generation via `go generate`
- Write to stderr and exit 1 if action returns non-nil error
- Added support for TOML to the `altsrc` loader
- `SkipArgReorder` was added to allow users to skip the argument reordering.
This is useful if you want to consider all "flags" after an argument as
arguments rather than flags (the default behavior of the stdlib `flag`
library). This is backported functionality from the [removal of the flag
reordering](https://github.com/urfave/cli/pull/398) in the unreleased version
2
- For formatted errors (those implementing `ErrorFormatter`), the errors will
be formatted during output. Compatible with `pkg/errors`.
### Changed
- Raise minimum tested/supported Go version to 1.2+
### Fixed
- Consider empty environment variables as set (previously environment variables
with the equivalent of `""` would be skipped rather than their value used).
- Return an error if the value in a given environment variable cannot be parsed
as the flag type. Previously these errors were silently swallowed.
- Print full error when an invalid flag is specified (which includes the invalid flag)
- `App.Writer` defaults to `stdout` when `nil`
- If no action is specified on a command or app, the help is now printed instead of `panic`ing
- `App.Metadata` is initialized automatically now (previously was `nil` unless initialized)
- Correctly show help message if `-h` is provided to a subcommand
- `context.(Global)IsSet` now respects environment variables. Previously it
would return `false` if a flag was specified in the environment rather than
as an argument
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
- `altsrc`s import paths were updated to use `gopkg.in/urfave/cli.v1`. This
fixes issues that occurred when `gopkg.in/urfave/cli.v1` was imported as well
as `altsrc` where Go would complain that the types didn't match
## [1.18.1] - 2016-08-28
### Fixed
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user (backported)
## [1.18.0] - 2016-06-27
### Added
- `./runtests` test runner with coverage tracking by default
- testing on OS X
- testing on Windows
- `UintFlag`, `Uint64Flag`, and `Int64Flag` types and supporting code
### Changed
- Use spaces for alignment in help/usage output instead of tabs, making the
output alignment consistent regardless of tab width
### Fixed
- Printing of command aliases in help text
- Printing of visible flags for both struct and struct pointer flags
- Display the `help` subcommand when using `CommandCategories`
- No longer swallows `panic`s that occur within the `Action`s themselves when
detecting the signature of the `Action` field
## [1.17.1] - 2016-08-28
### Fixed
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
## [1.17.0] - 2016-05-09
### Added
- Pluggable flag-level help text rendering via `cli.DefaultFlagStringFunc`
- `context.GlobalBoolT` was added as an analogue to `context.GlobalBool`
- Support for hiding commands by setting `Hidden: true` -- this will hide the
commands in help output
### Changed
- `Float64Flag`, `IntFlag`, and `DurationFlag` default values are no longer
quoted in help text output.
- All flag types now include `(default: {value})` strings following usage when a
default value can be (reasonably) detected.
- `IntSliceFlag` and `StringSliceFlag` usage strings are now more consistent
with non-slice flag types
- Apps now exit with a code of 3 if an unknown subcommand is specified
(previously they printed "No help topic for...", but still exited 0. This
makes it easier to script around apps built using `cli` since they can trust
that a 0 exit code indicated a successful execution.
- cleanups based on [Go Report Card
feedback](https://goreportcard.com/report/github.com/urfave/cli)
## [1.16.1] - 2016-08-28
### Fixed
- Removed deprecation warnings to STDERR to avoid them leaking to the end-user
## [1.16.0] - 2016-05-02
### Added
- `Hidden` field on all flag struct types to omit from generated help text
### Changed
- `BashCompletionFlag` (`--enable-bash-completion`) is now omitted from
generated help text via the `Hidden` field
### Fixed
- handling of error values in `HandleAction` and `HandleExitCoder`
## [1.15.0] - 2016-04-30
### Added
- This file!
- Support for placeholders in flag usage strings
- `App.Metadata` map for arbitrary data/state management
- `Set` and `GlobalSet` methods on `*cli.Context` for altering values after
parsing.
- Support for nested lookup of dot-delimited keys in structures loaded from
YAML.
### Changed
- The `App.Action` and `Command.Action` now prefer a return signature of
`func(*cli.Context) error`, as defined by `cli.ActionFunc`. If a non-nil
`error` is returned, there may be two outcomes:
- If the error fulfills `cli.ExitCoder`, then `os.Exit` will be called
automatically
- Else the error is bubbled up and returned from `App.Run`
- Specifying an `Action` with the legacy return signature of
`func(*cli.Context)` will produce a deprecation message to stderr
- Specifying an `Action` that is not a `func` type will produce a non-zero exit
from `App.Run`
- Specifying an `Action` func that has an invalid (input) signature will
produce a non-zero exit from `App.Run`
### Deprecated
- <a name="deprecated-cli-app-runandexitonerror"></a>
`cli.App.RunAndExitOnError`, which should now be done by returning an error
that fulfills `cli.ExitCoder` to `cli.App.Run`.
- <a name="deprecated-cli-app-action-signature"></a> the legacy signature for
`cli.App.Action` of `func(*cli.Context)`, which should now have a return
signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`.
### Fixed
- Added missing `*cli.Context.GlobalFloat64` method
## [1.14.0] - 2016-04-03 (backfilled 2016-04-25)
### Added
- Codebeat badge
- Support for categorization via `CategorizedHelp` and `Categories` on app.
### Changed
- Use `filepath.Base` instead of `path.Base` in `Name` and `HelpName`.
### Fixed
- Ensure version is not shown in help text when `HideVersion` set.
## [1.13.0] - 2016-03-06 (backfilled 2016-04-25)
### Added
- YAML file input support.
- `NArg` method on context.
## [1.12.0] - 2016-02-17 (backfilled 2016-04-25)
### Added
- Custom usage error handling.
- Custom text support in `USAGE` section of help output.
- Improved help messages for empty strings.
- AppVeyor CI configuration.
### Changed
- Removed `panic` from default help printer func.
- De-duping and optimizations.
### Fixed
- Correctly handle `Before`/`After` at command level when no subcommands.
- Case of literal `-` argument causing flag reordering.
- Environment variable hints on Windows.
- Docs updates.
## [1.11.1] - 2015-12-21 (backfilled 2016-04-25)
### Changed
- Use `path.Base` in `Name` and `HelpName`
- Export `GetName` on flag types.
### Fixed
- Flag parsing when skipping is enabled.
- Test output cleanup.
- Move completion check to account for empty input case.
## [1.11.0] - 2015-11-15 (backfilled 2016-04-25)
### Added
- Destination scan support for flags.
- Testing against `tip` in Travis CI config.
### Changed
- Go version in Travis CI config.
### Fixed
- Removed redundant tests.
- Use correct example naming in tests.
## [1.10.2] - 2015-10-29 (backfilled 2016-04-25)
### Fixed
- Remove unused var in bash completion.
## [1.10.1] - 2015-10-21 (backfilled 2016-04-25)
### Added
- Coverage and reference logos in README.
### Fixed
- Use specified values in help and version parsing.
- Only display app version and help message once.
## [1.10.0] - 2015-10-06 (backfilled 2016-04-25)
### Added
- More tests for existing functionality.
- `ArgsUsage` at app and command level for help text flexibility.
### Fixed
- Honor `HideHelp` and `HideVersion` in `App.Run`.
- Remove juvenile word from README.
## [1.9.0] - 2015-09-08 (backfilled 2016-04-25)
### Added
- `FullName` on command with accompanying help output update.
- Set default `$PROG` in bash completion.
### Changed
- Docs formatting.
### Fixed
- Removed self-referential imports in tests.
## [1.8.0] - 2015-06-30 (backfilled 2016-04-25)
### Added
- Support for `Copyright` at app level.
- `Parent` func at context level to walk up context lineage.
### Fixed
- Global flag processing at top level.
## [1.7.1] - 2015-06-11 (backfilled 2016-04-25)
### Added
- Aggregate errors from `Before`/`After` funcs.
- Doc comments on flag structs.
- Include non-global flags when checking version and help.
- Travis CI config updates.
### Fixed
- Ensure slice type flags have non-nil values.
- Collect global flags from the full command hierarchy.
- Docs prose.
## [1.7.0] - 2015-05-03 (backfilled 2016-04-25)
### Changed
- `HelpPrinter` signature includes output writer.
### Fixed
- Specify go 1.1+ in docs.
- Set `Writer` when running command as app.
## [1.6.0] - 2015-03-23 (backfilled 2016-04-25)
### Added
- Multiple author support.
- `NumFlags` at context level.
- `Aliases` at command level.
### Deprecated
- `ShortName` at command level.
### Fixed
- Subcommand help output.
- Backward compatible support for deprecated `Author` and `Email` fields.
- Docs regarding `Names`/`Aliases`.
## [1.5.0] - 2015-02-20 (backfilled 2016-04-25)
### Added
- `After` hook func support at app and command level.
### Fixed
- Use parsed context when running command as subcommand.
- Docs prose.
## [1.4.1] - 2015-01-09 (backfilled 2016-04-25)
### Added
- Support for hiding `-h / --help` flags, but not `help` subcommand.
- Stop flag parsing after `--`.
### Fixed
- Help text for generic flags to specify single value.
- Use double quotes in output for defaults.
- Use `ParseInt` instead of `ParseUint` for int environment var values.
- Use `0` as base when parsing int environment var values.
## [1.4.0] - 2014-12-12 (backfilled 2016-04-25)
### Added
- Support for environment variable lookup "cascade".
- Support for `Stdout` on app for output redirection.
### Fixed
- Print command help instead of app help in `ShowCommandHelp`.
## [1.3.1] - 2014-11-13 (backfilled 2016-04-25)
### Added
- Docs and example code updates.
### Changed
- Default `-v / --version` flag made optional.
## [1.3.0] - 2014-08-10 (backfilled 2016-04-25)
### Added
- `FlagNames` at context level.
- Exposed `VersionPrinter` var for more control over version output.
- Zsh completion hook.
- `AUTHOR` section in default app help template.
- Contribution guidelines.
- `DurationFlag` type.
## [1.2.0] - 2014-08-02
### Added
- Support for environment variable defaults on flags plus tests.
## [1.1.0] - 2014-07-15
### Added
- Bash completion.
- Optional hiding of built-in help command.
- Optional skipping of flag parsing at command level.
- `Author`, `Email`, and `Compiled` metadata on app.
- `Before` hook func support at app and command level.
- `CommandNotFound` func support at app level.
- Command reference available on context.
- `GenericFlag` type.
- `Float64Flag` type.
- `BoolTFlag` type.
- `IsSet` flag helper on context.
- More flag lookup funcs at context level.
- More tests &amp; docs.
### Changed
- Help template updates to account for presence/absence of flags.
- Separated subcommand help template.
- Exposed `HelpPrinter` var for more control over help output.
## [1.0.0] - 2013-11-01
### Added
- `help` flag in default app flag set and each command flag set.
- Custom handling of argument parsing errors.
- Command lookup by name at app level.
- `StringSliceFlag` type and supporting `StringSlice` type.
- `IntSliceFlag` type and supporting `IntSlice` type.
- Slice type flag lookups by name at context level.
- Export of app and command help functions.
- More tests &amp; docs.
## 0.1.0 - 2013-07-22
### Added
- Initial implementation.
[Unreleased]: https://github.com/urfave/cli/compare/v1.21.0...HEAD
[1.21.0]: https://github.com/urfave/cli/compare/v1.20.0...v1.21.0
[1.20.0]: https://github.com/urfave/cli/compare/v1.19.1...v1.20.0
[1.19.1]: https://github.com/urfave/cli/compare/v1.19.0...v1.19.1
[1.19.0]: https://github.com/urfave/cli/compare/v1.18.0...v1.19.0
[1.18.0]: https://github.com/urfave/cli/compare/v1.17.0...v1.18.0
[1.17.0]: https://github.com/urfave/cli/compare/v1.16.0...v1.17.0
[1.16.0]: https://github.com/urfave/cli/compare/v1.15.0...v1.16.0
[1.15.0]: https://github.com/urfave/cli/compare/v1.14.0...v1.15.0
[1.14.0]: https://github.com/urfave/cli/compare/v1.13.0...v1.14.0
[1.13.0]: https://github.com/urfave/cli/compare/v1.12.0...v1.13.0
[1.12.0]: https://github.com/urfave/cli/compare/v1.11.1...v1.12.0
[1.11.1]: https://github.com/urfave/cli/compare/v1.11.0...v1.11.1
[1.11.0]: https://github.com/urfave/cli/compare/v1.10.2...v1.11.0
[1.10.2]: https://github.com/urfave/cli/compare/v1.10.1...v1.10.2
[1.10.1]: https://github.com/urfave/cli/compare/v1.10.0...v1.10.1
[1.10.0]: https://github.com/urfave/cli/compare/v1.9.0...v1.10.0
[1.9.0]: https://github.com/urfave/cli/compare/v1.8.0...v1.9.0
[1.8.0]: https://github.com/urfave/cli/compare/v1.7.1...v1.8.0
[1.7.1]: https://github.com/urfave/cli/compare/v1.7.0...v1.7.1
[1.7.0]: https://github.com/urfave/cli/compare/v1.6.0...v1.7.0
[1.6.0]: https://github.com/urfave/cli/compare/v1.5.0...v1.6.0
[1.5.0]: https://github.com/urfave/cli/compare/v1.4.1...v1.5.0
[1.4.1]: https://github.com/urfave/cli/compare/v1.4.0...v1.4.1
[1.4.0]: https://github.com/urfave/cli/compare/v1.3.1...v1.4.0
[1.3.1]: https://github.com/urfave/cli/compare/v1.3.0...v1.3.1
[1.3.0]: https://github.com/urfave/cli/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/urfave/cli/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/urfave/cli/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/urfave/cli/compare/v0.1.0...v1.0.0
-19
View File
@@ -1,19 +0,0 @@
## Contributing
**NOTE**: the primary maintainer(s) may be found in
[./MAINTAINERS.md](./MAINTAINERS.md).
Feel free to put up a pull request to fix a bug or maybe add a feature. I will
give it a code review and make sure that it does not break backwards
compatibility. If I or any other collaborators agree that it is in line with
the vision of the project, we will work with you to get the code into
a mergeable state and merge it into the master branch.
If you have contributed something significant to the project, we will most
likely add you as a collaborator. As a collaborator you are given the ability
to merge others pull requests. It is very important that new code does not
break existing code, so be careful about what code you do choose to merge.
If you feel like you have contributed to the project but have not yet been added
as a collaborator, we probably forgot to add you :sweat_smile:. Please open an
issue!
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016 Jeremy Saenz & Contributors
Copyright (c) 2023 Jeremy Saenz & Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
-4
View File
@@ -1,4 +0,0 @@
- @meatballhat
- @lynncyrin
- @AudriusButkevicius
- @asahasrabuddhe
+28 -1503
View File
File diff suppressed because it is too large Load Diff
+45 -35
View File
@@ -1,9 +1,9 @@
package cli
import (
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
@@ -11,9 +11,10 @@ import (
)
var (
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
// unused variable. commented for now. will remove in future if agreed upon by everyone
//runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
@@ -94,6 +95,10 @@ type App struct {
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
CustomAppHelpTemplate string
// Boolean to enable short-option handling so user can combine several
// single-character bool arguements into one
// i.e. foobar -o -v -> foobar -ov
UseShortOptionHandling bool
didSetup bool
}
@@ -116,7 +121,6 @@ func NewApp() *App {
HelpName: filepath.Base(os.Args[0]),
Usage: "A new cli application",
UsageText: "",
Version: "0.0.0",
BashComplete: DefaultAppComplete,
Action: helpCommand.Action,
Compiled: compileTime(),
@@ -138,7 +142,7 @@ func (a *App) Setup() {
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
}
newCmds := []Command{}
var newCmds []Command
for _, c := range a.Commands {
if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
@@ -154,6 +158,10 @@ func (a *App) Setup() {
}
}
if a.Version == "" {
a.HideVersion = true
}
if !a.HideVersion {
a.appendFlag(VersionFlag)
}
@@ -173,6 +181,14 @@ func (a *App) Setup() {
}
}
func (a *App) newFlagSet() (*flag.FlagSet, error) {
return flagSet(a.Name, a.Flags)
}
func (a *App) useShortOptionHandling() bool {
return a.UseShortOptionHandling
}
// Run is the entry point to the cli app. Parses the arguments slice and routes
// to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
@@ -186,19 +202,17 @@ func (a *App) Run(arguments []string) (err error) {
// always appends the completion flag at the end of the command
shellComplete, arguments := checkShellCompleteFlag(a, arguments)
// parse flags
set, err := flagSet(a.Name, a.Flags)
set, err := a.newFlagSet()
if err != nil {
return err
}
set.SetOutput(ioutil.Discard)
err = set.Parse(arguments[1:])
err = parseIter(set, a, arguments[1:], shellComplete)
nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, nil)
if nerr != nil {
fmt.Fprintln(a.Writer, nerr)
ShowAppHelp(context)
_, _ = fmt.Fprintln(a.Writer, nerr)
_ = ShowAppHelp(context)
return nerr
}
context.shellComplete = shellComplete
@@ -213,13 +227,13 @@ func (a *App) Run(arguments []string) (err error) {
a.handleExitCoder(context, err)
return err
}
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
ShowAppHelp(context)
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
_ = ShowAppHelp(context)
return err
}
if !a.HideHelp && checkHelp(context) {
ShowAppHelp(context)
_ = ShowAppHelp(context)
return nil
}
@@ -230,11 +244,11 @@ func (a *App) Run(arguments []string) (err error) {
cerr := checkRequiredFlags(a.Flags, context)
if cerr != nil {
ShowAppHelp(context)
_ = ShowAppHelp(context)
return cerr
}
if a.After != nil {
if a.After != nil && !context.shellComplete {
defer func() {
if afterErr := a.After(context); afterErr != nil {
if err != nil {
@@ -246,11 +260,9 @@ func (a *App) Run(arguments []string) (err error) {
}()
}
if a.Before != nil {
if a.Before != nil && !context.shellComplete {
beforeErr := a.Before(context)
if beforeErr != nil {
fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
ShowAppHelp(context)
a.handleExitCoder(context, beforeErr)
err = beforeErr
return err
@@ -284,7 +296,7 @@ func (a *App) Run(arguments []string) (err error) {
// code in the cli.ExitCoder
func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil {
fmt.Fprintln(a.errWriter(), err)
_, _ = fmt.Fprintln(a.errWriter(), err)
OsExiter(1)
}
}
@@ -311,24 +323,22 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
}
a.Commands = newCmds
// parse flags
set, err := flagSet(a.Name, a.Flags)
set, err := a.newFlagSet()
if err != nil {
return err
}
set.SetOutput(ioutil.Discard)
err = set.Parse(ctx.Args().Tail())
err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete)
nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, ctx)
if nerr != nil {
fmt.Fprintln(a.Writer, nerr)
fmt.Fprintln(a.Writer)
_, _ = fmt.Fprintln(a.Writer, nerr)
_, _ = fmt.Fprintln(a.Writer)
if len(a.Commands) > 0 {
ShowSubcommandHelp(context)
_ = ShowSubcommandHelp(context)
} else {
ShowCommandHelp(ctx, context.Args().First())
_ = ShowCommandHelp(ctx, context.Args().First())
}
return nerr
}
@@ -343,8 +353,8 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
a.handleExitCoder(context, err)
return err
}
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
ShowSubcommandHelp(context)
_, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
_ = ShowSubcommandHelp(context)
return err
}
@@ -360,11 +370,11 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
cerr := checkRequiredFlags(a.Flags, context)
if cerr != nil {
ShowSubcommandHelp(context)
_ = ShowSubcommandHelp(context)
return cerr
}
if a.After != nil {
if a.After != nil && !context.shellComplete {
defer func() {
afterErr := a.After(context)
if afterErr != nil {
@@ -378,7 +388,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
}()
}
if a.Before != nil {
if a.Before != nil && !context.shellComplete {
beforeErr := a.Before(context)
if beforeErr != nil {
a.handleExitCoder(context, beforeErr)
@@ -440,7 +450,7 @@ func (a *App) VisibleCategories() []*CommandCategory {
// VisibleCommands returns a slice of the Commands with Hidden=false
func (a *App) VisibleCommands() []Command {
ret := []Command{}
var ret []Command
for _, command := range a.Commands {
if !command.Hidden {
ret = append(ret, command)
-26
View File
@@ -1,26 +0,0 @@
version: "{build}"
os: Windows Server 2016
image: Visual Studio 2017
clone_folder: c:\gopath\src\github.com\urfave\cli
environment:
GOPATH: C:\gopath
GOVERSION: 1.8.x
PYTHON: C:\Python36-x64
PYTHON_VERSION: 3.6.x
PYTHON_ARCH: 64
install:
- set PATH=%GOPATH%\bin;C:\go\bin;%PATH%
- go version
- go env
- go get github.com/urfave/gfmrun/...
- go get -v -t ./...
build_script:
- python runtests vet
- python runtests test
- python runtests gfmrun
+1 -1
View File
@@ -19,4 +19,4 @@
// }
package cli
//go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go
//go:generate go run flag-gen/main.go flag-gen/assets_vfsdata.go
+101 -104
View File
@@ -3,7 +3,6 @@ package cli
import (
"flag"
"fmt"
"io/ioutil"
"sort"
"strings"
)
@@ -99,8 +98,10 @@ type Commands []Command
// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) (err error) {
if len(c.Subcommands) > 0 {
return c.startApp(ctx)
if !c.SkipFlagParsing {
if len(c.Subcommands) > 0 {
return c.startApp(ctx)
}
}
if !c.HideHelp && (HelpFlag != BoolFlag{}) {
@@ -111,7 +112,11 @@ func (c Command) Run(ctx *Context) (err error) {
)
}
set, err := c.parseFlags(ctx.Args().Tail())
if ctx.App.UseShortOptionHandling {
c.UseShortOptionHandling = true
}
set, err := c.parseFlags(ctx.Args().Tail(), ctx.shellComplete)
context := NewContext(ctx.App, set, ctx)
context.Command = c
@@ -125,9 +130,9 @@ func (c Command) Run(ctx *Context) (err error) {
context.App.handleExitCoder(context, err)
return err
}
fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
fmt.Fprintln(context.App.Writer)
ShowCommandHelp(context, c.Name)
_, _ = fmt.Fprintln(context.App.Writer, "Incorrect Usage:", err.Error())
_, _ = fmt.Fprintln(context.App.Writer)
_ = ShowCommandHelp(context, c.Name)
return err
}
@@ -137,7 +142,7 @@ func (c Command) Run(ctx *Context) (err error) {
cerr := checkRequiredFlags(c.Flags, context)
if cerr != nil {
ShowCommandHelp(context, c.Name)
_ = ShowCommandHelp(context, c.Name)
return cerr
}
@@ -158,7 +163,6 @@ func (c Command) Run(ctx *Context) (err error) {
if c.Before != nil {
err = c.Before(context)
if err != nil {
ShowCommandHelp(context, c.Name)
context.App.handleExitCoder(context, err)
return err
}
@@ -176,60 +180,27 @@ func (c Command) Run(ctx *Context) (err error) {
return err
}
func (c *Command) parseFlags(args Args) (*flag.FlagSet, error) {
set, err := flagSet(c.Name, c.Flags)
if err != nil {
return nil, err
}
set.SetOutput(ioutil.Discard)
func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) {
if c.SkipFlagParsing {
set, err := c.newFlagSet()
if err != nil {
return nil, err
}
return set, set.Parse(append([]string{"--"}, args...))
}
if !c.SkipArgReorder {
args = reorderArgs(args)
args = reorderArgs(c.Flags, args)
}
PARSE:
err = set.Parse(args)
set, err := c.newFlagSet()
if err != nil {
return nil, err
}
err = parseIter(set, c, args, shellComplete)
if err != nil {
if c.UseShortOptionHandling {
// To enable short-option handling (e.g., "-it" vs "-i -t")
// we have to iteratively catch parsing errors. This way
// we achieve LR parsing without transforming any arguments.
// Otherwise, there is no way we can discriminate combined
// short options from common arguments that should be left
// untouched.
errStr := err.Error()
trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: ")
if errStr == trimmed {
return nil, err
}
// regenerate the initial args with the split short opts
newArgs := Args{}
for i, arg := range args {
if arg != trimmed {
newArgs = append(newArgs, arg)
continue
}
shortOpts := translateShortOptions(set, Args{trimmed})
if len(shortOpts) == 1 {
return nil, err
}
// add each short option and all remaining arguments
newArgs = append(newArgs, shortOpts...)
newArgs = append(newArgs, args[i+1:]...)
args = newArgs
// now reset the flagset parse again
set, err = flagSet(c.Name, c.Flags)
if err != nil {
return nil, err
}
set.SetOutput(ioutil.Discard)
goto PARSE
}
}
return nil, err
}
@@ -241,63 +212,87 @@ PARSE:
return set, nil
}
// reorderArgs moves all flags before arguments as this is what flag expects
func reorderArgs(args []string) []string {
var nonflags, flags []string
readFlagValue := false
for i, arg := range args {
if arg == "--" {
nonflags = append(nonflags, args[i:]...)
break
}
if readFlagValue && !strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") {
readFlagValue = false
flags = append(flags, arg)
continue
}
readFlagValue = false
if arg != "-" && strings.HasPrefix(arg, "-") {
flags = append(flags, arg)
readFlagValue = !strings.Contains(arg, "=")
} else {
nonflags = append(nonflags, arg)
}
}
return append(flags, nonflags...)
func (c *Command) newFlagSet() (*flag.FlagSet, error) {
return flagSet(c.Name, c.Flags)
}
func translateShortOptions(set *flag.FlagSet, flagArgs Args) []string {
allCharsFlags := func (s string) bool {
for i := range s {
f := set.Lookup(string(s[i]))
if f == nil {
return false
}
func (c *Command) useShortOptionHandling() bool {
return c.UseShortOptionHandling
}
// reorderArgs moves all flags (via reorderedArgs) before the rest of
// the arguments (remainingArgs) as this is what flag expects.
func reorderArgs(commandFlags []Flag, args []string) []string {
var remainingArgs, reorderedArgs []string
nextIndexMayContainValue := false
for i, arg := range args {
// if we're expecting an option-value, check if this arg is a value, in
// which case it should be re-ordered next to its associated flag
if nextIndexMayContainValue && !argIsFlag(commandFlags, arg) {
nextIndexMayContainValue = false
reorderedArgs = append(reorderedArgs, arg)
} else if arg == "--" {
// don't reorder any args after the -- delimiter As described in the POSIX spec:
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02
// > Guideline 10:
// > The first -- argument that is not an option-argument should be accepted
// > as a delimiter indicating the end of options. Any following arguments
// > should be treated as operands, even if they begin with the '-' character.
// make sure the "--" delimiter itself is at the start
remainingArgs = append([]string{"--"}, remainingArgs...)
remainingArgs = append(remainingArgs, args[i+1:]...)
break
// checks if this is an arg that should be re-ordered
} else if argIsFlag(commandFlags, arg) {
// we have determined that this is a flag that we should re-order
reorderedArgs = append(reorderedArgs, arg)
// if this arg does not contain a "=", then the next index may contain the value for this flag
nextIndexMayContainValue = !strings.Contains(arg, "=")
// simply append any remaining args
} else {
remainingArgs = append(remainingArgs, arg)
}
return true
}
// separate combined flags
var flagArgsSeparated []string
for _, flagArg := range flagArgs {
if strings.HasPrefix(flagArg, "-") && strings.HasPrefix(flagArg, "--") == false && len(flagArg) > 2 {
if !allCharsFlags(flagArg[1:]) {
flagArgsSeparated = append(flagArgsSeparated, flagArg)
continue
return append(reorderedArgs, remainingArgs...)
}
// argIsFlag checks if an arg is one of our command flags
func argIsFlag(commandFlags []Flag, arg string) bool {
if arg == "-" || arg == "--" {
// `-` is never a flag
// `--` is an option-value when following a flag, and a delimiter indicating the end of options in other cases.
return false
}
// flags always start with a -
if !strings.HasPrefix(arg, "-") {
return false
}
// this line turns `--flag` into `flag`
if strings.HasPrefix(arg, "--") {
arg = strings.Replace(arg, "-", "", 2)
}
// this line turns `-flag` into `flag`
if strings.HasPrefix(arg, "-") {
arg = strings.Replace(arg, "-", "", 1)
}
// this line turns `flag=value` into `flag`
arg = strings.Split(arg, "=")[0]
// look through all the flags, to see if the `arg` is one of our flags
for _, flag := range commandFlags {
for _, key := range strings.Split(flag.GetName(), ",") {
key := strings.TrimSpace(key)
if key == arg {
return true
}
for _, flagChar := range flagArg[1:] {
flagArgsSeparated = append(flagArgsSeparated, "-"+string(flagChar))
}
} else {
flagArgsSeparated = append(flagArgsSeparated, flagArg)
}
}
return flagArgsSeparated
// return false if this arg was not one of our flags
return false
}
// Names returns the names including short names and aliases.
@@ -324,6 +319,7 @@ func (c Command) HasName(name string) bool {
func (c Command) startApp(ctx *Context) error {
app := NewApp()
app.Metadata = ctx.App.Metadata
app.ExitErrHandler = ctx.App.ExitErrHandler
// set the name and usage
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
if c.HelpName == "" {
@@ -352,6 +348,7 @@ func (c Command) startApp(ctx *Context) error {
app.Email = ctx.App.Email
app.Writer = ctx.App.Writer
app.ErrWriter = ctx.App.ErrWriter
app.UseShortOptionHandling = ctx.App.UseShortOptionHandling
app.categories = CommandCategories{}
for _, command := range c.Subcommands {
+28 -8
View File
@@ -87,6 +87,14 @@ func (c *Context) IsSet(name string) bool {
for _, f := range flags {
eachName(f.GetName(), func(name string) {
if isSet, ok := c.setFlags[name]; isSet || !ok {
// Check if a flag is set
if isSet {
// If the flag is set, also set its other aliases
eachName(f.GetName(), func(name string) {
c.setFlags[name] = true
})
}
return
}
@@ -139,8 +147,8 @@ func (c *Context) GlobalIsSet(name string) bool {
// FlagNames returns a slice of flag names used in this context.
func (c *Context) FlagNames() (names []string) {
for _, flag := range c.Command.Flags {
name := strings.Split(flag.GetName(), ",")[0]
for _, f := range c.Command.Flags {
name := strings.Split(f.GetName(), ",")[0]
if name == "help" {
continue
}
@@ -151,8 +159,8 @@ func (c *Context) FlagNames() (names []string) {
// GlobalFlagNames returns a slice of global flag names used by the app.
func (c *Context) GlobalFlagNames() (names []string) {
for _, flag := range c.App.Flags {
name := strings.Split(flag.GetName(), ",")[0]
for _, f := range c.App.Flags {
name := strings.Split(f.GetName(), ",")[0]
if name == "help" || name == "version" {
continue
}
@@ -250,7 +258,7 @@ func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
switch ff.Value.(type) {
case *StringSlice:
default:
set.Set(name, ff.Value.String())
_ = set.Set(name, ff.Value.String())
}
}
@@ -313,9 +321,21 @@ func checkRequiredFlags(flags []Flag, context *Context) requiredFlagsErr {
var missingFlags []string
for _, f := range flags {
if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
key := strings.Split(f.GetName(), ",")[0]
if !context.IsSet(key) {
missingFlags = append(missingFlags, key)
var flagPresent bool
var flagName string
for _, key := range strings.Split(f.GetName(), ",") {
key = strings.TrimSpace(key)
if len(key) > 1 {
flagName = key
}
if context.IsSet(key) {
flagPresent = true
}
}
if !flagPresent && flagName != "" {
missingFlags = append(missingFlags, flagName)
}
}
}
-93
View File
@@ -1,93 +0,0 @@
[
{
"name": "Bool",
"type": "bool",
"value": false,
"context_default": "false",
"parser": "strconv.ParseBool(f.Value.String())"
},
{
"name": "BoolT",
"type": "bool",
"value": false,
"doctail": " that is true by default",
"context_default": "false",
"parser": "strconv.ParseBool(f.Value.String())"
},
{
"name": "Duration",
"type": "time.Duration",
"doctail": " (see https://golang.org/pkg/time/#ParseDuration)",
"context_default": "0",
"parser": "time.ParseDuration(f.Value.String())"
},
{
"name": "Float64",
"type": "float64",
"context_default": "0",
"parser": "strconv.ParseFloat(f.Value.String(), 64)"
},
{
"name": "Generic",
"type": "Generic",
"dest": false,
"context_default": "nil",
"context_type": "interface{}"
},
{
"name": "Int64",
"type": "int64",
"context_default": "0",
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)"
},
{
"name": "Int",
"type": "int",
"context_default": "0",
"parser": "strconv.ParseInt(f.Value.String(), 0, 64)",
"parser_cast": "int(parsed)"
},
{
"name": "IntSlice",
"type": "*IntSlice",
"dest": false,
"context_default": "nil",
"context_type": "[]int",
"parser": "(f.Value.(*IntSlice)).Value(), error(nil)"
},
{
"name": "Int64Slice",
"type": "*Int64Slice",
"dest": false,
"context_default": "nil",
"context_type": "[]int64",
"parser": "(f.Value.(*Int64Slice)).Value(), error(nil)"
},
{
"name": "String",
"type": "string",
"context_default": "\"\"",
"parser": "f.Value.String(), error(nil)"
},
{
"name": "StringSlice",
"type": "*StringSlice",
"dest": false,
"context_default": "nil",
"context_type": "[]string",
"parser": "(f.Value.(*StringSlice)).Value(), error(nil)"
},
{
"name": "Uint64",
"type": "uint64",
"context_default": "0",
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)"
},
{
"name": "Uint",
"type": "uint",
"context_default": "0",
"parser": "strconv.ParseUint(f.Value.String(), 0, 64)",
"parser_cast": "uint(parsed)"
}
]
+27 -473
View File
@@ -9,7 +9,6 @@ import (
"strconv"
"strings"
"syscall"
"time"
)
const defaultPlaceholder = "value"
@@ -83,6 +82,21 @@ type RequiredFlag interface {
IsRequired() bool
}
// DocGenerationFlag is an interface that allows documentation generation for the flag
type DocGenerationFlag interface {
Flag
// TakesValue returns true if the flag takes a value, otherwise false
TakesValue() bool
// GetUsage returns the usage string for the flag
GetUsage() string
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
GetValue() string
}
// errorableFlag is an interface that allows us to return errors during apply
// it allows flags defined in this library to return errors in a fashion backwards compatible
// TODO remove in v2 and modify the existing Flag interface to return errors
@@ -105,6 +119,7 @@ func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
f.Apply(set)
}
}
set.SetOutput(ioutil.Discard)
return set, nil
}
@@ -116,475 +131,12 @@ func eachName(longName string, fn func(string)) {
}
}
// Generic is a generic parseable type identified by a specific flag
type Generic interface {
Set(value string) error
String() string
}
// Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
// Ignores parsing errors
func (f GenericFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
func (f GenericFlag) ApplyWithError(set *flag.FlagSet) error {
val := f.Value
if fileEnvVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
if err := val.Set(fileEnvVal); err != nil {
return fmt.Errorf("could not parse %s as value for flag %s: %s", fileEnvVal, f.Name, err)
}
}
eachName(f.Name, func(name string) {
set.Var(f.Value, name, f.Usage)
})
return nil
}
// StringSlice is an opaque type for []string to satisfy flag.Value and flag.Getter
type StringSlice []string
// Set appends the string value to the list of values
func (f *StringSlice) Set(value string) error {
*f = append(*f, value)
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *StringSlice) String() string {
return fmt.Sprintf("%s", *f)
}
// Value returns the slice of strings set by this flag
func (f *StringSlice) Value() []string {
return *f
}
// Get returns the slice of strings set by this flag
func (f *StringSlice) Get() interface{} {
return *f
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f StringSliceFlag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
newVal := &StringSlice{}
for _, s := range strings.Split(envVal, ",") {
s = strings.TrimSpace(s)
if err := newVal.Set(s); err != nil {
return fmt.Errorf("could not parse %s as string value for flag %s: %s", envVal, f.Name, err)
}
}
if f.Value == nil {
f.Value = newVal
} else {
*f.Value = *newVal
}
}
eachName(f.Name, func(name string) {
if f.Value == nil {
f.Value = &StringSlice{}
}
set.Var(f.Value, name, f.Usage)
})
return nil
}
// IntSlice is an opaque type for []int to satisfy flag.Value and flag.Getter
type IntSlice []int
// Set parses the value into an integer and appends it to the list of values
func (f *IntSlice) Set(value string) error {
tmp, err := strconv.Atoi(value)
if err != nil {
return err
}
*f = append(*f, tmp)
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *IntSlice) String() string {
return fmt.Sprintf("%#v", *f)
}
// Value returns the slice of ints set by this flag
func (f *IntSlice) Value() []int {
return *f
}
// Get returns the slice of ints set by this flag
func (f *IntSlice) Get() interface{} {
return *f
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f IntSliceFlag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
newVal := &IntSlice{}
for _, s := range strings.Split(envVal, ",") {
s = strings.TrimSpace(s)
if err := newVal.Set(s); err != nil {
return fmt.Errorf("could not parse %s as int slice value for flag %s: %s", envVal, f.Name, err)
}
}
if f.Value == nil {
f.Value = newVal
} else {
*f.Value = *newVal
}
}
eachName(f.Name, func(name string) {
if f.Value == nil {
f.Value = &IntSlice{}
}
set.Var(f.Value, name, f.Usage)
})
return nil
}
// Int64Slice is an opaque type for []int to satisfy flag.Value and flag.Getter
type Int64Slice []int64
// Set parses the value into an integer and appends it to the list of values
func (f *Int64Slice) Set(value string) error {
tmp, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return err
}
*f = append(*f, tmp)
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *Int64Slice) String() string {
return fmt.Sprintf("%#v", *f)
}
// Value returns the slice of ints set by this flag
func (f *Int64Slice) Value() []int64 {
return *f
}
// Get returns the slice of ints set by this flag
func (f *Int64Slice) Get() interface{} {
return *f
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f Int64SliceFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f Int64SliceFlag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
newVal := &Int64Slice{}
for _, s := range strings.Split(envVal, ",") {
s = strings.TrimSpace(s)
if err := newVal.Set(s); err != nil {
return fmt.Errorf("could not parse %s as int64 slice value for flag %s: %s", envVal, f.Name, err)
}
}
if f.Value == nil {
f.Value = newVal
} else {
*f.Value = *newVal
}
}
eachName(f.Name, func(name string) {
if f.Value == nil {
f.Value = &Int64Slice{}
}
set.Var(f.Value, name, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f BoolFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f BoolFlag) ApplyWithError(set *flag.FlagSet) error {
val := false
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
if envVal == "" {
val = false
} else {
envValBool, err := strconv.ParseBool(envVal)
if err != nil {
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
}
val = envValBool
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.BoolVar(f.Destination, name, val, f.Usage)
return
}
set.Bool(name, val, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f BoolTFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f BoolTFlag) ApplyWithError(set *flag.FlagSet) error {
val := true
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
if envVal == "" {
val = false
} else {
envValBool, err := strconv.ParseBool(envVal)
if err != nil {
return fmt.Errorf("could not parse %s as bool value for flag %s: %s", envVal, f.Name, err)
}
val = envValBool
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.BoolVar(f.Destination, name, val, f.Usage)
return
}
set.Bool(name, val, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f StringFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f StringFlag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
f.Value = envVal
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.StringVar(f.Destination, name, f.Value, f.Usage)
return
}
set.String(name, f.Value, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f IntFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f IntFlag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
envValInt, err := strconv.ParseInt(envVal, 0, 64)
if err != nil {
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
}
f.Value = int(envValInt)
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.IntVar(f.Destination, name, f.Value, f.Usage)
return
}
set.Int(name, f.Value, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f Int64Flag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f Int64Flag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
envValInt, err := strconv.ParseInt(envVal, 0, 64)
if err != nil {
return fmt.Errorf("could not parse %s as int value for flag %s: %s", envVal, f.Name, err)
}
f.Value = envValInt
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.Int64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Int64(name, f.Value, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f UintFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f UintFlag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err != nil {
return fmt.Errorf("could not parse %s as uint value for flag %s: %s", envVal, f.Name, err)
}
f.Value = uint(envValInt)
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.UintVar(f.Destination, name, f.Value, f.Usage)
return
}
set.Uint(name, f.Value, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f Uint64Flag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f Uint64Flag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
envValInt, err := strconv.ParseUint(envVal, 0, 64)
if err != nil {
return fmt.Errorf("could not parse %s as uint64 value for flag %s: %s", envVal, f.Name, err)
}
f.Value = uint64(envValInt)
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.Uint64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Uint64(name, f.Value, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f DurationFlag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f DurationFlag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
envValDuration, err := time.ParseDuration(envVal)
if err != nil {
return fmt.Errorf("could not parse %s as duration for flag %s: %s", envVal, f.Name, err)
}
f.Value = envValDuration
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.DurationVar(f.Destination, name, f.Value, f.Usage)
return
}
set.Duration(name, f.Value, f.Usage)
})
return nil
}
// Apply populates the flag given the flag set and environment
// Ignores errors
func (f Float64Flag) Apply(set *flag.FlagSet) {
f.ApplyWithError(set)
}
// ApplyWithError populates the flag given the flag set and environment
func (f Float64Flag) ApplyWithError(set *flag.FlagSet) error {
if envVal, ok := flagFromFileEnv(f.FilePath, f.EnvVar); ok {
envValFloat, err := strconv.ParseFloat(envVal, 10)
if err != nil {
return fmt.Errorf("could not parse %s as float64 value for flag %s: %s", envVal, f.Name, err)
}
f.Value = float64(envValFloat)
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.Float64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Float64(name, f.Value, f.Usage)
})
return nil
}
func visibleFlags(fl []Flag) []Flag {
visible := []Flag{}
for _, flag := range fl {
field := flagValue(flag).FieldByName("Hidden")
var visible []Flag
for _, f := range fl {
field := flagValue(f).FieldByName("Hidden")
if !field.IsValid() || !field.Bool() {
visible = append(visible, flag)
visible = append(visible, f)
}
}
return visible
@@ -729,7 +281,7 @@ func stringifyFlag(f Flag) string {
}
func stringifyIntSliceFlag(f IntSliceFlag) string {
defaultVals := []string{}
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.Itoa(i))
@@ -740,7 +292,7 @@ func stringifyIntSliceFlag(f IntSliceFlag) string {
}
func stringifyInt64SliceFlag(f Int64SliceFlag) string {
defaultVals := []string{}
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, i := range f.Value.Value() {
defaultVals = append(defaultVals, strconv.FormatInt(i, 10))
@@ -751,7 +303,7 @@ func stringifyInt64SliceFlag(f Int64SliceFlag) string {
}
func stringifyStringSliceFlag(f StringSliceFlag) string {
defaultVals := []string{}
var defaultVals []string
if f.Value != nil && len(f.Value.Value()) > 0 {
for _, s := range f.Value.Value() {
if len(s) > 0 {
@@ -786,8 +338,10 @@ func flagFromFileEnv(filePath, envName string) (val string, ok bool) {
}
}
for _, fileVar := range strings.Split(filePath, ",") {
if data, err := ioutil.ReadFile(fileVar); err == nil {
return string(data), true
if fileVar != "" {
if data, err := ioutil.ReadFile(fileVar); err == nil {
return string(data), true
}
}
}
return "", false
-718
View File
@@ -1,718 +0,0 @@
package cli
import (
"flag"
"strconv"
"time"
)
// WARNING: This file is generated!
// BoolFlag is a flag with type bool
type BoolFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Destination *bool
}
// String returns a readable representation of this value
// (for usage defaults)
func (f BoolFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f BoolFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f BoolFlag) IsRequired() bool {
return f.Required
}
// Bool looks up the value of a local BoolFlag, returns
// false if not found
func (c *Context) Bool(name string) bool {
return lookupBool(name, c.flagSet)
}
// GlobalBool looks up the value of a global BoolFlag, returns
// false if not found
func (c *Context) GlobalBool(name string) bool {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupBool(name, fs)
}
return false
}
func lookupBool(name string, set *flag.FlagSet) bool {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseBool(f.Value.String())
if err != nil {
return false
}
return parsed
}
return false
}
// BoolTFlag is a flag with type bool that is true by default
type BoolTFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Destination *bool
}
// String returns a readable representation of this value
// (for usage defaults)
func (f BoolTFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f BoolTFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f BoolTFlag) IsRequired() bool {
return f.Required
}
// BoolT looks up the value of a local BoolTFlag, returns
// false if not found
func (c *Context) BoolT(name string) bool {
return lookupBoolT(name, c.flagSet)
}
// GlobalBoolT looks up the value of a global BoolTFlag, returns
// false if not found
func (c *Context) GlobalBoolT(name string) bool {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupBoolT(name, fs)
}
return false
}
func lookupBoolT(name string, set *flag.FlagSet) bool {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseBool(f.Value.String())
if err != nil {
return false
}
return parsed
}
return false
}
// DurationFlag is a flag with type time.Duration (see https://golang.org/pkg/time/#ParseDuration)
type DurationFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value time.Duration
Destination *time.Duration
}
// String returns a readable representation of this value
// (for usage defaults)
func (f DurationFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f DurationFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f DurationFlag) IsRequired() bool {
return f.Required
}
// Duration looks up the value of a local DurationFlag, returns
// 0 if not found
func (c *Context) Duration(name string) time.Duration {
return lookupDuration(name, c.flagSet)
}
// GlobalDuration looks up the value of a global DurationFlag, returns
// 0 if not found
func (c *Context) GlobalDuration(name string) time.Duration {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupDuration(name, fs)
}
return 0
}
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
f := set.Lookup(name)
if f != nil {
parsed, err := time.ParseDuration(f.Value.String())
if err != nil {
return 0
}
return parsed
}
return 0
}
// Float64Flag is a flag with type float64
type Float64Flag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value float64
Destination *float64
}
// String returns a readable representation of this value
// (for usage defaults)
func (f Float64Flag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f Float64Flag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f Float64Flag) IsRequired() bool {
return f.Required
}
// Float64 looks up the value of a local Float64Flag, returns
// 0 if not found
func (c *Context) Float64(name string) float64 {
return lookupFloat64(name, c.flagSet)
}
// GlobalFloat64 looks up the value of a global Float64Flag, returns
// 0 if not found
func (c *Context) GlobalFloat64(name string) float64 {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupFloat64(name, fs)
}
return 0
}
func lookupFloat64(name string, set *flag.FlagSet) float64 {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseFloat(f.Value.String(), 64)
if err != nil {
return 0
}
return parsed
}
return 0
}
// GenericFlag is a flag with type Generic
type GenericFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value Generic
}
// String returns a readable representation of this value
// (for usage defaults)
func (f GenericFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f GenericFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f GenericFlag) IsRequired() bool {
return f.Required
}
// Generic looks up the value of a local GenericFlag, returns
// nil if not found
func (c *Context) Generic(name string) interface{} {
return lookupGeneric(name, c.flagSet)
}
// GlobalGeneric looks up the value of a global GenericFlag, returns
// nil if not found
func (c *Context) GlobalGeneric(name string) interface{} {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupGeneric(name, fs)
}
return nil
}
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
f := set.Lookup(name)
if f != nil {
parsed, err := f.Value, error(nil)
if err != nil {
return nil
}
return parsed
}
return nil
}
// Int64Flag is a flag with type int64
type Int64Flag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value int64
Destination *int64
}
// String returns a readable representation of this value
// (for usage defaults)
func (f Int64Flag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f Int64Flag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f Int64Flag) IsRequired() bool {
return f.Required
}
// Int64 looks up the value of a local Int64Flag, returns
// 0 if not found
func (c *Context) Int64(name string) int64 {
return lookupInt64(name, c.flagSet)
}
// GlobalInt64 looks up the value of a global Int64Flag, returns
// 0 if not found
func (c *Context) GlobalInt64(name string) int64 {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupInt64(name, fs)
}
return 0
}
func lookupInt64(name string, set *flag.FlagSet) int64 {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return parsed
}
return 0
}
// IntFlag is a flag with type int
type IntFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value int
Destination *int
}
// String returns a readable representation of this value
// (for usage defaults)
func (f IntFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f IntFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f IntFlag) IsRequired() bool {
return f.Required
}
// Int looks up the value of a local IntFlag, returns
// 0 if not found
func (c *Context) Int(name string) int {
return lookupInt(name, c.flagSet)
}
// GlobalInt looks up the value of a global IntFlag, returns
// 0 if not found
func (c *Context) GlobalInt(name string) int {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupInt(name, fs)
}
return 0
}
func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseInt(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return int(parsed)
}
return 0
}
// IntSliceFlag is a flag with type *IntSlice
type IntSliceFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value *IntSlice
}
// String returns a readable representation of this value
// (for usage defaults)
func (f IntSliceFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f IntSliceFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f IntSliceFlag) IsRequired() bool {
return f.Required
}
// IntSlice looks up the value of a local IntSliceFlag, returns
// nil if not found
func (c *Context) IntSlice(name string) []int {
return lookupIntSlice(name, c.flagSet)
}
// GlobalIntSlice looks up the value of a global IntSliceFlag, returns
// nil if not found
func (c *Context) GlobalIntSlice(name string) []int {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupIntSlice(name, fs)
}
return nil
}
func lookupIntSlice(name string, set *flag.FlagSet) []int {
f := set.Lookup(name)
if f != nil {
parsed, err := (f.Value.(*IntSlice)).Value(), error(nil)
if err != nil {
return nil
}
return parsed
}
return nil
}
// Int64SliceFlag is a flag with type *Int64Slice
type Int64SliceFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value *Int64Slice
}
// String returns a readable representation of this value
// (for usage defaults)
func (f Int64SliceFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f Int64SliceFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f Int64SliceFlag) IsRequired() bool {
return f.Required
}
// Int64Slice looks up the value of a local Int64SliceFlag, returns
// nil if not found
func (c *Context) Int64Slice(name string) []int64 {
return lookupInt64Slice(name, c.flagSet)
}
// GlobalInt64Slice looks up the value of a global Int64SliceFlag, returns
// nil if not found
func (c *Context) GlobalInt64Slice(name string) []int64 {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupInt64Slice(name, fs)
}
return nil
}
func lookupInt64Slice(name string, set *flag.FlagSet) []int64 {
f := set.Lookup(name)
if f != nil {
parsed, err := (f.Value.(*Int64Slice)).Value(), error(nil)
if err != nil {
return nil
}
return parsed
}
return nil
}
// StringFlag is a flag with type string
type StringFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value string
Destination *string
}
// String returns a readable representation of this value
// (for usage defaults)
func (f StringFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f StringFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f StringFlag) IsRequired() bool {
return f.Required
}
// String looks up the value of a local StringFlag, returns
// "" if not found
func (c *Context) String(name string) string {
return lookupString(name, c.flagSet)
}
// GlobalString looks up the value of a global StringFlag, returns
// "" if not found
func (c *Context) GlobalString(name string) string {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupString(name, fs)
}
return ""
}
func lookupString(name string, set *flag.FlagSet) string {
f := set.Lookup(name)
if f != nil {
parsed, err := f.Value.String(), error(nil)
if err != nil {
return ""
}
return parsed
}
return ""
}
// StringSliceFlag is a flag with type *StringSlice
type StringSliceFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value *StringSlice
}
// String returns a readable representation of this value
// (for usage defaults)
func (f StringSliceFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f StringSliceFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f StringSliceFlag) IsRequired() bool {
return f.Required
}
// StringSlice looks up the value of a local StringSliceFlag, returns
// nil if not found
func (c *Context) StringSlice(name string) []string {
return lookupStringSlice(name, c.flagSet)
}
// GlobalStringSlice looks up the value of a global StringSliceFlag, returns
// nil if not found
func (c *Context) GlobalStringSlice(name string) []string {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupStringSlice(name, fs)
}
return nil
}
func lookupStringSlice(name string, set *flag.FlagSet) []string {
f := set.Lookup(name)
if f != nil {
parsed, err := (f.Value.(*StringSlice)).Value(), error(nil)
if err != nil {
return nil
}
return parsed
}
return nil
}
// Uint64Flag is a flag with type uint64
type Uint64Flag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value uint64
Destination *uint64
}
// String returns a readable representation of this value
// (for usage defaults)
func (f Uint64Flag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f Uint64Flag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f Uint64Flag) IsRequired() bool {
return f.Required
}
// Uint64 looks up the value of a local Uint64Flag, returns
// 0 if not found
func (c *Context) Uint64(name string) uint64 {
return lookupUint64(name, c.flagSet)
}
// GlobalUint64 looks up the value of a global Uint64Flag, returns
// 0 if not found
func (c *Context) GlobalUint64(name string) uint64 {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupUint64(name, fs)
}
return 0
}
func lookupUint64(name string, set *flag.FlagSet) uint64 {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return parsed
}
return 0
}
// UintFlag is a flag with type uint
type UintFlag struct {
Name string
Usage string
EnvVar string
FilePath string
Required bool
Hidden bool
Value uint
Destination *uint
}
// String returns a readable representation of this value
// (for usage defaults)
func (f UintFlag) String() string {
return FlagStringer(f)
}
// GetName returns the name of the flag
func (f UintFlag) GetName() string {
return f.Name
}
// IsRequired returns the whether or not the flag is required
func (f UintFlag) IsRequired() bool {
return f.Required
}
// Uint looks up the value of a local UintFlag, returns
// 0 if not found
func (c *Context) Uint(name string) uint {
return lookupUint(name, c.flagSet)
}
// GlobalUint looks up the value of a global UintFlag, returns
// 0 if not found
func (c *Context) GlobalUint(name string) uint {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupUint(name, fs)
}
return 0
}
func lookupUint(name string, set *flag.FlagSet) uint {
f := set.Lookup(name)
if f != nil {
parsed, err := strconv.ParseUint(f.Value.String(), 0, 64)
if err != nil {
return 0
}
return uint(parsed)
}
return 0
}

Some files were not shown because too many files have changed in this diff Show More