mirror of
https://github.com/xtaci/kcptun.git
synced 2024-04-21 12:32:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86f2e5b066 | ||
|
|
386bd58956 | ||
|
|
4789212001 | ||
|
|
997b217d4b | ||
|
|
00ce2f86af | ||
|
|
968e76e5ae | ||
|
|
a3cc866b28 | ||
|
|
b4684d7d90 | ||
|
|
baca7d7e6c | ||
|
|
208deccd09 | ||
|
|
2e5215a35d | ||
|
|
b9cb84efdf | ||
|
|
f7a642525c | ||
|
|
51e6fed5fc |
@@ -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 [Reed–Solomon code](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) belongs to the class of non-binary cyclic error-correcting codes. The Reed–Solomon code is based on univariate polynomials over finite fields.
|
||||
|
||||
+13
-11
@@ -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
|
||||
|
||||
+22
-3
@@ -1,18 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
kcp "github.com/xtaci/kcp-go/v5"
|
||||
"github.com/xtaci/kcptun/generic"
|
||||
"github.com/xtaci/tcpraw"
|
||||
)
|
||||
|
||||
func dial(config *Config, block kcp.BlockCrypt) (*kcp.UDPSession, error) {
|
||||
mp, err := generic.ParseMultiPort(config.RemoteAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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", config.RemoteAddr)
|
||||
conn, err := tcpraw.Dial("tcp", remoteAddr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tcpraw.Dial()")
|
||||
}
|
||||
return kcp.NewConn(config.RemoteAddr, block, config.DataShard, config.ParityShard, conn)
|
||||
return kcp.NewConn(remoteAddr, block, config.DataShard, config.ParityShard, conn)
|
||||
}
|
||||
return kcp.DialWithOptions(config.RemoteAddr, block, config.DataShard, config.ParityShard)
|
||||
return kcp.DialWithOptions(remoteAddr, block, config.DataShard, config.ParityShard)
|
||||
|
||||
}
|
||||
|
||||
+18
-6
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type MultiPort struct {
|
||||
Host string
|
||||
MinPort uint64
|
||||
MaxPort uint64
|
||||
}
|
||||
|
||||
// Parse mulitport listener or dialer
|
||||
func ParseMultiPort(addr string) (*MultiPort, error) {
|
||||
remoteAddrMatcher := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
|
||||
matches := remoteAddrMatcher.FindStringSubmatch(addr)
|
||||
|
||||
if len(matches) >= 4 {
|
||||
var minPort, maxPort int
|
||||
minPort, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maxPort = minPort
|
||||
|
||||
// multiport assignment
|
||||
if matches[3] != "" {
|
||||
maxPort, err = strconv.Atoi(matches[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if (minPort > maxPort) || minPort > 65535 || maxPort > 65535 || minPort == 0 || maxPort == 0 {
|
||||
return nil, errors.Errorf("invalid port range specified: minport:%v -> maxport %v", minPort, maxPort)
|
||||
}
|
||||
|
||||
mp := new(MultiPort)
|
||||
mp.Host = matches[1]
|
||||
mp.MinPort = uint64(minPort)
|
||||
mp.MaxPort = uint64(maxPort)
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("malformed address:%v", addr)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package generic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDial(t *testing.T) {
|
||||
reg := regexp.MustCompile(`(.*)\:([0-9]{1,5})-?([0-9]{1,5})?`)
|
||||
matches := reg.FindStringSubmatch("www.unknown.unknown:20000-21000")
|
||||
for i := 0; i < len(matches); i++ {
|
||||
fmt.Println(matches[i])
|
||||
}
|
||||
|
||||
minPort, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
maxPort, err := strconv.Atoi(matches[3])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log("minport:", minPort)
|
||||
t.Log("maxport:", maxPort)
|
||||
|
||||
remoteAddr := fmt.Sprintf("%v:%v", matches[1], uint64(minPort)+1000%uint64(maxPort-minPort+1))
|
||||
|
||||
t.Log("RemoteAddr:", remoteAddr)
|
||||
|
||||
testcase2 := "1.2.3.4:20000"
|
||||
matches = reg.FindStringSubmatch(testcase2)
|
||||
for i := 0; i < len(matches); i++ {
|
||||
t.Log(testcase2, "submatch", i, matches[i])
|
||||
}
|
||||
|
||||
testcase3 := ":20000-20001"
|
||||
matches = reg.FindStringSubmatch(testcase3)
|
||||
for i := 0; i < len(matches); i++ {
|
||||
t.Log(testcase3, "submatch", i, matches[i])
|
||||
}
|
||||
|
||||
testcase4 := ":20000"
|
||||
matches = reg.FindStringSubmatch(testcase4)
|
||||
for i := 0; i < len(matches); i++ {
|
||||
t.Log(testcase4, "submatch", i, matches[i])
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.19
|
||||
github.com/xtaci/tcpraw v1.2.25
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect
|
||||
golang.org/x/crypto v0.5.0
|
||||
)
|
||||
|
||||
go 1.14
|
||||
require (
|
||||
github.com/coreos/go-iptables v0.6.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.6 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/templexxx/cpu v0.1.0 // indirect
|
||||
github.com/templexxx/xorsimd v0.4.2 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
)
|
||||
|
||||
go 1.17
|
||||
|
||||
@@ -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,124 @@ 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.18 h1:NRH2dIxcO1w5dsNpQxSrxfhxjk4YNg6TrS/Nj0FuUPc=
|
||||
github.com/xtaci/smux v1.5.18/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.19 h1:QFoVBZPuJnoTs8QQ52TO2469nz8KxGHDpiEb+s3QzCQ=
|
||||
github.com/xtaci/smux v1.5.19/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
|
||||
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -145,11 +157,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=
|
||||
|
||||
+27
-14
@@ -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",
|
||||
@@ -432,22 +432,35 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
if config.TCP { // tcp dual stack
|
||||
if conn, err := tcpraw.Listen("tcp", config.Listen); err == nil {
|
||||
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
} else {
|
||||
log.Println(err)
|
||||
mp, err := generic.ParseMultiPort(config.Listen)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// create multiple listener
|
||||
for port := mp.MinPort; port <= mp.MaxPort; port++ {
|
||||
listenAddr := fmt.Sprintf("%v:%v", mp.Host, port)
|
||||
if config.TCP { // tcp dual stack
|
||||
if conn, err := tcpraw.Listen("tcp", listenAddr); err == nil {
|
||||
log.Printf("Listening on: %v/tcp", listenAddr)
|
||||
lis, err := kcp.ServeConn(block, config.DataShard, config.ParityShard, conn)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
} else {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// udp stack
|
||||
log.Printf("Listening on: %v/udp", listenAddr)
|
||||
lis, err := kcp.ListenWithOptions(listenAddr, block, config.DataShard, config.ParityShard)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
}
|
||||
|
||||
// udp stack
|
||||
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
|
||||
checkError(err)
|
||||
wg.Add(1)
|
||||
go loop(lis)
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
+135
-53
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
Generated
Vendored
+1
@@ -5,6 +5,7 @@
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
+19
-5
@@ -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
@@ -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
@@ -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
|
||||
Generated
Vendored
+1
@@ -5,6 +5,7 @@
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
// +build amd64 arm64
|
||||
|
||||
package snappy
|
||||
|
||||
+1
-1
@@ -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
@@ -1 +0,0 @@
|
||||
module github.com/golang/snappy
|
||||
+6
-4
@@ -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
@@ -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
@@ -6,7 +6,7 @@ See [godoc](https://godoc.org/github.com/google/gopacket) for more details.
|
||||
[](https://travis-ci.org/google/gopacket)
|
||||
[](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
@@ -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
@@ -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(ð)
|
||||
// ... 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(ð)
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,3 +0,0 @@
|
||||
module github.com/klauspost/cpuid/v2
|
||||
|
||||
go 1.15
|
||||
+107
-5
@@ -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
@@ -1,8 +1,5 @@
|
||||
# Reed-Solomon
|
||||
[](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
|
||||
[](https://pkg.go.dev/github.com/klauspost/reedsolomon) [](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
File diff suppressed because one or more lines are too long
-339
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1576
-1
File diff suppressed because it is too large
Load Diff
+38688
-1
File diff suppressed because it is too large
Load Diff
+10
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,5 +0,0 @@
|
||||
module github.com/klauspost/reedsolomon
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/klauspost/cpuid/v2 v2.0.14
|
||||
-2
@@ -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=
|
||||
+1259
File diff suppressed because it is too large
Load Diff
+1266
File diff suppressed because it is too large
Load Diff
+81
-11
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
*.out
|
||||
*.swp
|
||||
*.8
|
||||
*.6
|
||||
_obj
|
||||
_test*
|
||||
markdown
|
||||
tags
|
||||
+17
@@ -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
@@ -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
@@ -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 `–`, and `---` is translated into
|
||||
`—`. 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>⁄<sub>5</sub>`, which renders as
|
||||
<sup>4</sup>⁄<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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user