Compare commits

...
18 Commits
Author SHA1 Message Date
xtaci 5ad73ea8eb Squashed commit of the following:
commit 90b1b24d0d
Author: xtaci <daniel820313@gmail.com>
Date:   Fri Jul 28 00:56:06 2023 +0800

    upd Dockerfile

commit 3349f6118b
Author: xtaci <daniel820313@gmail.com>
Date:   Fri Jul 28 00:44:26 2023 +0800

    upd dockerfile
2023-07-28 01:01:15 +08:00
xtaci 0e917594d9 upd Dockerfile 2023-07-28 00:31:59 +08:00
432e75c76a Bump golang.org/x/net from 0.5.0 to 0.7.0 (#910)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-07 11:00:29 +08:00
xtaci f7d957fc35 fixed a bug that smux.OpenStream /smux.Close will hang forever in rare
case
2023-02-14 22:25:52 +08:00
xtaci bd274abccc upd deps to smux 2023-02-14 22:21:40 +08:00
xtaci 0603885bb6 upd vendor 2023-02-14 20:47:58 +08:00
xtaci eed5f5ff0e upd deps to smux@v1.5.22 2023-02-14 20:47:34 +08:00
xtaci ec099786cb upd deps to smux 2023-02-12 20:10:15 +08:00
xtaci 86f2e5b066 upd deps to smux 2023-02-07 22:17:00 +08:00
xtaciandGitHub 386bd58956 Update README.md 2023-02-07 21:39:11 +08:00
xtaci 4789212001 add parameters explaination 2023-02-07 21:37:46 +08:00
xtaci 997b217d4b upd deps to smux 2023-02-07 21:32:30 +08:00
xtaci 00ce2f86af upd vendor 2023-02-07 12:31:25 +08:00
xtaci 968e76e5ae upd deps 2023-02-07 12:17:25 +08:00
Qijia LiuandGitHub a3cc866b28 support windows arm64 (#900) 2022-12-20 14:23:29 +08:00
xtaciandGitHub b4684d7d90 Update README.md 2022-11-02 20:28:50 +08:00
fesilyandGitHub baca7d7e6c add unix support on client (#892) 2022-10-28 13:02:11 +08:00
xtaci 208deccd09 use rand port instead of sequential pattern 2022-10-15 11:07:47 +08:00
409 changed files with 89862 additions and 10664 deletions
+8 -4
View File
@@ -1,13 +1,17 @@
FROM golang:1.14.9-alpine3.11 as builder
FROM golang:1.20.6-alpine3.18 as builder
MAINTAINER xtaci <daniel820313@gmail.com>
ENV GO111MODULE=on
RUN apk update && \
apk upgrade && \
apk add git gcc libc-dev linux-headers
RUN go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/client && go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/server
RUN git clone https://github.com/xtaci/kcptun.git
RUN cd kcptun && \
go build -mod=vendor -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" -o /client github.com/xtaci/kcptun/client && \
go build -mod=vendor -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" -o /server github.com/xtaci/kcptun/server
FROM alpine:3.11
FROM alpine:3.18
RUN apk add --no-cache iptables
COPY --from=builder /go/bin /bin
COPY --from=builder /client /bin
COPY --from=builder /server /bin
EXPOSE 29900/udp
EXPOSE 12948
+17 -2
View File
@@ -146,7 +146,7 @@ COMMANDS:
GLOBAL OPTIONS:
--localaddr value, -l value local listen address (default: ":12948")
--remoteaddr value, -r value kcp server address (default: "vps:29900")
--remoteaddr value, -r value kcp server address, eg: "IP:29900" a for single port, "IP:minport-maxport" for port range (default: "vps:29900")
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none (default: "aes")
--mode value profiles: fast3, fast2, fast, normal, manual (default: "fast")
@@ -188,7 +188,7 @@ COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--listen value, -l value kcp server listen address (default: ":29900")
--listen value, -l value kcp server listen address, eg: "IP:29900" for a single port, "IP:minport-maxport" for port range (default: ":29900")
--target value, -t value target server address, or path/to/unix_socket (default: "127.0.0.1:12948")
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, sm4, none (default: "aes")
@@ -216,6 +216,21 @@ GLOBAL OPTIONS:
--version, -v print the version
```
#### Multiport Dialer
kcptun supports multi-port dialer like below:
```
client: --remoteaddr IP:minport-maxport
server: --listen IP:minport-maxport
eg:
client: --remoteaddr IP:3000-4000
server: --listen 0.0.0.0:3000-4000
```
by specifying port-range, kcptun will automatically switch to next random port within port-range when establishing each new connection.
#### Forward Error Correction
In coding theory, the [ReedSolomon code](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) belongs to the class of non-binary cyclic error-correcting codes. The ReedSolomon code is based on univariate polynomials over finite fields.
+13 -11
View File
@@ -57,12 +57,6 @@ for os in ${OSES[@]}; do
$sum kcptun-${os}-386-$VERSION.tar.gz
done
#Apple M1 device
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_darwin_arm64 github.com/xtaci/kcptun/server
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_darwin_arm64 github.com/xtaci/kcptun/client
tar -zcf kcptun-darwin-arm64-$VERSION.tar.gz client_darwin_arm64 server_darwin_arm64
$sum kcptun-darwin-arm64-$VERSION.tar.gz
# ARM
ARMS=(5 6 7)
for v in ${ARMS[@]}; do
@@ -74,11 +68,19 @@ $sum kcptun-linux-arm$v-$VERSION.tar.gz
done
# ARM64
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm64 github.com/xtaci/kcptun/client
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm64 github.com/xtaci/kcptun/server
if $UPX; then upx -9 client_linux_arm64 server_linux_arm64*;fi
tar -zcf kcptun-linux-arm64-$VERSION.tar.gz client_linux_arm64 server_linux_arm64
$sum kcptun-linux-arm64-$VERSION.tar.gz
OSES=(linux darwin windows)
for os in ${OSES[@]}; do
suffix=""
if [ "$os" == "windows" ]
then
suffix=".exe"
fi
env CGO_ENABLED=0 GOOS=$os GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_arm64${suffix} github.com/xtaci/kcptun/client
env CGO_ENABLED=0 GOOS=$os GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_arm64${suffix} github.com/xtaci/kcptun/server
if $UPX; then upx -9 client_${os}_arm64${suffix} server_${os}_arm64${suffix};fi
tar -zcf kcptun-${os}-arm64-$VERSION.tar.gz client_${os}_arm64${suffix} server_${os}_arm64${suffix}
$sum kcptun-${os}-arm64-$VERSION.tar.gz
done
#MIPS32LE
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
+9 -7
View File
@@ -1,6 +1,8 @@
package main
import (
"crypto/rand"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
@@ -9,19 +11,19 @@ import (
"github.com/xtaci/tcpraw"
)
var dialCount uint64
func dial(config *Config, block kcp.BlockCrypt) (*kcp.UDPSession, error) {
defer func() {
dialCount++
}()
mp, err := generic.ParseMultiPort(config.RemoteAddr)
if err != nil {
return nil, err
}
remoteAddr := fmt.Sprintf("%v:%v", mp.Host, uint64(mp.MinPort)+dialCount%uint64(mp.MaxPort-mp.MinPort+1))
var randport uint64
err = binary.Read(rand.Reader, binary.LittleEndian, &randport)
if err != nil {
return nil, err
}
remoteAddr := fmt.Sprintf("%v:%v", mp.Host, uint64(mp.MinPort)+randport%uint64(mp.MaxPort-mp.MinPort+1))
if config.TCP {
conn, err := tcpraw.Dial("tcp", remoteAddr)
+18 -6
View File
@@ -98,7 +98,7 @@ func main() {
cli.StringFlag{
Name: "remoteaddr, r",
Value: "vps:29900",
Usage: "kcp server address",
Usage: `kcp server address, eg: "IP:29900" a for single port, "IP:minport-maxport" for port range`,
},
cli.StringFlag{
Name: "key",
@@ -302,10 +302,22 @@ func main() {
}
log.Println("version:", VERSION)
addr, err := net.ResolveTCPAddr("tcp", config.LocalAddr)
checkError(err)
listener, err := net.ListenTCP("tcp", addr)
checkError(err)
var listener net.Listener
var isUnix bool
if _, _, err := net.SplitHostPort(config.LocalAddr); err != nil {
isUnix = true
}
if isUnix {
addr, err := net.ResolveUnixAddr("unix", config.LocalAddr)
checkError(err)
listener, err = net.ListenUnix("unix", addr)
checkError(err)
} else {
addr, err := net.ResolveTCPAddr("tcp", config.LocalAddr)
checkError(err)
listener, err = net.ListenTCP("tcp", addr)
checkError(err)
}
log.Println("smux version:", config.SmuxVer)
log.Println("listening on:", listener.Addr())
@@ -440,7 +452,7 @@ func main() {
muxes := make([]timedSession, numconn)
rr := uint16(0)
for {
p1, err := listener.AcceptTCP()
p1, err := listener.Accept()
if err != nil {
log.Fatalf("%+v", err)
}
+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.24
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.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
)
go 1.17
+62 -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,125 @@ 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.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
github.com/xtaci/smux v1.5.24/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/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
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/text v0.7.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 +158,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=
+1 -1
View File
@@ -133,7 +133,7 @@ func main() {
cli.StringFlag{
Name: "listen,l",
Value: ":29900",
Usage: "kcp server listen address",
Usage: `kcp server listen address, eg: "IP:29900" for a single port, "IP:minport-maxport" for port range`,
},
cli.StringFlag{
Name: "target, t",
+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 {
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Brian Goff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+14
View File
@@ -0,0 +1,14 @@
package md2man
import (
"github.com/russross/blackfriday/v2"
)
// Render converts a markdown document into a roff formatted document.
func Render(doc []byte) []byte {
renderer := NewRoffRenderer()
return blackfriday.Run(doc,
[]blackfriday.Option{blackfriday.WithRenderer(renderer),
blackfriday.WithExtensions(renderer.GetExtensions())}...)
}
+336
View File
@@ -0,0 +1,336 @@
package md2man
import (
"fmt"
"io"
"os"
"strings"
"github.com/russross/blackfriday/v2"
)
// roffRenderer implements the blackfriday.Renderer interface for creating
// roff format (manpages) from markdown text
type roffRenderer struct {
extensions blackfriday.Extensions
listCounters []int
firstHeader bool
firstDD bool
listDepth int
}
const (
titleHeader = ".TH "
topLevelHeader = "\n\n.SH "
secondLevelHdr = "\n.SH "
otherHeader = "\n.SS "
crTag = "\n"
emphTag = "\\fI"
emphCloseTag = "\\fP"
strongTag = "\\fB"
strongCloseTag = "\\fP"
breakTag = "\n.br\n"
paraTag = "\n.PP\n"
hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n"
linkTag = "\n\\[la]"
linkCloseTag = "\\[ra]"
codespanTag = "\\fB\\fC"
codespanCloseTag = "\\fR"
codeTag = "\n.PP\n.RS\n\n.nf\n"
codeCloseTag = "\n.fi\n.RE\n"
quoteTag = "\n.PP\n.RS\n"
quoteCloseTag = "\n.RE\n"
listTag = "\n.RS\n"
listCloseTag = "\n.RE\n"
dtTag = "\n.TP\n"
dd2Tag = "\n"
tableStart = "\n.TS\nallbox;\n"
tableEnd = ".TE\n"
tableCellStart = "T{\n"
tableCellEnd = "\nT}\n"
)
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
// from markdown
func NewRoffRenderer() *roffRenderer { // nolint: golint
var extensions blackfriday.Extensions
extensions |= blackfriday.NoIntraEmphasis
extensions |= blackfriday.Tables
extensions |= blackfriday.FencedCode
extensions |= blackfriday.SpaceHeadings
extensions |= blackfriday.Footnotes
extensions |= blackfriday.Titleblock
extensions |= blackfriday.DefinitionLists
return &roffRenderer{
extensions: extensions,
}
}
// GetExtensions returns the list of extensions used by this renderer implementation
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
return r.extensions
}
// RenderHeader handles outputting the header at document start
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
// disable hyphenation
out(w, ".nh\n")
}
// RenderFooter handles outputting the footer at the document end; the roff
// renderer has no footer information
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
}
// RenderNode is called for each node in a markdown document; based on the node
// type the equivalent roff output is sent to the writer
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
var walkAction = blackfriday.GoToNext
switch node.Type {
case blackfriday.Text:
escapeSpecialChars(w, node.Literal)
case blackfriday.Softbreak:
out(w, crTag)
case blackfriday.Hardbreak:
out(w, breakTag)
case blackfriday.Emph:
if entering {
out(w, emphTag)
} else {
out(w, emphCloseTag)
}
case blackfriday.Strong:
if entering {
out(w, strongTag)
} else {
out(w, strongCloseTag)
}
case blackfriday.Link:
if !entering {
out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag)
}
case blackfriday.Image:
// ignore images
walkAction = blackfriday.SkipChildren
case blackfriday.Code:
out(w, codespanTag)
escapeSpecialChars(w, node.Literal)
out(w, codespanCloseTag)
case blackfriday.Document:
break
case blackfriday.Paragraph:
// roff .PP markers break lists
if r.listDepth > 0 {
return blackfriday.GoToNext
}
if entering {
out(w, paraTag)
} else {
out(w, crTag)
}
case blackfriday.BlockQuote:
if entering {
out(w, quoteTag)
} else {
out(w, quoteCloseTag)
}
case blackfriday.Heading:
r.handleHeading(w, node, entering)
case blackfriday.HorizontalRule:
out(w, hruleTag)
case blackfriday.List:
r.handleList(w, node, entering)
case blackfriday.Item:
r.handleItem(w, node, entering)
case blackfriday.CodeBlock:
out(w, codeTag)
escapeSpecialChars(w, node.Literal)
out(w, codeCloseTag)
case blackfriday.Table:
r.handleTable(w, node, entering)
case blackfriday.TableHead:
case blackfriday.TableBody:
case blackfriday.TableRow:
// no action as cell entries do all the nroff formatting
return blackfriday.GoToNext
case blackfriday.TableCell:
r.handleTableCell(w, node, entering)
case blackfriday.HTMLSpan:
// ignore other HTML tags
default:
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
}
return walkAction
}
func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
switch node.Level {
case 1:
if !r.firstHeader {
out(w, titleHeader)
r.firstHeader = true
break
}
out(w, topLevelHeader)
case 2:
out(w, secondLevelHdr)
default:
out(w, otherHeader)
}
}
}
func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
openTag := listTag
closeTag := listCloseTag
if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// tags for definition lists handled within Item node
openTag = ""
closeTag = ""
}
if entering {
r.listDepth++
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
r.listCounters = append(r.listCounters, 1)
}
out(w, openTag)
} else {
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
r.listCounters = r.listCounters[:len(r.listCounters)-1]
}
out(w, closeTag)
r.listDepth--
}
}
func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
r.listCounters[len(r.listCounters)-1]++
} else if node.ListFlags&blackfriday.ListTypeTerm != 0 {
// DT (definition term): line just before DD (see below).
out(w, dtTag)
r.firstDD = true
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
// DD (definition description): line that starts with ": ".
//
// We have to distinguish between the first DD and the
// subsequent ones, as there should be no vertical
// whitespace between the DT and the first DD.
if r.firstDD {
r.firstDD = false
} else {
out(w, dd2Tag)
}
} else {
out(w, ".IP \\(bu 2\n")
}
} else {
out(w, "\n")
}
}
func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
out(w, tableStart)
// call walker to count cells (and rows?) so format section can be produced
columns := countColumns(node)
out(w, strings.Repeat("l ", columns)+"\n")
out(w, strings.Repeat("l ", columns)+".\n")
} else {
out(w, tableEnd)
}
}
func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
if entering {
var start string
if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
start = "\t"
}
if node.IsHeader {
start += codespanTag
} else if nodeLiteralSize(node) > 30 {
start += tableCellStart
}
out(w, start)
} else {
var end string
if node.IsHeader {
end = codespanCloseTag
} else if nodeLiteralSize(node) > 30 {
end = tableCellEnd
}
if node.Next == nil && end != tableCellEnd {
// Last cell: need to carriage return if we are at the end of the
// header row and content isn't wrapped in a "tablecell"
end += crTag
}
out(w, end)
}
}
func nodeLiteralSize(node *blackfriday.Node) int {
total := 0
for n := node.FirstChild; n != nil; n = n.FirstChild {
total += len(n.Literal)
}
return total
}
// because roff format requires knowing the column count before outputting any table
// data we need to walk a table tree and count the columns
func countColumns(node *blackfriday.Node) int {
var columns int
node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
switch node.Type {
case blackfriday.TableRow:
if !entering {
return blackfriday.Terminate
}
case blackfriday.TableCell:
if entering {
columns++
}
default:
}
return blackfriday.GoToNext
})
return columns
}
func out(w io.Writer, output string) {
io.WriteString(w, output) // nolint: errcheck
}
func escapeSpecialChars(w io.Writer, text []byte) {
for i := 0; i < len(text); i++ {
// escape initial apostrophe or period
if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
out(w, "\\&")
}
// directly copy normal characters
org := i
for i < len(text) && text[i] != '\\' {
i++
}
if i > org {
w.Write(text[org:i]) // nolint: errcheck
}
// escape a character
if i >= len(text) {
break
}
w.Write([]byte{'\\', text[i]}) // nolint: errcheck
}
}
+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
}
+494
View File
@@ -0,0 +1,494 @@
// Copyright 2020 The 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
#include "textflag.h"
// The asm code generally follows the pure Go code in decode_other.go, except
// where marked with a "!!!".
// func decode(dst, src []byte) int
//
// All local variables fit into registers. The non-zero stack size is only to
// spill registers and push args when issuing a CALL. The register allocation:
// - R2 scratch
// - R3 scratch
// - R4 length or x
// - R5 offset
// - R6 &src[s]
// - R7 &dst[d]
// + R8 dst_base
// + R9 dst_len
// + R10 dst_base + dst_len
// + R11 src_base
// + R12 src_len
// + R13 src_base + src_len
// - R14 used by doCopy
// - R15 used by doCopy
//
// The registers R8-R13 (marked with a "+") are set at the start of the
// function, and after a CALL returns, and are not otherwise modified.
//
// The d variable is implicitly R7 - R8, and len(dst)-d is R10 - R7.
// The s variable is implicitly R6 - R11, and len(src)-s is R13 - R6.
TEXT ·decode(SB), NOSPLIT, $56-56
// Initialize R6, R7 and R8-R13.
MOVD dst_base+0(FP), R8
MOVD dst_len+8(FP), R9
MOVD R8, R7
MOVD R8, R10
ADD R9, R10, R10
MOVD src_base+24(FP), R11
MOVD src_len+32(FP), R12
MOVD R11, R6
MOVD R11, R13
ADD R12, R13, R13
loop:
// for s < len(src)
CMP R13, R6
BEQ end
// R4 = uint32(src[s])
//
// switch src[s] & 0x03
MOVBU (R6), R4
MOVW R4, R3
ANDW $3, R3
MOVW $1, R1
CMPW R1, R3
BGE tagCopy
// ----------------------------------------
// The code below handles literal tags.
// case tagLiteral:
// x := uint32(src[s] >> 2)
// switch
MOVW $60, R1
LSRW $2, R4, R4
CMPW R4, R1
BLS tagLit60Plus
// case x < 60:
// s++
ADD $1, R6, R6
doLit:
// This is the end of the inner "switch", when we have a literal tag.
//
// We assume that R4 == x and x fits in a uint32, where x is the variable
// used in the pure Go decode_other.go code.
// length = int(x) + 1
//
// Unlike the pure Go code, we don't need to check if length <= 0 because
// R4 can hold 64 bits, so the increment cannot overflow.
ADD $1, R4, R4
// Prepare to check if copying length bytes will run past the end of dst or
// src.
//
// R2 = len(dst) - d
// R3 = len(src) - s
MOVD R10, R2
SUB R7, R2, R2
MOVD R13, R3
SUB R6, R3, R3
// !!! Try a faster technique for short (16 or fewer bytes) copies.
//
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
// goto callMemmove // Fall back on calling runtime·memmove.
// }
//
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
// against 21 instead of 16, because it cannot assume that all of its input
// is contiguous in memory and so it needs to leave enough source bytes to
// read the next tag without refilling buffers, but Go's Decode assumes
// contiguousness (the src argument is a []byte).
CMP $16, R4
BGT callMemmove
CMP $16, R2
BLT callMemmove
CMP $16, R3
BLT callMemmove
// !!! Implement the copy from src to dst as a 16-byte load and store.
// (Decode's documentation says that dst and src must not overlap.)
//
// This always copies 16 bytes, instead of only length bytes, but that's
// OK. If the input is a valid Snappy encoding then subsequent iterations
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
// non-nil error), so the overrun will be ignored.
//
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
// 16-byte loads and stores. This technique probably wouldn't be as
// effective on architectures that are fussier about alignment.
LDP 0(R6), (R14, R15)
STP (R14, R15), 0(R7)
// d += length
// s += length
ADD R4, R7, R7
ADD R4, R6, R6
B loop
callMemmove:
// if length > len(dst)-d || length > len(src)-s { etc }
CMP R2, R4
BGT errCorrupt
CMP R3, R4
BGT errCorrupt
// copy(dst[d:], src[s:s+length])
//
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
// R7, R6 and R4 as arguments. Coincidentally, we also need to spill those
// three registers to the stack, to save local variables across the CALL.
MOVD R7, 8(RSP)
MOVD R6, 16(RSP)
MOVD R4, 24(RSP)
MOVD R7, 32(RSP)
MOVD R6, 40(RSP)
MOVD R4, 48(RSP)
CALL runtime·memmove(SB)
// Restore local variables: unspill registers from the stack and
// re-calculate R8-R13.
MOVD 32(RSP), R7
MOVD 40(RSP), R6
MOVD 48(RSP), R4
MOVD dst_base+0(FP), R8
MOVD dst_len+8(FP), R9
MOVD R8, R10
ADD R9, R10, R10
MOVD src_base+24(FP), R11
MOVD src_len+32(FP), R12
MOVD R11, R13
ADD R12, R13, R13
// d += length
// s += length
ADD R4, R7, R7
ADD R4, R6, R6
B loop
tagLit60Plus:
// !!! This fragment does the
//
// s += x - 58; if uint(s) > uint(len(src)) { etc }
//
// checks. In the asm version, we code it once instead of once per switch case.
ADD R4, R6, R6
SUB $58, R6, R6
MOVD R6, R3
SUB R11, R3, R3
CMP R12, R3
BGT errCorrupt
// case x == 60:
MOVW $61, R1
CMPW R1, R4
BEQ tagLit61
BGT tagLit62Plus
// x = uint32(src[s-1])
MOVBU -1(R6), R4
B doLit
tagLit61:
// case x == 61:
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
MOVHU -2(R6), R4
B doLit
tagLit62Plus:
CMPW $62, R4
BHI tagLit63
// case x == 62:
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
MOVHU -3(R6), R4
MOVBU -1(R6), R3
ORR R3<<16, R4
B doLit
tagLit63:
// case x == 63:
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
MOVWU -4(R6), R4
B doLit
// The code above handles literal tags.
// ----------------------------------------
// The code below handles copy tags.
tagCopy4:
// case tagCopy4:
// s += 5
ADD $5, R6, R6
// if uint(s) > uint(len(src)) { etc }
MOVD R6, R3
SUB R11, R3, R3
CMP R12, R3
BGT errCorrupt
// length = 1 + int(src[s-5])>>2
MOVD $1, R1
ADD R4>>2, R1, R4
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
MOVWU -4(R6), R5
B doCopy
tagCopy2:
// case tagCopy2:
// s += 3
ADD $3, R6, R6
// if uint(s) > uint(len(src)) { etc }
MOVD R6, R3
SUB R11, R3, R3
CMP R12, R3
BGT errCorrupt
// length = 1 + int(src[s-3])>>2
MOVD $1, R1
ADD R4>>2, R1, R4
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
MOVHU -2(R6), R5
B doCopy
tagCopy:
// We have a copy tag. We assume that:
// - R3 == src[s] & 0x03
// - R4 == src[s]
CMP $2, R3
BEQ tagCopy2
BGT tagCopy4
// case tagCopy1:
// s += 2
ADD $2, R6, R6
// if uint(s) > uint(len(src)) { etc }
MOVD R6, R3
SUB R11, R3, R3
CMP R12, R3
BGT errCorrupt
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
MOVD R4, R5
AND $0xe0, R5
MOVBU -1(R6), R3
ORR R5<<3, R3, R5
// length = 4 + int(src[s-2])>>2&0x7
MOVD $7, R1
AND R4>>2, R1, R4
ADD $4, R4, R4
doCopy:
// This is the end of the outer "switch", when we have a copy tag.
//
// We assume that:
// - R4 == length && R4 > 0
// - R5 == offset
// if offset <= 0 { etc }
MOVD $0, R1
CMP R1, R5
BLE errCorrupt
// if d < offset { etc }
MOVD R7, R3
SUB R8, R3, R3
CMP R5, R3
BLT errCorrupt
// if length > len(dst)-d { etc }
MOVD R10, R3
SUB R7, R3, R3
CMP R3, R4
BGT errCorrupt
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
//
// Set:
// - R14 = len(dst)-d
// - R15 = &dst[d-offset]
MOVD R10, R14
SUB R7, R14, R14
MOVD R7, R15
SUB R5, R15, R15
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
//
// First, try using two 8-byte load/stores, similar to the doLit technique
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
// and not one 16-byte load/store, and the first store has to be before the
// second load, due to the overlap if offset is in the range [8, 16).
//
// if length > 16 || offset < 8 || len(dst)-d < 16 {
// goto slowForwardCopy
// }
// copy 16 bytes
// d += length
CMP $16, R4
BGT slowForwardCopy
CMP $8, R5
BLT slowForwardCopy
CMP $16, R14
BLT slowForwardCopy
MOVD 0(R15), R2
MOVD R2, 0(R7)
MOVD 8(R15), R3
MOVD R3, 8(R7)
ADD R4, R7, R7
B loop
slowForwardCopy:
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
// can still try 8-byte load stores, provided we can overrun up to 10 extra
// bytes. As above, the overrun will be fixed up by subsequent iterations
// of the outermost loop.
//
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
// commentary says:
//
// ----
//
// The main part of this loop is a simple copy of eight bytes at a time
// until we've copied (at least) the requested amount of bytes. However,
// if d and d-offset are less than eight bytes apart (indicating a
// repeating pattern of length < 8), we first need to expand the pattern in
// order to get the correct results. For instance, if the buffer looks like
// this, with the eight-byte <d-offset> and <d> patterns marked as
// intervals:
//
// abxxxxxxxxxxxx
// [------] d-offset
// [------] d
//
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
// once, after which we can move <d> two bytes without moving <d-offset>:
//
// ababxxxxxxxxxx
// [------] d-offset
// [------] d
//
// and repeat the exercise until the two no longer overlap.
//
// This allows us to do very well in the special case of one single byte
// repeated many times, without taking a big hit for more general cases.
//
// The worst case of extra writing past the end of the match occurs when
// offset == 1 and length == 1; the last copy will read from byte positions
// [0..7] and write to [4..11], whereas it was only supposed to write to
// position 1. Thus, ten excess bytes.
//
// ----
//
// That "10 byte overrun" worst case is confirmed by Go's
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
// and finishSlowForwardCopy algorithm.
//
// if length > len(dst)-d-10 {
// goto verySlowForwardCopy
// }
SUB $10, R14, R14
CMP R14, R4
BGT verySlowForwardCopy
makeOffsetAtLeast8:
// !!! As above, expand the pattern so that offset >= 8 and we can use
// 8-byte load/stores.
//
// for offset < 8 {
// copy 8 bytes from dst[d-offset:] to dst[d:]
// length -= offset
// d += offset
// offset += offset
// // The two previous lines together means that d-offset, and therefore
// // R15, is unchanged.
// }
CMP $8, R5
BGE fixUpSlowForwardCopy
MOVD (R15), R3
MOVD R3, (R7)
SUB R5, R4, R4
ADD R5, R7, R7
ADD R5, R5, R5
B makeOffsetAtLeast8
fixUpSlowForwardCopy:
// !!! Add length (which might be negative now) to d (implied by R7 being
// &dst[d]) so that d ends up at the right place when we jump back to the
// top of the loop. Before we do that, though, we save R7 to R2 so that, if
// length is positive, copying the remaining length bytes will write to the
// right place.
MOVD R7, R2
ADD R4, R7, R7
finishSlowForwardCopy:
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
// length means that we overrun, but as above, that will be fixed up by
// subsequent iterations of the outermost loop.
MOVD $0, R1
CMP R1, R4
BLE loop
MOVD (R15), R3
MOVD R3, (R2)
ADD $8, R15, R15
ADD $8, R2, R2
SUB $8, R4, R4
B finishSlowForwardCopy
verySlowForwardCopy:
// verySlowForwardCopy is a simple implementation of forward copy. In C
// parlance, this is a do/while loop instead of a while loop, since we know
// that length > 0. In Go syntax:
//
// for {
// dst[d] = dst[d - offset]
// d++
// length--
// if length == 0 {
// break
// }
// }
MOVB (R15), R3
MOVB R3, (R7)
ADD $1, R15, R15
ADD $1, R7, R7
SUB $1, R4, R4
CBNZ R4, verySlowForwardCopy
B loop
// The code above handles copy tags.
// ----------------------------------------
end:
// This is the end of the "for s < len(src)".
//
// if d != len(dst) { etc }
CMP R10, R7
BNE errCorrupt
// return 0
MOVD $0, ret+48(FP)
RET
errCorrupt:
// return decodeErrCodeCorrupt
MOVD $1, R2
MOVD R2, ret+48(FP)
RET
@@ -5,6 +5,7 @@
// +build !appengine
// +build gc
// +build !noasm
// +build amd64 arm64
package snappy
+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
+722
View File
@@ -0,0 +1,722 @@
// Copyright 2020 The 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
#include "textflag.h"
// The asm code generally follows the pure Go code in encode_other.go, except
// where marked with a "!!!".
// ----------------------------------------------------------------------------
// func emitLiteral(dst, lit []byte) int
//
// All local variables fit into registers. The register allocation:
// - R3 len(lit)
// - R4 n
// - R6 return value
// - R8 &dst[i]
// - R10 &lit[0]
//
// The 32 bytes of stack space is to call runtime·memmove.
//
// The unusual register allocation of local variables, such as R10 for the
// source pointer, matches the allocation used at the call site in encodeBlock,
// which makes it easier to manually inline this function.
TEXT ·emitLiteral(SB), NOSPLIT, $32-56
MOVD dst_base+0(FP), R8
MOVD lit_base+24(FP), R10
MOVD lit_len+32(FP), R3
MOVD R3, R6
MOVW R3, R4
SUBW $1, R4, R4
CMPW $60, R4
BLT oneByte
CMPW $256, R4
BLT twoBytes
threeBytes:
MOVD $0xf4, R2
MOVB R2, 0(R8)
MOVW R4, 1(R8)
ADD $3, R8, R8
ADD $3, R6, R6
B memmove
twoBytes:
MOVD $0xf0, R2
MOVB R2, 0(R8)
MOVB R4, 1(R8)
ADD $2, R8, R8
ADD $2, R6, R6
B memmove
oneByte:
LSLW $2, R4, R4
MOVB R4, 0(R8)
ADD $1, R8, R8
ADD $1, R6, R6
memmove:
MOVD R6, ret+48(FP)
// copy(dst[i:], lit)
//
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
// R8, R10 and R3 as arguments.
MOVD R8, 8(RSP)
MOVD R10, 16(RSP)
MOVD R3, 24(RSP)
CALL runtime·memmove(SB)
RET
// ----------------------------------------------------------------------------
// func emitCopy(dst []byte, offset, length int) int
//
// All local variables fit into registers. The register allocation:
// - R3 length
// - R7 &dst[0]
// - R8 &dst[i]
// - R11 offset
//
// The unusual register allocation of local variables, such as R11 for the
// offset, matches the allocation used at the call site in encodeBlock, which
// makes it easier to manually inline this function.
TEXT ·emitCopy(SB), NOSPLIT, $0-48
MOVD dst_base+0(FP), R8
MOVD R8, R7
MOVD offset+24(FP), R11
MOVD length+32(FP), R3
loop0:
// for length >= 68 { etc }
CMPW $68, R3
BLT step1
// Emit a length 64 copy, encoded as 3 bytes.
MOVD $0xfe, R2
MOVB R2, 0(R8)
MOVW R11, 1(R8)
ADD $3, R8, R8
SUB $64, R3, R3
B loop0
step1:
// if length > 64 { etc }
CMP $64, R3
BLE step2
// Emit a length 60 copy, encoded as 3 bytes.
MOVD $0xee, R2
MOVB R2, 0(R8)
MOVW R11, 1(R8)
ADD $3, R8, R8
SUB $60, R3, R3
step2:
// if length >= 12 || offset >= 2048 { goto step3 }
CMP $12, R3
BGE step3
CMPW $2048, R11
BGE step3
// Emit the remaining copy, encoded as 2 bytes.
MOVB R11, 1(R8)
LSRW $3, R11, R11
AND $0xe0, R11, R11
SUB $4, R3, R3
LSLW $2, R3
AND $0xff, R3, R3
ORRW R3, R11, R11
ORRW $1, R11, R11
MOVB R11, 0(R8)
ADD $2, R8, R8
// Return the number of bytes written.
SUB R7, R8, R8
MOVD R8, ret+40(FP)
RET
step3:
// Emit the remaining copy, encoded as 3 bytes.
SUB $1, R3, R3
AND $0xff, R3, R3
LSLW $2, R3, R3
ORRW $2, R3, R3
MOVB R3, 0(R8)
MOVW R11, 1(R8)
ADD $3, R8, R8
// Return the number of bytes written.
SUB R7, R8, R8
MOVD R8, ret+40(FP)
RET
// ----------------------------------------------------------------------------
// func extendMatch(src []byte, i, j int) int
//
// All local variables fit into registers. The register allocation:
// - R6 &src[0]
// - R7 &src[j]
// - R13 &src[len(src) - 8]
// - R14 &src[len(src)]
// - R15 &src[i]
//
// The unusual register allocation of local variables, such as R15 for a source
// pointer, matches the allocation used at the call site in encodeBlock, which
// makes it easier to manually inline this function.
TEXT ·extendMatch(SB), NOSPLIT, $0-48
MOVD src_base+0(FP), R6
MOVD src_len+8(FP), R14
MOVD i+24(FP), R15
MOVD j+32(FP), R7
ADD R6, R14, R14
ADD R6, R15, R15
ADD R6, R7, R7
MOVD R14, R13
SUB $8, R13, R13
cmp8:
// As long as we are 8 or more bytes before the end of src, we can load and
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
CMP R13, R7
BHI cmp1
MOVD (R15), R3
MOVD (R7), R4
CMP R4, R3
BNE bsf
ADD $8, R15, R15
ADD $8, R7, R7
B cmp8
bsf:
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
// the index of the first byte that differs.
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
// combination of which finds the least significant bit which is set.
// The arm64 architecture is little-endian, and the shift by 3 converts
// a bit index to a byte index.
EOR R3, R4, R4
RBIT R4, R4
CLZ R4, R4
ADD R4>>3, R7, R7
// Convert from &src[ret] to ret.
SUB R6, R7, R7
MOVD R7, ret+40(FP)
RET
cmp1:
// In src's tail, compare 1 byte at a time.
CMP R7, R14
BLS extendMatchEnd
MOVB (R15), R3
MOVB (R7), R4
CMP R4, R3
BNE extendMatchEnd
ADD $1, R15, R15
ADD $1, R7, R7
B cmp1
extendMatchEnd:
// Convert from &src[ret] to ret.
SUB R6, R7, R7
MOVD R7, ret+40(FP)
RET
// ----------------------------------------------------------------------------
// func encodeBlock(dst, src []byte) (d int)
//
// All local variables fit into registers, other than "var table". The register
// allocation:
// - R3 . .
// - R4 . .
// - R5 64 shift
// - R6 72 &src[0], tableSize
// - R7 80 &src[s]
// - R8 88 &dst[d]
// - R9 96 sLimit
// - R10 . &src[nextEmit]
// - R11 104 prevHash, currHash, nextHash, offset
// - R12 112 &src[base], skip
// - R13 . &src[nextS], &src[len(src) - 8]
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
// - R15 120 candidate
// - R16 . hash constant, 0x1e35a7bd
// - R17 . &table
// - . 128 table
//
// The second column (64, 72, etc) is the stack offset to spill the registers
// when calling other functions. We could pack this slightly tighter, but it's
// simpler to have a dedicated spill map independent of the function called.
//
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
// extra 64 bytes, to call other functions, and an extra 64 bytes, to spill
// local variables (registers) during calls gives 32768 + 64 + 64 = 32896.
TEXT ·encodeBlock(SB), 0, $32896-56
MOVD dst_base+0(FP), R8
MOVD src_base+24(FP), R7
MOVD src_len+32(FP), R14
// shift, tableSize := uint32(32-8), 1<<8
MOVD $24, R5
MOVD $256, R6
MOVW $0xa7bd, R16
MOVKW $(0x1e35<<16), R16
calcShift:
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
// shift--
// }
MOVD $16384, R2
CMP R2, R6
BGE varTable
CMP R14, R6
BGE varTable
SUB $1, R5, R5
LSL $1, R6, R6
B calcShift
varTable:
// var table [maxTableSize]uint16
//
// In the asm code, unlike the Go code, we can zero-initialize only the
// first tableSize elements. Each uint16 element is 2 bytes and each
// iterations writes 64 bytes, so we can do only tableSize/32 writes
// instead of the 2048 writes that would zero-initialize all of table's
// 32768 bytes. This clear could overrun the first tableSize elements, but
// it won't overrun the allocated stack size.
ADD $128, RSP, R17
MOVD R17, R4
// !!! R6 = &src[tableSize]
ADD R6<<1, R17, R6
memclr:
STP.P (ZR, ZR), 64(R4)
STP (ZR, ZR), -48(R4)
STP (ZR, ZR), -32(R4)
STP (ZR, ZR), -16(R4)
CMP R4, R6
BHI memclr
// !!! R6 = &src[0]
MOVD R7, R6
// sLimit := len(src) - inputMargin
MOVD R14, R9
SUB $15, R9, R9
// !!! Pre-emptively spill R5, R6 and R9 to the stack. Their values don't
// change for the rest of the function.
MOVD R5, 64(RSP)
MOVD R6, 72(RSP)
MOVD R9, 96(RSP)
// nextEmit := 0
MOVD R6, R10
// s := 1
ADD $1, R7, R7
// nextHash := hash(load32(src, s), shift)
MOVW 0(R7), R11
MULW R16, R11, R11
LSRW R5, R11, R11
outer:
// for { etc }
// skip := 32
MOVD $32, R12
// nextS := s
MOVD R7, R13
// candidate := 0
MOVD $0, R15
inner0:
// for { etc }
// s := nextS
MOVD R13, R7
// bytesBetweenHashLookups := skip >> 5
MOVD R12, R14
LSR $5, R14, R14
// nextS = s + bytesBetweenHashLookups
ADD R14, R13, R13
// skip += bytesBetweenHashLookups
ADD R14, R12, R12
// if nextS > sLimit { goto emitRemainder }
MOVD R13, R3
SUB R6, R3, R3
CMP R9, R3
BHI emitRemainder
// candidate = int(table[nextHash])
MOVHU 0(R17)(R11<<1), R15
// table[nextHash] = uint16(s)
MOVD R7, R3
SUB R6, R3, R3
MOVH R3, 0(R17)(R11<<1)
// nextHash = hash(load32(src, nextS), shift)
MOVW 0(R13), R11
MULW R16, R11
LSRW R5, R11, R11
// if load32(src, s) != load32(src, candidate) { continue } break
MOVW 0(R7), R3
MOVW (R6)(R15), R4
CMPW R4, R3
BNE inner0
fourByteMatch:
// As per the encode_other.go code:
//
// A 4-byte match has been found. We'll later see etc.
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
// on inputMargin in encode.go.
MOVD R7, R3
SUB R10, R3, R3
CMP $16, R3
BLE emitLiteralFastPath
// ----------------------------------------
// Begin inline of the emitLiteral call.
//
// d += emitLiteral(dst[d:], src[nextEmit:s])
MOVW R3, R4
SUBW $1, R4, R4
MOVW $60, R2
CMPW R2, R4
BLT inlineEmitLiteralOneByte
MOVW $256, R2
CMPW R2, R4
BLT inlineEmitLiteralTwoBytes
inlineEmitLiteralThreeBytes:
MOVD $0xf4, R1
MOVB R1, 0(R8)
MOVW R4, 1(R8)
ADD $3, R8, R8
B inlineEmitLiteralMemmove
inlineEmitLiteralTwoBytes:
MOVD $0xf0, R1
MOVB R1, 0(R8)
MOVB R4, 1(R8)
ADD $2, R8, R8
B inlineEmitLiteralMemmove
inlineEmitLiteralOneByte:
LSLW $2, R4, R4
MOVB R4, 0(R8)
ADD $1, R8, R8
inlineEmitLiteralMemmove:
// Spill local variables (registers) onto the stack; call; unspill.
//
// copy(dst[i:], lit)
//
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
// R8, R10 and R3 as arguments.
MOVD R8, 8(RSP)
MOVD R10, 16(RSP)
MOVD R3, 24(RSP)
// Finish the "d +=" part of "d += emitLiteral(etc)".
ADD R3, R8, R8
MOVD R7, 80(RSP)
MOVD R8, 88(RSP)
MOVD R15, 120(RSP)
CALL runtime·memmove(SB)
MOVD 64(RSP), R5
MOVD 72(RSP), R6
MOVD 80(RSP), R7
MOVD 88(RSP), R8
MOVD 96(RSP), R9
MOVD 120(RSP), R15
ADD $128, RSP, R17
MOVW $0xa7bd, R16
MOVKW $(0x1e35<<16), R16
B inner1
inlineEmitLiteralEnd:
// End inline of the emitLiteral call.
// ----------------------------------------
emitLiteralFastPath:
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
MOVB R3, R4
SUBW $1, R4, R4
AND $0xff, R4, R4
LSLW $2, R4, R4
MOVB R4, (R8)
ADD $1, R8, R8
// !!! Implement the copy from lit to dst as a 16-byte load and store.
// (Encode's documentation says that dst and src must not overlap.)
//
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
// OK. Subsequent iterations will fix up the overrun.
//
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
// 16-byte loads and stores. This technique probably wouldn't be as
// effective on architectures that are fussier about alignment.
LDP 0(R10), (R0, R1)
STP (R0, R1), 0(R8)
ADD R3, R8, R8
inner1:
// for { etc }
// base := s
MOVD R7, R12
// !!! offset := base - candidate
MOVD R12, R11
SUB R15, R11, R11
SUB R6, R11, R11
// ----------------------------------------
// Begin inline of the extendMatch call.
//
// s = extendMatch(src, candidate+4, s+4)
// !!! R14 = &src[len(src)]
MOVD src_len+32(FP), R14
ADD R6, R14, R14
// !!! R13 = &src[len(src) - 8]
MOVD R14, R13
SUB $8, R13, R13
// !!! R15 = &src[candidate + 4]
ADD $4, R15, R15
ADD R6, R15, R15
// !!! s += 4
ADD $4, R7, R7
inlineExtendMatchCmp8:
// As long as we are 8 or more bytes before the end of src, we can load and
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
CMP R13, R7
BHI inlineExtendMatchCmp1
MOVD (R15), R3
MOVD (R7), R4
CMP R4, R3
BNE inlineExtendMatchBSF
ADD $8, R15, R15
ADD $8, R7, R7
B inlineExtendMatchCmp8
inlineExtendMatchBSF:
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
// the index of the first byte that differs.
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
// combination of which finds the least significant bit which is set.
// The arm64 architecture is little-endian, and the shift by 3 converts
// a bit index to a byte index.
EOR R3, R4, R4
RBIT R4, R4
CLZ R4, R4
ADD R4>>3, R7, R7
B inlineExtendMatchEnd
inlineExtendMatchCmp1:
// In src's tail, compare 1 byte at a time.
CMP R7, R14
BLS inlineExtendMatchEnd
MOVB (R15), R3
MOVB (R7), R4
CMP R4, R3
BNE inlineExtendMatchEnd
ADD $1, R15, R15
ADD $1, R7, R7
B inlineExtendMatchCmp1
inlineExtendMatchEnd:
// End inline of the extendMatch call.
// ----------------------------------------
// ----------------------------------------
// Begin inline of the emitCopy call.
//
// d += emitCopy(dst[d:], base-candidate, s-base)
// !!! length := s - base
MOVD R7, R3
SUB R12, R3, R3
inlineEmitCopyLoop0:
// for length >= 68 { etc }
MOVW $68, R2
CMPW R2, R3
BLT inlineEmitCopyStep1
// Emit a length 64 copy, encoded as 3 bytes.
MOVD $0xfe, R1
MOVB R1, 0(R8)
MOVW R11, 1(R8)
ADD $3, R8, R8
SUBW $64, R3, R3
B inlineEmitCopyLoop0
inlineEmitCopyStep1:
// if length > 64 { etc }
MOVW $64, R2
CMPW R2, R3
BLE inlineEmitCopyStep2
// Emit a length 60 copy, encoded as 3 bytes.
MOVD $0xee, R1
MOVB R1, 0(R8)
MOVW R11, 1(R8)
ADD $3, R8, R8
SUBW $60, R3, R3
inlineEmitCopyStep2:
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
MOVW $12, R2
CMPW R2, R3
BGE inlineEmitCopyStep3
MOVW $2048, R2
CMPW R2, R11
BGE inlineEmitCopyStep3
// Emit the remaining copy, encoded as 2 bytes.
MOVB R11, 1(R8)
LSRW $8, R11, R11
LSLW $5, R11, R11
SUBW $4, R3, R3
AND $0xff, R3, R3
LSLW $2, R3, R3
ORRW R3, R11, R11
ORRW $1, R11, R11
MOVB R11, 0(R8)
ADD $2, R8, R8
B inlineEmitCopyEnd
inlineEmitCopyStep3:
// Emit the remaining copy, encoded as 3 bytes.
SUBW $1, R3, R3
LSLW $2, R3, R3
ORRW $2, R3, R3
MOVB R3, 0(R8)
MOVW R11, 1(R8)
ADD $3, R8, R8
inlineEmitCopyEnd:
// End inline of the emitCopy call.
// ----------------------------------------
// nextEmit = s
MOVD R7, R10
// if s >= sLimit { goto emitRemainder }
MOVD R7, R3
SUB R6, R3, R3
CMP R3, R9
BLS emitRemainder
// As per the encode_other.go code:
//
// We could immediately etc.
// x := load64(src, s-1)
MOVD -1(R7), R14
// prevHash := hash(uint32(x>>0), shift)
MOVW R14, R11
MULW R16, R11, R11
LSRW R5, R11, R11
// table[prevHash] = uint16(s-1)
MOVD R7, R3
SUB R6, R3, R3
SUB $1, R3, R3
MOVHU R3, 0(R17)(R11<<1)
// currHash := hash(uint32(x>>8), shift)
LSR $8, R14, R14
MOVW R14, R11
MULW R16, R11, R11
LSRW R5, R11, R11
// candidate = int(table[currHash])
MOVHU 0(R17)(R11<<1), R15
// table[currHash] = uint16(s)
ADD $1, R3, R3
MOVHU R3, 0(R17)(R11<<1)
// if uint32(x>>8) == load32(src, candidate) { continue }
MOVW (R6)(R15), R4
CMPW R4, R14
BEQ inner1
// nextHash = hash(uint32(x>>16), shift)
LSR $8, R14, R14
MOVW R14, R11
MULW R16, R11, R11
LSRW R5, R11, R11
// s++
ADD $1, R7, R7
// break out of the inner1 for loop, i.e. continue the outer loop.
B outer
emitRemainder:
// if nextEmit < len(src) { etc }
MOVD src_len+32(FP), R3
ADD R6, R3, R3
CMP R3, R10
BEQ encodeBlockEnd
// d += emitLiteral(dst[d:], src[nextEmit:])
//
// Push args.
MOVD R8, 8(RSP)
MOVD $0, 16(RSP) // Unnecessary, as the callee ignores it, but conservative.
MOVD $0, 24(RSP) // Unnecessary, as the callee ignores it, but conservative.
MOVD R10, 32(RSP)
SUB R10, R3, R3
MOVD R3, 40(RSP)
MOVD R3, 48(RSP) // Unnecessary, as the callee ignores it, but conservative.
// Spill local variables (registers) onto the stack; call; unspill.
MOVD R8, 88(RSP)
CALL ·emitLiteral(SB)
MOVD 88(RSP), R8
// Finish the "d +=" part of "d += emitLiteral(etc)".
MOVD 56(RSP), R1
ADD R1, R8, R8
encodeBlockEnd:
MOVD dst_base+0(FP), R3
SUB R3, R8, R8
MOVD R8, d+48(FP)
RET
@@ -5,6 +5,7 @@
// +build !appengine
// +build gc
// +build !noasm
// +build amd64 arm64
package snappy
+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
+166
View File
@@ -0,0 +1,166 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
// This file implements the ASF RMCP payload specified in section 3.2.2.3 of
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
const (
// ASFRMCPEnterprise is the IANA-assigned Enterprise Number of the ASF-RMCP.
ASFRMCPEnterprise uint32 = 4542
)
// ASFDataIdentifier encapsulates fields used to uniquely identify the format of
// the data block.
//
// While the enterprise number is almost always 4542 (ASF-RMCP), we support
// registering layers using structs of this type as a key in case any users are
// using OEM-extensions.
type ASFDataIdentifier struct {
// Enterprise is the IANA Enterprise Number associated with the entity that
// defines the message type. A list can be found at
// https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers.
// This can be thought of as the namespace for the message type.
Enterprise uint32
// Type is the message type, defined by the entity associated with the
// enterprise above. No pressure, but in the context of EN 4542, 1 byte is
// the difference between sending a ping and telling a machine to do an
// unconditional power down (0x80 and 0x12 respectively).
Type uint8
}
// LayerType returns the payload layer type corresponding to an ASF message
// type.
func (a ASFDataIdentifier) LayerType() gopacket.LayerType {
if lt := asfDataLayerTypes[a]; lt != 0 {
return lt
}
// some layer types don't have a payload, e.g. ASF-RMCP Presence Ping.
return gopacket.LayerTypePayload
}
// RegisterASFLayerType allows specifying that the data block of ASF packets
// with a given enterprise number and type should be processed by a given layer
// type. This overrides any existing registrations, including defaults.
func RegisterASFLayerType(a ASFDataIdentifier, l gopacket.LayerType) {
asfDataLayerTypes[a] = l
}
var (
// ASFDataIdentifierPresencePong is the message type of the response to a
// Presence Ping message. It indicates the sender is ASF-RMCP-aware.
ASFDataIdentifierPresencePong = ASFDataIdentifier{
Enterprise: ASFRMCPEnterprise,
Type: 0x40,
}
// ASFDataIdentifierPresencePing is a message type sent to a managed client
// to solicit a Presence Pong response. Clients may ignore this if the RMCP
// version is unsupported. Sending this message with a sequence number <255
// is the recommended way of finding out whether an implementation sends
// RMCP ACKs (e.g. iDRAC does, Super Micro does not).
//
// Systems implementing IPMI must respond to this ping to conform to the
// spec, so it is a good substitute for an ICMP ping.
ASFDataIdentifierPresencePing = ASFDataIdentifier{
Enterprise: ASFRMCPEnterprise,
Type: 0x80,
}
// asfDataLayerTypes is used to find the next layer for a given ASF header.
asfDataLayerTypes = map[ASFDataIdentifier]gopacket.LayerType{
ASFDataIdentifierPresencePong: LayerTypeASFPresencePong,
}
)
// ASF defines ASF's generic RMCP message Data block format. See section
// 3.2.2.3.
type ASF struct {
BaseLayer
ASFDataIdentifier
// Tag is used to match request/response pairs. The tag of a response is set
// to that of the message it is responding to. If a message is
// unidirectional, i.e. not part of a request/response pair, this is set to
// 255.
Tag uint8
// 1 byte reserved, set to 0x00.
// Length is the length of this layer's payload in bytes.
Length uint8
}
// LayerType returns LayerTypeASF. It partially satisfies Layer and
// SerializableLayer.
func (*ASF) LayerType() gopacket.LayerType {
return LayerTypeASF
}
// CanDecode returns LayerTypeASF. It partially satisfies DecodingLayer.
func (a *ASF) CanDecode() gopacket.LayerClass {
return a.LayerType()
}
// DecodeFromBytes makes the layer represent the provided bytes. It partially
// satisfies DecodingLayer.
func (a *ASF) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return fmt.Errorf("invalid ASF data header, length %v less than 8",
len(data))
}
a.BaseLayer.Contents = data[:8]
a.BaseLayer.Payload = data[8:]
a.Enterprise = binary.BigEndian.Uint32(data[:4])
a.Type = uint8(data[4])
a.Tag = uint8(data[5])
// 1 byte reserved
a.Length = uint8(data[7])
return nil
}
// NextLayerType returns the layer type corresponding to the message type of
// this ASF data layer. This partially satisfies DecodingLayer.
func (a *ASF) NextLayerType() gopacket.LayerType {
return a.ASFDataIdentifier.LayerType()
}
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
// partially satisfying SerializableLayer.
func (a *ASF) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
payload := b.Bytes()
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
bytes[4] = uint8(a.Type)
bytes[5] = a.Tag
bytes[6] = 0x00
if opts.FixLengths {
a.Length = uint8(len(payload))
}
bytes[7] = a.Length
return nil
}
// decodeASF decodes the byte slice into an RMCP-ASF data struct.
func decodeASF(data []byte, p gopacket.PacketBuilder) error {
return decodingLayerDecoder(&ASF{}, data, p)
}
+194
View File
@@ -0,0 +1,194 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
// This file implements the RMCP ASF Presence Pong message, specified in section
// 3.2.4.3 of
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf. It
// also contains non-competing elements from IPMI v2.0, specified in section
// 13.2.4 of
// https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf.
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
type (
// ASFEntity is the type of individual entities that a Presence Pong
// response can indicate support of. The entities currently implemented by
// the spec are IPMI and ASFv1.
ASFEntity uint8
// ASFInteraction is the type of individual interactions that a Presence
// Pong response can indicate support for. The interactions currently
// implemented by the spec are RMCP security extensions. Although not
// specified, IPMI uses this field to indicate support for DASH, which is
// supported as well.
ASFInteraction uint8
)
const (
// ASFDCMIEnterprise is the IANA-assigned Enterprise Number of the Data
// Center Manageability Interface Forum. The Presence Pong response's
// Enterprise field being set to this value indicates support for DCMI. The
// DCMI spec regards the OEM field as reserved, so these should be null.
ASFDCMIEnterprise uint32 = 36465
// ASFPresencePongEntityIPMI ANDs with Presence Pong's supported entities
// field if the managed system supports IPMI.
ASFPresencePongEntityIPMI ASFEntity = 1 << 7
// ASFPresencePongEntityASFv1 ANDs with Presence Pong's supported entities
// field if the managed system supports ASF v1.0.
ASFPresencePongEntityASFv1 ASFEntity = 1
// ASFPresencePongInteractionSecurityExtensions ANDs with Presence Pong's
// supported interactions field if the managed system supports RMCP v2.0
// security extensions. See section 3.2.3.
ASFPresencePongInteractionSecurityExtensions ASFInteraction = 1 << 7
// ASFPresencePongInteractionDASH ANDs with Presence Pong's supported
// interactions field if the managed system supports DMTF DASH. See
// https://www.dmtf.org/standards/dash.
ASFPresencePongInteractionDASH ASFInteraction = 1 << 5
)
// ASFPresencePong defines the structure of a Presence Pong message's payload.
// See section 3.2.4.3.
type ASFPresencePong struct {
BaseLayer
// Enterprise is the IANA Enterprise Number of an entity that has defined
// OEM-specific capabilities for the managed client. If no such capabilities
// exist, this is set to ASF's IANA Enterprise Number.
Enterprise uint32
// OEM identifies OEM-specific capabilities. Its structure is defined by the
// OEM. This is set to 0s if no OEM-specific capabilities exist. This
// implementation does not change byte order from the wire for this field.
OEM [4]byte
// We break out entities and interactions into separate booleans as
// discovery is the entire point of this type of message, so we assume they
// are accessed. It also makes gopacket's default layer printing more
// useful.
// IPMI is true if IPMI is supported by the managed system. There is no
// explicit version in the specification, however given the dates, this is
// assumed to be IPMI v1.0. Support for IPMI is contained in the "supported
// entities" field of the presence pong payload.
IPMI bool
// ASFv1 indicates support for ASF v1.0. This seems somewhat redundant as
// ASF must be supported in order to receive a response. This is contained
// in the "supported entities" field of the presence pong payload.
ASFv1 bool
// SecurityExtensions indicates support for RMCP Security Extensions,
// specified in ASF v2.0. This will always be false for v1.x
// implementations. This is contained in the "supported interactions" field
// of the presence pong payload. This field is defined in ASF v1.0, but has
// no useful value.
SecurityExtensions bool
// DASH is true if DMTF DASH is supported. This is not specified in ASF
// v2.0, but in IPMI v2.0, however the former does not preclude it, so we
// support it.
DASH bool
// 6 bytes reserved after the entities and interactions fields, set to 0s.
}
// SupportsDCMI returns whether the Presence Pong message indicates support for
// the Data Center Management Interface, which is an extension of IPMI v2.0.
func (a *ASFPresencePong) SupportsDCMI() bool {
return a.Enterprise == ASFDCMIEnterprise && a.IPMI && a.ASFv1
}
// LayerType returns LayerTypeASFPresencePong. It partially satisfies Layer and
// SerializableLayer.
func (*ASFPresencePong) LayerType() gopacket.LayerType {
return LayerTypeASFPresencePong
}
// CanDecode returns LayerTypeASFPresencePong. It partially satisfies
// DecodingLayer.
func (a *ASFPresencePong) CanDecode() gopacket.LayerClass {
return a.LayerType()
}
// DecodeFromBytes makes the layer represent the provided bytes. It partially
// satisfies DecodingLayer.
func (a *ASFPresencePong) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 16 {
df.SetTruncated()
return fmt.Errorf("invalid ASF presence pong payload, length %v less than 16",
len(data))
}
a.BaseLayer.Contents = data[:16]
a.BaseLayer.Payload = data[16:]
a.Enterprise = binary.BigEndian.Uint32(data[:4])
copy(a.OEM[:], data[4:8]) // N.B. no byte order change
a.IPMI = data[8]&uint8(ASFPresencePongEntityIPMI) != 0
a.ASFv1 = data[8]&uint8(ASFPresencePongEntityASFv1) != 0
a.SecurityExtensions = data[9]&uint8(ASFPresencePongInteractionSecurityExtensions) != 0
a.DASH = data[9]&uint8(ASFPresencePongInteractionDASH) != 0
// ignore remaining 6 bytes; should be set to 0s
return nil
}
// NextLayerType returns LayerTypePayload, as there are no further layers to
// decode. This partially satisfies DecodingLayer.
func (a *ASFPresencePong) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
// partially satisfying SerializableLayer.
func (a *ASFPresencePong) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(16)
if err != nil {
return err
}
binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
copy(bytes[4:8], a.OEM[:])
bytes[8] = 0
if a.IPMI {
bytes[8] |= uint8(ASFPresencePongEntityIPMI)
}
if a.ASFv1 {
bytes[8] |= uint8(ASFPresencePongEntityASFv1)
}
bytes[9] = 0
if a.SecurityExtensions {
bytes[9] |= uint8(ASFPresencePongInteractionSecurityExtensions)
}
if a.DASH {
bytes[9] |= uint8(ASFPresencePongInteractionDASH)
}
// zero-out remaining 6 bytes
for i := 10; i < len(bytes); i++ {
bytes[i] = 0x00
}
return nil
}
// decodeASFPresencePong decodes the byte slice into an RMCP-ASF Presence Pong
// struct.
func decodeASFPresencePong(data []byte, p gopacket.PacketBuilder) error {
return decodingLayerDecoder(&ASFPresencePong{}, data, p)
}
+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}
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2018 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
const (
//ERSPANIIVersionObsolete - The obsolete value for the version field
ERSPANIIVersionObsolete = 0x0
// ERSPANIIVersion - The current value for the version field
ERSPANIIVersion = 0x1
)
// ERSPANII contains all of the fields found in an ERSPAN Type II header
// https://tools.ietf.org/html/draft-foschiano-erspan-03
type ERSPANII struct {
BaseLayer
IsTruncated bool
Version, CoS, TrunkEncap uint8
VLANIdentifier, SessionID, Reserved uint16
Index uint32
}
func (erspan2 *ERSPANII) LayerType() gopacket.LayerType { return LayerTypeERSPANII }
// DecodeFromBytes decodes the given bytes into this layer.
func (erspan2 *ERSPANII) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
erspan2Length := 8
erspan2.Version = data[0] & 0xF0 >> 4
erspan2.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
erspan2.CoS = data[2] & 0xE0 >> 5
erspan2.TrunkEncap = data[2] & 0x18 >> 3
erspan2.IsTruncated = data[2]&0x4>>2 != 0
erspan2.SessionID = binary.BigEndian.Uint16(data[2:4]) & 0x03FF
erspan2.Reserved = binary.BigEndian.Uint16(data[4:6]) & 0xFFF0 >> 4
erspan2.Index = binary.BigEndian.Uint32(data[4:8]) & 0x000FFFFF
erspan2.Contents = data[:erspan2Length]
erspan2.Payload = data[erspan2Length:]
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (erspan2 *ERSPANII) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
twoByteInt := uint16(erspan2.Version&0xF)<<12 | erspan2.VLANIdentifier&0x0FFF
binary.BigEndian.PutUint16(bytes, twoByteInt)
twoByteInt = uint16(erspan2.CoS&0x7)<<13 | uint16(erspan2.TrunkEncap&0x3)<<11 | erspan2.SessionID&0x03FF
if erspan2.IsTruncated {
twoByteInt |= 0x400
}
binary.BigEndian.PutUint16(bytes[2:], twoByteInt)
fourByteInt := uint32(erspan2.Reserved&0x0FFF)<<20 | erspan2.Index&0x000FFFFF
binary.BigEndian.PutUint32(bytes[4:], fourByteInt)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (erspan2 *ERSPANII) CanDecode() gopacket.LayerClass {
return LayerTypeERSPANII
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (erspan2 *ERSPANII) NextLayerType() gopacket.LayerType {
return LayerTypeEthernet
}
func decodeERSPANII(data []byte, p gopacket.PacketBuilder) error {
erspan2 := &ERSPANII{}
return decodingLayerDecoder(erspan2, data, p)
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// FuzzLayer is a fuzz target for the layers package of gopacket
// A fuzz target is a function processing a binary blob (byte slice)
// The process here is to interpret this data as a packet, and print the layers contents.
// The decoding options and the starting layer are encoded in the first bytes.
// The function returns 1 if this is a valid packet (no error layer)
func FuzzLayer(data []byte) int {
if len(data) < 3 {
return 0
}
// use the first two bytes to choose the top level layer
startLayer := binary.BigEndian.Uint16(data[:2])
var fuzzOpts = gopacket.DecodeOptions{
Lazy: data[2]&0x1 != 0,
NoCopy: data[2]&0x2 != 0,
SkipDecodeRecovery: data[2]&0x4 != 0,
DecodeStreamsAsDatagrams: data[2]&0x8 != 0,
}
p := gopacket.NewPacket(data[3:], gopacket.LayerType(startLayer), fuzzOpts)
for _, l := range p.Layers() {
gopacket.LayerString(l)
}
if p.ErrorLayer() != nil {
return 0
}
return 1
}
+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]))
+560
View File
@@ -0,0 +1,560 @@
// Copyright 2020 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
const (
// RFC 2865 3. Packet Format
// `The minimum length is 20 and maximum length is 4096.`
radiusMinimumRecordSizeInBytes int = 20
radiusMaximumRecordSizeInBytes int = 4096
// RFC 2865 5. Attributes
// `The Length field is one octet, and indicates the length of this Attribute including the Type, Length and Value fields.`
// `The Value field is zero or more octets and contains information specific to the Attribute.`
radiusAttributesMinimumRecordSizeInBytes int = 2
)
// RADIUS represents a Remote Authentication Dial In User Service layer.
type RADIUS struct {
BaseLayer
Code RADIUSCode
Identifier RADIUSIdentifier
Length RADIUSLength
Authenticator RADIUSAuthenticator
Attributes []RADIUSAttribute
}
// RADIUSCode represents packet type.
type RADIUSCode uint8
// constants that define RADIUSCode.
const (
RADIUSCodeAccessRequest RADIUSCode = 1 // RFC2865 3. Packet Format
RADIUSCodeAccessAccept RADIUSCode = 2 // RFC2865 3. Packet Format
RADIUSCodeAccessReject RADIUSCode = 3 // RFC2865 3. Packet Format
RADIUSCodeAccountingRequest RADIUSCode = 4 // RFC2865 3. Packet Format
RADIUSCodeAccountingResponse RADIUSCode = 5 // RFC2865 3. Packet Format
RADIUSCodeAccessChallenge RADIUSCode = 11 // RFC2865 3. Packet Format
RADIUSCodeStatusServer RADIUSCode = 12 // RFC2865 3. Packet Format (experimental)
RADIUSCodeStatusClient RADIUSCode = 13 // RFC2865 3. Packet Format (experimental)
RADIUSCodeReserved RADIUSCode = 255 // RFC2865 3. Packet Format
)
// String returns a string version of a RADIUSCode.
func (t RADIUSCode) String() (s string) {
switch t {
case RADIUSCodeAccessRequest:
s = "Access-Request"
case RADIUSCodeAccessAccept:
s = "Access-Accept"
case RADIUSCodeAccessReject:
s = "Access-Reject"
case RADIUSCodeAccountingRequest:
s = "Accounting-Request"
case RADIUSCodeAccountingResponse:
s = "Accounting-Response"
case RADIUSCodeAccessChallenge:
s = "Access-Challenge"
case RADIUSCodeStatusServer:
s = "Status-Server"
case RADIUSCodeStatusClient:
s = "Status-Client"
case RADIUSCodeReserved:
s = "Reserved"
default:
s = fmt.Sprintf("Unknown(%d)", t)
}
return
}
// RADIUSIdentifier represents packet identifier.
type RADIUSIdentifier uint8
// RADIUSLength represents packet length.
type RADIUSLength uint16
// RADIUSAuthenticator represents authenticator.
type RADIUSAuthenticator [16]byte
// RADIUSAttribute represents attributes.
type RADIUSAttribute struct {
Type RADIUSAttributeType
Length RADIUSAttributeLength
Value RADIUSAttributeValue
}
// RADIUSAttributeType represents attribute type.
type RADIUSAttributeType uint8
// constants that define RADIUSAttributeType.
const (
RADIUSAttributeTypeUserName RADIUSAttributeType = 1 // RFC2865 5.1. User-Name
RADIUSAttributeTypeUserPassword RADIUSAttributeType = 2 // RFC2865 5.2. User-Password
RADIUSAttributeTypeCHAPPassword RADIUSAttributeType = 3 // RFC2865 5.3. CHAP-Password
RADIUSAttributeTypeNASIPAddress RADIUSAttributeType = 4 // RFC2865 5.4. NAS-IP-Address
RADIUSAttributeTypeNASPort RADIUSAttributeType = 5 // RFC2865 5.5. NAS-Port
RADIUSAttributeTypeServiceType RADIUSAttributeType = 6 // RFC2865 5.6. Service-Type
RADIUSAttributeTypeFramedProtocol RADIUSAttributeType = 7 // RFC2865 5.7. Framed-Protocol
RADIUSAttributeTypeFramedIPAddress RADIUSAttributeType = 8 // RFC2865 5.8. Framed-IP-Address
RADIUSAttributeTypeFramedIPNetmask RADIUSAttributeType = 9 // RFC2865 5.9. Framed-IP-Netmask
RADIUSAttributeTypeFramedRouting RADIUSAttributeType = 10 // RFC2865 5.10. Framed-Routing
RADIUSAttributeTypeFilterId RADIUSAttributeType = 11 // RFC2865 5.11. Filter-Id
RADIUSAttributeTypeFramedMTU RADIUSAttributeType = 12 // RFC2865 5.12. Framed-MTU
RADIUSAttributeTypeFramedCompression RADIUSAttributeType = 13 // RFC2865 5.13. Framed-Compression
RADIUSAttributeTypeLoginIPHost RADIUSAttributeType = 14 // RFC2865 5.14. Login-IP-Host
RADIUSAttributeTypeLoginService RADIUSAttributeType = 15 // RFC2865 5.15. Login-Service
RADIUSAttributeTypeLoginTCPPort RADIUSAttributeType = 16 // RFC2865 5.16. Login-TCP-Port
RADIUSAttributeTypeReplyMessage RADIUSAttributeType = 18 // RFC2865 5.18. Reply-Message
RADIUSAttributeTypeCallbackNumber RADIUSAttributeType = 19 // RFC2865 5.19. Callback-Number
RADIUSAttributeTypeCallbackId RADIUSAttributeType = 20 // RFC2865 5.20. Callback-Id
RADIUSAttributeTypeFramedRoute RADIUSAttributeType = 22 // RFC2865 5.22. Framed-Route
RADIUSAttributeTypeFramedIPXNetwork RADIUSAttributeType = 23 // RFC2865 5.23. Framed-IPX-Network
RADIUSAttributeTypeState RADIUSAttributeType = 24 // RFC2865 5.24. State
RADIUSAttributeTypeClass RADIUSAttributeType = 25 // RFC2865 5.25. Class
RADIUSAttributeTypeVendorSpecific RADIUSAttributeType = 26 // RFC2865 5.26. Vendor-Specific
RADIUSAttributeTypeSessionTimeout RADIUSAttributeType = 27 // RFC2865 5.27. Session-Timeout
RADIUSAttributeTypeIdleTimeout RADIUSAttributeType = 28 // RFC2865 5.28. Idle-Timeout
RADIUSAttributeTypeTerminationAction RADIUSAttributeType = 29 // RFC2865 5.29. Termination-Action
RADIUSAttributeTypeCalledStationId RADIUSAttributeType = 30 // RFC2865 5.30. Called-Station-Id
RADIUSAttributeTypeCallingStationId RADIUSAttributeType = 31 // RFC2865 5.31. Calling-Station-Id
RADIUSAttributeTypeNASIdentifier RADIUSAttributeType = 32 // RFC2865 5.32. NAS-Identifier
RADIUSAttributeTypeProxyState RADIUSAttributeType = 33 // RFC2865 5.33. Proxy-State
RADIUSAttributeTypeLoginLATService RADIUSAttributeType = 34 // RFC2865 5.34. Login-LAT-Service
RADIUSAttributeTypeLoginLATNode RADIUSAttributeType = 35 // RFC2865 5.35. Login-LAT-Node
RADIUSAttributeTypeLoginLATGroup RADIUSAttributeType = 36 // RFC2865 5.36. Login-LAT-Group
RADIUSAttributeTypeFramedAppleTalkLink RADIUSAttributeType = 37 // RFC2865 5.37. Framed-AppleTalk-Link
RADIUSAttributeTypeFramedAppleTalkNetwork RADIUSAttributeType = 38 // RFC2865 5.38. Framed-AppleTalk-Network
RADIUSAttributeTypeFramedAppleTalkZone RADIUSAttributeType = 39 // RFC2865 5.39. Framed-AppleTalk-Zone
RADIUSAttributeTypeAcctStatusType RADIUSAttributeType = 40 // RFC2866 5.1. Acct-Status-Type
RADIUSAttributeTypeAcctDelayTime RADIUSAttributeType = 41 // RFC2866 5.2. Acct-Delay-Time
RADIUSAttributeTypeAcctInputOctets RADIUSAttributeType = 42 // RFC2866 5.3. Acct-Input-Octets
RADIUSAttributeTypeAcctOutputOctets RADIUSAttributeType = 43 // RFC2866 5.4. Acct-Output-Octets
RADIUSAttributeTypeAcctSessionId RADIUSAttributeType = 44 // RFC2866 5.5. Acct-Session-Id
RADIUSAttributeTypeAcctAuthentic RADIUSAttributeType = 45 // RFC2866 5.6. Acct-Authentic
RADIUSAttributeTypeAcctSessionTime RADIUSAttributeType = 46 // RFC2866 5.7. Acct-Session-Time
RADIUSAttributeTypeAcctInputPackets RADIUSAttributeType = 47 // RFC2866 5.8. Acct-Input-Packets
RADIUSAttributeTypeAcctOutputPackets RADIUSAttributeType = 48 // RFC2866 5.9. Acct-Output-Packets
RADIUSAttributeTypeAcctTerminateCause RADIUSAttributeType = 49 // RFC2866 5.10. Acct-Terminate-Cause
RADIUSAttributeTypeAcctMultiSessionId RADIUSAttributeType = 50 // RFC2866 5.11. Acct-Multi-Session-Id
RADIUSAttributeTypeAcctLinkCount RADIUSAttributeType = 51 // RFC2866 5.12. Acct-Link-Count
RADIUSAttributeTypeAcctInputGigawords RADIUSAttributeType = 52 // RFC2869 5.1. Acct-Input-Gigawords
RADIUSAttributeTypeAcctOutputGigawords RADIUSAttributeType = 53 // RFC2869 5.2. Acct-Output-Gigawords
RADIUSAttributeTypeEventTimestamp RADIUSAttributeType = 55 // RFC2869 5.3. Event-Timestamp
RADIUSAttributeTypeCHAPChallenge RADIUSAttributeType = 60 // RFC2865 5.40. CHAP-Challenge
RADIUSAttributeTypeNASPortType RADIUSAttributeType = 61 // RFC2865 5.41. NAS-Port-Type
RADIUSAttributeTypePortLimit RADIUSAttributeType = 62 // RFC2865 5.42. Port-Limit
RADIUSAttributeTypeLoginLATPort RADIUSAttributeType = 63 // RFC2865 5.43. Login-LAT-Port
RADIUSAttributeTypeTunnelType RADIUSAttributeType = 64 // RFC2868 3.1. Tunnel-Type
RADIUSAttributeTypeTunnelMediumType RADIUSAttributeType = 65 // RFC2868 3.2. Tunnel-Medium-Type
RADIUSAttributeTypeTunnelClientEndpoint RADIUSAttributeType = 66 // RFC2868 3.3. Tunnel-Client-Endpoint
RADIUSAttributeTypeTunnelServerEndpoint RADIUSAttributeType = 67 // RFC2868 3.4. Tunnel-Server-Endpoint
RADIUSAttributeTypeAcctTunnelConnection RADIUSAttributeType = 68 // RFC2867 4.1. Acct-Tunnel-Connection
RADIUSAttributeTypeTunnelPassword RADIUSAttributeType = 69 // RFC2868 3.5. Tunnel-Password
RADIUSAttributeTypeARAPPassword RADIUSAttributeType = 70 // RFC2869 5.4. ARAP-Password
RADIUSAttributeTypeARAPFeatures RADIUSAttributeType = 71 // RFC2869 5.5. ARAP-Features
RADIUSAttributeTypeARAPZoneAccess RADIUSAttributeType = 72 // RFC2869 5.6. ARAP-Zone-Access
RADIUSAttributeTypeARAPSecurity RADIUSAttributeType = 73 // RFC2869 5.7. ARAP-Security
RADIUSAttributeTypeARAPSecurityData RADIUSAttributeType = 74 // RFC2869 5.8. ARAP-Security-Data
RADIUSAttributeTypePasswordRetry RADIUSAttributeType = 75 // RFC2869 5.9. Password-Retry
RADIUSAttributeTypePrompt RADIUSAttributeType = 76 // RFC2869 5.10. Prompt
RADIUSAttributeTypeConnectInfo RADIUSAttributeType = 77 // RFC2869 5.11. Connect-Info
RADIUSAttributeTypeConfigurationToken RADIUSAttributeType = 78 // RFC2869 5.12. Configuration-Token
RADIUSAttributeTypeEAPMessage RADIUSAttributeType = 79 // RFC2869 5.13. EAP-Message
RADIUSAttributeTypeMessageAuthenticator RADIUSAttributeType = 80 // RFC2869 5.14. Message-Authenticator
RADIUSAttributeTypeTunnelPrivateGroupID RADIUSAttributeType = 81 // RFC2868 3.6. Tunnel-Private-Group-ID
RADIUSAttributeTypeTunnelAssignmentID RADIUSAttributeType = 82 // RFC2868 3.7. Tunnel-Assignment-ID
RADIUSAttributeTypeTunnelPreference RADIUSAttributeType = 83 // RFC2868 3.8. Tunnel-Preference
RADIUSAttributeTypeARAPChallengeResponse RADIUSAttributeType = 84 // RFC2869 5.15. ARAP-Challenge-Response
RADIUSAttributeTypeAcctInterimInterval RADIUSAttributeType = 85 // RFC2869 5.16. Acct-Interim-Interval
RADIUSAttributeTypeAcctTunnelPacketsLost RADIUSAttributeType = 86 // RFC2867 4.2. Acct-Tunnel-Packets-Lost
RADIUSAttributeTypeNASPortId RADIUSAttributeType = 87 // RFC2869 5.17. NAS-Port-Id
RADIUSAttributeTypeFramedPool RADIUSAttributeType = 88 // RFC2869 5.18. Framed-Pool
RADIUSAttributeTypeTunnelClientAuthID RADIUSAttributeType = 90 // RFC2868 3.9. Tunnel-Client-Auth-ID
RADIUSAttributeTypeTunnelServerAuthID RADIUSAttributeType = 91 // RFC2868 3.10. Tunnel-Server-Auth-ID
)
// RADIUSAttributeType represents attribute length.
type RADIUSAttributeLength uint8
// RADIUSAttributeType represents attribute value.
type RADIUSAttributeValue []byte
// String returns a string version of a RADIUSAttributeType.
func (t RADIUSAttributeType) String() (s string) {
switch t {
case RADIUSAttributeTypeUserName:
s = "User-Name"
case RADIUSAttributeTypeUserPassword:
s = "User-Password"
case RADIUSAttributeTypeCHAPPassword:
s = "CHAP-Password"
case RADIUSAttributeTypeNASIPAddress:
s = "NAS-IP-Address"
case RADIUSAttributeTypeNASPort:
s = "NAS-Port"
case RADIUSAttributeTypeServiceType:
s = "Service-Type"
case RADIUSAttributeTypeFramedProtocol:
s = "Framed-Protocol"
case RADIUSAttributeTypeFramedIPAddress:
s = "Framed-IP-Address"
case RADIUSAttributeTypeFramedIPNetmask:
s = "Framed-IP-Netmask"
case RADIUSAttributeTypeFramedRouting:
s = "Framed-Routing"
case RADIUSAttributeTypeFilterId:
s = "Filter-Id"
case RADIUSAttributeTypeFramedMTU:
s = "Framed-MTU"
case RADIUSAttributeTypeFramedCompression:
s = "Framed-Compression"
case RADIUSAttributeTypeLoginIPHost:
s = "Login-IP-Host"
case RADIUSAttributeTypeLoginService:
s = "Login-Service"
case RADIUSAttributeTypeLoginTCPPort:
s = "Login-TCP-Port"
case RADIUSAttributeTypeReplyMessage:
s = "Reply-Message"
case RADIUSAttributeTypeCallbackNumber:
s = "Callback-Number"
case RADIUSAttributeTypeCallbackId:
s = "Callback-Id"
case RADIUSAttributeTypeFramedRoute:
s = "Framed-Route"
case RADIUSAttributeTypeFramedIPXNetwork:
s = "Framed-IPX-Network"
case RADIUSAttributeTypeState:
s = "State"
case RADIUSAttributeTypeClass:
s = "Class"
case RADIUSAttributeTypeVendorSpecific:
s = "Vendor-Specific"
case RADIUSAttributeTypeSessionTimeout:
s = "Session-Timeout"
case RADIUSAttributeTypeIdleTimeout:
s = "Idle-Timeout"
case RADIUSAttributeTypeTerminationAction:
s = "Termination-Action"
case RADIUSAttributeTypeCalledStationId:
s = "Called-Station-Id"
case RADIUSAttributeTypeCallingStationId:
s = "Calling-Station-Id"
case RADIUSAttributeTypeNASIdentifier:
s = "NAS-Identifier"
case RADIUSAttributeTypeProxyState:
s = "Proxy-State"
case RADIUSAttributeTypeLoginLATService:
s = "Login-LAT-Service"
case RADIUSAttributeTypeLoginLATNode:
s = "Login-LAT-Node"
case RADIUSAttributeTypeLoginLATGroup:
s = "Login-LAT-Group"
case RADIUSAttributeTypeFramedAppleTalkLink:
s = "Framed-AppleTalk-Link"
case RADIUSAttributeTypeFramedAppleTalkNetwork:
s = "Framed-AppleTalk-Network"
case RADIUSAttributeTypeFramedAppleTalkZone:
s = "Framed-AppleTalk-Zone"
case RADIUSAttributeTypeAcctStatusType:
s = "Acct-Status-Type"
case RADIUSAttributeTypeAcctDelayTime:
s = "Acct-Delay-Time"
case RADIUSAttributeTypeAcctInputOctets:
s = "Acct-Input-Octets"
case RADIUSAttributeTypeAcctOutputOctets:
s = "Acct-Output-Octets"
case RADIUSAttributeTypeAcctSessionId:
s = "Acct-Session-Id"
case RADIUSAttributeTypeAcctAuthentic:
s = "Acct-Authentic"
case RADIUSAttributeTypeAcctSessionTime:
s = "Acct-Session-Time"
case RADIUSAttributeTypeAcctInputPackets:
s = "Acct-Input-Packets"
case RADIUSAttributeTypeAcctOutputPackets:
s = "Acct-Output-Packets"
case RADIUSAttributeTypeAcctTerminateCause:
s = "Acct-Terminate-Cause"
case RADIUSAttributeTypeAcctMultiSessionId:
s = "Acct-Multi-Session-Id"
case RADIUSAttributeTypeAcctLinkCount:
s = "Acct-Link-Count"
case RADIUSAttributeTypeAcctInputGigawords:
s = "Acct-Input-Gigawords"
case RADIUSAttributeTypeAcctOutputGigawords:
s = "Acct-Output-Gigawords"
case RADIUSAttributeTypeEventTimestamp:
s = "Event-Timestamp"
case RADIUSAttributeTypeCHAPChallenge:
s = "CHAP-Challenge"
case RADIUSAttributeTypeNASPortType:
s = "NAS-Port-Type"
case RADIUSAttributeTypePortLimit:
s = "Port-Limit"
case RADIUSAttributeTypeLoginLATPort:
s = "Login-LAT-Port"
case RADIUSAttributeTypeTunnelType:
s = "Tunnel-Type"
case RADIUSAttributeTypeTunnelMediumType:
s = "Tunnel-Medium-Type"
case RADIUSAttributeTypeTunnelClientEndpoint:
s = "Tunnel-Client-Endpoint"
case RADIUSAttributeTypeTunnelServerEndpoint:
s = "Tunnel-Server-Endpoint"
case RADIUSAttributeTypeAcctTunnelConnection:
s = "Acct-Tunnel-Connection"
case RADIUSAttributeTypeTunnelPassword:
s = "Tunnel-Password"
case RADIUSAttributeTypeARAPPassword:
s = "ARAP-Password"
case RADIUSAttributeTypeARAPFeatures:
s = "ARAP-Features"
case RADIUSAttributeTypeARAPZoneAccess:
s = "ARAP-Zone-Access"
case RADIUSAttributeTypeARAPSecurity:
s = "ARAP-Security"
case RADIUSAttributeTypeARAPSecurityData:
s = "ARAP-Security-Data"
case RADIUSAttributeTypePasswordRetry:
s = "Password-Retry"
case RADIUSAttributeTypePrompt:
s = "Prompt"
case RADIUSAttributeTypeConnectInfo:
s = "Connect-Info"
case RADIUSAttributeTypeConfigurationToken:
s = "Configuration-Token"
case RADIUSAttributeTypeEAPMessage:
s = "EAP-Message"
case RADIUSAttributeTypeMessageAuthenticator:
s = "Message-Authenticator"
case RADIUSAttributeTypeTunnelPrivateGroupID:
s = "Tunnel-Private-Group-ID"
case RADIUSAttributeTypeTunnelAssignmentID:
s = "Tunnel-Assignment-ID"
case RADIUSAttributeTypeTunnelPreference:
s = "Tunnel-Preference"
case RADIUSAttributeTypeARAPChallengeResponse:
s = "ARAP-Challenge-Response"
case RADIUSAttributeTypeAcctInterimInterval:
s = "Acct-Interim-Interval"
case RADIUSAttributeTypeAcctTunnelPacketsLost:
s = "Acct-Tunnel-Packets-Lost"
case RADIUSAttributeTypeNASPortId:
s = "NAS-Port-Id"
case RADIUSAttributeTypeFramedPool:
s = "Framed-Pool"
case RADIUSAttributeTypeTunnelClientAuthID:
s = "Tunnel-Client-Auth-ID"
case RADIUSAttributeTypeTunnelServerAuthID:
s = "Tunnel-Server-Auth-ID"
default:
s = fmt.Sprintf("Unknown(%d)", t)
}
return
}
// Len returns the length of a RADIUS packet.
func (radius *RADIUS) Len() (int, error) {
n := radiusMinimumRecordSizeInBytes
for _, v := range radius.Attributes {
alen, err := attributeValueLength(v.Value)
if err != nil {
return 0, err
}
n += int(alen) + 2 // Added Type and Length
}
return n, nil
}
// LayerType returns LayerTypeRADIUS.
func (radius *RADIUS) LayerType() gopacket.LayerType {
return LayerTypeRADIUS
}
// DecodeFromBytes decodes the given bytes into this layer.
func (radius *RADIUS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) > radiusMaximumRecordSizeInBytes {
df.SetTruncated()
return fmt.Errorf("RADIUS length %d too big", len(data))
}
if len(data) < radiusMinimumRecordSizeInBytes {
df.SetTruncated()
return fmt.Errorf("RADIUS length %d too short", len(data))
}
radius.BaseLayer = BaseLayer{Contents: data}
radius.Code = RADIUSCode(data[0])
radius.Identifier = RADIUSIdentifier(data[1])
radius.Length = RADIUSLength(binary.BigEndian.Uint16(data[2:4]))
if int(radius.Length) > radiusMaximumRecordSizeInBytes {
df.SetTruncated()
return fmt.Errorf("RADIUS length %d too big", radius.Length)
}
if int(radius.Length) < radiusMinimumRecordSizeInBytes {
df.SetTruncated()
return fmt.Errorf("RADIUS length %d too short", radius.Length)
}
// RFC 2865 3. Packet Format
// `If the packet is shorter than the Length field indicates, it MUST be silently discarded.`
if int(radius.Length) > len(data) {
df.SetTruncated()
return fmt.Errorf("RADIUS length %d too big", radius.Length)
}
// RFC 2865 3. Packet Format
// `Octets outside the range of the Length field MUST be treated as padding and ignored on reception.`
if int(radius.Length) < len(data) {
df.SetTruncated()
data = data[:radius.Length]
}
copy(radius.Authenticator[:], data[4:20])
if len(data) == radiusMinimumRecordSizeInBytes {
return nil
}
pos := radiusMinimumRecordSizeInBytes
for {
if len(data) == pos {
break
}
if len(data[pos:]) < radiusAttributesMinimumRecordSizeInBytes {
df.SetTruncated()
return fmt.Errorf("RADIUS attributes length %d too short", len(data[pos:]))
}
attr := RADIUSAttribute{}
attr.Type = RADIUSAttributeType(data[pos])
attr.Length = RADIUSAttributeLength(data[pos+1])
if int(attr.Length) > len(data[pos:]) {
df.SetTruncated()
return fmt.Errorf("RADIUS attributes length %d too big", attr.Length)
}
if int(attr.Length) < radiusAttributesMinimumRecordSizeInBytes {
df.SetTruncated()
return fmt.Errorf("RADIUS attributes length %d too short", attr.Length)
}
if int(attr.Length) > radiusAttributesMinimumRecordSizeInBytes {
attr.Value = make([]byte, attr.Length-2)
copy(attr.Value[:], data[pos+2:pos+int(attr.Length)])
radius.Attributes = append(radius.Attributes, attr)
}
pos += int(attr.Length)
}
for _, v := range radius.Attributes {
if v.Type == RADIUSAttributeTypeEAPMessage {
radius.BaseLayer.Payload = append(radius.BaseLayer.Payload, v.Value...)
}
}
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (radius *RADIUS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
plen, err := radius.Len()
if err != nil {
return err
}
if opts.FixLengths {
radius.Length = RADIUSLength(plen)
}
data, err := b.PrependBytes(plen)
if err != nil {
return err
}
data[0] = byte(radius.Code)
data[1] = byte(radius.Identifier)
binary.BigEndian.PutUint16(data[2:], uint16(radius.Length))
copy(data[4:20], radius.Authenticator[:])
pos := radiusMinimumRecordSizeInBytes
for _, v := range radius.Attributes {
if opts.FixLengths {
v.Length, err = attributeValueLength(v.Value)
if err != nil {
return err
}
}
data[pos] = byte(v.Type)
data[pos+1] = byte(v.Length)
copy(data[pos+2:], v.Value[:])
pos += len(v.Value) + 2 // Added Type and Length
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (radius *RADIUS) CanDecode() gopacket.LayerClass {
return LayerTypeRADIUS
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (radius *RADIUS) NextLayerType() gopacket.LayerType {
if len(radius.BaseLayer.Payload) > 0 {
return LayerTypeEAP
} else {
return gopacket.LayerTypeZero
}
}
// Payload returns the EAP Type-Data for EAP-Message attributes.
func (radius *RADIUS) Payload() []byte {
return radius.BaseLayer.Payload
}
func decodeRADIUS(data []byte, p gopacket.PacketBuilder) error {
radius := &RADIUS{}
err := radius.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(radius)
p.SetApplicationLayer(radius)
next := radius.NextLayerType()
if next == gopacket.LayerTypeZero {
return nil
}
return p.NextDecoder(next)
}
func attributeValueLength(v []byte) (RADIUSAttributeLength, error) {
n := len(v)
if n > 255 {
return 0, fmt.Errorf("RADIUS attribute value length %d too long", n)
} else {
return RADIUSAttributeLength(n), nil
}
}
+170
View File
@@ -0,0 +1,170 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
// This file implements the ASF-RMCP header specified in section 3.2.2.2 of
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
import (
"fmt"
"github.com/google/gopacket"
)
// RMCPClass is the class of a RMCP layer's payload, e.g. ASF or IPMI. This is a
// 4-bit unsigned int on the wire; all but 6 (ASF), 7 (IPMI) and 8 (OEM-defined)
// are currently reserved.
type RMCPClass uint8
// LayerType returns the payload layer type corresponding to a RMCP class.
func (c RMCPClass) LayerType() gopacket.LayerType {
if lt := rmcpClassLayerTypes[uint8(c)]; lt != 0 {
return lt
}
return gopacket.LayerTypePayload
}
func (c RMCPClass) String() string {
return fmt.Sprintf("%v(%v)", uint8(c), c.LayerType())
}
const (
// RMCPVersion1 identifies RMCP v1.0 in the Version header field. Lower
// values are considered legacy, while higher values are reserved by the
// specification.
RMCPVersion1 uint8 = 0x06
// RMCPNormal indicates a "normal" message, i.e. not an acknowledgement.
RMCPNormal uint8 = 0
// RMCPAck indicates a message is acknowledging a received normal message.
RMCPAck uint8 = 1 << 7
// RMCPClassASF identifies an RMCP message as containing an ASF-RMCP
// payload.
RMCPClassASF RMCPClass = 0x06
// RMCPClassIPMI identifies an RMCP message as containing an IPMI payload.
RMCPClassIPMI RMCPClass = 0x07
// RMCPClassOEM identifies an RMCP message as containing an OEM-defined
// payload.
RMCPClassOEM RMCPClass = 0x08
)
var (
rmcpClassLayerTypes = [16]gopacket.LayerType{
RMCPClassASF: LayerTypeASF,
// RMCPClassIPMI is to implement; RMCPClassOEM is deliberately not
// implemented, so we return LayerTypePayload
}
)
// RegisterRMCPLayerType allows specifying that the payload of a RMCP packet of
// a certain class should processed by the provided layer type. This overrides
// any existing registrations, including defaults.
func RegisterRMCPLayerType(c RMCPClass, l gopacket.LayerType) {
rmcpClassLayerTypes[c] = l
}
// RMCP describes the format of an RMCP header, which forms a UDP payload. See
// section 3.2.2.2.
type RMCP struct {
BaseLayer
// Version identifies the version of the RMCP header. 0x06 indicates RMCP
// v1.0; lower values are legacy, higher values are reserved.
Version uint8
// Sequence is the sequence number assicated with the message. Note that
// this rolls over to 0 after 254, not 255. Seq num 255 indicates the
// receiver must not send an ACK.
Sequence uint8
// Ack indicates whether this packet is an acknowledgement. If it is, the
// payload will be empty.
Ack bool
// Class idicates the structure of the payload. There are only 2^4 valid
// values, however there is no uint4 data type. N.B. the Ack bit has been
// split off into another field. The most significant 4 bits of this field
// will always be 0.
Class RMCPClass
}
// LayerType returns LayerTypeRMCP. It partially satisfies Layer and
// SerializableLayer.
func (*RMCP) LayerType() gopacket.LayerType {
return LayerTypeRMCP
}
// CanDecode returns LayerTypeRMCP. It partially satisfies DecodingLayer.
func (r *RMCP) CanDecode() gopacket.LayerClass {
return r.LayerType()
}
// DecodeFromBytes makes the layer represent the provided bytes. It partially
// satisfies DecodingLayer.
func (r *RMCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("invalid RMCP header, length %v less than 4",
len(data))
}
r.BaseLayer.Contents = data[:4]
r.BaseLayer.Payload = data[4:]
r.Version = uint8(data[0])
// 1 byte reserved
r.Sequence = uint8(data[2])
r.Ack = data[3]&RMCPAck != 0
r.Class = RMCPClass(data[3] & 0xF)
return nil
}
// NextLayerType returns the data layer of this RMCP layer. This partially
// satisfies DecodingLayer.
func (r *RMCP) NextLayerType() gopacket.LayerType {
return r.Class.LayerType()
}
// Payload returns the data layer. It partially satisfies ApplicationLayer.
func (r *RMCP) Payload() []byte {
return r.BaseLayer.Payload
}
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
// partially satisfying SerializableLayer.
func (r *RMCP) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
// The IPMI v1.5 spec contains a pad byte for frame sizes of certain lengths
// to work around issues in LAN chips. This is no longer necessary as of
// IPMI v2.0 (renamed to "legacy pad") so we do not attempt to add it. The
// same approach is taken by FreeIPMI:
// http://git.savannah.gnu.org/cgit/freeipmi.git/tree/libfreeipmi/interface/ipmi-lan-interface.c?id=b5ffcd38317daf42074458879f4c55ba6804a595#n836
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
bytes[0] = r.Version
bytes[1] = 0x00
bytes[2] = r.Sequence
bytes[3] = bool2uint8(r.Ack)<<7 | uint8(r.Class) // thanks, BFD layer
return nil
}
// decodeRMCP decodes the byte slice into an RMCP type, and sets the application
// layer to it.
func decodeRMCP(data []byte, p gopacket.PacketBuilder) error {
rmcp := &RMCP{}
err := rmcp.DecodeFromBytes(data, p)
p.AddLayer(rmcp)
p.SetApplicationLayer(rmcp)
if err != nil {
return err
}
return p.NextDecoder(rmcp.NextLayerType())
}
+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)
}
+101
View File
@@ -0,0 +1,101 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
package gopacket
// Created by gen.go, don't edit manually
// Generated at 2019-06-18 11:37:31.308731293 +0600 +06 m=+0.000842599
// LayersDecoder returns DecodingLayerFunc for specified
// DecodingLayerContainer, LayerType value to start decoding with and
// some DecodeFeedback.
func LayersDecoder(dl DecodingLayerContainer, first LayerType, df DecodeFeedback) DecodingLayerFunc {
firstDec, ok := dl.Decoder(first)
if !ok {
return func([]byte, *[]LayerType) (LayerType, error) {
return first, nil
}
}
if dlc, ok := dl.(DecodingLayerSparse); ok {
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
*decoded = (*decoded)[:0] // Truncated decoded layers.
typ := first
decoder := firstDec
for {
if err := decoder.DecodeFromBytes(data, df); err != nil {
return LayerTypeZero, err
}
*decoded = append(*decoded, typ)
typ = decoder.NextLayerType()
if data = decoder.LayerPayload(); len(data) == 0 {
break
}
if decoder, ok = dlc.Decoder(typ); !ok {
return typ, nil
}
}
return LayerTypeZero, nil
}
}
if dlc, ok := dl.(DecodingLayerArray); ok {
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
*decoded = (*decoded)[:0] // Truncated decoded layers.
typ := first
decoder := firstDec
for {
if err := decoder.DecodeFromBytes(data, df); err != nil {
return LayerTypeZero, err
}
*decoded = append(*decoded, typ)
typ = decoder.NextLayerType()
if data = decoder.LayerPayload(); len(data) == 0 {
break
}
if decoder, ok = dlc.Decoder(typ); !ok {
return typ, nil
}
}
return LayerTypeZero, nil
}
}
if dlc, ok := dl.(DecodingLayerMap); ok {
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
*decoded = (*decoded)[:0] // Truncated decoded layers.
typ := first
decoder := firstDec
for {
if err := decoder.DecodeFromBytes(data, df); err != nil {
return LayerTypeZero, err
}
*decoded = append(*decoded, typ)
typ = decoder.NextLayerType()
if data = decoder.LayerPayload(); len(data) == 0 {
break
}
if decoder, ok = dlc.Decoder(typ); !ok {
return typ, nil
}
}
return LayerTypeZero, nil
}
}
dlc := dl
return func(data []byte, decoded *[]LayerType) (LayerType, error) {
*decoded = (*decoded)[:0] // Truncated decoded layers.
typ := first
decoder := firstDec
for {
if err := decoder.DecodeFromBytes(data, df); err != nil {
return LayerTypeZero, err
}
*decoded = append(*decoded, typ)
typ = decoder.NextLayerType()
if data = decoder.LayerPayload(); len(data) == 0 {
break
}
if decoder, ok = dlc.Decoder(typ); !ok {
return typ, nil
}
}
return LayerTypeZero, nil
}
}
+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=
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+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
}
+41
View File
@@ -0,0 +1,41 @@
//go:build !noasm && !nounsafe && !gccgo && !appengine
/**
* Reed-Solomon Coding over 8-bit values.
*
* Copyright 2023, Klaus Post
*/
package reedsolomon
import (
"unsafe"
)
// AllocAligned allocates 'shards' slices, with 'each' bytes.
// Each slice will start on a 64 byte aligned boundary.
func AllocAligned(shards, each int) [][]byte {
if false {
res := make([][]byte, shards)
for i := range res {
res[i] = make([]byte, each)
}
return res
}
const (
alignEach = 64
alignStart = 64
)
eachAligned := ((each + alignEach - 1) / alignEach) * alignEach
total := make([]byte, eachAligned*shards+63)
align := uint(uintptr(unsafe.Pointer(&total[0]))) & (alignStart - 1)
if align > 0 {
total = total[alignStart-align:]
}
res := make([][]byte, shards)
for i := range res {
res[i] = total[:each:eachAligned]
total = total[eachAligned:]
}
return res
}
+23
View File
@@ -0,0 +1,23 @@
//go:build noasm || nounsafe || gccgo || appengine
/**
* Reed-Solomon Coding over 8-bit values.
*
* Copyright 2023, Klaus Post
*/
package reedsolomon
// AllocAligned allocates 'shards' slices, with 'each' bytes.
// Each slice will start on a 64 byte aligned boundary.
func AllocAligned(shards, each int) [][]byte {
eachAligned := ((each + 63) / 64) * 64
total := make([]byte, eachAligned*shards+63)
// We cannot do initial align without "unsafe", just use native alignment.
res := make([][]byte, shards)
for i := range res {
res[i] = total[:each:eachAligned]
total = total[eachAligned:]
}
return res
}
+8
View File
@@ -0,0 +1,8 @@
*.out
*.swp
*.8
*.6
_obj
_test*
markdown
tags
+17
View File
@@ -0,0 +1,17 @@
sudo: false
language: go
go:
- "1.10.x"
- "1.11.x"
- tip
matrix:
fast_finish: true
allow_failures:
- go: tip
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d -s .)
- go tool vet .
- go test -v ./...
+29
View File
@@ -0,0 +1,29 @@
Blackfriday is distributed under the Simplified BSD License:
> Copyright © 2011 Russ Ross
> All rights reserved.
>
> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions
> are met:
>
> 1. Redistributions of source code must retain the above copyright
> notice, this list of conditions and the following disclaimer.
>
> 2. Redistributions in binary form must reproduce the above
> copyright notice, this list of conditions and the following
> disclaimer in the documentation and/or other materials provided with
> the distribution.
>
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
> FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
> COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
> INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
> BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
> ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> POSSIBILITY OF SUCH DAMAGE.
+335
View File
@@ -0,0 +1,335 @@
Blackfriday
[![Build Status][BuildV2SVG]][BuildV2URL]
[![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL]
===========
Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It
is paranoid about its input (so you can safely feed it user-supplied
data), it is fast, it supports common extensions (tables, smart
punctuation substitutions, etc.), and it is safe for all utf-8
(unicode) input.
HTML output is currently supported, along with Smartypants
extensions.
It started as a translation from C of [Sundown][3].
Installation
------------
Blackfriday is compatible with modern Go releases in module mode.
With Go installed:
go get github.com/russross/blackfriday/v2
will resolve and add the package to the current development module,
then build and install it. Alternatively, you can achieve the same
if you import it in a package:
import "github.com/russross/blackfriday/v2"
and `go get` without parameters.
Legacy GOPATH mode is unsupported.
Versions
--------
Currently maintained and recommended version of Blackfriday is `v2`. It's being
developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the
documentation is available at
https://pkg.go.dev/github.com/russross/blackfriday/v2.
It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`.
Version 2 offers a number of improvements over v1:
* Cleaned up API
* A separate call to [`Parse`][4], which produces an abstract syntax tree for
the document
* Latest bug fixes
* Flexibility to easily add your own rendering extensions
Potential drawbacks:
* Our benchmarks show v2 to be slightly slower than v1. Currently in the
ballpark of around 15%.
* API breakage. If you can't afford modifying your code to adhere to the new API
and don't care too much about the new features, v2 is probably not for you.
* Several bug fixes are trailing behind and still need to be forward-ported to
v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for
tracking.
If you are still interested in the legacy `v1`, you can import it from
`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found
here: https://pkg.go.dev/github.com/russross/blackfriday.
Usage
-----
For the most sensible markdown processing, it is as simple as getting your input
into a byte slice and calling:
```go
output := blackfriday.Run(input)
```
Your input will be parsed and the output rendered with a set of most popular
extensions enabled. If you want the most basic feature set, corresponding with
the bare Markdown specification, use:
```go
output := blackfriday.Run(input, blackfriday.WithNoExtensions())
```
### Sanitize untrusted content
Blackfriday itself does nothing to protect against malicious content. If you are
dealing with user-supplied markdown, we recommend running Blackfriday's output
through HTML sanitizer such as [Bluemonday][5].
Here's an example of simple usage of Blackfriday together with Bluemonday:
```go
import (
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday/v2"
)
// ...
unsafe := blackfriday.Run(input)
html := bluemonday.UGCPolicy().SanitizeBytes(unsafe)
```
### Custom options
If you want to customize the set of options, use `blackfriday.WithExtensions`,
`blackfriday.WithRenderer` and `blackfriday.WithRefOverride`.
### `blackfriday-tool`
You can also check out `blackfriday-tool` for a more complete example
of how to use it. Download and install it using:
go get github.com/russross/blackfriday-tool
This is a simple command-line tool that allows you to process a
markdown file using a standalone program. You can also browse the
source directly on github if you are just looking for some example
code:
* <https://github.com/russross/blackfriday-tool>
Note that if you have not already done so, installing
`blackfriday-tool` will be sufficient to download and install
blackfriday in addition to the tool itself. The tool binary will be
installed in `$GOPATH/bin`. This is a statically-linked binary that
can be copied to wherever you need it without worrying about
dependencies and library versions.
### Sanitized anchor names
Blackfriday includes an algorithm for creating sanitized anchor names
corresponding to a given input text. This algorithm is used to create
anchors for headings when `AutoHeadingIDs` extension is enabled. The
algorithm has a specification, so that other packages can create
compatible anchor names and links to those anchors.
The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names.
[`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to
create compatible links to the anchor names generated by blackfriday.
This algorithm is also implemented in a small standalone package at
[`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients
that want a small package and don't need full functionality of blackfriday.
Features
--------
All features of Sundown are supported, including:
* **Compatibility**. The Markdown v1.0.3 test suite passes with
the `--tidy` option. Without `--tidy`, the differences are
mostly in whitespace and entity escaping, where blackfriday is
more consistent and cleaner.
* **Common extensions**, including table support, fenced code
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
* **Safety**. Blackfriday is paranoid when parsing, making it safe
to feed untrusted user input without fear of bad things
happening. The test suite stress tests this and there are no
known inputs that make it crash. If you find one, please let me
know and send me the input that does it.
NOTE: "safety" in this context means *runtime safety only*. In order to
protect yourself against JavaScript injection in untrusted content, see
[this example](https://github.com/russross/blackfriday#sanitize-untrusted-content).
* **Fast processing**. It is fast enough to render on-demand in
most web applications without having to cache the output.
* **Thread safety**. You can run multiple parsers in different
goroutines without ill effect. There is no dependence on global
shared state.
* **Minimal dependencies**. Blackfriday only depends on standard
library packages in Go. The source code is pretty
self-contained, so it is easy to add to any project, including
Google App Engine projects.
* **Standards compliant**. Output successfully validates using the
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
Extensions
----------
In addition to the standard markdown syntax, this package
implements the following extensions:
* **Intra-word emphasis supression**. The `_` character is
commonly used inside words when discussing code, so having
markdown interpret it as an emphasis command is usually the
wrong thing. Blackfriday lets you treat all emphasis markers as
normal characters when they occur inside a word.
* **Tables**. Tables can be created by drawing them in the input
using a simple syntax:
```
Name | Age
--------|------
Bob | 27
Alice | 23
```
* **Fenced code blocks**. In addition to the normal 4-space
indentation to mark code blocks, you can explicitly mark them
and supply a language (to make syntax highlighting simple). Just
mark it like this:
```go
func getTrue() bool {
return true
}
```
You can use 3 or more backticks to mark the beginning of the
block, and the same number to mark the end of the block.
To preserve classes of fenced code blocks while using the bluemonday
HTML sanitizer, use the following policy:
```go
p := bluemonday.UGCPolicy()
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
html := p.SanitizeBytes(unsafe)
```
* **Definition lists**. A simple definition list is made of a single-line
term followed by a colon and the definition for that term.
Cat
: Fluffy animal everyone likes
Internet
: Vector of transmission for pictures of cats
Terms must be separated from the previous definition by a blank line.
* **Footnotes**. A marker in the text that will become a superscript number;
a footnote definition that will be placed in a list of footnotes at the
end of the document. A footnote looks like this:
This is a footnote.[^1]
[^1]: the footnote text.
* **Autolinking**. Blackfriday can find URLs that have not been
explicitly marked as links and turn them into links.
* **Strikethrough**. Use two tildes (`~~`) to mark text that
should be crossed out.
* **Hard line breaks**. With this extension enabled newlines in the input
translate into line breaks in the output. This extension is off by default.
* **Smart quotes**. Smartypants-style punctuation substitution is
supported, turning normal double- and single-quote marks into
curly quotes, etc.
* **LaTeX-style dash parsing** is an additional option, where `--`
is translated into `&ndash;`, and `---` is translated into
`&mdash;`. This differs from most smartypants processors, which
turn a single hyphen into an ndash and a double hyphen into an
mdash.
* **Smart fractions**, where anything that looks like a fraction
is translated into suitable HTML (instead of just a few special
cases like most smartypant processors). For example, `4/5`
becomes `<sup>4</sup>&frasl;<sub>5</sub>`, which renders as
<sup>4</sup>&frasl;<sub>5</sub>.
Other renderers
---------------
Blackfriday is structured to allow alternative rendering engines. Here
are a few of note:
* [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown):
provides a GitHub Flavored Markdown renderer with fenced code block
highlighting, clickable heading anchor links.
It's not customizable, and its goal is to produce HTML output
equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode),
except the rendering is performed locally.
* [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
but for markdown.
* [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex):
renders output as LaTeX.
* [bfchroma](https://github.com/Depado/bfchroma/): provides convenience
integration with the [Chroma](https://github.com/alecthomas/chroma) code
highlighting library. bfchroma is only compatible with v2 of Blackfriday and
provides a drop-in renderer ready to use with Blackfriday, as well as
options and means for further customization.
* [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer.
* [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style
TODO
----
* More unit testing
* Improve Unicode support. It does not understand all Unicode
rules (about what constitutes a letter, a punctuation symbol,
etc.), so it may fail to detect word boundaries correctly in
some instances. It is safe on all UTF-8 input.
License
-------
[Blackfriday is distributed under the Simplified BSD License](LICENSE.txt)
[1]: https://daringfireball.net/projects/markdown/ "Markdown"
[2]: https://golang.org/ "Go Language"
[3]: https://github.com/vmg/sundown "Sundown"
[4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func"
[5]: https://github.com/microcosm-cc/bluemonday "Bluemonday"
[BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2
[BuildV2URL]: https://travis-ci.org/russross/blackfriday
[PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2
[PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2
File diff suppressed because it is too large Load Diff

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