summaryrefslogtreecommitdiff
path: root/vendor/github.com
diff options
context:
space:
mode:
authorkali kaneko (leap communications) <kali@leap.se>2021-11-29 01:46:27 +0100
committerkali kaneko (leap communications) <kali@leap.se>2021-11-29 18:14:16 +0100
commit18f52af5be3a9a0c73811706108f790d65ee9c67 (patch)
treee13cbacb47d56919caa9c44a2b45dec1497a7860 /vendor/github.com
parentebcef0d57b6ecb5a40c6579f6be07182dd3033ba (diff)
[pkg] update vendor
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/cretz/bine/LICENSE21
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_authenticate.go108
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_circuit.go38
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_conf.go65
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_event.go1218
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_hiddenservice.go23
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_misc.go92
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_onion.go201
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_protocolinfo.go76
-rw-r--r--vendor/github.com/cretz/bine/control/cmd_stream.go31
-rw-r--r--vendor/github.com/cretz/bine/control/conn.go102
-rw-r--r--vendor/github.com/cretz/bine/control/doc.go10
-rw-r--r--vendor/github.com/cretz/bine/control/keyval.go40
-rw-r--r--vendor/github.com/cretz/bine/control/response.go106
-rw-r--r--vendor/github.com/cretz/bine/control/status.go64
-rw-r--r--vendor/github.com/cretz/bine/process/process.go82
-rw-r--r--vendor/github.com/cretz/bine/tor/dialer.go111
-rw-r--r--vendor/github.com/cretz/bine/tor/doc.go7
-rw-r--r--vendor/github.com/cretz/bine/tor/listen.go343
-rw-r--r--vendor/github.com/cretz/bine/tor/log.go16
-rw-r--r--vendor/github.com/cretz/bine/tor/tor.go453
-rw-r--r--vendor/github.com/cretz/bine/torutil/doc.go2
-rw-r--r--vendor/github.com/cretz/bine/torutil/ed25519/ed25519.go189
-rw-r--r--vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/README.md1
-rw-r--r--vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/const.go1422
-rw-r--r--vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/edwards25519.go1793
-rw-r--r--vendor/github.com/cretz/bine/torutil/key.go85
-rw-r--r--vendor/github.com/cretz/bine/torutil/string.go112
-rw-r--r--vendor/github.com/google/uuid/.travis.yml9
-rw-r--r--vendor/github.com/google/uuid/CONTRIBUTING.md10
-rw-r--r--vendor/github.com/google/uuid/CONTRIBUTORS9
-rw-r--r--vendor/github.com/google/uuid/LICENSE27
-rw-r--r--vendor/github.com/google/uuid/README.md19
-rw-r--r--vendor/github.com/google/uuid/dce.go80
-rw-r--r--vendor/github.com/google/uuid/doc.go12
-rw-r--r--vendor/github.com/google/uuid/go.mod1
-rw-r--r--vendor/github.com/google/uuid/hash.go53
-rw-r--r--vendor/github.com/google/uuid/marshal.go38
-rw-r--r--vendor/github.com/google/uuid/node.go90
-rw-r--r--vendor/github.com/google/uuid/node_js.go12
-rw-r--r--vendor/github.com/google/uuid/node_net.go33
-rw-r--r--vendor/github.com/google/uuid/sql.go59
-rw-r--r--vendor/github.com/google/uuid/time.go123
-rw-r--r--vendor/github.com/google/uuid/util.go43
-rw-r--r--vendor/github.com/google/uuid/uuid.go251
-rw-r--r--vendor/github.com/google/uuid/version1.go44
-rw-r--r--vendor/github.com/google/uuid/version4.go51
-rw-r--r--vendor/github.com/gopherjs/gopherjs/LICENSE24
-rw-r--r--vendor/github.com/gopherjs/gopherjs/js/js.go168
-rw-r--r--vendor/github.com/jtolds/gls/LICENSE18
-rw-r--r--vendor/github.com/jtolds/gls/README.md89
-rw-r--r--vendor/github.com/jtolds/gls/context.go153
-rw-r--r--vendor/github.com/jtolds/gls/gen_sym.go21
-rw-r--r--vendor/github.com/jtolds/gls/gid.go25
-rw-r--r--vendor/github.com/jtolds/gls/id_pool.go34
-rw-r--r--vendor/github.com/jtolds/gls/stack_tags.go147
-rw-r--r--vendor/github.com/jtolds/gls/stack_tags_js.go75
-rw-r--r--vendor/github.com/jtolds/gls/stack_tags_main.go30
-rw-r--r--vendor/github.com/klauspost/cpuid/.gitignore24
-rw-r--r--vendor/github.com/klauspost/cpuid/.travis.yml46
-rw-r--r--vendor/github.com/klauspost/cpuid/CONTRIBUTING.txt35
-rw-r--r--vendor/github.com/klauspost/cpuid/LICENSE22
-rw-r--r--vendor/github.com/klauspost/cpuid/README.md191
-rw-r--r--vendor/github.com/klauspost/cpuid/cpuid.go1504
-rw-r--r--vendor/github.com/klauspost/cpuid/cpuid_386.s42
-rw-r--r--vendor/github.com/klauspost/cpuid/cpuid_amd64.s42
-rw-r--r--vendor/github.com/klauspost/cpuid/cpuid_arm64.s26
-rw-r--r--vendor/github.com/klauspost/cpuid/detect_arm64.go219
-rw-r--r--vendor/github.com/klauspost/cpuid/detect_intel.go33
-rw-r--r--vendor/github.com/klauspost/cpuid/detect_ref.go14
-rw-r--r--vendor/github.com/klauspost/cpuid/go.mod3
-rw-r--r--vendor/github.com/klauspost/reedsolomon/.gitignore26
-rw-r--r--vendor/github.com/klauspost/reedsolomon/.travis.yml77
-rw-r--r--vendor/github.com/klauspost/reedsolomon/LICENSE23
-rw-r--r--vendor/github.com/klauspost/reedsolomon/README.md395
-rw-r--r--vendor/github.com/klauspost/reedsolomon/appveyor.yml20
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois.go929
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.go338
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.s400
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_amd64.go138
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_amd64.s368
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_arm64.go67
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_arm64.s125
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.go408
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.s18526
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_gen_none.go11
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_gen_switch_amd64.go293
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_noasm.go44
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_notamd64.go13
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_ppc64le.go75
-rw-r--r--vendor/github.com/klauspost/reedsolomon/galois_ppc64le.s124
-rw-r--r--vendor/github.com/klauspost/reedsolomon/gen.go249
-rw-r--r--vendor/github.com/klauspost/reedsolomon/go.mod7
-rw-r--r--vendor/github.com/klauspost/reedsolomon/go.sum2
-rw-r--r--vendor/github.com/klauspost/reedsolomon/inversion_tree.go160
-rw-r--r--vendor/github.com/klauspost/reedsolomon/matrix.go279
-rw-r--r--vendor/github.com/klauspost/reedsolomon/options.go175
-rw-r--r--vendor/github.com/klauspost/reedsolomon/reedsolomon.go1011
-rw-r--r--vendor/github.com/klauspost/reedsolomon/streaming.go603
-rw-r--r--vendor/github.com/mmcloughlin/avo/LICENSE29
-rw-r--r--vendor/github.com/mmcloughlin/avo/attr/attr.go102
-rw-r--r--vendor/github.com/mmcloughlin/avo/buildtags/buildtags.go312
-rw-r--r--vendor/github.com/mmcloughlin/avo/gotypes/components.go253
-rw-r--r--vendor/github.com/mmcloughlin/avo/gotypes/doc.go2
-rw-r--r--vendor/github.com/mmcloughlin/avo/gotypes/signature.go177
-rw-r--r--vendor/github.com/mmcloughlin/avo/internal/prnt/printer.go60
-rw-r--r--vendor/github.com/mmcloughlin/avo/internal/stack/stack.go73
-rw-r--r--vendor/github.com/mmcloughlin/avo/ir/doc.go2
-rw-r--r--vendor/github.com/mmcloughlin/avo/ir/ir.go355
-rw-r--r--vendor/github.com/mmcloughlin/avo/operand/checks.go247
-rw-r--r--vendor/github.com/mmcloughlin/avo/operand/const.go36
-rw-r--r--vendor/github.com/mmcloughlin/avo/operand/doc.go2
-rw-r--r--vendor/github.com/mmcloughlin/avo/operand/types.go151
-rw-r--r--vendor/github.com/mmcloughlin/avo/operand/zconst.go75
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/alloc.go190
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/cfg.go81
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/cleanup.go123
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/isa.go31
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/pass.go100
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/reg.go139
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/textflag.go42
-rw-r--r--vendor/github.com/mmcloughlin/avo/pass/verify.go32
-rw-r--r--vendor/github.com/mmcloughlin/avo/printer/goasm.go186
-rw-r--r--vendor/github.com/mmcloughlin/avo/printer/printer.go98
-rw-r--r--vendor/github.com/mmcloughlin/avo/printer/stubs.go45
-rw-r--r--vendor/github.com/mmcloughlin/avo/reg/collection.go54
-rw-r--r--vendor/github.com/mmcloughlin/avo/reg/doc.go2
-rw-r--r--vendor/github.com/mmcloughlin/avo/reg/set.go112
-rw-r--r--vendor/github.com/mmcloughlin/avo/reg/types.go304
-rw-r--r--vendor/github.com/mmcloughlin/avo/reg/x86.go331
-rw-r--r--vendor/github.com/mmcloughlin/avo/src/src.go62
-rw-r--r--vendor/github.com/mmcloughlin/avo/x86/doc.go2
-rw-r--r--vendor/github.com/mmcloughlin/avo/x86/gen.go4
-rw-r--r--vendor/github.com/mmcloughlin/avo/x86/zctors.go34629
-rw-r--r--vendor/github.com/pion/datachannel/.gitignore2
-rw-r--r--vendor/github.com/pion/datachannel/.golangci.yml8
-rw-r--r--vendor/github.com/pion/datachannel/DESIGN.md20
-rw-r--r--vendor/github.com/pion/datachannel/LICENSE21
-rw-r--r--vendor/github.com/pion/datachannel/README.md45
-rw-r--r--vendor/github.com/pion/datachannel/codecov.yml20
-rw-r--r--vendor/github.com/pion/datachannel/datachannel.go378
-rw-r--r--vendor/github.com/pion/datachannel/go.mod11
-rw-r--r--vendor/github.com/pion/datachannel/go.sum38
-rw-r--r--vendor/github.com/pion/datachannel/message.go94
-rw-r--r--vendor/github.com/pion/datachannel/message_channel_ack.go22
-rw-r--r--vendor/github.com/pion/datachannel/message_channel_open.go123
-rw-r--r--vendor/github.com/pion/datachannel/renovate.json15
-rw-r--r--vendor/github.com/pion/dtls/v2/.editorconfig21
-rw-r--r--vendor/github.com/pion/dtls/v2/.gitignore24
-rw-r--r--vendor/github.com/pion/dtls/v2/.golangci.yml89
-rw-r--r--vendor/github.com/pion/dtls/v2/LICENSE21
-rw-r--r--vendor/github.com/pion/dtls/v2/Makefile6
-rw-r--r--vendor/github.com/pion/dtls/v2/README.md155
-rw-r--r--vendor/github.com/pion/dtls/v2/certificate.go67
-rw-r--r--vendor/github.com/pion/dtls/v2/cipher_suite.go213
-rw-r--r--vendor/github.com/pion/dtls/v2/cipher_suite_go114.go40
-rw-r--r--vendor/github.com/pion/dtls/v2/codecov.yml20
-rw-r--r--vendor/github.com/pion/dtls/v2/compression_method.go9
-rw-r--r--vendor/github.com/pion/dtls/v2/config.go193
-rw-r--r--vendor/github.com/pion/dtls/v2/conn.go978
-rw-r--r--vendor/github.com/pion/dtls/v2/crypto.go221
-rw-r--r--vendor/github.com/pion/dtls/v2/dtls.go2
-rw-r--r--vendor/github.com/pion/dtls/v2/errors.go141
-rw-r--r--vendor/github.com/pion/dtls/v2/errors_errno.go25
-rw-r--r--vendor/github.com/pion/dtls/v2/errors_noerrno.go14
-rw-r--r--vendor/github.com/pion/dtls/v2/flight.go75
-rw-r--r--vendor/github.com/pion/dtls/v2/flight0handler.go102
-rw-r--r--vendor/github.com/pion/dtls/v2/flight1handler.go112
-rw-r--r--vendor/github.com/pion/dtls/v2/flight2handler.go61
-rw-r--r--vendor/github.com/pion/dtls/v2/flight3handler.go194
-rw-r--r--vendor/github.com/pion/dtls/v2/flight4handler.go337
-rw-r--r--vendor/github.com/pion/dtls/v2/flight5handler.go323
-rw-r--r--vendor/github.com/pion/dtls/v2/flight6handler.go82
-rw-r--r--vendor/github.com/pion/dtls/v2/flighthandler.go57
-rw-r--r--vendor/github.com/pion/dtls/v2/fragment_buffer.go111
-rw-r--r--vendor/github.com/pion/dtls/v2/fuzz.go38
-rw-r--r--vendor/github.com/pion/dtls/v2/go.mod12
-rw-r--r--vendor/github.com/pion/dtls/v2/go.sum48
-rw-r--r--vendor/github.com/pion/dtls/v2/handshake_cache.go171
-rw-r--r--vendor/github.com/pion/dtls/v2/handshaker.go334
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go108
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go71
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go11
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go11
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go92
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go101
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go22
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go22
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go100
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go11
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go11
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go27
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/closer/closer.go45
-rw-r--r--vendor/github.com/pion/dtls/v2/internal/util/util.go39
-rw-r--r--vendor/github.com/pion/dtls/v2/listener.go80
-rw-r--r--vendor/github.com/pion/dtls/v2/packet.go9
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go251
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go164
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go104
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go72
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go100
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go22
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go99
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go50
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go37
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go126
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go224
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go24
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go9
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go93
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go160
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go26
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go28
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go48
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/content.go21
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go104
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go14
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go96
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go43
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go78
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go21
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go62
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go62
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go70
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go45
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go59
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go29
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go25
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go145
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go50
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go66
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go100
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go61
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go125
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go56
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go27
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go62
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go106
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go22
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go119
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go49
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go16
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go61
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go99
-rw-r--r--vendor/github.com/pion/dtls/v2/pkg/protocol/version.go21
-rw-r--r--vendor/github.com/pion/dtls/v2/renovate.json15
-rw-r--r--vendor/github.com/pion/dtls/v2/resume.go19
-rw-r--r--vendor/github.com/pion/dtls/v2/srtp_protection_profile.go14
-rw-r--r--vendor/github.com/pion/dtls/v2/state.go194
-rw-r--r--vendor/github.com/pion/dtls/v2/util.go38
-rw-r--r--vendor/github.com/pion/ice/v2/.gitignore24
-rw-r--r--vendor/github.com/pion/ice/v2/.golangci.yml89
-rw-r--r--vendor/github.com/pion/ice/v2/LICENSE21
-rw-r--r--vendor/github.com/pion/ice/v2/README.md67
-rw-r--r--vendor/github.com/pion/ice/v2/agent.go1233
-rw-r--r--vendor/github.com/pion/ice/v2/agent_config.go252
-rw-r--r--vendor/github.com/pion/ice/v2/agent_stats.go113
-rw-r--r--vendor/github.com/pion/ice/v2/candidate.go68
-rw-r--r--vendor/github.com/pion/ice/v2/candidate_base.go496
-rw-r--r--vendor/github.com/pion/ice/v2/candidate_host.go76
-rw-r--r--vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go60
-rw-r--r--vendor/github.com/pion/ice/v2/candidate_relay.go73
-rw-r--r--vendor/github.com/pion/ice/v2/candidate_server_reflexive.go59
-rw-r--r--vendor/github.com/pion/ice/v2/candidatepair.go98
-rw-r--r--vendor/github.com/pion/ice/v2/candidatepair_state.go37
-rw-r--r--vendor/github.com/pion/ice/v2/candidaterelatedaddress.go30
-rw-r--r--vendor/github.com/pion/ice/v2/candidatetype.go62
-rw-r--r--vendor/github.com/pion/ice/v2/codecov.yml20
-rw-r--r--vendor/github.com/pion/ice/v2/context.go37
-rw-r--r--vendor/github.com/pion/ice/v2/errors.go132
-rw-r--r--vendor/github.com/pion/ice/v2/external_ip_mapper.go143
-rw-r--r--vendor/github.com/pion/ice/v2/gather.go497
-rw-r--r--vendor/github.com/pion/ice/v2/go.mod16
-rw-r--r--vendor/github.com/pion/ice/v2/go.sum60
-rw-r--r--vendor/github.com/pion/ice/v2/ice.go76
-rw-r--r--vendor/github.com/pion/ice/v2/icecontrol.go87
-rw-r--r--vendor/github.com/pion/ice/v2/mdns.go63
-rw-r--r--vendor/github.com/pion/ice/v2/networktype.go130
-rw-r--r--vendor/github.com/pion/ice/v2/priority.go33
-rw-r--r--vendor/github.com/pion/ice/v2/rand.go53
-rw-r--r--vendor/github.com/pion/ice/v2/renovate.json15
-rw-r--r--vendor/github.com/pion/ice/v2/role.go43
-rw-r--r--vendor/github.com/pion/ice/v2/selection.go287
-rw-r--r--vendor/github.com/pion/ice/v2/stats.go177
-rw-r--r--vendor/github.com/pion/ice/v2/stun.go24
-rw-r--r--vendor/github.com/pion/ice/v2/tcp_mux.go295
-rw-r--r--vendor/github.com/pion/ice/v2/tcp_packet_conn.go240
-rw-r--r--vendor/github.com/pion/ice/v2/tcptype.go48
-rw-r--r--vendor/github.com/pion/ice/v2/transport.go145
-rw-r--r--vendor/github.com/pion/ice/v2/url.go225
-rw-r--r--vendor/github.com/pion/ice/v2/usecandidate.go23
-rw-r--r--vendor/github.com/pion/ice/v2/util.go233
-rw-r--r--vendor/github.com/pion/interceptor/.gitignore24
-rw-r--r--vendor/github.com/pion/interceptor/.golangci.yml89
-rw-r--r--vendor/github.com/pion/interceptor/LICENSE21
-rw-r--r--vendor/github.com/pion/interceptor/README.md81
-rw-r--r--vendor/github.com/pion/interceptor/chain.go75
-rw-r--r--vendor/github.com/pion/interceptor/codecov.yml20
-rw-r--r--vendor/github.com/pion/interceptor/errors.go51
-rw-r--r--vendor/github.com/pion/interceptor/go.mod10
-rw-r--r--vendor/github.com/pion/interceptor/go.sum21
-rw-r--r--vendor/github.com/pion/interceptor/interceptor.go108
-rw-r--r--vendor/github.com/pion/interceptor/noop.go40
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/errors.go6
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go162
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/generator_option.go44
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/nack.go14
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/receive_log.go134
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go119
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/responder_option.go23
-rw-r--r--vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go74
-rw-r--r--vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go166
-rw-r--r--vendor/github.com/pion/interceptor/pkg/report/receiver_option.go34
-rw-r--r--vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go159
-rw-r--r--vendor/github.com/pion/interceptor/pkg/report/report.go2
-rw-r--r--vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go139
-rw-r--r--vendor/github.com/pion/interceptor/pkg/report/sender_option.go34
-rw-r--r--vendor/github.com/pion/interceptor/pkg/report/sender_stream.go37
-rw-r--r--vendor/github.com/pion/interceptor/registry.go20
-rw-r--r--vendor/github.com/pion/interceptor/renovate.json15
-rw-r--r--vendor/github.com/pion/interceptor/streaminfo.go34
-rw-r--r--vendor/github.com/pion/logging/.golangci.yml13
-rw-r--r--vendor/github.com/pion/logging/.travis.yml19
-rw-r--r--vendor/github.com/pion/logging/LICENSE21
-rw-r--r--vendor/github.com/pion/logging/README.md41
-rw-r--r--vendor/github.com/pion/logging/go.mod3
-rw-r--r--vendor/github.com/pion/logging/go.sum0
-rw-r--r--vendor/github.com/pion/logging/logger.go228
-rw-r--r--vendor/github.com/pion/logging/scoped.go72
-rw-r--r--vendor/github.com/pion/mdns/.golangci.yml13
-rw-r--r--vendor/github.com/pion/mdns/.travis.yml19
-rw-r--r--vendor/github.com/pion/mdns/LICENSE21
-rw-r--r--vendor/github.com/pion/mdns/README.md63
-rw-r--r--vendor/github.com/pion/mdns/config.go27
-rw-r--r--vendor/github.com/pion/mdns/conn.go316
-rw-r--r--vendor/github.com/pion/mdns/errors.go10
-rw-r--r--vendor/github.com/pion/mdns/go.mod9
-rw-r--r--vendor/github.com/pion/mdns/go.sum16
-rw-r--r--vendor/github.com/pion/mdns/renovate.json15
-rw-r--r--vendor/github.com/pion/randutil/.travis.yml142
-rw-r--r--vendor/github.com/pion/randutil/LICENSE21
-rw-r--r--vendor/github.com/pion/randutil/README.md14
-rw-r--r--vendor/github.com/pion/randutil/codecov.yml20
-rw-r--r--vendor/github.com/pion/randutil/crypto.go30
-rw-r--r--vendor/github.com/pion/randutil/go.mod3
-rw-r--r--vendor/github.com/pion/randutil/math.go72
-rw-r--r--vendor/github.com/pion/randutil/renovate.json15
-rw-r--r--vendor/github.com/pion/rtcp/.gitignore24
-rw-r--r--vendor/github.com/pion/rtcp/.golangci.yml89
-rw-r--r--vendor/github.com/pion/rtcp/LICENSE21
-rw-r--r--vendor/github.com/pion/rtcp/README.md49
-rw-r--r--vendor/github.com/pion/rtcp/codecov.yml20
-rw-r--r--vendor/github.com/pion/rtcp/compound_packet.go136
-rw-r--r--vendor/github.com/pion/rtcp/doc.go39
-rw-r--r--vendor/github.com/pion/rtcp/errors.go30
-rw-r--r--vendor/github.com/pion/rtcp/full_intra_request.go107
-rw-r--r--vendor/github.com/pion/rtcp/fuzz.go51
-rw-r--r--vendor/github.com/pion/rtcp/go.mod5
-rw-r--r--vendor/github.com/pion/rtcp/go.sum11
-rw-r--r--vendor/github.com/pion/rtcp/goodbye.go146
-rw-r--r--vendor/github.com/pion/rtcp/header.go140
-rw-r--r--vendor/github.com/pion/rtcp/packet.go115
-rw-r--r--vendor/github.com/pion/rtcp/picture_loss_indication.go91
-rw-r--r--vendor/github.com/pion/rtcp/rapid_resynchronization_request.go88
-rw-r--r--vendor/github.com/pion/rtcp/raw_packet.go44
-rw-r--r--vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go284
-rw-r--r--vendor/github.com/pion/rtcp/receiver_report.go193
-rw-r--r--vendor/github.com/pion/rtcp/reception_report.go130
-rw-r--r--vendor/github.com/pion/rtcp/renovate.json15
-rw-r--r--vendor/github.com/pion/rtcp/sender_report.go260
-rw-r--r--vendor/github.com/pion/rtcp/slice_loss_indication.go115
-rw-r--r--vendor/github.com/pion/rtcp/source_description.go352
-rw-r--r--vendor/github.com/pion/rtcp/transport_layer_cc.go560
-rw-r--r--vendor/github.com/pion/rtcp/transport_layer_nack.go174
-rw-r--r--vendor/github.com/pion/rtcp/util.go38
-rw-r--r--vendor/github.com/pion/rtp/.gitignore24
-rw-r--r--vendor/github.com/pion/rtp/.golangci.yml89
-rw-r--r--vendor/github.com/pion/rtp/LICENSE21
-rw-r--r--vendor/github.com/pion/rtp/README.md53
-rw-r--r--vendor/github.com/pion/rtp/abssendtimeextension.go78
-rw-r--r--vendor/github.com/pion/rtp/audiolevelextension.go60
-rw-r--r--vendor/github.com/pion/rtp/codecov.yml20
-rw-r--r--vendor/github.com/pion/rtp/codecs/codecs.go2
-rw-r--r--vendor/github.com/pion/rtp/codecs/common.go8
-rw-r--r--vendor/github.com/pion/rtp/codecs/error.go11
-rw-r--r--vendor/github.com/pion/rtp/codecs/g711_packet.go22
-rw-r--r--vendor/github.com/pion/rtp/codecs/g722_packet.go22
-rw-r--r--vendor/github.com/pion/rtp/codecs/h264_packet.go205
-rw-r--r--vendor/github.com/pion/rtp/codecs/opus_packet.go44
-rw-r--r--vendor/github.com/pion/rtp/codecs/vp8_packet.go143
-rw-r--r--vendor/github.com/pion/rtp/codecs/vp9_packet.go385
-rw-r--r--vendor/github.com/pion/rtp/depacketizer.go6
-rw-r--r--vendor/github.com/pion/rtp/error.go21
-rw-r--r--vendor/github.com/pion/rtp/go.mod5
-rw-r--r--vendor/github.com/pion/rtp/go.sum2
-rw-r--r--vendor/github.com/pion/rtp/packet.go490
-rw-r--r--vendor/github.com/pion/rtp/packetizer.go91
-rw-r--r--vendor/github.com/pion/rtp/partitionheadchecker.go6
-rw-r--r--vendor/github.com/pion/rtp/rand.go8
-rw-r--r--vendor/github.com/pion/rtp/renovate.json15
-rw-r--r--vendor/github.com/pion/rtp/rtp.go2
-rw-r--r--vendor/github.com/pion/rtp/sequencer.go57
-rw-r--r--vendor/github.com/pion/rtp/transportccextension.go39
-rw-r--r--vendor/github.com/pion/sctp/.gitignore1
-rw-r--r--vendor/github.com/pion/sctp/.golangci.yml82
-rw-r--r--vendor/github.com/pion/sctp/DESIGN.md20
-rw-r--r--vendor/github.com/pion/sctp/LICENSE21
-rw-r--r--vendor/github.com/pion/sctp/README.md54
-rw-r--r--vendor/github.com/pion/sctp/ack_timer.go105
-rw-r--r--vendor/github.com/pion/sctp/association.go2241
-rw-r--r--vendor/github.com/pion/sctp/association_stats.go61
-rw-r--r--vendor/github.com/pion/sctp/chunk.go9
-rw-r--r--vendor/github.com/pion/sctp/chunk_abort.go88
-rw-r--r--vendor/github.com/pion/sctp/chunk_cookie_ack.go44
-rw-r--r--vendor/github.com/pion/sctp/chunk_cookie_echo.go46
-rw-r--r--vendor/github.com/pion/sctp/chunk_error.go95
-rw-r--r--vendor/github.com/pion/sctp/chunk_forward_tsn.go145
-rw-r--r--vendor/github.com/pion/sctp/chunk_heartbeat.go75
-rw-r--r--vendor/github.com/pion/sctp/chunk_heartbeat_ack.go86
-rw-r--r--vendor/github.com/pion/sctp/chunk_init.go123
-rw-r--r--vendor/github.com/pion/sctp/chunk_init_ack.go126
-rw-r--r--vendor/github.com/pion/sctp/chunk_init_common.go155
-rw-r--r--vendor/github.com/pion/sctp/chunk_payload_data.go195
-rw-r--r--vendor/github.com/pion/sctp/chunk_reconfig.go99
-rw-r--r--vendor/github.com/pion/sctp/chunk_selective_ack.go142
-rw-r--r--vendor/github.com/pion/sctp/chunkheader.go90
-rw-r--r--vendor/github.com/pion/sctp/chunktype.go67
-rw-r--r--vendor/github.com/pion/sctp/codecov.yml20
-rw-r--r--vendor/github.com/pion/sctp/control_queue.go29
-rw-r--r--vendor/github.com/pion/sctp/error_cause.go91
-rw-r--r--vendor/github.com/pion/sctp/error_cause_header.go47
-rw-r--r--vendor/github.com/pion/sctp/error_cause_invalid_mandatory_parameter.go19
-rw-r--r--vendor/github.com/pion/sctp/error_cause_protocol_violation.go50
-rw-r--r--vendor/github.com/pion/sctp/error_cause_unrecognized_chunk_type.go28
-rw-r--r--vendor/github.com/pion/sctp/go.mod14
-rw-r--r--vendor/github.com/pion/sctp/go.sum36
-rw-r--r--vendor/github.com/pion/sctp/packet.go178
-rw-r--r--vendor/github.com/pion/sctp/param.go35
-rw-r--r--vendor/github.com/pion/sctp/param_chunk_list.go28
-rw-r--r--vendor/github.com/pion/sctp/param_forward_tsn_supported.go28
-rw-r--r--vendor/github.com/pion/sctp/param_heartbeat_info.go21
-rw-r--r--vendor/github.com/pion/sctp/param_outgoing_reset_request.go88
-rw-r--r--vendor/github.com/pion/sctp/param_random.go21
-rw-r--r--vendor/github.com/pion/sctp/param_reconfig_response.go92
-rw-r--r--vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go73
-rw-r--r--vendor/github.com/pion/sctp/param_state_cookie.go46
-rw-r--r--vendor/github.com/pion/sctp/param_supported_extensions.go29
-rw-r--r--vendor/github.com/pion/sctp/paramheader.go63
-rw-r--r--vendor/github.com/pion/sctp/paramtype.go106
-rw-r--r--vendor/github.com/pion/sctp/payload_queue.go179
-rw-r--r--vendor/github.com/pion/sctp/pending_queue.go138
-rw-r--r--vendor/github.com/pion/sctp/reassembly_queue.go353
-rw-r--r--vendor/github.com/pion/sctp/renovate.json15
-rw-r--r--vendor/github.com/pion/sctp/rtx_timer.go219
-rw-r--r--vendor/github.com/pion/sctp/sctp.go2
-rw-r--r--vendor/github.com/pion/sctp/stream.go357
-rw-r--r--vendor/github.com/pion/sctp/util.go58
-rw-r--r--vendor/github.com/pion/sdp/v3/.gitignore24
-rw-r--r--vendor/github.com/pion/sdp/v3/.golangci.yml89
-rw-r--r--vendor/github.com/pion/sdp/v3/LICENSE21
-rw-r--r--vendor/github.com/pion/sdp/v3/README.md59
-rw-r--r--vendor/github.com/pion/sdp/v3/base_lexer.go231
-rw-r--r--vendor/github.com/pion/sdp/v3/codecov.yml20
-rw-r--r--vendor/github.com/pion/sdp/v3/common_description.go110
-rw-r--r--vendor/github.com/pion/sdp/v3/direction.go59
-rw-r--r--vendor/github.com/pion/sdp/v3/extmap.go108
-rw-r--r--vendor/github.com/pion/sdp/v3/fuzz.go31
-rw-r--r--vendor/github.com/pion/sdp/v3/go.mod8
-rw-r--r--vendor/github.com/pion/sdp/v3/go.sum13
-rw-r--r--vendor/github.com/pion/sdp/v3/jsep.go205
-rw-r--r--vendor/github.com/pion/sdp/v3/marshal.go136
-rw-r--r--vendor/github.com/pion/sdp/v3/media_description.go80
-rw-r--r--vendor/github.com/pion/sdp/v3/renovate.json15
-rw-r--r--vendor/github.com/pion/sdp/v3/sdp.go2
-rw-r--r--vendor/github.com/pion/sdp/v3/session_description.go146
-rw-r--r--vendor/github.com/pion/sdp/v3/time_description.go51
-rw-r--r--vendor/github.com/pion/sdp/v3/unmarshal.go907
-rw-r--r--vendor/github.com/pion/sdp/v3/util.go318
-rw-r--r--vendor/github.com/pion/srtp/v2/.gitignore24
-rw-r--r--vendor/github.com/pion/srtp/v2/.golangci.yml89
-rw-r--r--vendor/github.com/pion/srtp/v2/DESIGN.md20
-rw-r--r--vendor/github.com/pion/srtp/v2/LICENSE21
-rw-r--r--vendor/github.com/pion/srtp/v2/README.md54
-rw-r--r--vendor/github.com/pion/srtp/v2/codecov.yml20
-rw-r--r--vendor/github.com/pion/srtp/v2/context.go196
-rw-r--r--vendor/github.com/pion/srtp/v2/errors.go40
-rw-r--r--vendor/github.com/pion/srtp/v2/go.mod11
-rw-r--r--vendor/github.com/pion/srtp/v2/go.sum34
-rw-r--r--vendor/github.com/pion/srtp/v2/key_derivation.go63
-rw-r--r--vendor/github.com/pion/srtp/v2/keying.go54
-rw-r--r--vendor/github.com/pion/srtp/v2/option.go54
-rw-r--r--vendor/github.com/pion/srtp/v2/protection_profile.go67
-rw-r--r--vendor/github.com/pion/srtp/v2/renovate.json15
-rw-r--r--vendor/github.com/pion/srtp/v2/session.go150
-rw-r--r--vendor/github.com/pion/srtp/v2/session_srtcp.go180
-rw-r--r--vendor/github.com/pion/srtp/v2/session_srtp.go171
-rw-r--r--vendor/github.com/pion/srtp/v2/srtcp.go77
-rw-r--r--vendor/github.com/pion/srtp/v2/srtp.go68
-rw-r--r--vendor/github.com/pion/srtp/v2/srtp_cipher.go46
-rw-r--r--vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go198
-rw-r--r--vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go228
-rw-r--r--vendor/github.com/pion/srtp/v2/stream.go8
-rw-r--r--vendor/github.com/pion/srtp/v2/stream_srtcp.go157
-rw-r--r--vendor/github.com/pion/srtp/v2/stream_srtp.go154
-rw-r--r--vendor/github.com/pion/srtp/v2/util.go33
-rw-r--r--vendor/github.com/pion/stun/.codecov.yml9
-rw-r--r--vendor/github.com/pion/stun/.gitignore17
-rw-r--r--vendor/github.com/pion/stun/.golangci.yml93
-rw-r--r--vendor/github.com/pion/stun/.goreleaser.yml39
-rw-r--r--vendor/github.com/pion/stun/.travis.yml135
-rw-r--r--vendor/github.com/pion/stun/AUTHORS10
-rw-r--r--vendor/github.com/pion/stun/Dockerfile5
-rw-r--r--vendor/github.com/pion/stun/LICENSE.md7
-rw-r--r--vendor/github.com/pion/stun/Makefile61
-rw-r--r--vendor/github.com/pion/stun/README.md184
-rw-r--r--vendor/github.com/pion/stun/addr.go134
-rw-r--r--vendor/github.com/pion/stun/agent.go228
-rw-r--r--vendor/github.com/pion/stun/appveyor.yml22
-rw-r--r--vendor/github.com/pion/stun/attributes.go226
-rw-r--r--vendor/github.com/pion/stun/attributes_debug.go33
-rw-r--r--vendor/github.com/pion/stun/checks.go45
-rw-r--r--vendor/github.com/pion/stun/checks_debug.go61
-rw-r--r--vendor/github.com/pion/stun/client.go631
-rw-r--r--vendor/github.com/pion/stun/errorcode.go158
-rw-r--r--vendor/github.com/pion/stun/errors.go62
-rw-r--r--vendor/github.com/pion/stun/fingerprint.go67
-rw-r--r--vendor/github.com/pion/stun/fingerprint_debug.go18
-rw-r--r--vendor/github.com/pion/stun/fuzz.go140
-rw-r--r--vendor/github.com/pion/stun/go.mod3
-rw-r--r--vendor/github.com/pion/stun/go.sum0
-rw-r--r--vendor/github.com/pion/stun/go.test.sh24
-rw-r--r--vendor/github.com/pion/stun/helpers.go105
-rw-r--r--vendor/github.com/pion/stun/integrity.go124
-rw-r--r--vendor/github.com/pion/stun/integrity_debug.go18
-rw-r--r--vendor/github.com/pion/stun/internal/hmac/hmac.go101
-rw-r--r--vendor/github.com/pion/stun/internal/hmac/pool.go92
-rw-r--r--vendor/github.com/pion/stun/internal/hmac/vendor.sh4
-rw-r--r--vendor/github.com/pion/stun/message.go588
-rw-r--r--vendor/github.com/pion/stun/renovate.json15
-rw-r--r--vendor/github.com/pion/stun/stun.go51
-rw-r--r--vendor/github.com/pion/stun/textattrs.go129
-rw-r--r--vendor/github.com/pion/stun/uattrs.go63
-rw-r--r--vendor/github.com/pion/stun/xor.go62
-rw-r--r--vendor/github.com/pion/stun/xoraddr.go145
-rw-r--r--vendor/github.com/pion/transport/LICENSE21
-rw-r--r--vendor/github.com/pion/transport/connctx/connctx.go172
-rw-r--r--vendor/github.com/pion/transport/connctx/pipe.go11
-rw-r--r--vendor/github.com/pion/transport/deadline/deadline.go110
-rw-r--r--vendor/github.com/pion/transport/packetio/buffer.go347
-rw-r--r--vendor/github.com/pion/transport/packetio/errors.go27
-rw-r--r--vendor/github.com/pion/transport/packetio/hardlimit.go5
-rw-r--r--vendor/github.com/pion/transport/packetio/no_hardlimit.go5
-rw-r--r--vendor/github.com/pion/transport/replaydetector/fixedbig.go78
-rw-r--r--vendor/github.com/pion/transport/replaydetector/replaydetector.go116
-rw-r--r--vendor/github.com/pion/transport/vnet/.gitignore1
-rw-r--r--vendor/github.com/pion/transport/vnet/README.md239
-rw-r--r--vendor/github.com/pion/transport/vnet/chunk.go283
-rw-r--r--vendor/github.com/pion/transport/vnet/chunk_queue.go52
-rw-r--r--vendor/github.com/pion/transport/vnet/conn.go246
-rw-r--r--vendor/github.com/pion/transport/vnet/conn_map.go136
-rw-r--r--vendor/github.com/pion/transport/vnet/errors.go19
-rw-r--r--vendor/github.com/pion/transport/vnet/interface.go40
-rw-r--r--vendor/github.com/pion/transport/vnet/nat.go338
-rw-r--r--vendor/github.com/pion/transport/vnet/net.go677
-rw-r--r--vendor/github.com/pion/transport/vnet/resolver.go89
-rw-r--r--vendor/github.com/pion/transport/vnet/router.go605
-rw-r--r--vendor/github.com/pion/transport/vnet/udpproxy.go176
-rw-r--r--vendor/github.com/pion/transport/vnet/vnet.go2
-rw-r--r--vendor/github.com/pion/turn/v2/.gitignore9
-rw-r--r--vendor/github.com/pion/turn/v2/.golangci.yml88
-rw-r--r--vendor/github.com/pion/turn/v2/.goreleaser.yml109
-rw-r--r--vendor/github.com/pion/turn/v2/DESIGN.md31
-rw-r--r--vendor/github.com/pion/turn/v2/LICENSE.md7
-rw-r--r--vendor/github.com/pion/turn/v2/README.md96
-rw-r--r--vendor/github.com/pion/turn/v2/client.go569
-rw-r--r--vendor/github.com/pion/turn/v2/codecov.yml20
-rw-r--r--vendor/github.com/pion/turn/v2/errors.go28
-rw-r--r--vendor/github.com/pion/turn/v2/go.mod11
-rw-r--r--vendor/github.com/pion/turn/v2/go.sum28
-rw-r--r--vendor/github.com/pion/turn/v2/internal/allocation/allocation.go259
-rw-r--r--vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go186
-rw-r--r--vendor/github.com/pion/turn/v2/internal/allocation/channel_bind.go43
-rw-r--r--vendor/github.com/pion/turn/v2/internal/allocation/errors.go17
-rw-r--r--vendor/github.com/pion/turn/v2/internal/allocation/five_tuple.go36
-rw-r--r--vendor/github.com/pion/turn/v2/internal/allocation/permission.go40
-rw-r--r--vendor/github.com/pion/turn/v2/internal/client/binding.go151
-rw-r--r--vendor/github.com/pion/turn/v2/internal/client/conn.go613
-rw-r--r--vendor/github.com/pion/turn/v2/internal/client/errors.go37
-rw-r--r--vendor/github.com/pion/turn/v2/internal/client/periodic_timer.go82
-rw-r--r--vendor/github.com/pion/turn/v2/internal/client/permission.go90
-rw-r--r--vendor/github.com/pion/turn/v2/internal/client/transaction.go185
-rw-r--r--vendor/github.com/pion/turn/v2/internal/client/trylock.go24
-rw-r--r--vendor/github.com/pion/turn/v2/internal/ipnet/util.go40
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/addr.go65
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/chandata.go140
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/chann.go67
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/data.go30
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go18
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/evenport.go55
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/fuzz.go111
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/lifetime.go52
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/peeraddr.go42
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/proto.go30
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/relayedaddr.go40
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/reqfamily.go61
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go65
-rw-r--r--vendor/github.com/pion/turn/v2/internal/proto/rsrvtoken.go39
-rw-r--r--vendor/github.com/pion/turn/v2/internal/server/errors.go26
-rw-r--r--vendor/github.com/pion/turn/v2/internal/server/server.go109
-rw-r--r--vendor/github.com/pion/turn/v2/internal/server/stun.go22
-rw-r--r--vendor/github.com/pion/turn/v2/internal/server/turn.go352
-rw-r--r--vendor/github.com/pion/turn/v2/internal/server/util.go133
-rw-r--r--vendor/github.com/pion/turn/v2/lt_cred.go56
-rw-r--r--vendor/github.com/pion/turn/v2/relay_address_generator_none.go45
-rw-r--r--vendor/github.com/pion/turn/v2/relay_address_generator_range.go92
-rw-r--r--vendor/github.com/pion/turn/v2/relay_address_generator_static.go55
-rw-r--r--vendor/github.com/pion/turn/v2/renovate.json15
-rw-r--r--vendor/github.com/pion/turn/v2/server.go161
-rw-r--r--vendor/github.com/pion/turn/v2/server_config.go116
-rw-r--r--vendor/github.com/pion/turn/v2/stun_conn.go121
-rw-r--r--vendor/github.com/pion/udp/.gitignore15
-rw-r--r--vendor/github.com/pion/udp/.golangci.yml12
-rw-r--r--vendor/github.com/pion/udp/LICENSE21
-rw-r--r--vendor/github.com/pion/udp/README.md39
-rw-r--r--vendor/github.com/pion/udp/conn.go296
-rw-r--r--vendor/github.com/pion/udp/go.mod5
-rw-r--r--vendor/github.com/pion/udp/go.sum19
-rw-r--r--vendor/github.com/pion/webrtc/v3/.codacy.yaml3
-rw-r--r--vendor/github.com/pion/webrtc/v3/.eslintrc.json3
-rw-r--r--vendor/github.com/pion/webrtc/v3/.gitignore24
-rw-r--r--vendor/github.com/pion/webrtc/v3/.golangci.yml89
-rw-r--r--vendor/github.com/pion/webrtc/v3/DESIGN.md43
-rw-r--r--vendor/github.com/pion/webrtc/v3/LICENSE21
-rw-r--r--vendor/github.com/pion/webrtc/v3/README.md271
-rw-r--r--vendor/github.com/pion/webrtc/v3/api.go73
-rw-r--r--vendor/github.com/pion/webrtc/v3/api_js.go31
-rw-r--r--vendor/github.com/pion/webrtc/v3/atomicbool.go20
-rw-r--r--vendor/github.com/pion/webrtc/v3/bundlepolicy.go78
-rw-r--r--vendor/github.com/pion/webrtc/v3/certificate.go185
-rw-r--r--vendor/github.com/pion/webrtc/v3/codecov.yml20
-rw-r--r--vendor/github.com/pion/webrtc/v3/configuration.go51
-rw-r--r--vendor/github.com/pion/webrtc/v3/configuration_common.go24
-rw-r--r--vendor/github.com/pion/webrtc/v3/configuration_js.go35
-rw-r--r--vendor/github.com/pion/webrtc/v3/constants.go25
-rw-r--r--vendor/github.com/pion/webrtc/v3/datachannel.go597
-rw-r--r--vendor/github.com/pion/webrtc/v3/datachannel_js.go319
-rw-r--r--vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go71
-rw-r--r--vendor/github.com/pion/webrtc/v3/datachannelinit.go33
-rw-r--r--vendor/github.com/pion/webrtc/v3/datachannelmessage.go10
-rw-r--r--vendor/github.com/pion/webrtc/v3/datachannelparameters.go12
-rw-r--r--vendor/github.com/pion/webrtc/v3/datachannelstate.go61
-rw-r--r--vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go14
-rw-r--r--vendor/github.com/pion/webrtc/v3/dtlsparameters.go7
-rw-r--r--vendor/github.com/pion/webrtc/v3/dtlsrole.go92
-rw-r--r--vendor/github.com/pion/webrtc/v3/dtlstransport.go430
-rw-r--r--vendor/github.com/pion/webrtc/v3/dtlstransportstate.go71
-rw-r--r--vendor/github.com/pion/webrtc/v3/errors.go220
-rw-r--r--vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go24
-rw-r--r--vendor/github.com/pion/webrtc/v3/go.mod23
-rw-r--r--vendor/github.com/pion/webrtc/v3/go.sum151
-rw-r--r--vendor/github.com/pion/webrtc/v3/ice_go.go10
-rw-r--r--vendor/github.com/pion/webrtc/v3/icecandidate.go169
-rw-r--r--vendor/github.com/pion/webrtc/v3/icecandidateinit.go9
-rw-r--r--vendor/github.com/pion/webrtc/v3/icecandidatepair.go29
-rw-r--r--vendor/github.com/pion/webrtc/v3/icecandidatetype.go94
-rw-r--r--vendor/github.com/pion/webrtc/v3/icecomponent.go47
-rw-r--r--vendor/github.com/pion/webrtc/v3/iceconnectionstate.go94
-rw-r--r--vendor/github.com/pion/webrtc/v3/icecredentialtype.go43
-rw-r--r--vendor/github.com/pion/webrtc/v3/icegatherer.go367
-rw-r--r--vendor/github.com/pion/webrtc/v3/icegathererstate.go48
-rw-r--r--vendor/github.com/pion/webrtc/v3/icegatheringstate.go53
-rw-r--r--vendor/github.com/pion/webrtc/v3/icegatheroptions.go7
-rw-r--r--vendor/github.com/pion/webrtc/v3/iceparameters.go9
-rw-r--r--vendor/github.com/pion/webrtc/v3/iceprotocol.go47
-rw-r--r--vendor/github.com/pion/webrtc/v3/icerole.go45
-rw-r--r--vendor/github.com/pion/webrtc/v3/iceserver.go68
-rw-r--r--vendor/github.com/pion/webrtc/v3/iceserver_js.go42
-rw-r--r--vendor/github.com/pion/webrtc/v3/icetcp.go18
-rw-r--r--vendor/github.com/pion/webrtc/v3/icetransport.go368
-rw-r--r--vendor/github.com/pion/webrtc/v3/icetransportpolicy.go65
-rw-r--r--vendor/github.com/pion/webrtc/v3/icetransportstate.go107
-rw-r--r--vendor/github.com/pion/webrtc/v3/interceptor.go107
-rw-r--r--vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go75
-rw-r--r--vendor/github.com/pion/webrtc/v3/internal/mux/mux.go145
-rw-r--r--vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go101
-rw-r--r--vendor/github.com/pion/webrtc/v3/internal/util/util.go72
-rw-r--r--vendor/github.com/pion/webrtc/v3/js_compare.go13
-rw-r--r--vendor/github.com/pion/webrtc/v3/js_compare_legacy.go13
-rw-r--r--vendor/github.com/pion/webrtc/v3/js_utils.go168
-rw-r--r--vendor/github.com/pion/webrtc/v3/mediaengine.go533
-rw-r--r--vendor/github.com/pion/webrtc/v3/networktype.go104
-rw-r--r--vendor/github.com/pion/webrtc/v3/oauthcredential.go15
-rw-r--r--vendor/github.com/pion/webrtc/v3/offeransweroptions.go26
-rw-r--r--vendor/github.com/pion/webrtc/v3/operations.go87
-rw-r--r--vendor/github.com/pion/webrtc/v3/package.json11
-rw-r--r--vendor/github.com/pion/webrtc/v3/peerconnection.go2323
-rw-r--r--vendor/github.com/pion/webrtc/v3/peerconnection_js.go676
-rw-r--r--vendor/github.com/pion/webrtc/v3/peerconnectionstate.go94
-rw-r--r--vendor/github.com/pion/webrtc/v3/pkg/media/media.go25
-rw-r--r--vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go160
-rw-r--r--vendor/github.com/pion/webrtc/v3/renovate.json15
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtcpfeedback.go31
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go66
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpcapabilities.go9
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpcodec.go118
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go10
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go8
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go8
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go6
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpreceiver.go361
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpsender.go261
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtpsendparameters.go7
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtptransceiver.go187
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go85
-rw-r--r--vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go12
-rw-r--r--vendor/github.com/pion/webrtc/v3/sctpcapabilities.go6
-rw-r--r--vendor/github.com/pion/webrtc/v3/sctptransport.go379
-rw-r--r--vendor/github.com/pion/webrtc/v3/sctptransportstate.go54
-rw-r--r--vendor/github.com/pion/webrtc/v3/sdp.go643
-rw-r--r--vendor/github.com/pion/webrtc/v3/sdpsemantics.go74
-rw-r--r--vendor/github.com/pion/webrtc/v3/sdptype.go100
-rw-r--r--vendor/github.com/pion/webrtc/v3/sessiondescription.go21
-rw-r--r--vendor/github.com/pion/webrtc/v3/settingengine.go265
-rw-r--r--vendor/github.com/pion/webrtc/v3/settingengine_js.go19
-rw-r--r--vendor/github.com/pion/webrtc/v3/signalingstate.go188
-rw-r--r--vendor/github.com/pion/webrtc/v3/srtp_writer_future.go118
-rw-r--r--vendor/github.com/pion/webrtc/v3/stats.go1452
-rw-r--r--vendor/github.com/pion/webrtc/v3/stats_go.go93
-rw-r--r--vendor/github.com/pion/webrtc/v3/track_local.go75
-rw-r--r--vendor/github.com/pion/webrtc/v3/track_local_static.go267
-rw-r--r--vendor/github.com/pion/webrtc/v3/track_remote.go183
-rw-r--r--vendor/github.com/pion/webrtc/v3/webrtc.go17
-rw-r--r--vendor/github.com/pion/webrtc/v3/yarn.lock795
-rw-r--r--vendor/github.com/pkg/errors/.gitignore24
-rw-r--r--vendor/github.com/pkg/errors/.travis.yml10
-rw-r--r--vendor/github.com/pkg/errors/LICENSE23
-rw-r--r--vendor/github.com/pkg/errors/Makefile44
-rw-r--r--vendor/github.com/pkg/errors/README.md59
-rw-r--r--vendor/github.com/pkg/errors/appveyor.yml32
-rw-r--r--vendor/github.com/pkg/errors/errors.go288
-rw-r--r--vendor/github.com/pkg/errors/go113.go38
-rw-r--r--vendor/github.com/pkg/errors/stack.go177
-rw-r--r--vendor/github.com/smartystreets/assertions/.gitignore5
-rw-r--r--vendor/github.com/smartystreets/assertions/.travis.yml11
-rw-r--r--vendor/github.com/smartystreets/assertions/CONTRIBUTING.md12
-rw-r--r--vendor/github.com/smartystreets/assertions/LICENSE.md23
-rw-r--r--vendor/github.com/smartystreets/assertions/README.md611
-rw-r--r--vendor/github.com/smartystreets/assertions/collections.go244
-rw-r--r--vendor/github.com/smartystreets/assertions/doc.go109
-rw-r--r--vendor/github.com/smartystreets/assertions/equal_method.go75
-rw-r--r--vendor/github.com/smartystreets/assertions/equality.go328
-rw-r--r--vendor/github.com/smartystreets/assertions/filter.go31
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/go-render/LICENSE27
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/go-render/render/render.go481
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/go-render/render/render_time.go26
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/.gitignore5
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/.travis.yml4
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE202
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md58
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go94
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go61
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go88
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go541
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go39
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go39
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go41
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go152
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go86
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go53
-rw-r--r--vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go36
-rw-r--r--vendor/github.com/smartystreets/assertions/messages.go97
-rw-r--r--vendor/github.com/smartystreets/assertions/panic.go115
-rw-r--r--vendor/github.com/smartystreets/assertions/quantity.go141
-rw-r--r--vendor/github.com/smartystreets/assertions/serializer.go63
-rw-r--r--vendor/github.com/smartystreets/assertions/strings.go227
-rw-r--r--vendor/github.com/smartystreets/assertions/time.go202
-rw-r--r--vendor/github.com/smartystreets/assertions/type.go134
-rw-r--r--vendor/github.com/smartystreets/goconvey/LICENSE.md23
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/assertions.go71
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/context.go272
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/convey.goconvey4
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/discovery.go103
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/doc.go218
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go28
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/init.go81
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/nilReporter.go15
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/console.go16
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go5
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go40
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go33
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/init.go94
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/json.go88
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go60
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go80
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go39
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey2
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go179
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go108
-rw-r--r--vendor/github.com/smartystreets/goconvey/convey/reporting/story.go73
-rw-r--r--vendor/github.com/templexxx/cpu/.gitignore12
-rw-r--r--vendor/github.com/templexxx/cpu/LICENSE32
-rw-r--r--vendor/github.com/templexxx/cpu/README.md23
-rw-r--r--vendor/github.com/templexxx/cpu/cpu.go234
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_386.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_amd64.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_amd64p32.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_arm.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_arm64.go102
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_mips.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_mips64.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_mips64le.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_mipsle.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_no_init.go16
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_ppc64x.go68
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_s390x.go153
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_s390x.s55
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_wasm.go7
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_x86.go425
-rw-r--r--vendor/github.com/templexxx/cpu/cpu_x86.s32
-rw-r--r--vendor/github.com/templexxx/xorsimd/.gitattributes1
-rw-r--r--vendor/github.com/templexxx/xorsimd/.gitignore13
-rw-r--r--vendor/github.com/templexxx/xorsimd/LICENSE21
-rw-r--r--vendor/github.com/templexxx/xorsimd/README.md46
-rw-r--r--vendor/github.com/templexxx/xorsimd/go.mod5
-rw-r--r--vendor/github.com/templexxx/xorsimd/go.sum2
-rw-r--r--vendor/github.com/templexxx/xorsimd/xor.go89
-rw-r--r--vendor/github.com/templexxx/xorsimd/xor_amd64.go95
-rw-r--r--vendor/github.com/templexxx/xorsimd/xor_generic.go205
-rw-r--r--vendor/github.com/templexxx/xorsimd/xoravx2_amd64.s124
-rw-r--r--vendor/github.com/templexxx/xorsimd/xoravx512_amd64.s124
-rw-r--r--vendor/github.com/templexxx/xorsimd/xorbytes_amd64.s72
-rw-r--r--vendor/github.com/templexxx/xorsimd/xorsse2_amd64.s123
-rw-r--r--vendor/github.com/tjfoc/gmsm/LICENSE201
-rw-r--r--vendor/github.com/tjfoc/gmsm/sm4/sm4.go350
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/.gitignore25
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/.travis.yml20
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/LICENSE22
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/README.md285
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/autotune.go64
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/batchconn.go12
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/crypt.go618
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/donate.pngbin0 -> 4420 bytes
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/entropy.go52
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/fec.go381
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/flame.pngbin0 -> 57142 bytes
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/frame.pngbin0 -> 36006 bytes
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/go.mod20
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/go.sum69
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/kcp-go.pngbin0 -> 9153 bytes
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/kcp.go1080
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/readloop.go39
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/readloop_generic.go11
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/readloop_linux.go111
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/sess.go1075
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/snmp.go164
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/timedsched.go146
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/tx.go24
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/tx_generic.go11
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/tx_linux.go51
-rw-r--r--vendor/github.com/xtaci/kcp-go/v5/wechat_donate.jpgbin0 -> 35307 bytes
-rw-r--r--vendor/github.com/xtaci/smux/.gitignore24
-rw-r--r--vendor/github.com/xtaci/smux/.travis.yml17
-rw-r--r--vendor/github.com/xtaci/smux/LICENSE21
-rw-r--r--vendor/github.com/xtaci/smux/README.md136
-rw-r--r--vendor/github.com/xtaci/smux/alloc.go72
-rw-r--r--vendor/github.com/xtaci/smux/curve.jpgbin0 -> 106626 bytes
-rw-r--r--vendor/github.com/xtaci/smux/frame.go81
-rw-r--r--vendor/github.com/xtaci/smux/go.mod3
-rw-r--r--vendor/github.com/xtaci/smux/go.sum0
-rw-r--r--vendor/github.com/xtaci/smux/mux.go110
-rw-r--r--vendor/github.com/xtaci/smux/mux.jpgbin0 -> 6199 bytes
-rw-r--r--vendor/github.com/xtaci/smux/session.go525
-rw-r--r--vendor/github.com/xtaci/smux/shaper.go16
-rw-r--r--vendor/github.com/xtaci/smux/smux.pngbin0 -> 9891 bytes
-rw-r--r--vendor/github.com/xtaci/smux/stream.go549
875 files changed, 153353 insertions, 0 deletions
diff --git a/vendor/github.com/cretz/bine/LICENSE b/vendor/github.com/cretz/bine/LICENSE
new file mode 100644
index 0000000..6a6444d
--- /dev/null
+++ b/vendor/github.com/cretz/bine/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Chad Retz
+
+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. \ No newline at end of file
diff --git a/vendor/github.com/cretz/bine/control/cmd_authenticate.go b/vendor/github.com/cretz/bine/control/cmd_authenticate.go
new file mode 100644
index 0000000..c43d864
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_authenticate.go
@@ -0,0 +1,108 @@
+package control
+
+import (
+ "crypto/hmac"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/hex"
+ "io/ioutil"
+ "strings"
+)
+
+// Authenticate authenticates with the Tor instance using the "best" possible
+// authentication method if not already authenticated and sets the Authenticated
+// field. The password argument is optional, and will only be used if the
+// "SAFECOOKIE" and "NULL" authentication methods are not available and
+// "HASHEDPASSWORD" is.
+func (c *Conn) Authenticate(password string) error {
+ if c.Authenticated {
+ return nil
+ }
+ // Determine the supported authentication methods, and the cookie path.
+ pi, err := c.ProtocolInfo()
+ if err != nil {
+ return err
+ }
+ // Get the bytes to pass to with authenticate
+ var authBytes []byte
+ if pi.HasAuthMethod("NULL") {
+ // No auth bytes
+ } else if pi.HasAuthMethod("SAFECOOKIE") {
+ if pi.CookieFile == "" {
+ return c.protoErr("Invalid (empty) COOKIEFILE")
+ }
+ cookie, err := ioutil.ReadFile(pi.CookieFile)
+ if err != nil {
+ return c.protoErr("Failed to read COOKIEFILE: %v", err)
+ } else if len(cookie) != 32 {
+ return c.protoErr("Invalid cookie file length: %v", len(cookie))
+ }
+
+ // Send an AUTHCHALLENGE command, and parse the response.
+ var clientNonce [32]byte
+ if _, err := rand.Read(clientNonce[:]); err != nil {
+ return c.protoErr("Failed to generate clientNonce: %v", err)
+ }
+ resp, err := c.SendRequest("AUTHCHALLENGE %s %s", "SAFECOOKIE", hex.EncodeToString(clientNonce[:]))
+ if err != nil {
+ return err
+ }
+ splitResp := strings.Split(resp.Reply, " ")
+ if len(splitResp) != 3 || !strings.HasPrefix(splitResp[1], "SERVERHASH=") ||
+ !strings.HasPrefix(splitResp[2], "SERVERNONCE=") {
+ return c.protoErr("Invalid AUTHCHALLENGE response")
+ }
+ serverHash, err := hex.DecodeString(splitResp[1][11:])
+ if err != nil {
+ return c.protoErr("Failed to decode ServerHash: %v", err)
+ }
+ if len(serverHash) != 32 {
+ return c.protoErr("Invalid ServerHash length: %d", len(serverHash))
+ }
+ serverNonce, err := hex.DecodeString(splitResp[2][12:])
+ if err != nil {
+ return c.protoErr("Failed to decode ServerNonce: %v", err)
+ }
+ if len(serverNonce) != 32 {
+ return c.protoErr("Invalid ServerNonce length: %d", len(serverNonce))
+ }
+
+ // Validate the ServerHash.
+ m := hmac.New(sha256.New, []byte("Tor safe cookie authentication server-to-controller hash"))
+ m.Write(cookie)
+ m.Write(clientNonce[:])
+ m.Write(serverNonce)
+ dervServerHash := m.Sum(nil)
+ if !hmac.Equal(serverHash, dervServerHash) {
+ return c.protoErr("invalid ServerHash: mismatch")
+ }
+
+ // Calculate the ClientHash, and issue the AUTHENTICATE.
+ m = hmac.New(sha256.New, []byte("Tor safe cookie authentication controller-to-server hash"))
+ m.Write(cookie)
+ m.Write(clientNonce[:])
+ m.Write(serverNonce)
+ authBytes = m.Sum(nil)
+ } else if pi.HasAuthMethod("HASHEDPASSWORD") {
+ // Despite the name HASHEDPASSWORD, the raw password is actually sent. According to the code, this can either be
+ // a QuotedString, or base16 encoded, so go with the later since it's easier to handle.
+ if password == "" {
+ return c.protoErr("password auth needs a password")
+ }
+ authBytes = []byte(password)
+ } else {
+ return c.protoErr("No supported authentication methods")
+ }
+ // Send it
+ if err = c.sendAuthenticate(authBytes); err == nil {
+ c.Authenticated = true
+ }
+ return err
+}
+
+func (c *Conn) sendAuthenticate(byts []byte) error {
+ if len(byts) == 0 {
+ return c.sendRequestIgnoreResponse("AUTHENTICATE")
+ }
+ return c.sendRequestIgnoreResponse("AUTHENTICATE %v", hex.EncodeToString(byts))
+}
diff --git a/vendor/github.com/cretz/bine/control/cmd_circuit.go b/vendor/github.com/cretz/bine/control/cmd_circuit.go
new file mode 100644
index 0000000..f796145
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_circuit.go
@@ -0,0 +1,38 @@
+package control
+
+import (
+ "strings"
+)
+
+// ExtendCircuit invokes EXTENDCIRCUIT and returns the circuit ID on success.
+func (c *Conn) ExtendCircuit(circuitID string, path []string, purpose string) (string, error) {
+ if circuitID == "" {
+ circuitID = "0"
+ }
+ cmd := "EXTENDCIRCUIT " + circuitID
+ if len(path) > 0 {
+ cmd += " " + strings.Join(path, ",")
+ }
+ if purpose != "" {
+ cmd += " purpose=" + purpose
+ }
+ resp, err := c.SendRequest(cmd)
+ if err != nil {
+ return "", err
+ }
+ return resp.Reply[strings.LastIndexByte(resp.Reply, ' ')+1:], nil
+}
+
+// SetCircuitPurpose invokes SETCIRCUITPURPOSE.
+func (c *Conn) SetCircuitPurpose(circuitID string, purpose string) error {
+ return c.sendRequestIgnoreResponse("SETCIRCUITPURPOSE %v purpose=%v", circuitID, purpose)
+}
+
+// CloseCircuit invokes CLOSECIRCUIT.
+func (c *Conn) CloseCircuit(circuitID string, flags []string) error {
+ cmd := "CLOSECIRCUIT " + circuitID
+ for _, flag := range flags {
+ cmd += " " + flag
+ }
+ return c.sendRequestIgnoreResponse(cmd)
+}
diff --git a/vendor/github.com/cretz/bine/control/cmd_conf.go b/vendor/github.com/cretz/bine/control/cmd_conf.go
new file mode 100644
index 0000000..c0446e5
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_conf.go
@@ -0,0 +1,65 @@
+package control
+
+import (
+ "strings"
+
+ "github.com/cretz/bine/torutil"
+)
+
+// SetConf invokes SETCONF.
+func (c *Conn) SetConf(entries ...*KeyVal) error {
+ return c.sendSetConf("SETCONF", entries)
+}
+
+// ResetConf invokes RESETCONF.
+func (c *Conn) ResetConf(entries ...*KeyVal) error {
+ return c.sendSetConf("RESETCONF", entries)
+}
+
+func (c *Conn) sendSetConf(cmd string, entries []*KeyVal) error {
+ for _, entry := range entries {
+ cmd += " " + entry.Key
+ if entry.ValSet() {
+ cmd += "=" + torutil.EscapeSimpleQuotedStringIfNeeded(entry.Val)
+ }
+ }
+ return c.sendRequestIgnoreResponse(cmd)
+}
+
+// GetConf invokes GETCONF and returns the values for the requested keys.
+func (c *Conn) GetConf(keys ...string) ([]*KeyVal, error) {
+ resp, err := c.SendRequest("GETCONF %v", strings.Join(keys, " "))
+ if err != nil {
+ return nil, err
+ }
+ data := resp.DataWithReply()
+ ret := make([]*KeyVal, 0, len(data))
+ for _, data := range data {
+ key, val, ok := torutil.PartitionString(data, '=')
+ entry := &KeyVal{Key: key}
+ if ok {
+ if entry.Val, err = torutil.UnescapeSimpleQuotedStringIfNeeded(val); err != nil {
+ return nil, err
+ }
+ if len(entry.Val) == 0 {
+ entry.ValSetAndEmpty = true
+ }
+ }
+ ret = append(ret, entry)
+ }
+ return ret, nil
+}
+
+// SaveConf invokes SAVECONF.
+func (c *Conn) SaveConf(force bool) error {
+ cmd := "SAVECONF"
+ if force {
+ cmd += " FORCE"
+ }
+ return c.sendRequestIgnoreResponse(cmd)
+}
+
+// LoadConf invokes LOADCONF.
+func (c *Conn) LoadConf(conf string) error {
+ return c.sendRequestIgnoreResponse("+LOADCONF\r\n%v\r\n.", conf)
+}
diff --git a/vendor/github.com/cretz/bine/control/cmd_event.go b/vendor/github.com/cretz/bine/control/cmd_event.go
new file mode 100644
index 0000000..578a800
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_event.go
@@ -0,0 +1,1218 @@
+package control
+
+import (
+ "context"
+ "errors"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/cretz/bine/torutil"
+)
+
+// EventCode represents an asynchronous event code (ref control spec 4.1).
+type EventCode string
+
+// Event codes
+const (
+ EventCodeAddrMap EventCode = "ADDRMAP"
+ EventCodeBandwidth EventCode = "BW"
+ EventCodeBuildTimeoutSet EventCode = "BUILDTIMEOUT_SET"
+ EventCodeCellStats EventCode = "CELL_STATS"
+ EventCodeCircuit EventCode = "CIRC"
+ EventCodeCircuitBandwidth EventCode = "CIRC_BW"
+ EventCodeCircuitMinor EventCode = "CIRC_MINOR"
+ EventCodeClientsSeen EventCode = "CLIENTS_SEEN"
+ EventCodeConfChanged EventCode = "CONF_CHANGED"
+ EventCodeConnBandwidth EventCode = "CONN_BW"
+ EventCodeDescChanged EventCode = "DESCCHANGED"
+ EventCodeGuard EventCode = "GUARD"
+ EventCodeHSDesc EventCode = "HS_DESC"
+ EventCodeHSDescContent EventCode = "HS_DESC_CONTENT"
+ EventCodeLogDebug EventCode = "DEBUG"
+ EventCodeLogErr EventCode = "ERR"
+ EventCodeLogInfo EventCode = "INFO"
+ EventCodeLogNotice EventCode = "NOTICE"
+ EventCodeLogWarn EventCode = "WARN"
+ EventCodeNetworkLiveness EventCode = "NETWORK_LIVENESS"
+ EventCodeNetworkStatus EventCode = "NS"
+ EventCodeNewConsensus EventCode = "NEWCONSENSUS"
+ EventCodeNewDesc EventCode = "NEWDESC"
+ EventCodeORConn EventCode = "ORCONN"
+ EventCodeSignal EventCode = "SIGNAL"
+ EventCodeStatusClient EventCode = "STATUS_CLIENT"
+ EventCodeStatusGeneral EventCode = "STATUS_GENERAL"
+ EventCodeStatusServer EventCode = "STATUS_SERVER"
+ EventCodeStream EventCode = "STREAM"
+ EventCodeStreamBandwidth EventCode = "STREAM_BW"
+ EventCodeTokenBucketEmpty EventCode = "TB_EMPTY"
+ EventCodeTransportLaunched EventCode = "TRANSPORT_LAUNCHED"
+)
+
+// EventCodeUnrecognized is a special event code that is only used with
+// AddEventListener and RemoveEventListener to listen for events that are
+// unrecognized by this library (i.e. UnrecognizedEvent).
+var EventCodeUnrecognized EventCode = "<unrecognized>"
+
+var recognizedEventCodes = []EventCode{
+ EventCodeAddrMap,
+ EventCodeBandwidth,
+ EventCodeBuildTimeoutSet,
+ EventCodeCellStats,
+ EventCodeCircuit,
+ EventCodeCircuitBandwidth,
+ EventCodeCircuitMinor,
+ EventCodeClientsSeen,
+ EventCodeConfChanged,
+ EventCodeConnBandwidth,
+ EventCodeDescChanged,
+ EventCodeGuard,
+ EventCodeHSDesc,
+ EventCodeHSDescContent,
+ EventCodeLogDebug,
+ EventCodeLogErr,
+ EventCodeLogInfo,
+ EventCodeLogNotice,
+ EventCodeLogWarn,
+ EventCodeNetworkLiveness,
+ EventCodeNetworkStatus,
+ EventCodeNewConsensus,
+ EventCodeNewDesc,
+ EventCodeORConn,
+ EventCodeSignal,
+ EventCodeStatusClient,
+ EventCodeStatusGeneral,
+ EventCodeStatusServer,
+ EventCodeStream,
+ EventCodeStreamBandwidth,
+ EventCodeTokenBucketEmpty,
+ EventCodeTransportLaunched,
+}
+
+var recognizedEventCodesByCode = mapEventCodes()
+
+func mapEventCodes() map[EventCode]struct{} {
+ ret := make(map[EventCode]struct{}, len(recognizedEventCodes))
+ for _, eventCode := range recognizedEventCodes {
+ ret[eventCode] = struct{}{}
+ }
+ return ret
+}
+
+// EventCodes returns a new slice of all event codes that are recognized (i.e.
+// does not include EventCodeUnrecognized).
+func EventCodes() []EventCode {
+ ret := make([]EventCode, len(recognizedEventCodes))
+ copy(ret, recognizedEventCodes)
+ return ret
+}
+
+// ErrEventWaitSynchronousResponseOccurred is returned from EventWait (see docs)
+var ErrEventWaitSynchronousResponseOccurred = errors.New("Synchronous event occurred during EventWait")
+
+// EventWait waits for the predicate to be satisified or a non-event message to
+// come through. If a non-event comes through, the error
+// ErrEventWaitSynchronousResponseOccurred is returned. If there is an error in
+// the predicate or if the context completes or there is an error internally
+// handling the event, the error is returned. Otherwise, the event that true was
+// returned from the predicate for is returned.
+func (c *Conn) EventWait(
+ ctx context.Context, events []EventCode, predicate func(Event) (bool, error),
+) (Event, error) {
+ eventCh := make(chan Event, 10)
+ defer close(eventCh)
+ if err := c.AddEventListener(eventCh, events...); err != nil {
+ return nil, err
+ }
+ defer c.RemoveEventListener(eventCh, events...)
+ eventCtx, eventCancel := context.WithCancel(ctx)
+ defer eventCancel()
+ errCh := make(chan error, 1)
+ go func() { errCh <- c.HandleEvents(eventCtx) }()
+ for {
+ select {
+ case <-eventCtx.Done():
+ return nil, eventCtx.Err()
+ case err := <-errCh:
+ return nil, err
+ case event := <-eventCh:
+ if ok, err := predicate(event); err != nil {
+ return nil, err
+ } else if ok {
+ return event, nil
+ }
+ }
+ }
+}
+
+// HandleEvents loops until the context is closed dispatching async events. Can
+// dispatch events even after context is done and of course during synchronous
+// request. This will always end with an error, either from ctx.Done() or from
+// an error reading/handling the event.
+func (c *Conn) HandleEvents(ctx context.Context) error {
+ errCh := make(chan error, 1)
+ go func() {
+ for ctx.Err() == nil {
+ if err := c.HandleNextEvent(); err != nil {
+ errCh <- err
+ break
+ }
+ }
+ }()
+ select {
+ case err := <-errCh:
+ return err
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+}
+
+// HandleNextEvent attempts to read and handle the next event. It will return on
+// first message seen, event or not. Otherwise it will wait until there is a
+// message read.
+func (c *Conn) HandleNextEvent() error {
+ c.readLock.Lock()
+ defer c.readLock.Unlock()
+ // We'll just peek for the next 3 bytes and see if they are async
+ byts, err := c.conn.R.Peek(3)
+ if err != nil {
+ return err
+ }
+ statusCode, err := strconv.Atoi(string(byts))
+ if err != nil || statusCode != StatusAsyncEvent {
+ return err
+ }
+ // Read the entire thing and handle it
+ resp, err := c.ReadResponse()
+ if err != nil {
+ return err
+ }
+ c.relayAsyncEvents(resp)
+ return nil
+}
+
+// AddEventListener adds the given channel as an event listener for the given
+// events. Then Tor is notified about which events should be listened to.
+// Callers are expected to call RemoveEventListener for the channel and all
+// event codes used here before closing the channel. If no events are provided,
+// this is essentially a no-op. The EventCodeUnrecognized event code can be used
+// to listen for unrecognized events.
+func (c *Conn) AddEventListener(ch chan<- Event, events ...EventCode) error {
+ c.addEventListenerToMap(ch, events...)
+ // If there is an error updating the events, remove what we just added
+ err := c.sendSetEvents()
+ if err != nil {
+ c.removeEventListenerFromMap(ch, events...)
+ }
+ return err
+}
+
+// RemoveEventListener removes the given channel from being sent to by the given
+// event codes. It is not an error to remove a channel from events
+// AddEventListener was not called for. Tor is notified about events which may
+// no longer be listened to. If no events are provided, this is essentially a
+// no-op.
+func (c *Conn) RemoveEventListener(ch chan<- Event, events ...EventCode) error {
+ c.removeEventListenerFromMap(ch, events...)
+ return c.sendSetEvents()
+}
+
+func (c *Conn) addEventListenerToMap(ch chan<- Event, events ...EventCode) {
+ c.eventListenersLock.Lock()
+ defer c.eventListenersLock.Unlock()
+ for _, event := range events {
+ // Must completely replace the array, never mutate it
+ prevArr := c.eventListeners[event]
+ newArr := make([]chan<- Event, len(prevArr)+1)
+ copy(newArr, prevArr)
+ newArr[len(newArr)-1] = ch
+ c.eventListeners[event] = newArr
+ }
+}
+
+func (c *Conn) removeEventListenerFromMap(ch chan<- Event, events ...EventCode) {
+ c.eventListenersLock.Lock()
+ defer c.eventListenersLock.Unlock()
+ for _, event := range events {
+ arr := c.eventListeners[event]
+ index := -1
+ for i, listener := range arr {
+ if listener == ch {
+ index = i
+ break
+ }
+ }
+ if index != -1 {
+ if len(arr) == 1 {
+ delete(c.eventListeners, event)
+ } else {
+ // Must completely replace the array, never mutate it
+ newArr := make([]chan<- Event, len(arr)-1)
+ copy(newArr, arr[:index])
+ copy(newArr[index:], arr[index+1:])
+ c.eventListeners[event] = newArr
+ }
+ }
+ }
+}
+
+func (c *Conn) sendSetEvents() error {
+ c.eventListenersLock.RLock()
+ cmd := "SETEVENTS"
+ for event := range c.eventListeners {
+ cmd += " " + string(event)
+ }
+ c.eventListenersLock.RUnlock()
+ return c.sendRequestIgnoreResponse(cmd)
+}
+
+func (c *Conn) relayAsyncEvents(resp *Response) {
+ var code, data string
+ var dataArray []string
+ if len(resp.Data) == 1 {
+ // On single line, part up to space, newline, or EOL is the code, rest is data
+ if index := strings.Index(resp.Data[0], " "); index != -1 {
+ code, data = resp.Data[0][:index], resp.Data[0][index+1:]
+ } else if index := strings.Index(resp.Data[0], "\r\n"); index != -1 {
+ code, data = resp.Data[0][:index], resp.Data[0][index+2:]
+ } else {
+ code, data = resp.Data[0], ""
+ }
+ } else if len(resp.Data) > 0 {
+ // If there are multiple lines, the entire first line is the code
+ code, dataArray = resp.Data[0], resp.Data[1:]
+ } else {
+ // Otherwise, the reply line has the data
+ code, data, _ = torutil.PartitionString(resp.Reply, ' ')
+ }
+ // Only relay if there are chans
+ eventCode := EventCode(code)
+ c.eventListenersLock.RLock()
+ chans := c.eventListeners[eventCode]
+ if _, ok := recognizedEventCodesByCode[eventCode]; !ok {
+ chans = append(chans, c.eventListeners[EventCodeUnrecognized]...)
+ }
+ c.eventListenersLock.RUnlock()
+ if len(chans) == 0 {
+ return
+ }
+ // Parse the event and only send if known event
+ if event := ParseEvent(eventCode, data, dataArray); event != nil {
+ for _, ch := range chans {
+ // Just send, if closed or blocking, that's not our problem
+ ch <- event
+ }
+ }
+}
+
+// Zero on fail
+func parseISOTime(str string) time.Time {
+ // Essentially time.RFC3339 but without 'T' or TZ info
+ const layout = "2006-01-02 15:04:05"
+ ret, err := time.Parse(layout, str)
+ if err != nil {
+ ret = time.Time{}
+ }
+ return ret
+}
+
+// Zero on fail
+func parseISOTime2Frac(str string) time.Time {
+ // Essentially time.RFC3339Nano but without TZ info
+ const layout = "2006-01-02T15:04:05.999999999"
+ ret, err := time.Parse(layout, str)
+ if err != nil {
+ ret = time.Time{}
+ }
+ return ret
+}
+
+// Event is the base interface for all known asynchronous events.
+type Event interface {
+ Code() EventCode
+}
+
+// ParseEvent returns an Event for the given code and data info. Raw is the raw
+// single line if it is a single-line event (even if it has newlines), dataArray
+// is the array of lines for multi-line events. Only one of the two needs to be
+// set. The response is never nil, but may be UnrecognizedEvent. Format errors
+// are ignored per the Tor spec.
+func ParseEvent(code EventCode, raw string, dataArray []string) Event {
+ switch code {
+ case EventCodeAddrMap:
+ return ParseAddrMapEvent(raw)
+ case EventCodeBandwidth:
+ return ParseBandwidthEvent(raw)
+ case EventCodeBuildTimeoutSet:
+ return ParseBuildTimeoutSetEvent(raw)
+ case EventCodeCellStats:
+ return ParseCellStatsEvent(raw)
+ case EventCodeCircuit:
+ return ParseCircuitEvent(raw)
+ case EventCodeCircuitBandwidth:
+ return ParseCircuitBandwidthEvent(raw)
+ case EventCodeCircuitMinor:
+ return ParseCircuitMinorEvent(raw)
+ case EventCodeClientsSeen:
+ return ParseClientsSeenEvent(raw)
+ case EventCodeConfChanged:
+ return ParseConfChangedEvent(dataArray)
+ case EventCodeConnBandwidth:
+ return ParseConnBandwidthEvent(raw)
+ case EventCodeDescChanged:
+ return ParseDescChangedEvent(raw)
+ case EventCodeGuard:
+ return ParseGuardEvent(raw)
+ case EventCodeHSDesc:
+ return ParseHSDescEvent(raw)
+ case EventCodeHSDescContent:
+ return ParseHSDescContentEvent(raw)
+ case EventCodeLogDebug, EventCodeLogErr, EventCodeLogInfo, EventCodeLogNotice, EventCodeLogWarn:
+ return ParseLogEvent(code, raw)
+ case EventCodeNetworkLiveness:
+ return ParseNetworkLivenessEvent(raw)
+ case EventCodeNetworkStatus:
+ return ParseNetworkStatusEvent(raw)
+ case EventCodeNewConsensus:
+ return ParseNewConsensusEvent(raw)
+ case EventCodeNewDesc:
+ return ParseNewDescEvent(raw)
+ case EventCodeORConn:
+ return ParseORConnEvent(raw)
+ case EventCodeSignal:
+ return ParseSignalEvent(raw)
+ case EventCodeStatusClient, EventCodeStatusGeneral, EventCodeStatusServer:
+ return ParseStatusEvent(code, raw)
+ case EventCodeStream:
+ return ParseStreamEvent(raw)
+ case EventCodeStreamBandwidth:
+ return ParseStreamBandwidthEvent(raw)
+ case EventCodeTokenBucketEmpty:
+ return ParseTokenBucketEmptyEvent(raw)
+ case EventCodeTransportLaunched:
+ return ParseTransportLaunchedEvent(raw)
+ default:
+ return ParseUnrecognizedEvent(code, raw, dataArray)
+ }
+}
+
+// CircuitEvent is CIRC in spec.
+type CircuitEvent struct {
+ Raw string
+ CircuitID string
+ Status string
+ Path []string
+ BuildFlags []string
+ Purpose string
+ HSState string
+ RendQuery string
+ TimeCreated time.Time
+ Reason string
+ RemoteReason string
+ SocksUsername string
+ SocksPassword string
+}
+
+// ParseCircuitEvent parses the event.
+func ParseCircuitEvent(raw string) *CircuitEvent {
+ event := &CircuitEvent{Raw: raw}
+ event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ')
+ var ok bool
+ event.Status, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ first := true
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "BUILD_FLAGS":
+ event.BuildFlags = strings.Split(val, ",")
+ case "PURPOSE":
+ event.Purpose = val
+ case "HS_STATE":
+ event.HSState = val
+ case "REND_QUERY":
+ event.RendQuery = val
+ case "TIME_CREATED":
+ event.TimeCreated = parseISOTime2Frac(val)
+ case "REASON":
+ event.Reason = val
+ case "REMOTE_REASON":
+ event.RemoteReason = val
+ case "SOCKS_USERNAME":
+ event.SocksUsername = val
+ case "SOCKS_PASSWORD":
+ event.SocksPassword = val
+ default:
+ if first {
+ event.Path = strings.Split(val, ",")
+ }
+ }
+ first = false
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*CircuitEvent) Code() EventCode { return EventCodeCircuit }
+
+// StreamEvent is STREAM in spec.
+type StreamEvent struct {
+ Raw string
+ StreamID string
+ Status string
+ CircuitID string
+ TargetAddress string
+ TargetPort int
+ Reason string
+ RemoteReason string
+ Source string
+ SourceAddress string
+ SourcePort int
+ Purpose string
+}
+
+// ParseStreamEvent parses the event.
+func ParseStreamEvent(raw string) *StreamEvent {
+ event := &StreamEvent{Raw: raw}
+ event.StreamID, raw, _ = torutil.PartitionString(raw, ' ')
+ event.Status, raw, _ = torutil.PartitionString(raw, ' ')
+ event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ')
+ var ok bool
+ event.TargetAddress, raw, ok = torutil.PartitionString(raw, ' ')
+ if target, port, hasPort := torutil.PartitionStringFromEnd(event.TargetAddress, ':'); hasPort {
+ event.TargetAddress = target
+ event.TargetPort, _ = strconv.Atoi(port)
+ }
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "REASON":
+ event.Reason = val
+ case "REMOTE_REASON":
+ event.RemoteReason = val
+ case "SOURCE":
+ event.Source = val
+ case "SOURCE_ADDR":
+ event.SourceAddress = val
+ if source, port, hasPort := torutil.PartitionStringFromEnd(event.SourceAddress, ':'); hasPort {
+ event.SourceAddress = source
+ event.SourcePort, _ = strconv.Atoi(port)
+ }
+ case "PURPOSE":
+ event.Purpose = val
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*StreamEvent) Code() EventCode { return EventCodeStream }
+
+// ORConnEvent is ORCONN in spec.
+type ORConnEvent struct {
+ Raw string
+ Target string
+ Status string
+ Reason string
+ NumCircuits int
+ ConnID string
+}
+
+// ParseORConnEvent parses the event.
+func ParseORConnEvent(raw string) *ORConnEvent {
+ event := &ORConnEvent{Raw: raw}
+ event.Target, raw, _ = torutil.PartitionString(raw, ' ')
+ var ok bool
+ event.Status, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "REASON":
+ event.Reason = val
+ case "NCIRCS":
+ event.NumCircuits, _ = strconv.Atoi(val)
+ case "ID":
+ event.ConnID = val
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*ORConnEvent) Code() EventCode { return EventCodeORConn }
+
+// BandwidthEvent is BW in spec.
+type BandwidthEvent struct {
+ Raw string
+ BytesRead int64
+ BytesWritten int64
+}
+
+// ParseBandwidthEvent parses the event.
+func ParseBandwidthEvent(raw string) *BandwidthEvent {
+ event := &BandwidthEvent{Raw: raw}
+ var temp string
+ temp, raw, _ = torutil.PartitionString(raw, ' ')
+ event.BytesRead, _ = strconv.ParseInt(temp, 10, 64)
+ temp, raw, _ = torutil.PartitionString(raw, ' ')
+ event.BytesWritten, _ = strconv.ParseInt(temp, 10, 64)
+ return event
+}
+
+// Code implements Event.Code
+func (*BandwidthEvent) Code() EventCode { return EventCodeBandwidth }
+
+// LogEvent is DEBUG, ERR, INFO, NOTICE, and WARN in spec.
+type LogEvent struct {
+ Severity EventCode
+ Raw string
+}
+
+// ParseLogEvent parses the event.
+func ParseLogEvent(severity EventCode, raw string) *LogEvent {
+ return &LogEvent{Severity: severity, Raw: raw}
+}
+
+// Code implements Event.Code
+func (l *LogEvent) Code() EventCode { return l.Severity }
+
+// NewDescEvent is NEWDESC in spec.
+type NewDescEvent struct {
+ Raw string
+ Descs []string
+}
+
+// ParseNewDescEvent parses the event.
+func ParseNewDescEvent(raw string) *NewDescEvent {
+ return &NewDescEvent{Raw: raw, Descs: strings.Split(raw, " ")}
+}
+
+// Code implements Event.Code
+func (*NewDescEvent) Code() EventCode { return EventCodeNewDesc }
+
+// AddrMapEvent is ADDRMAP in spec.
+type AddrMapEvent struct {
+ Raw string
+ Address string
+ NewAddress string
+ ErrorCode string
+ // Zero if no expire
+ Expires time.Time
+ // Sans double quotes
+ Cached string
+}
+
+// ParseAddrMapEvent parses the event.
+func ParseAddrMapEvent(raw string) *AddrMapEvent {
+ event := &AddrMapEvent{Raw: raw}
+ event.Address, raw, _ = torutil.PartitionString(raw, ' ')
+ event.NewAddress, raw, _ = torutil.PartitionString(raw, ' ')
+ var ok bool
+ // Skip local expiration, use UTC one later
+ _, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "error":
+ event.ErrorCode = val
+ case "EXPIRES":
+ val, _ = torutil.UnescapeSimpleQuotedString(val)
+ event.Expires = parseISOTime(val)
+ case "CACHED":
+ event.Cached, _ = torutil.UnescapeSimpleQuotedStringIfNeeded(val)
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*AddrMapEvent) Code() EventCode { return EventCodeAddrMap }
+
+// DescChangedEvent is DESCCHANGED in spec.
+type DescChangedEvent struct {
+ Raw string
+}
+
+// ParseDescChangedEvent parses the event.
+func ParseDescChangedEvent(raw string) *DescChangedEvent {
+ return &DescChangedEvent{Raw: raw}
+}
+
+// Code implements Event.Code
+func (*DescChangedEvent) Code() EventCode { return EventCodeDescChanged }
+
+// StatusEvent is STATUS_CLIENT, STATUS_GENERAL, and STATUS_SERVER in spec.
+type StatusEvent struct {
+ Raw string
+ Type EventCode
+ Severity string
+ Action string
+ Arguments map[string]string
+}
+
+// ParseStatusEvent parses the event.
+func ParseStatusEvent(typ EventCode, raw string) *StatusEvent {
+ event := &StatusEvent{Raw: raw, Type: typ, Arguments: map[string]string{}}
+ event.Severity, raw, _ = torutil.PartitionString(raw, ' ')
+ var ok bool
+ event.Action, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ event.Arguments[key], _ = torutil.UnescapeSimpleQuotedStringIfNeeded(val)
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (s *StatusEvent) Code() EventCode { return s.Type }
+
+// GuardEvent is GUARD in spec.
+type GuardEvent struct {
+ Raw string
+ Type string
+ Name string
+ Status string
+}
+
+// ParseGuardEvent parses the event.
+func ParseGuardEvent(raw string) *GuardEvent {
+ event := &GuardEvent{Raw: raw}
+ event.Type, raw, _ = torutil.PartitionString(raw, ' ')
+ event.Name, raw, _ = torutil.PartitionString(raw, ' ')
+ event.Status, raw, _ = torutil.PartitionString(raw, ' ')
+ return event
+}
+
+// Code implements Event.Code
+func (*GuardEvent) Code() EventCode { return EventCodeGuard }
+
+// NetworkStatusEvent is NS in spec.
+type NetworkStatusEvent struct {
+ Raw string
+}
+
+// ParseNetworkStatusEvent parses the event.
+func ParseNetworkStatusEvent(raw string) *NetworkStatusEvent {
+ return &NetworkStatusEvent{Raw: raw}
+}
+
+// Code implements Event.Code
+func (*NetworkStatusEvent) Code() EventCode { return EventCodeNetworkStatus }
+
+// StreamBandwidthEvent is STREAM_BW in spec.
+type StreamBandwidthEvent struct {
+ Raw string
+ BytesRead int64
+ BytesWritten int64
+ Time time.Time
+}
+
+// ParseStreamBandwidthEvent parses the event.
+func ParseStreamBandwidthEvent(raw string) *StreamBandwidthEvent {
+ event := &StreamBandwidthEvent{Raw: raw}
+ var temp string
+ temp, raw, _ = torutil.PartitionString(raw, ' ')
+ event.BytesRead, _ = strconv.ParseInt(temp, 10, 64)
+ temp, raw, _ = torutil.PartitionString(raw, ' ')
+ event.BytesWritten, _ = strconv.ParseInt(temp, 10, 64)
+ temp, raw, _ = torutil.PartitionString(raw, ' ')
+ temp, _ = torutil.UnescapeSimpleQuotedString(temp)
+ event.Time = parseISOTime2Frac(temp)
+ return event
+}
+
+// Code implements Event.Code
+func (*StreamBandwidthEvent) Code() EventCode { return EventCodeStreamBandwidth }
+
+// ClientsSeenEvent is CLIENTS_SEEN in spec.
+type ClientsSeenEvent struct {
+ Raw string
+ TimeStarted time.Time
+ CountrySummary map[string]int
+ IPVersions map[string]int
+}
+
+// ParseClientsSeenEvent parses the event.
+func ParseClientsSeenEvent(raw string) *ClientsSeenEvent {
+ event := &ClientsSeenEvent{Raw: raw}
+ var temp string
+ var ok bool
+ temp, raw, ok = torutil.PartitionString(raw, ' ')
+ temp, _ = torutil.UnescapeSimpleQuotedString(temp)
+ event.TimeStarted = parseISOTime(temp)
+ strToMap := func(str string) map[string]int {
+ ret := map[string]int{}
+ for _, keyVal := range strings.Split(str, ",") {
+ key, val, _ := torutil.PartitionString(keyVal, '=')
+ ret[key], _ = strconv.Atoi(val)
+ }
+ return ret
+ }
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "CountrySummary":
+ event.CountrySummary = strToMap(val)
+ case "IPVersions":
+ event.IPVersions = strToMap(val)
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*ClientsSeenEvent) Code() EventCode { return EventCodeClientsSeen }
+
+// NewConsensusEvent is NEWCONSENSUS in spec.
+type NewConsensusEvent struct {
+ Raw string
+}
+
+// ParseNewConsensusEvent parses the event.
+func ParseNewConsensusEvent(raw string) *NewConsensusEvent {
+ return &NewConsensusEvent{Raw: raw}
+}
+
+// Code implements Event.Code
+func (*NewConsensusEvent) Code() EventCode { return EventCodeNewConsensus }
+
+// BuildTimeoutSetEvent is BUILDTIMEOUT_SET in spec.
+type BuildTimeoutSetEvent struct {
+ Raw string
+ Type string
+ TotalTimes int
+ Timeout time.Duration
+ Xm int
+ Alpha float32
+ Quantile float32
+ TimeoutRate float32
+ CloseTimeout time.Duration
+ CloseRate float32
+}
+
+// ParseBuildTimeoutSetEvent parses the event.
+func ParseBuildTimeoutSetEvent(raw string) *BuildTimeoutSetEvent {
+ event := &BuildTimeoutSetEvent{Raw: raw}
+ var ok bool
+ event.Type, raw, ok = torutil.PartitionString(raw, ' ')
+ _, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ parseFloat := func(val string) float32 {
+ f, _ := strconv.ParseFloat(val, 32)
+ return float32(f)
+ }
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "TOTAL_TIMES":
+ event.TotalTimes, _ = strconv.Atoi(val)
+ case "TIMEOUT_MS":
+ if ms, err := strconv.ParseInt(val, 10, 64); err == nil {
+ event.Timeout = time.Duration(ms) * time.Millisecond
+ }
+ case "XM":
+ event.Xm, _ = strconv.Atoi(val)
+ case "ALPHA":
+ event.Alpha = parseFloat(val)
+ case "CUTOFF_QUANTILE":
+ event.Quantile = parseFloat(val)
+ case "TIMEOUT_RATE":
+ event.TimeoutRate = parseFloat(val)
+ case "CLOSE_MS":
+ if ms, err := strconv.ParseInt(val, 10, 64); err == nil {
+ event.CloseTimeout = time.Duration(ms) * time.Millisecond
+ }
+ case "CLOSE_RATE":
+ event.CloseRate = parseFloat(val)
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*BuildTimeoutSetEvent) Code() EventCode { return EventCodeBuildTimeoutSet }
+
+// SignalEvent is SIGNAL in spec.
+type SignalEvent struct {
+ Raw string
+}
+
+// ParseSignalEvent parses the event.
+func ParseSignalEvent(raw string) *SignalEvent {
+ return &SignalEvent{Raw: raw}
+}
+
+// Code implements Event.Code
+func (*SignalEvent) Code() EventCode { return EventCodeSignal }
+
+// ConfChangedEvent is CONF_CHANGED in spec.
+type ConfChangedEvent struct {
+ Raw []string
+}
+
+// ParseConfChangedEvent parses the event.
+func ParseConfChangedEvent(raw []string) *ConfChangedEvent {
+ // TODO: break into KeyVal and unescape strings
+ return &ConfChangedEvent{Raw: raw}
+}
+
+// Code implements Event.Code
+func (*ConfChangedEvent) Code() EventCode { return EventCodeConfChanged }
+
+// CircuitMinorEvent is CIRC_MINOR in spec.
+type CircuitMinorEvent struct {
+ Raw string
+ CircuitID string
+ Event string
+ Path []string
+ BuildFlags []string
+ Purpose string
+ HSState string
+ RendQuery string
+ TimeCreated time.Time
+ OldPurpose string
+ OldHSState string
+}
+
+// ParseCircuitMinorEvent parses the event.
+func ParseCircuitMinorEvent(raw string) *CircuitMinorEvent {
+ event := &CircuitMinorEvent{Raw: raw}
+ event.CircuitID, raw, _ = torutil.PartitionString(raw, ' ')
+ var ok bool
+ event.Event, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ first := true
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "BUILD_FLAGS":
+ event.BuildFlags = strings.Split(val, ",")
+ case "PURPOSE":
+ event.Purpose = val
+ case "HS_STATE":
+ event.HSState = val
+ case "REND_QUERY":
+ event.RendQuery = val
+ case "TIME_CREATED":
+ event.TimeCreated = parseISOTime2Frac(val)
+ case "OLD_PURPOSE":
+ event.OldPurpose = val
+ case "OLD_HS_STATE":
+ event.OldHSState = val
+ default:
+ if first {
+ event.Path = strings.Split(val, ",")
+ }
+ }
+ first = false
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*CircuitMinorEvent) Code() EventCode { return EventCodeCircuitMinor }
+
+// TransportLaunchedEvent is TRANSPORT_LAUNCHED in spec.
+type TransportLaunchedEvent struct {
+ Raw string
+ Type string
+ Name string
+ Address string
+ Port int
+}
+
+// ParseTransportLaunchedEvent parses the event.
+func ParseTransportLaunchedEvent(raw string) *TransportLaunchedEvent {
+ event := &TransportLaunchedEvent{Raw: raw}
+ event.Type, raw, _ = torutil.PartitionString(raw, ' ')
+ event.Name, raw, _ = torutil.PartitionString(raw, ' ')
+ event.Address, raw, _ = torutil.PartitionString(raw, ' ')
+ var temp string
+ temp, raw, _ = torutil.PartitionString(raw, ' ')
+ event.Port, _ = strconv.Atoi(temp)
+ return event
+}
+
+// Code implements Event.Code
+func (*TransportLaunchedEvent) Code() EventCode { return EventCodeTransportLaunched }
+
+// ConnBandwidthEvent is CONN_BW in spec.
+type ConnBandwidthEvent struct {
+ Raw string
+ ConnID string
+ ConnType string
+ BytesRead int64
+ BytesWritten int64
+}
+
+// ParseConnBandwidthEvent parses the event.
+func ParseConnBandwidthEvent(raw string) *ConnBandwidthEvent {
+ event := &ConnBandwidthEvent{Raw: raw}
+ ok := true
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "ID":
+ event.ConnID = val
+ case "TYPE":
+ event.ConnType = val
+ case "READ":
+ event.BytesRead, _ = strconv.ParseInt(val, 10, 64)
+ case "WRITTEN":
+ event.BytesWritten, _ = strconv.ParseInt(val, 10, 64)
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*ConnBandwidthEvent) Code() EventCode { return EventCodeConnBandwidth }
+
+// CircuitBandwidthEvent is CIRC_BW in spec.
+type CircuitBandwidthEvent struct {
+ Raw string
+ CircuitID string
+ BytesRead int64
+ BytesWritten int64
+ Time time.Time
+}
+
+// ParseCircuitBandwidthEvent parses the event.
+func ParseCircuitBandwidthEvent(raw string) *CircuitBandwidthEvent {
+ event := &CircuitBandwidthEvent{Raw: raw}
+ ok := true
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "ID":
+ event.CircuitID = val
+ case "READ":
+ event.BytesRead, _ = strconv.ParseInt(val, 10, 64)
+ case "WRITTEN":
+ event.BytesWritten, _ = strconv.ParseInt(val, 10, 64)
+ case "TIME":
+ event.Time = parseISOTime2Frac(val)
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*CircuitBandwidthEvent) Code() EventCode { return EventCodeCircuitBandwidth }
+
+// CellStatsEvent is CELL_STATS in spec.
+type CellStatsEvent struct {
+ Raw string
+ CircuitID string
+ InboundQueueID string
+ InboundConnID string
+ InboundAdded map[string]int
+ InboundRemoved map[string]int
+ InboundTime map[string]int
+ OutboundQueueID string
+ OutboundConnID string
+ OutboundAdded map[string]int
+ OutboundRemoved map[string]int
+ OutboundTime map[string]int
+}
+
+// ParseCellStatsEvent parses the event.
+func ParseCellStatsEvent(raw string) *CellStatsEvent {
+ event := &CellStatsEvent{Raw: raw}
+ ok := true
+ var attr string
+ toIntMap := func(val string) map[string]int {
+ ret := map[string]int{}
+ for _, v := range strings.Split(val, ",") {
+ key, val, _ := torutil.PartitionString(v, ':')
+ ret[key], _ = strconv.Atoi(val)
+ }
+ return ret
+ }
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "ID":
+ event.CircuitID = val
+ case "InboundQueue":
+ event.InboundQueueID = val
+ case "InboundConn":
+ event.InboundConnID = val
+ case "InboundAdded":
+ event.InboundAdded = toIntMap(val)
+ case "InboundRemoved":
+ event.InboundRemoved = toIntMap(val)
+ case "InboundTime":
+ event.OutboundTime = toIntMap(val)
+ case "OutboundQueue":
+ event.OutboundQueueID = val
+ case "OutboundConn":
+ event.OutboundConnID = val
+ case "OutboundAdded":
+ event.OutboundAdded = toIntMap(val)
+ case "OutboundRemoved":
+ event.OutboundRemoved = toIntMap(val)
+ case "OutboundTime":
+ event.OutboundTime = toIntMap(val)
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*CellStatsEvent) Code() EventCode { return EventCodeCellStats }
+
+// TokenBucketEmptyEvent is TB_EMPTY in spec.
+type TokenBucketEmptyEvent struct {
+ Raw string
+ BucketName string
+ ConnID string
+ ReadBucketEmpty time.Duration
+ WriteBucketEmpty time.Duration
+ LastRefil time.Duration
+}
+
+// ParseTokenBucketEmptyEvent parses the event.
+func ParseTokenBucketEmptyEvent(raw string) *TokenBucketEmptyEvent {
+ event := &TokenBucketEmptyEvent{Raw: raw}
+ var ok bool
+ event.BucketName, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, _ := torutil.PartitionString(attr, '=')
+ switch key {
+ case "ID":
+ event.ConnID = val
+ case "READ":
+ i, _ := strconv.ParseInt(val, 10, 64)
+ event.ReadBucketEmpty = time.Duration(i) * time.Millisecond
+ case "WRITTEN":
+ i, _ := strconv.ParseInt(val, 10, 64)
+ event.WriteBucketEmpty = time.Duration(i) * time.Millisecond
+ case "LAST":
+ i, _ := strconv.ParseInt(val, 10, 64)
+ event.LastRefil = time.Duration(i) * time.Millisecond
+ }
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*TokenBucketEmptyEvent) Code() EventCode { return EventCodeTokenBucketEmpty }
+
+// HSDescEvent is HS_DESC in spec.
+type HSDescEvent struct {
+ Raw string
+ Action string
+ Address string
+ AuthType string
+ HSDir string
+ DescID string
+ Reason string
+ Replica int
+ HSDirIndex string
+}
+
+// ParseHSDescEvent parses the event.
+func ParseHSDescEvent(raw string) *HSDescEvent {
+ event := &HSDescEvent{Raw: raw}
+ event.Action, raw, _ = torutil.PartitionString(raw, ' ')
+ event.Address, raw, _ = torutil.PartitionString(raw, ' ')
+ event.AuthType, raw, _ = torutil.PartitionString(raw, ' ')
+ var ok bool
+ event.HSDir, raw, ok = torutil.PartitionString(raw, ' ')
+ var attr string
+ first := true
+ for ok {
+ attr, raw, ok = torutil.PartitionString(raw, ' ')
+ key, val, valOk := torutil.PartitionString(attr, '=')
+ switch key {
+ case "REASON":
+ event.Reason = val
+ case "REPLICA":
+ event.Replica, _ = strconv.Atoi(val)
+ case "HSDIR_INDEX":
+ event.HSDirIndex = val
+ default:
+ if first && !valOk {
+ event.DescID = attr
+ }
+ }
+ first = false
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*HSDescEvent) Code() EventCode { return EventCodeHSDesc }
+
+// HSDescContentEvent is HS_DESC_CONTENT in spec.
+type HSDescContentEvent struct {
+ Raw string
+ Address string
+ DescID string
+ HSDir string
+ Descriptor string
+}
+
+// ParseHSDescContentEvent parses the event.
+func ParseHSDescContentEvent(raw string) *HSDescContentEvent {
+ event := &HSDescContentEvent{Raw: raw}
+ event.Address, raw, _ = torutil.PartitionString(raw, ' ')
+ event.DescID, raw, _ = torutil.PartitionString(raw, ' ')
+ newlineIndex := strings.Index(raw, "\r\n")
+ if newlineIndex != -1 {
+ event.HSDir, event.Descriptor = raw[:newlineIndex], raw[newlineIndex+2:]
+ }
+ return event
+}
+
+// Code implements Event.Code
+func (*HSDescContentEvent) Code() EventCode { return EventCodeHSDescContent }
+
+// NetworkLivenessEvent is NETWORK_LIVENESS in spec.
+type NetworkLivenessEvent struct {
+ Raw string
+}
+
+// ParseNetworkLivenessEvent parses the event.
+func ParseNetworkLivenessEvent(raw string) *NetworkLivenessEvent {
+ return &NetworkLivenessEvent{Raw: raw}
+}
+
+// Code implements Event.Code
+func (*NetworkLivenessEvent) Code() EventCode { return EventCodeNetworkLiveness }
+
+// UnrecognizedEvent is any unrecognized event code.
+type UnrecognizedEvent struct {
+ EventCode EventCode
+ RawSingleLine string
+ RawMultiLine []string
+}
+
+// ParseUnrecognizedEvent creates an unrecognized event with the given values.
+func ParseUnrecognizedEvent(eventCode EventCode, rawSingleLine string, rawMultiLine []string) *UnrecognizedEvent {
+ return &UnrecognizedEvent{EventCode: eventCode, RawSingleLine: rawSingleLine, RawMultiLine: rawMultiLine}
+}
+
+// Code implements Event.Code
+func (u *UnrecognizedEvent) Code() EventCode { return u.EventCode }
diff --git a/vendor/github.com/cretz/bine/control/cmd_hiddenservice.go b/vendor/github.com/cretz/bine/control/cmd_hiddenservice.go
new file mode 100644
index 0000000..ab52142
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_hiddenservice.go
@@ -0,0 +1,23 @@
+package control
+
+// GetHiddenServiceDescriptorAsync invokes HSFETCH.
+func (c *Conn) GetHiddenServiceDescriptorAsync(address string, server string) error {
+ cmd := "HSFETCH " + address
+ if server != "" {
+ cmd += " SERVER=" + server
+ }
+ return c.sendRequestIgnoreResponse(cmd)
+}
+
+// PostHiddenServiceDescriptorAsync invokes HSPOST.
+func (c *Conn) PostHiddenServiceDescriptorAsync(desc string, servers []string, address string) error {
+ cmd := "+HSPOST"
+ for _, server := range servers {
+ cmd += " SERVER=" + server
+ }
+ if address != "" {
+ cmd += "HSADDRESS=" + address
+ }
+ cmd += "\r\n" + desc + "\r\n."
+ return c.sendRequestIgnoreResponse(cmd)
+}
diff --git a/vendor/github.com/cretz/bine/control/cmd_misc.go b/vendor/github.com/cretz/bine/control/cmd_misc.go
new file mode 100644
index 0000000..6c3fa49
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_misc.go
@@ -0,0 +1,92 @@
+package control
+
+import (
+ "strings"
+
+ "github.com/cretz/bine/torutil"
+)
+
+// Signal invokes SIGNAL.
+func (c *Conn) Signal(signal string) error {
+ return c.sendRequestIgnoreResponse("SIGNAL %v", signal)
+}
+
+// Quit invokes QUIT.
+func (c *Conn) Quit() error {
+ return c.sendRequestIgnoreResponse("QUIT")
+}
+
+// MapAddresses invokes MAPADDRESS and returns mapped addresses.
+func (c *Conn) MapAddresses(addresses ...*KeyVal) ([]*KeyVal, error) {
+ cmd := "MAPADDRESS"
+ for _, address := range addresses {
+ cmd += " " + address.Key + "=" + address.Val
+ }
+ resp, err := c.SendRequest(cmd)
+ if err != nil {
+ return nil, err
+ }
+ data := resp.DataWithReply()
+ ret := make([]*KeyVal, 0, len(data))
+ for _, address := range data {
+ mappedAddress := &KeyVal{}
+ mappedAddress.Key, mappedAddress.Val, _ = torutil.PartitionString(address, '=')
+ ret = append(ret, mappedAddress)
+ }
+ return ret, nil
+}
+
+// GetInfo invokes GETINTO and returns values for requested keys.
+func (c *Conn) GetInfo(keys ...string) ([]*KeyVal, error) {
+ resp, err := c.SendRequest("GETINFO %v", strings.Join(keys, " "))
+ if err != nil {
+ return nil, err
+ }
+ ret := make([]*KeyVal, 0, len(resp.Data))
+ for _, val := range resp.Data {
+ infoVal := &KeyVal{}
+ infoVal.Key, infoVal.Val, _ = torutil.PartitionString(val, '=')
+ if infoVal.Val, err = torutil.UnescapeSimpleQuotedStringIfNeeded(infoVal.Val); err != nil {
+ return nil, err
+ }
+ ret = append(ret, infoVal)
+ }
+ return ret, nil
+}
+
+// PostDescriptor invokes POSTDESCRIPTOR.
+func (c *Conn) PostDescriptor(descriptor string, purpose string, cache string) error {
+ cmd := "+POSTDESCRIPTOR"
+ if purpose != "" {
+ cmd += " purpose=" + purpose
+ }
+ if cache != "" {
+ cmd += " cache=" + cache
+ }
+ cmd += "\r\n" + descriptor + "\r\n."
+ return c.sendRequestIgnoreResponse(cmd)
+}
+
+// UseFeatures invokes USEFEATURE.
+func (c *Conn) UseFeatures(features ...string) error {
+ return c.sendRequestIgnoreResponse("USEFEATURE " + strings.Join(features, " "))
+}
+
+// ResolveAsync invokes RESOLVE.
+func (c *Conn) ResolveAsync(address string, reverse bool) error {
+ cmd := "RESOLVE "
+ if reverse {
+ cmd += "mode=reverse "
+ }
+ return c.sendRequestIgnoreResponse(cmd + address)
+}
+
+// TakeOwnership invokes TAKEOWNERSHIP.
+func (c *Conn) TakeOwnership() error {
+ return c.sendRequestIgnoreResponse("TAKEOWNERSHIP")
+}
+
+// DropGuards invokes DROPGUARDS.
+func (c *Conn) DropGuards() error {
+ return c.sendRequestIgnoreResponse("DROPGUARDS")
+}
diff --git a/vendor/github.com/cretz/bine/control/cmd_onion.go b/vendor/github.com/cretz/bine/control/cmd_onion.go
new file mode 100644
index 0000000..b298d36
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_onion.go
@@ -0,0 +1,201 @@
+package control
+
+import (
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/base64"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/cretz/bine/torutil"
+ "github.com/cretz/bine/torutil/ed25519"
+)
+
+// KeyType is a key type for Key in AddOnion.
+type KeyType string
+
+const (
+ // KeyTypeNew is NEW.
+ KeyTypeNew KeyType = "NEW"
+ // KeyTypeRSA1024 is RSA1024.
+ KeyTypeRSA1024 KeyType = "RSA1024"
+ // KeyTypeED25519V3 is ED25519-V3.
+ KeyTypeED25519V3 KeyType = "ED25519-V3"
+)
+
+// KeyAlgo is a key algorithm for GenKey on AddOnion.
+type KeyAlgo string
+
+const (
+ // KeyAlgoBest is BEST.
+ KeyAlgoBest KeyAlgo = "BEST"
+ // KeyAlgoRSA1024 is RSA1024.
+ KeyAlgoRSA1024 KeyAlgo = "RSA1024"
+ // KeyAlgoED25519V3 is ED25519-V3.
+ KeyAlgoED25519V3 KeyAlgo = "ED25519-V3"
+)
+
+// Key is a type of key to use for AddOnion. Implementations include GenKey,
+// RSAKey, and ED25519Key.
+type Key interface {
+ // Type is the KeyType for AddOnion.
+ Type() KeyType
+ // Blob is the serialized key for AddOnion.
+ Blob() string
+}
+
+// KeyFromString creates a Key for AddOnion based on a response string.
+func KeyFromString(str string) (Key, error) {
+ typ, blob, _ := torutil.PartitionString(str, ':')
+ switch KeyType(typ) {
+ case KeyTypeNew:
+ return GenKeyFromBlob(blob), nil
+ case KeyTypeRSA1024:
+ return RSA1024KeyFromBlob(blob)
+ case KeyTypeED25519V3:
+ return ED25519KeyFromBlob(blob)
+ default:
+ return nil, fmt.Errorf("Unrecognized key type: %v", typ)
+ }
+}
+
+// GenKey is a Key for AddOnion that asks Tor to generate a key for the given
+// algorithm.
+type GenKey KeyAlgo
+
+// GenKeyFromBlob creates a GenKey for the given response blob which is a
+// KeyAlgo.
+func GenKeyFromBlob(blob string) GenKey { return GenKey(KeyAlgo(blob)) }
+
+// Type implements Key.Type.
+func (GenKey) Type() KeyType { return KeyTypeNew }
+
+// Blob implements Key.Blob.
+func (g GenKey) Blob() string { return string(g) }
+
+// RSAKey is a Key for AddOnion that is a RSA-1024 key (i.e. v2).
+type RSAKey struct{ *rsa.PrivateKey }
+
+// RSA1024KeyFromBlob creates a RSAKey for the given response blob.
+func RSA1024KeyFromBlob(blob string) (*RSAKey, error) {
+ byts, err := base64.StdEncoding.DecodeString(blob)
+ if err != nil {
+ return nil, err
+ }
+ rsaKey, err := x509.ParsePKCS1PrivateKey(byts)
+ if err != nil {
+ return nil, err
+ }
+ return &RSAKey{rsaKey}, nil
+}
+
+// Type implements Key.Type.
+func (*RSAKey) Type() KeyType { return KeyTypeRSA1024 }
+
+// Blob implements Key.Blob.
+func (r *RSAKey) Blob() string {
+ return base64.StdEncoding.EncodeToString(x509.MarshalPKCS1PrivateKey(r.PrivateKey))
+}
+
+// ED25519Key is a Key for AddOnion that is a ed25519 key (i.e. v3).
+type ED25519Key struct{ ed25519.KeyPair }
+
+// ED25519KeyFromBlob creates a ED25519Key for the given response blob.
+func ED25519KeyFromBlob(blob string) (*ED25519Key, error) {
+ byts, err := base64.StdEncoding.DecodeString(blob)
+ if err != nil {
+ return nil, err
+ }
+ return &ED25519Key{ed25519.PrivateKey(byts).KeyPair()}, nil
+}
+
+// Type implements Key.Type.
+func (*ED25519Key) Type() KeyType { return KeyTypeED25519V3 }
+
+// Blob implements Key.Blob.
+func (e *ED25519Key) Blob() string { return base64.StdEncoding.EncodeToString(e.PrivateKey()) }
+
+// AddOnionRequest is a set of request params for AddOnion.
+type AddOnionRequest struct {
+ // Key is the key to use or GenKey if Tor should generate it.
+ Key Key
+ // Flags are ADD_ONION flags.
+ Flags []string
+ // MaxStreams is ADD_ONION MaxStreams.
+ MaxStreams int
+ // Ports are ADD_ONION Port values. Key is virtual port, Val is target
+ // port (or can be empty to use virtual port).
+ Ports []*KeyVal
+ // ClientAuths are ADD_ONION ClientAuth values. If value is empty string,
+ // Tor will generate the password.
+ ClientAuths map[string]string
+}
+
+// AddOnionResponse is the response for AddOnion.
+type AddOnionResponse struct {
+ // ServiceID is the ADD_ONION response ServiceID value.
+ ServiceID string
+ // Key is the ADD_ONION response PrivateKey value.
+ Key Key
+ // ClientAuths are the ADD_ONION response ClientAuth values.
+ ClientAuths map[string]string
+ // RawResponse is the raw ADD_ONION response.
+ RawResponse *Response
+}
+
+// AddOnion invokes ADD_ONION and returns its response.
+func (c *Conn) AddOnion(req *AddOnionRequest) (*AddOnionResponse, error) {
+ // Build command
+ if req.Key == nil {
+ return nil, c.protoErr("Key required")
+ }
+ cmd := "ADD_ONION " + string(req.Key.Type()) + ":" + req.Key.Blob()
+ if len(req.Flags) > 0 {
+ cmd += " Flags=" + strings.Join(req.Flags, ",")
+ }
+ if req.MaxStreams > 0 {
+ cmd += " MaxStreams=" + strconv.Itoa(req.MaxStreams)
+ }
+ for _, port := range req.Ports {
+ cmd += " Port=" + port.Key
+ if port.Val != "" {
+ cmd += "," + port.Val
+ }
+ }
+ for name, blob := range req.ClientAuths {
+ cmd += " ClientAuth=" + name
+ if blob != "" {
+ cmd += ":" + blob
+ }
+ }
+ // Invoke and read response
+ resp, err := c.SendRequest(cmd)
+ if err != nil {
+ return nil, err
+ }
+ ret := &AddOnionResponse{RawResponse: resp}
+ for _, data := range resp.Data {
+ key, val, _ := torutil.PartitionString(data, '=')
+ switch key {
+ case "ServiceID":
+ ret.ServiceID = val
+ case "PrivateKey":
+ if ret.Key, err = KeyFromString(val); err != nil {
+ return nil, err
+ }
+ case "ClientAuth":
+ name, pass, _ := torutil.PartitionString(val, ':')
+ if ret.ClientAuths == nil {
+ ret.ClientAuths = map[string]string{}
+ }
+ ret.ClientAuths[name] = pass
+ }
+ }
+ return ret, nil
+}
+
+// DelOnion invokes DELONION.
+func (c *Conn) DelOnion(serviceID string) error {
+ return c.sendRequestIgnoreResponse("DEL_ONION %v", serviceID)
+}
diff --git a/vendor/github.com/cretz/bine/control/cmd_protocolinfo.go b/vendor/github.com/cretz/bine/control/cmd_protocolinfo.go
new file mode 100644
index 0000000..346b36d
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_protocolinfo.go
@@ -0,0 +1,76 @@
+package control
+
+import (
+ "strings"
+
+ "github.com/cretz/bine/torutil"
+)
+
+// ProtocolInfo is the protocol info result of Conn.ProtocolInfo.
+type ProtocolInfo struct {
+ AuthMethods []string
+ CookieFile string
+ TorVersion string
+ RawResponse *Response
+}
+
+// HasAuthMethod checks if ProtocolInfo contains the requested auth method.
+func (p *ProtocolInfo) HasAuthMethod(authMethod string) bool {
+ for _, m := range p.AuthMethods {
+ if m == authMethod {
+ return true
+ }
+ }
+ return false
+}
+
+// ProtocolInfo invokes PROTOCOLINFO on first invocation and returns a cached
+// result on all others.
+func (c *Conn) ProtocolInfo() (*ProtocolInfo, error) {
+ var err error
+ if c.protocolInfo == nil {
+ c.protocolInfo, err = c.sendProtocolInfo()
+ }
+ return c.protocolInfo, err
+}
+
+func (c *Conn) sendProtocolInfo() (*ProtocolInfo, error) {
+ resp, err := c.SendRequest("PROTOCOLINFO")
+ if err != nil {
+ return nil, err
+ }
+ // Check data vals
+ ret := &ProtocolInfo{RawResponse: resp}
+ for _, piece := range resp.Data {
+ key, val, ok := torutil.PartitionString(piece, ' ')
+ if !ok {
+ continue
+ }
+ switch key {
+ case "PROTOCOLINFO":
+ if val != "1" {
+ return nil, c.protoErr("Invalid PIVERSION: %v", val)
+ }
+ case "AUTH":
+ methods, cookieFile, _ := torutil.PartitionString(val, ' ')
+ if !strings.HasPrefix(methods, "METHODS=") {
+ continue
+ }
+ if cookieFile != "" {
+ if !strings.HasPrefix(cookieFile, "COOKIEFILE=") {
+ continue
+ }
+ if ret.CookieFile, err = torutil.UnescapeSimpleQuotedString(cookieFile[11:]); err != nil {
+ continue
+ }
+ }
+ ret.AuthMethods = strings.Split(methods[8:], ",")
+ case "VERSION":
+ torVersion, _, _ := torutil.PartitionString(val, ' ')
+ if strings.HasPrefix(torVersion, "Tor=") {
+ ret.TorVersion, err = torutil.UnescapeSimpleQuotedString(torVersion[4:])
+ }
+ }
+ }
+ return ret, nil
+}
diff --git a/vendor/github.com/cretz/bine/control/cmd_stream.go b/vendor/github.com/cretz/bine/control/cmd_stream.go
new file mode 100644
index 0000000..6fde0a8
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/cmd_stream.go
@@ -0,0 +1,31 @@
+package control
+
+import (
+ "strconv"
+)
+
+// AttachStream invokes ATTACHSTREAM.
+func (c *Conn) AttachStream(streamID string, circuitID string, hopNum int) error {
+ if circuitID == "" {
+ circuitID = "0"
+ }
+ cmd := "ATTACHSTREAM " + streamID + " " + circuitID
+ if hopNum > 0 {
+ cmd += " HOP=" + strconv.Itoa(hopNum)
+ }
+ return c.sendRequestIgnoreResponse(cmd)
+}
+
+// RedirectStream invokes REDIRECTSTREAM.
+func (c *Conn) RedirectStream(streamID string, address string, port int) error {
+ cmd := "REDIRECTSTREAM " + streamID + " " + address
+ if port > 0 {
+ cmd += " " + strconv.Itoa(port)
+ }
+ return c.sendRequestIgnoreResponse(cmd)
+}
+
+// CloseStream invokes CLOSESTREAM.
+func (c *Conn) CloseStream(streamID string, reason string) error {
+ return c.sendRequestIgnoreResponse("CLOSESTREAM %v %v", streamID, reason)
+}
diff --git a/vendor/github.com/cretz/bine/control/conn.go b/vendor/github.com/cretz/bine/control/conn.go
new file mode 100644
index 0000000..02995e8
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/conn.go
@@ -0,0 +1,102 @@
+package control
+
+import (
+ "fmt"
+ "io"
+ "net/textproto"
+ "sync"
+)
+
+// Conn is the connection to the Tor control port.
+type Conn struct {
+ // DebugWriter is the writer that debug logs for this library (not Tor
+ // itself) will be written to. If nil, no debug logs are generated/written.
+ DebugWriter io.Writer
+
+ // This is the underlying connection.
+ conn *textproto.Conn
+
+ // This is set lazily by ProtocolInfo().
+ protocolInfo *ProtocolInfo
+
+ // True if Authenticate has been called successfully.
+ Authenticated bool
+
+ // The lock fot eventListeners
+ eventListenersLock sync.RWMutex
+ // The value slices can be traversed outside of lock, they are completely
+ // replaced on change, never mutated. But the map itself must be locked on
+ // when reading or writing.
+ eventListeners map[EventCode][]chan<- Event
+
+ // This mutex is locked on when an entire response needs to be read. It
+ // helps synchronize accesses to the response by the asynchronous response
+ // listeners and the synchronous responses.
+ readLock sync.Mutex
+}
+
+// NewConn creates a Conn from the given textproto connection.
+func NewConn(conn *textproto.Conn) *Conn {
+ return &Conn{
+ conn: conn,
+ eventListeners: map[EventCode][]chan<- Event{},
+ }
+}
+
+func (c *Conn) sendRequestIgnoreResponse(format string, args ...interface{}) error {
+ _, err := c.SendRequest(format, args...)
+ return err
+}
+
+// SendRequest sends a synchronous request to Tor and awaits the response. If
+// the response errors, the error result will be set, but the response will be
+// set also. This is usually not directly used by callers, but instead called by
+// higher-level methods.
+func (c *Conn) SendRequest(format string, args ...interface{}) (*Response, error) {
+ if c.debugEnabled() {
+ c.debugf("Write line: %v", fmt.Sprintf(format, args...))
+ }
+ id, err := c.conn.Cmd(format, args...)
+ if err != nil {
+ return nil, err
+ }
+ c.readLock.Lock()
+ defer c.readLock.Unlock()
+ c.conn.StartResponse(id)
+ defer c.conn.EndResponse(id)
+ // Get the first non-async response
+ var resp *Response
+ for {
+ if resp, err = c.ReadResponse(); err != nil || !resp.IsAsync() {
+ break
+ }
+ c.relayAsyncEvents(resp)
+ }
+ if err == nil && !resp.IsOk() {
+ err = resp.Err
+ }
+ return resp, err
+}
+
+// Close sends a QUIT and closes the underlying Tor connection. This does not
+// error if the QUIT is not accepted but does relay any error that occurs while
+// closing the underlying connection.
+func (c *Conn) Close() error {
+ // Ignore the response and ignore the error
+ c.Quit()
+ return c.conn.Close()
+}
+
+func (c *Conn) debugEnabled() bool {
+ return c.DebugWriter != nil
+}
+
+func (c *Conn) debugf(format string, args ...interface{}) {
+ if w := c.DebugWriter; w != nil {
+ fmt.Fprintf(w, format+"\n", args...)
+ }
+}
+
+func (*Conn) protoErr(format string, args ...interface{}) textproto.ProtocolError {
+ return textproto.ProtocolError(fmt.Sprintf(format, args...))
+}
diff --git a/vendor/github.com/cretz/bine/control/doc.go b/vendor/github.com/cretz/bine/control/doc.go
new file mode 100644
index 0000000..2be3dc1
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/doc.go
@@ -0,0 +1,10 @@
+// Package control implements a low-level client for the Tor control spec
+// version 1.
+//
+// The primary entrypoint is the Conn struct, instantiated with NewConn. This is
+// the low-level layer to the control port of an already-running Tor instance.
+// Most developers will prefer the tor package adjacent to this one for a higher
+// level abstraction over the process and this connection.
+//
+// Some of this code is lifted from https://github.com/yawning/bulb with thanks.
+package control
diff --git a/vendor/github.com/cretz/bine/control/keyval.go b/vendor/github.com/cretz/bine/control/keyval.go
new file mode 100644
index 0000000..00cefd8
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/keyval.go
@@ -0,0 +1,40 @@
+package control
+
+// KeyVal is a simple key-value struct. In cases where Val can be nil, an empty
+// string represents that unless ValSetAndEmpty is true.
+type KeyVal struct {
+ // Key is the always-present key
+ Key string
+
+ // Val is the value. If it's an empty string and nils are accepted/supported
+ // where this is used, it means nil unless ValSetAndEmpty is true.
+ Val string
+
+ // ValSetAndEmpty is true when Val is an empty string, the associated
+ // command supports nils, and Val should NOT be treated as nil. False
+ // otherwise.
+ ValSetAndEmpty bool
+}
+
+// NewKeyVal creates a new key-value pair.
+func NewKeyVal(key string, val string) *KeyVal {
+ return &KeyVal{Key: key, Val: val}
+}
+
+// KeyVals creates multiple new key-value pairs from the given strings. The
+// provided set of strings must have a length that is a multiple of 2.
+func KeyVals(keysAndVals ...string) []*KeyVal {
+ if len(keysAndVals)%2 != 0 {
+ panic("Expected multiple of 2")
+ }
+ ret := make([]*KeyVal, len(keysAndVals)/2)
+ for i := 0; i < len(ret); i++ {
+ ret[i] = NewKeyVal(keysAndVals[i*2], keysAndVals[i*2+1])
+ }
+ return ret
+}
+
+// ValSet returns true if Val is either non empty or ValSetAndEmpty is true.
+func (k *KeyVal) ValSet() bool {
+ return len(k.Val) > 0 || k.ValSetAndEmpty
+}
diff --git a/vendor/github.com/cretz/bine/control/response.go b/vendor/github.com/cretz/bine/control/response.go
new file mode 100644
index 0000000..705905e
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/response.go
@@ -0,0 +1,106 @@
+package control
+
+import (
+ "net/textproto"
+ "strconv"
+ "strings"
+)
+
+// Response is a response to a control port command or an asynchronous event.
+type Response struct {
+ // Err is the status code and string representation associated with a
+ // response. Responses that have completed successfully will also have Err
+ // set to indicate such.
+ Err *textproto.Error
+
+ // Reply is the text on the EndReplyLine of the response.
+ Reply string
+
+ // Data is the MidReplyLines/DataReplyLines of the response. Dot encoded
+ // data is "decoded" and presented as a single string (terminal ".CRLF"
+ // removed, all intervening CRs stripped).
+ Data []string
+
+ // RawLines is all of the lines of a response, without CRLFs.
+ RawLines []string
+}
+
+// IsOk returns true if the response status code indicates success or an
+// asynchronous event.
+func (r *Response) IsOk() bool {
+ switch r.Err.Code {
+ case StatusOk, StatusOkUnnecessary, StatusAsyncEvent:
+ return true
+ default:
+ return false
+ }
+}
+
+// DataWithReply returns a combination of Data and Reply to give a full set of
+// the lines of the response.
+func (r *Response) DataWithReply() []string {
+ ret := make([]string, len(r.Data)+1)
+ copy(ret, r.Data)
+ ret[len(ret)-1] = r.Reply
+ return ret
+}
+
+// IsAsync returns true if the response is an asynchronous event.
+func (r *Response) IsAsync() bool {
+ return r.Err.Code == StatusAsyncEvent
+}
+
+// ReadResponse returns the next response object.
+func (c *Conn) ReadResponse() (*Response, error) {
+ var resp *Response
+ var statusCode int
+ for {
+ line, err := c.conn.ReadLine()
+ if err != nil {
+ return nil, err
+ }
+ c.debugf("Read line: %v", line)
+
+ // Parse the line that was just read.
+ if len(line) < 4 {
+ return nil, c.protoErr("Truncated response: %v", line)
+ }
+ if code, err := strconv.Atoi(line[0:3]); err != nil || code < 100 {
+ return nil, c.protoErr("Invalid status code: %v", line[0:3])
+ } else if resp == nil {
+ resp = &Response{}
+ statusCode = code
+ } else if code != statusCode {
+ // The status code should stay fixed for all lines of the response, since events can't be interleaved with
+ // response lines.
+ return nil, c.protoErr("Status code changed: %v != %v", code, statusCode)
+ }
+ resp.RawLines = append(resp.RawLines, line)
+ switch line[3] {
+ case ' ':
+ // Final line in the response.
+ resp.Reply = line[4:]
+ resp.Err = statusCodeToError(statusCode, resp.Reply)
+ return resp, nil
+ case '-':
+ // Continuation, keep reading.
+ resp.Data = append(resp.Data, line[4:])
+ case '+':
+ // A "dot-encoded" payload follows.
+ dotBody, err := c.conn.ReadDotBytes()
+ if err != nil {
+ return nil, err
+ }
+ dotBodyStr := strings.TrimRight(string(dotBody), "\n\r")
+ // c.debugf("Read dot body:\n---\n%v\n---", dotBodyStr)
+ resp.Data = append(resp.Data, line[4:]+"\r\n"+dotBodyStr)
+ dotLines := strings.Split(dotBodyStr, "\n")
+ for _, dotLine := range dotLines[:len(dotLines)-1] {
+ resp.RawLines = append(resp.RawLines, dotLine)
+ }
+ resp.RawLines = append(resp.RawLines, ".")
+ default:
+ return nil, c.protoErr("Invalid separator: '%v'", line[3])
+ }
+ }
+}
diff --git a/vendor/github.com/cretz/bine/control/status.go b/vendor/github.com/cretz/bine/control/status.go
new file mode 100644
index 0000000..8be3464
--- /dev/null
+++ b/vendor/github.com/cretz/bine/control/status.go
@@ -0,0 +1,64 @@
+package control
+
+import (
+ "fmt"
+ "net/textproto"
+ "strings"
+)
+
+// The various control port StatusCode constants.
+const (
+ StatusOk = 250
+ StatusOkUnnecessary = 251
+
+ StatusErrResourceExhausted = 451
+ StatusErrSyntaxError = 500
+ StatusErrUnrecognizedCmd = 510
+ StatusErrUnimplementedCmd = 511
+ StatusErrSyntaxErrorArg = 512
+ StatusErrUnrecognizedCmdArg = 513
+ StatusErrAuthenticationRequired = 514
+ StatusErrBadAuthentication = 515
+ StatusErrUnspecifiedTorError = 550
+ StatusErrInternalError = 551
+ StatusErrUnrecognizedEntity = 552
+ StatusErrInvalidConfigValue = 553
+ StatusErrInvalidDescriptor = 554
+ StatusErrUnmanagedEntity = 555
+
+ StatusAsyncEvent = 650
+)
+
+var statusCodeStringMap = map[int]string{
+ StatusOk: "OK",
+ StatusOkUnnecessary: "Operation was unnecessary",
+
+ StatusErrResourceExhausted: "Resource exhausted",
+ StatusErrSyntaxError: "Syntax error: protocol",
+ StatusErrUnrecognizedCmd: "Unrecognized command",
+ StatusErrUnimplementedCmd: "Unimplemented command",
+ StatusErrSyntaxErrorArg: "Syntax error in command argument",
+ StatusErrUnrecognizedCmdArg: "Unrecognized command argument",
+ StatusErrAuthenticationRequired: "Authentication required",
+ StatusErrBadAuthentication: "Bad authentication",
+ StatusErrUnspecifiedTorError: "Unspecified Tor error",
+ StatusErrInternalError: "Internal error",
+ StatusErrUnrecognizedEntity: "Unrecognized entity",
+ StatusErrInvalidConfigValue: "Invalid configuration value",
+ StatusErrInvalidDescriptor: "Invalid descriptor",
+ StatusErrUnmanagedEntity: "Unmanaged entity",
+
+ StatusAsyncEvent: "Asynchronous event notification",
+}
+
+func statusCodeToError(code int, reply string) *textproto.Error {
+ err := new(textproto.Error)
+ err.Code = code
+ if msg, ok := statusCodeStringMap[code]; ok {
+ trimmedReply := strings.TrimSpace(strings.TrimPrefix(reply, msg))
+ err.Msg = fmt.Sprintf("%s: %s", msg, trimmedReply)
+ } else {
+ err.Msg = fmt.Sprintf("Unknown status code (%03d): %s", code, reply)
+ }
+ return err
+}
diff --git a/vendor/github.com/cretz/bine/process/process.go b/vendor/github.com/cretz/bine/process/process.go
new file mode 100644
index 0000000..88c6389
--- /dev/null
+++ b/vendor/github.com/cretz/bine/process/process.go
@@ -0,0 +1,82 @@
+// Package process is the low-level abstraction for a Tor instance.
+//
+// The standard use is to create a Creator with NewCreator and the path to the
+// Tor executable. The child package 'embedded' can be used if Tor is statically
+// linked in the binary. Most developers will prefer the tor package adjacent to
+// this one for a higher level abstraction over the process and control port
+// connection.
+package process
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+
+ "github.com/cretz/bine/torutil"
+)
+
+// Process is the interface implemented by Tor processes.
+type Process interface {
+ // Start starts the Tor process in the background and returns. It is
+ // analagous to os/exec.Cmd.Start.
+ Start() error
+ // Wait waits for the Tor process to exit and returns error if it was not a
+ // successful exit. It is analagous to os/exec.Cmd.Wait.
+ Wait() error
+ // ControlConn is used for statically linked, embedded processes to create
+ // a controller connection. For non-embedded processes or Tor versions that
+ // don't support embedded control connections, ErrControlConnUnsupported is
+ // returned. Note, this should only be called once per process before
+ // Start, and the connection does not need to be closed.
+ EmbeddedControlConn() (net.Conn, error)
+}
+
+// Creator is the interface for process creation.
+type Creator interface {
+ New(ctx context.Context, args ...string) (Process, error)
+}
+
+type CmdCreatorFunc func(ctx context.Context, args ...string) (*exec.Cmd, error)
+
+// NewCreator creates a Creator for external Tor process execution based on the
+// given exe path.
+func NewCreator(exePath string) Creator {
+ return CmdCreatorFunc(func(ctx context.Context, args ...string) (*exec.Cmd, error) {
+ cmd := exec.CommandContext(ctx, exePath, args...)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ return cmd, nil
+ })
+}
+
+type exeProcess struct {
+ *exec.Cmd
+}
+
+func (c CmdCreatorFunc) New(ctx context.Context, args ...string) (Process, error) {
+ cmd, err := c(ctx, args...)
+ return &exeProcess{cmd}, err
+}
+
+// ErrControlConnUnsupported is returned by Process.EmbeddedControlConn when
+// it is unsupported.
+var ErrControlConnUnsupported = fmt.Errorf("Control conn not supported")
+
+func (e *exeProcess) EmbeddedControlConn() (net.Conn, error) {
+ return nil, ErrControlConnUnsupported
+}
+
+// ControlPortFromFileContents reads a control port file that is written by Tor
+// when ControlPortWriteToFile is set.
+func ControlPortFromFileContents(contents string) (int, error) {
+ contents = strings.TrimSpace(contents)
+ _, port, ok := torutil.PartitionString(contents, ':')
+ if !ok || !strings.HasPrefix(contents, "PORT=") {
+ return 0, fmt.Errorf("Invalid port format: %v", contents)
+ }
+ return strconv.Atoi(port)
+}
diff --git a/vendor/github.com/cretz/bine/tor/dialer.go b/vendor/github.com/cretz/bine/tor/dialer.go
new file mode 100644
index 0000000..9ef211e
--- /dev/null
+++ b/vendor/github.com/cretz/bine/tor/dialer.go
@@ -0,0 +1,111 @@
+package tor
+
+import (
+ "context"
+ "fmt"
+ "net"
+ "strings"
+
+ "golang.org/x/net/proxy"
+)
+
+// Dialer is a wrapper around a proxy.Dialer for dialing connections.
+type Dialer struct {
+ proxy.Dialer
+}
+
+// DialConf is the configuration used for Dialer.
+type DialConf struct {
+ // ProxyAddress is the address for the SOCKS5 proxy. If empty, it is looked
+ // up.
+ ProxyAddress string
+
+ // ProxyNetwork is the network for the SOCKS5 proxy. If ProxyAddress is
+ // empty, this value is ignored and overridden by what is looked up. If this
+ // is empty and ProxyAddress is not empty, it defaults to "tcp".
+ ProxyNetwork string
+
+ // ProxyAuth is the auth for the proxy. Since Tor's SOCKS5 proxy is
+ // unauthenticated, this is rarely needed. It can be used when
+ // IsolateSOCKSAuth is set to ensure separate circuits.
+ //
+ // This should not be confused with downstream SOCKS proxy authentication
+ // which is set via Tor values for Socks5ProxyUsername and
+ // Socks5ProxyPassword when Socks5Proxy is set.
+ ProxyAuth *proxy.Auth
+
+ // SkipEnableNetwork, if true, will skip the enable network step in Dialer.
+ SkipEnableNetwork bool
+
+ // Forward is the dialer to forward to. If nil, just uses normal net dialer.
+ Forward proxy.Dialer
+}
+
+// Dialer creates a new Dialer for the given configuration. Context can be nil.
+// If conf is nil, a default is used.
+func (t *Tor) Dialer(ctx context.Context, conf *DialConf) (*Dialer, error) {
+ if ctx == nil {
+ ctx = context.Background()
+ }
+ if conf == nil {
+ conf = &DialConf{}
+ }
+ // Enable the network if requested
+ if !conf.SkipEnableNetwork {
+ if err := t.EnableNetwork(ctx, true); err != nil {
+ return nil, err
+ }
+ }
+ // Lookup proxy address as needed
+ proxyNetwork := conf.ProxyNetwork
+ proxyAddress := conf.ProxyAddress
+ if proxyAddress == "" {
+ info, err := t.Control.GetInfo("net/listeners/socks")
+ if err != nil {
+ return nil, err
+ }
+ if len(info) != 1 || info[0].Key != "net/listeners/socks" {
+ return nil, fmt.Errorf("Unable to get socks proxy address")
+ }
+ proxyAddress = info[0].Val
+ if strings.HasPrefix(proxyAddress, "unix:") {
+ proxyAddress = proxyAddress[5:]
+ proxyNetwork = "unix"
+ } else {
+ proxyNetwork = "tcp"
+ }
+ } else if proxyNetwork == "" {
+ proxyNetwork = "tcp"
+ }
+
+ dialer, err := proxy.SOCKS5(proxyNetwork, proxyAddress, conf.ProxyAuth, conf.Forward)
+ if err != nil {
+ return nil, err
+ }
+ return &Dialer{dialer}, nil
+}
+
+// DialContext is the equivalent of net.DialContext.
+//
+// TODO: Remove when https://github.com/golang/go/issues/17759 is released.
+func (d *Dialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
+ errCh := make(chan error, 1)
+ connCh := make(chan net.Conn, 1)
+ go func() {
+ if conn, err := d.Dial(network, addr); err != nil {
+ errCh <- err
+ } else if ctx.Err() != nil {
+ conn.Close()
+ } else {
+ connCh <- conn
+ }
+ }()
+ select {
+ case err := <-errCh:
+ return nil, err
+ case conn := <-connCh:
+ return conn, nil
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ }
+}
diff --git a/vendor/github.com/cretz/bine/tor/doc.go b/vendor/github.com/cretz/bine/tor/doc.go
new file mode 100644
index 0000000..a58086a
--- /dev/null
+++ b/vendor/github.com/cretz/bine/tor/doc.go
@@ -0,0 +1,7 @@
+// Package tor is the high-level client for Tor.
+//
+// The Tor type is a combination of a Tor instance and a connection to it.
+// Use Start to create Tor. Then Dialer or Listener can be used.
+//
+// Some of this code is lifted from https://github.com/yawning/bulb with thanks.
+package tor
diff --git a/vendor/github.com/cretz/bine/tor/listen.go b/vendor/github.com/cretz/bine/tor/listen.go
new file mode 100644
index 0000000..6b142d2
--- /dev/null
+++ b/vendor/github.com/cretz/bine/tor/listen.go
@@ -0,0 +1,343 @@
+package tor
+
+import (
+ "context"
+ "crypto"
+ "crypto/rsa"
+ "fmt"
+ "net"
+ "strconv"
+
+ "github.com/cretz/bine/control"
+ "github.com/cretz/bine/torutil/ed25519"
+ othered25519 "golang.org/x/crypto/ed25519"
+)
+
+// OnionService implements net.Listener and net.Addr for an onion service.
+type OnionService struct {
+ // ID is the service ID for this onion service.
+ ID string
+
+ // Key is the private key for this service. It is either the set key, the
+ // generated key, or nil if asked to discard the key. If present, it is
+ // *crypto/rsa.PrivateKey (1024 bit) when Version3 is false or
+ // github.com/cretz/bine/torutil/ed25519.KeyPair when Version3 is true.
+ Key crypto.PrivateKey
+
+ // Version3 says whether or not this service is a V3 service.
+ Version3 bool
+
+ // ClientAuths is the credential set for clients. The keys are username and
+ // the values are credentials. The credentials will always be present even
+ // if Tor had to generate them.
+ ClientAuths map[string]string
+
+ // LocalListener is the local TCP listener. This is always present.
+ LocalListener net.Listener
+
+ // RemotePorts is the set of remote ports that are forwarded to the local
+ // listener. This will always have at least one value.
+ RemotePorts []int
+
+ // CloseLocalListenerOnClose is true if the local listener should be closed
+ // on Close. This is set to true if a listener was created by Listen and set
+ // to false of an existing LocalListener was provided to Listen.
+ CloseLocalListenerOnClose bool
+
+ // The Tor object that created this. Needed for Close.
+ Tor *Tor
+}
+
+// ListenConf is the configuration for Listen calls.
+type ListenConf struct {
+ // LocalPort is the local port to create a TCP listener on. If the port is
+ // 0, it is automatically chosen. This is ignored if LocalListener is set.
+ LocalPort int
+
+ // LocalListener is the specific local listener to back the onion service.
+ // If this is nil (the default), then a listener is created with LocalPort.
+ LocalListener net.Listener
+
+ // RemotePorts are the remote ports to serve the onion service on. If empty,
+ // it is the same as the local port or local listener. This must have at
+ // least one value if the local listener is not a *net.TCPListener.
+ RemotePorts []int
+
+ // Key is the private key to use. If not present, a key is generated based
+ // on whether Version3 is true or false. If present, it must be a
+ // *crypto/rsa.PrivateKey (1024 bit), a
+ // github.com/cretz/bine/torutil/ed25519.KeyPair, a
+ // golang.org/x/crypto/ed25519.PrivateKey, or a
+ // github.com/cretz/bine/control.Key.
+ Key crypto.PrivateKey
+
+ // Version3 determines whether, when Key is nil, a version 2 or version 3
+ // service/key will be generated. If true it is version 3 (an ed25519 key
+ // and v3 onion service) and if false it is version 2 (a RSA-1024 key and v2
+ // onion service). If Key is not nil, this value is ignored.
+ Version3 bool
+
+ // ClientAuths is the set of usernames and credentials for client
+ // authentication. The keys are usernames and the values are credentials. If
+ // a username is present but the credential is empty, a credential is
+ // generated by Tor for that user. If this is empty there is no
+ // authentication.
+ ClientAuths map[string]string
+
+ // MaxStreams is the maximum number of streams the service will accept. 0
+ // means unlimited.
+ MaxStreams int
+
+ // DiscardKey, if true and Key is nil (meaning a private key is generated),
+ // tells Tor not to return the generated private key. This value is ignored
+ // if Key is not nil.
+ DiscardKey bool
+
+ // Detach, if true, prevents the default behavior of the onion service being
+ // deleted when this controller connection is closed.
+ Detach bool
+
+ // NonAnonymous must be true if Tor options HiddenServiceSingleHopMode and
+ // HiddenServiceNonAnonymousMode are set. Otherwise, it must be false.
+ NonAnonymous bool
+
+ // MaxStreamsCloseCircuit determines whether to close the circuit when the
+ // maximum number of streams is exceeded. If true, the circuit is closed. If
+ // false, the stream is simply not connected but the circuit stays open.
+ MaxStreamsCloseCircuit bool
+
+ // NoWait if true will not wait until the onion service is published. If
+ // false, the network will be enabled if it's not and then we will wait
+ // until the onion service is published.
+ NoWait bool
+}
+
+// Listen creates an onion service and local listener. The context can be nil.
+// If conf is nil, the default struct value is used. Note, if this errors, any
+// listeners created here are closed but if a LocalListener is provided it may remain open.
+func (t *Tor) Listen(ctx context.Context, conf *ListenConf) (*OnionService, error) {
+ if ctx == nil {
+ ctx = context.Background()
+ }
+ // Create the service up here and make sure we close it no matter the error within
+ svc := &OnionService{Tor: t, CloseLocalListenerOnClose: conf.LocalListener == nil}
+ var err error
+
+ // Create the local listener if necessary
+ svc.LocalListener = conf.LocalListener
+ if svc.LocalListener == nil {
+ if svc.LocalListener, err = net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(conf.LocalPort)); err != nil {
+ return nil, err
+ }
+ }
+
+ // Henceforth, any error requires we close the svc
+
+ // Build the onion request
+ req := &control.AddOnionRequest{MaxStreams: conf.MaxStreams, ClientAuths: conf.ClientAuths}
+ // Set flags
+ if conf.DiscardKey {
+ req.Flags = append(req.Flags, "DiscardPK")
+ }
+ if conf.Detach {
+ req.Flags = append(req.Flags, "Detach")
+ }
+ if len(conf.ClientAuths) > 0 {
+ req.Flags = append(req.Flags, "BasicAuth")
+ }
+ if conf.NonAnonymous {
+ req.Flags = append(req.Flags, "NonAnonymous")
+ }
+ if conf.MaxStreamsCloseCircuit {
+ req.Flags = append(req.Flags, "MaxStreamsCloseCircuit")
+ }
+ // Set the key
+ switch key := conf.Key.(type) {
+ case nil:
+ svc.Version3 = conf.Version3
+ if conf.Version3 {
+ req.Key = control.GenKey(control.KeyAlgoED25519V3)
+ } else {
+ req.Key = control.GenKey(control.KeyAlgoRSA1024)
+ }
+ case control.GenKey:
+ svc.Version3 = conf.Version3
+ req.Key = key
+ case *rsa.PrivateKey:
+ svc.Key = key
+ svc.Version3 = false
+ if key.N == nil || key.N.BitLen() != 1024 {
+ err = fmt.Errorf("RSA key must be 1024 bits")
+ } else {
+ req.Key = &control.RSAKey{PrivateKey: key}
+ }
+ case *control.RSAKey:
+ svc.Key = key.PrivateKey
+ svc.Version3 = false
+ if key.N == nil || key.N.BitLen() != 1024 {
+ err = fmt.Errorf("RSA key must be 1024 bits")
+ } else {
+ req.Key = key
+ }
+ case ed25519.KeyPair:
+ svc.Key = key
+ svc.Version3 = true
+ req.Key = &control.ED25519Key{key}
+ case othered25519.PrivateKey:
+ properKey := ed25519.FromCryptoPrivateKey(key)
+ svc.Key = properKey
+ svc.Version3 = true
+ req.Key = &control.ED25519Key{properKey}
+ case *control.ED25519Key:
+ svc.Key = key.KeyPair
+ svc.Version3 = true
+ req.Key = key
+ default:
+ err = fmt.Errorf("Unrecognized key type: %T", key)
+ }
+
+ // Apply the remote ports
+ if err == nil {
+ if len(conf.RemotePorts) == 0 {
+ tcpAddr, ok := svc.LocalListener.Addr().(*net.TCPAddr)
+ if !ok {
+ err = fmt.Errorf("Unable to derive local TCP port")
+ } else {
+ svc.RemotePorts = []int{tcpAddr.Port}
+ }
+ } else {
+ svc.RemotePorts = make([]int, len(conf.RemotePorts))
+ copy(svc.RemotePorts, conf.RemotePorts)
+ }
+ }
+ // Apply the local ports with the remote ports
+ if err == nil {
+ localAddr := svc.LocalListener.Addr().String()
+ if _, ok := svc.LocalListener.(*net.UnixListener); ok {
+ localAddr = "unix:" + localAddr
+ }
+ for _, remotePort := range svc.RemotePorts {
+ req.Ports = append(req.Ports, &control.KeyVal{Key: strconv.Itoa(remotePort), Val: localAddr})
+ }
+ }
+
+ // Create the onion service
+ var resp *control.AddOnionResponse
+ if err == nil {
+ resp, err = t.Control.AddOnion(req)
+ }
+
+ // Apply the response to the service
+ if err == nil {
+ svc.ID = resp.ServiceID
+ switch key := resp.Key.(type) {
+ case nil:
+ // Do nothing
+ case *control.RSAKey:
+ svc.Key = key.PrivateKey
+ case *control.ED25519Key:
+ svc.Key = key.KeyPair
+ default:
+ err = fmt.Errorf("Unrecognized result key type: %T", key)
+ }
+ // Client auths are the conf and then overridden by results
+ if len(conf.ClientAuths) > 0 {
+ svc.ClientAuths = make(map[string]string, len(conf.ClientAuths))
+ for k, v := range conf.ClientAuths {
+ svc.ClientAuths[k] = v
+ }
+ for k, v := range resp.ClientAuths {
+ svc.ClientAuths[k] = v
+ }
+ }
+ }
+
+ // Wait if necessary
+ if err == nil && !conf.NoWait {
+ t.Debugf("Enabling network before waiting for publication")
+ // First make sure network is enabled
+ if err = t.EnableNetwork(ctx, true); err == nil {
+ t.Debugf("Waiting for publication")
+ // Now we'll take a similar approach to Stem. Several UPLOADs are sent out, so we count em. If we see
+ // UPLOADED, we succeeded. If we see failed, we count those. If there are as many failures as uploads, they
+ // all failed and it's a failure. NOTE: unlike Stem's comments that say they don't, we are actually seeing
+ // the service IDs for UPLOADED so we don't keep a map.
+ uploadsAttempted := 0
+ failures := []string{}
+ _, err = t.Control.EventWait(ctx, []control.EventCode{control.EventCodeHSDesc},
+ func(evt control.Event) (bool, error) {
+ hs, _ := evt.(*control.HSDescEvent)
+ if hs != nil && hs.Address == svc.ID {
+ switch hs.Action {
+ case "UPLOAD":
+ uploadsAttempted++
+ case "FAILED":
+ failures = append(failures,
+ fmt.Sprintf("Failed uploading to dir %v - reason: %v", hs.HSDir, hs.Reason))
+ if len(failures) == uploadsAttempted {
+ return false, fmt.Errorf("Failed all uploads, reasons: %v", failures)
+ }
+ case "UPLOADED":
+ return true, nil
+ }
+ }
+ return false, nil
+ })
+ }
+ }
+
+ // Give back err and close if there is an err
+ if err != nil {
+ if closeErr := svc.Close(); closeErr != nil {
+ err = fmt.Errorf("Error on listen: %v (also got error trying to close: %v)", err, closeErr)
+ }
+ return nil, err
+ }
+ return svc, nil
+}
+
+// Accept implements net.Listener.Accept.
+func (o *OnionService) Accept() (net.Conn, error) {
+ return o.LocalListener.Accept()
+}
+
+// Addr implements net.Listener.Addr just returning this object.
+func (o *OnionService) Addr() net.Addr {
+ return o
+}
+
+// Network implements net.Addr.Network always returning "tcp".
+func (o *OnionService) Network() string {
+ return "tcp"
+}
+
+// String implements net.Addr.String and returns "<serviceID>.onion:<virtport>".
+func (o *OnionService) String() string {
+ return fmt.Sprintf("%v.onion:%v", o.ID, o.RemotePorts[0])
+}
+
+// Close implements net.Listener.Close and deletes the onion service and closes
+// the LocalListener if CloseLocalListenerOnClose is true.
+func (o *OnionService) Close() (err error) {
+ o.Tor.Debugf("Closing onion %v", o)
+ // Delete the onion first
+ if o.ID != "" {
+ err = o.Tor.Control.DelOnion(o.ID)
+ o.ID = ""
+ }
+ // Now if the local one needs to be closed, do it
+ if o.CloseLocalListenerOnClose && o.LocalListener != nil {
+ if closeErr := o.LocalListener.Close(); closeErr != nil {
+ if err != nil {
+ err = fmt.Errorf("Unable to close onion: %v (also unable to close local listener: %v)", err, closeErr)
+ } else {
+ err = closeErr
+ }
+ }
+ o.LocalListener = nil
+ }
+ if err != nil {
+ o.Tor.Debugf("Failed closing onion: %v", err)
+ }
+ return
+}
diff --git a/vendor/github.com/cretz/bine/tor/log.go b/vendor/github.com/cretz/bine/tor/log.go
new file mode 100644
index 0000000..b53f318
--- /dev/null
+++ b/vendor/github.com/cretz/bine/tor/log.go
@@ -0,0 +1,16 @@
+package tor
+
+import "fmt"
+
+// DebugEnabled returns true if there is a DebugWriter.
+func (t *Tor) DebugEnabled() bool {
+ return t.DebugWriter != nil
+}
+
+// Debugf writes the formatted string with a newline appended to the DebugWriter
+// if present.
+func (t *Tor) Debugf(format string, args ...interface{}) {
+ if w := t.DebugWriter; w != nil {
+ fmt.Fprintf(w, format+"\n", args...)
+ }
+}
diff --git a/vendor/github.com/cretz/bine/tor/tor.go b/vendor/github.com/cretz/bine/tor/tor.go
new file mode 100644
index 0000000..0edd241
--- /dev/null
+++ b/vendor/github.com/cretz/bine/tor/tor.go
@@ -0,0 +1,453 @@
+package tor
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/textproto"
+ "os"
+ "path/filepath"
+ "strconv"
+ "time"
+
+ "github.com/cretz/bine/control"
+
+ "github.com/cretz/bine/process"
+)
+
+// Tor is the wrapper around the Tor process and control port connection. It
+// should be created with Start and developers should always call Close when
+// done.
+type Tor struct {
+ // Process is the Tor instance that is running.
+ Process process.Process
+
+ // Control is the Tor controller connection.
+ Control *control.Conn
+
+ // ProcessCancelFunc is the context cancellation func for the Tor process.
+ // It is used by Close and should not be called directly. This can be nil.
+ ProcessCancelFunc context.CancelFunc
+
+ // ControlPort is the port that Control is connected on. It is 0 if the
+ // connection is an embedded control connection.
+ ControlPort int
+
+ // DataDir is the path to the data directory that Tor is using.
+ DataDir string
+
+ // DeleteDataDirOnClose is true if, when Close is invoked, the entire
+ // directory will be deleted.
+ DeleteDataDirOnClose bool
+
+ // DebugWriter is the writer used for debug logs, or nil if debug logs
+ // should not be emitted.
+ DebugWriter io.Writer
+
+ // StopProcessOnClose, if true, will attempt to halt the process on close.
+ StopProcessOnClose bool
+
+ // GeoIPCreatedFile is the path, relative to DataDir, that was created from
+ // StartConf.GeoIPFileReader. It is empty if no file was created.
+ GeoIPCreatedFile string
+
+ // GeoIPv6CreatedFile is the path, relative to DataDir, that was created
+ // from StartConf.GeoIPFileReader. It is empty if no file was created.
+ GeoIPv6CreatedFile string
+}
+
+// StartConf is the configuration used for Start when starting a Tor instance. A
+// default instance with no fields set is the default used for Start.
+type StartConf struct {
+ // ExePath is the path to the Tor executable. If it is not present, "tor" is
+ // used either locally or on the PATH. This is ignored if ProcessCreator is
+ // set.
+ ExePath string
+
+ // ProcessCreator is the override to use a specific process creator. If set,
+ // ExePath is ignored.
+ ProcessCreator process.Creator
+
+ // UseEmbeddedControlConn can be set to true to use
+ // process.Process.EmbeddedControlConn() instead of creating a connection
+ // via ControlPort. Note, this only works when ProcessCreator is an
+ // embedded Tor creator with version >= 0.3.5.x.
+ UseEmbeddedControlConn bool
+
+ // ControlPort is the port to use for the Tor controller. If it is 0, Tor
+ // picks a port for use. This is ignored if UseEmbeddedControlConn is true.
+ ControlPort int
+
+ // DataDir is the directory used by Tor. If it is empty, a temporary
+ // directory is created in TempDataDirBase.
+ DataDir string
+
+ // TempDataDirBase is the parent directory that a temporary data directory
+ // will be created under for use by Tor. This is ignored if DataDir is not
+ // empty. If empty it is assumed to be the current working directory.
+ TempDataDirBase string
+
+ // RetainTempDataDir, if true, will not set the created temporary data
+ // directory to be deleted on close. This is ignored if DataDir is not
+ // empty.
+ RetainTempDataDir bool
+
+ // DisableCookieAuth, if true, will not use the default SAFECOOKIE
+ // authentication mechanism for the Tor controller.
+ DisableCookieAuth bool
+
+ // DisableEagerAuth, if true, will not authenticate on Start.
+ DisableEagerAuth bool
+
+ // EnableNetwork, if true, will connect to the wider Tor network on start.
+ EnableNetwork bool
+
+ // ExtraArgs is the set of extra args passed to the Tor instance when
+ // started.
+ ExtraArgs []string
+
+ // TorrcFile is the torrc file to set on start. If empty, a blank torrc is
+ // created in the data directory and is used instead.
+ TorrcFile string
+
+ // DebugWriter is the writer to use for debug logs, or nil for no debug
+ // logs.
+ DebugWriter io.Writer
+
+ // NoHush if true does not set --hush. By default --hush is set.
+ NoHush bool
+
+ // NoAutoSocksPort if true does not set "--SocksPort auto" as is done by
+ // default. This means the caller could set their own or just let it
+ // default to 9050.
+ NoAutoSocksPort bool
+
+ // GeoIPReader, if present, is called before start to copy geo IP files to
+ // the data directory. Errors are propagated. If the ReadCloser is present,
+ // it is copied to the data dir, overwriting as necessary, and then closed
+ // and the appropriate command line argument is added to reference it. If
+ // both the ReadCloser and error are nil, no copy or command line argument
+ // is used for that version. This is called twice, once with false and once
+ // with true for ipv6.
+ //
+ // This can be set to torutil/geoipembed.GeoIPReader to use an embedded
+ // source.
+ GeoIPFileReader func(ipv6 bool) (io.ReadCloser, error)
+}
+
+// Start a Tor instance and connect to it. If ctx is nil, context.Background()
+// is used. If conf is nil, a default instance is used.
+func Start(ctx context.Context, conf *StartConf) (*Tor, error) {
+ if ctx == nil {
+ ctx = context.Background()
+ }
+ if conf == nil {
+ conf = &StartConf{}
+ }
+ tor := &Tor{DataDir: conf.DataDir, DebugWriter: conf.DebugWriter, StopProcessOnClose: true}
+ // Create the data dir and make it absolute
+ if tor.DataDir == "" {
+ tempBase := conf.TempDataDirBase
+ if tempBase == "" {
+ tempBase = "."
+ }
+ var err error
+ if tempBase, err = filepath.Abs(tempBase); err != nil {
+ return nil, err
+ }
+ if tor.DataDir, err = ioutil.TempDir(tempBase, "data-dir-"); err != nil {
+ return nil, fmt.Errorf("Unable to create temp data dir: %v", err)
+ }
+ tor.Debugf("Created temp data directory at: %v", tor.DataDir)
+ tor.DeleteDataDirOnClose = !conf.RetainTempDataDir
+ } else if err := os.MkdirAll(tor.DataDir, 0700); err != nil {
+ return nil, fmt.Errorf("Unable to create data dir: %v", err)
+ }
+
+ // !!!! From this point on, we must close tor if we error !!!!
+
+ // Copy geoip stuff if necessary
+ err := tor.copyGeoIPFiles(conf)
+ // Start tor
+ if err == nil {
+ err = tor.startProcess(ctx, conf)
+ }
+ // Connect the controller
+ if err == nil {
+ err = tor.connectController(ctx, conf)
+ }
+ // Attempt eager auth w/ no password
+ if err == nil && !conf.DisableEagerAuth {
+ err = tor.Control.Authenticate("")
+ }
+ // If there was an error, we have to try to close here but it may leave the process open
+ if err != nil {
+ if closeErr := tor.Close(); closeErr != nil {
+ err = fmt.Errorf("Error on start: %v (also got error trying to close: %v)", err, closeErr)
+ }
+ }
+ return tor, err
+}
+
+func (t *Tor) copyGeoIPFiles(conf *StartConf) error {
+ if conf.GeoIPFileReader == nil {
+ return nil
+ }
+ if r, err := conf.GeoIPFileReader(false); err != nil {
+ return fmt.Errorf("Unable to read geoip file: %v", err)
+ } else if r != nil {
+ t.GeoIPCreatedFile = "geoip"
+ if err := createFile(filepath.Join(t.DataDir, "geoip"), r); err != nil {
+ return fmt.Errorf("Unable to create geoip file: %v", err)
+ }
+ }
+ if r, err := conf.GeoIPFileReader(true); err != nil {
+ return fmt.Errorf("Unable to read geoip6 file: %v", err)
+ } else if r != nil {
+ t.GeoIPv6CreatedFile = "geoip6"
+ if err := createFile(filepath.Join(t.DataDir, "geoip6"), r); err != nil {
+ return fmt.Errorf("Unable to create geoip6 file: %v", err)
+ }
+ }
+ return nil
+}
+
+func createFile(to string, from io.ReadCloser) error {
+ f, err := os.Create(to)
+ if err == nil {
+ _, err = io.Copy(f, from)
+ if closeErr := f.Close(); err == nil {
+ err = closeErr
+ }
+ }
+ if closeErr := from.Close(); err == nil {
+ err = closeErr
+ }
+ return err
+}
+
+func (t *Tor) startProcess(ctx context.Context, conf *StartConf) error {
+ // Get the creator
+ creator := conf.ProcessCreator
+ if creator == nil {
+ torPath := conf.ExePath
+ if torPath == "" {
+ torPath = "tor"
+ }
+ creator = process.NewCreator(torPath)
+ }
+ // Build the args
+ args := []string{"--DataDirectory", t.DataDir}
+ if !conf.DisableCookieAuth {
+ args = append(args, "--CookieAuthentication", "1")
+ }
+ if !conf.EnableNetwork {
+ args = append(args, "--DisableNetwork", "1")
+ }
+ if !conf.NoHush {
+ args = append(args, "--hush")
+ }
+ if !conf.NoAutoSocksPort {
+ args = append(args, "--SocksPort", "auto")
+ }
+ if t.GeoIPCreatedFile != "" {
+ args = append(args, "--GeoIPFile", filepath.Join(t.DataDir, t.GeoIPCreatedFile))
+ }
+ if t.GeoIPv6CreatedFile != "" {
+ args = append(args, "--GeoIPv6File", filepath.Join(t.DataDir, t.GeoIPv6CreatedFile))
+ }
+ // If there is no Torrc file, create a blank temp one
+ torrcFileName := conf.TorrcFile
+ if torrcFileName == "" {
+ torrcFile, err := ioutil.TempFile(t.DataDir, "torrc-")
+ if err != nil {
+ return err
+ }
+ torrcFileName = torrcFile.Name()
+ if err = torrcFile.Close(); err != nil {
+ return err
+ }
+ }
+ args = append(args, "-f", torrcFileName)
+ // Create file for Tor to write the control port to if it's not told to us and we're not embedded
+ var controlPortFileName string
+ var err error
+ if !conf.UseEmbeddedControlConn {
+ if conf.ControlPort == 0 {
+ controlPortFile, err := ioutil.TempFile(t.DataDir, "control-port-")
+ if err != nil {
+ return err
+ }
+ controlPortFileName = controlPortFile.Name()
+ if err = controlPortFile.Close(); err != nil {
+ return err
+ }
+ args = append(args, "--ControlPort", "auto", "--ControlPortWriteToFile", controlPortFile.Name())
+ } else {
+ args = append(args, "--ControlPort", strconv.Itoa(conf.ControlPort))
+ }
+ }
+ // Create process creator with args
+ var processCtx context.Context
+ processCtx, t.ProcessCancelFunc = context.WithCancel(ctx)
+ args = append(args, conf.ExtraArgs...)
+ p, err := creator.New(processCtx, args...)
+ if err != nil {
+ return err
+ }
+ // Use the embedded conn if requested
+ if conf.UseEmbeddedControlConn {
+ t.Debugf("Using embedded control connection")
+ conn, err := p.EmbeddedControlConn()
+ if err != nil {
+ return fmt.Errorf("Unable to get embedded control conn: %v", err)
+ }
+ t.Control = control.NewConn(textproto.NewConn(conn))
+ t.Control.DebugWriter = t.DebugWriter
+ }
+ // Start process with the args
+ t.Debugf("Starting tor with args %v", args)
+ if err = p.Start(); err != nil {
+ return err
+ }
+ t.Process = p
+ // If not embedded, try a few times to read the control port file if we need to
+ if !conf.UseEmbeddedControlConn {
+ t.ControlPort = conf.ControlPort
+ if t.ControlPort == 0 {
+ ControlPortCheck:
+ for i := 0; i < 10; i++ {
+ select {
+ case <-ctx.Done():
+ err = ctx.Err()
+ break ControlPortCheck
+ default:
+ // Try to read the controlport file, or wait a bit
+ var byts []byte
+ if byts, err = ioutil.ReadFile(controlPortFileName); err != nil {
+ break ControlPortCheck
+ } else if t.ControlPort, err = process.ControlPortFromFileContents(string(byts)); err == nil {
+ break ControlPortCheck
+ }
+ time.Sleep(200 * time.Millisecond)
+ }
+ }
+ if err != nil {
+ return fmt.Errorf("Unable to read control port file: %v", err)
+ }
+ }
+ }
+ return nil
+}
+
+func (t *Tor) connectController(ctx context.Context, conf *StartConf) error {
+ // This doesn't apply if already connected (e.g. using embedded conn)
+ if t.Control != nil {
+ return nil
+ }
+ t.Debugf("Connecting to control port %v", t.ControlPort)
+ textConn, err := textproto.Dial("tcp", "127.0.0.1:"+strconv.Itoa(t.ControlPort))
+ if err != nil {
+ return err
+ }
+ t.Control = control.NewConn(textConn)
+ t.Control.DebugWriter = t.DebugWriter
+ return nil
+}
+
+// EnableNetwork sets DisableNetwork to 0 and optionally waits for bootstrap to
+// complete. The context can be nil. If DisableNetwork isnt 1, this does
+// nothing.
+func (t *Tor) EnableNetwork(ctx context.Context, wait bool) error {
+ if ctx == nil {
+ ctx = context.Background()
+ }
+ // Only enable if DisableNetwork is 1
+ if vals, err := t.Control.GetConf("DisableNetwork"); err != nil {
+ return err
+ } else if len(vals) == 0 || vals[0].Key != "DisableNetwork" || vals[0].Val != "1" {
+ return nil
+ }
+ // Enable the network
+ if err := t.Control.SetConf(control.KeyVals("DisableNetwork", "0")...); err != nil {
+ return nil
+ }
+ // If not waiting, leave
+ if !wait {
+ return nil
+ }
+ // Wait for progress to hit 100
+ _, err := t.Control.EventWait(ctx, []control.EventCode{control.EventCodeStatusClient},
+ func(evt control.Event) (bool, error) {
+ if status, _ := evt.(*control.StatusEvent); status != nil && status.Action == "BOOTSTRAP" {
+ if status.Severity == "NOTICE" && status.Arguments["PROGRESS"] == "100" {
+ return true, nil
+ } else if status.Severity == "ERR" {
+ return false, fmt.Errorf("Failing bootstrapping, Tor warning: %v", status.Arguments["WARNING"])
+ }
+ }
+ return false, nil
+ })
+ return err
+}
+
+// Close sends a halt to the Tor process if it can, closes the controller
+// connection, and stops the process.
+func (t *Tor) Close() error {
+ t.Debugf("Closing Tor")
+ errs := []error{}
+ // If controller is authenticated, send the quit signal to the process. Otherwise, just close the controller.
+ sentHalt := false
+ if t.Control != nil {
+ if t.Control.Authenticated && t.StopProcessOnClose {
+ if err := t.Control.Signal("HALT"); err != nil {
+ errs = append(errs, fmt.Errorf("Unable to signal halt: %v", err))
+ } else {
+ sentHalt = true
+ }
+ }
+ // Now close the controller
+ if err := t.Control.Close(); err != nil {
+ errs = append(errs, fmt.Errorf("Unable to close contrlller: %v", err))
+ } else {
+ t.Control = nil
+ }
+ }
+ if t.Process != nil {
+ // If we didn't halt, we have to force kill w/ the cancel func
+ if !sentHalt && t.StopProcessOnClose {
+ t.ProcessCancelFunc()
+ }
+ // Wait for a bit to make sure it stopped
+ errCh := make(chan error, 1)
+ var waitErr error
+ go func() { errCh <- t.Process.Wait() }()
+ select {
+ case waitErr = <-errCh:
+ if waitErr != nil {
+ errs = append(errs, fmt.Errorf("Process wait failed: %v", waitErr))
+ }
+ case <-time.After(300 * time.Millisecond):
+ errs = append(errs, fmt.Errorf("Process did not exit after 300 ms"))
+ }
+ if waitErr == nil {
+ t.Process = nil
+ }
+ }
+ // Get rid of the entire data dir
+ if t.DeleteDataDirOnClose {
+ if err := os.RemoveAll(t.DataDir); err != nil {
+ errs = append(errs, fmt.Errorf("Failed to remove data dir %v: %v", t.DataDir, err))
+ }
+ }
+ // Combine the errors if present
+ if len(errs) == 0 {
+ return nil
+ } else if len(errs) == 1 {
+ t.Debugf("Error while closing Tor: %v", errs[0])
+ return errs[0]
+ }
+ t.Debugf("Errors while closing Tor: %v", errs)
+ return fmt.Errorf("Got %v errors while closing - %v", len(errs), errs)
+}
diff --git a/vendor/github.com/cretz/bine/torutil/doc.go b/vendor/github.com/cretz/bine/torutil/doc.go
new file mode 100644
index 0000000..4f0528d
--- /dev/null
+++ b/vendor/github.com/cretz/bine/torutil/doc.go
@@ -0,0 +1,2 @@
+// Package torutil has generic utilities shared across the library.
+package torutil
diff --git a/vendor/github.com/cretz/bine/torutil/ed25519/ed25519.go b/vendor/github.com/cretz/bine/torutil/ed25519/ed25519.go
new file mode 100644
index 0000000..c27378f
--- /dev/null
+++ b/vendor/github.com/cretz/bine/torutil/ed25519/ed25519.go
@@ -0,0 +1,189 @@
+// Package ed25519 implements Tor/BitTorrent-like ed25519 keys.
+//
+// See the following stack overflow post for details on why
+// golang.org/x/crypto/ed25519 can't be used:
+// https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different
+package ed25519
+
+import (
+ "crypto"
+ "crypto/rand"
+ "crypto/sha512"
+ "errors"
+ "io"
+
+ "github.com/cretz/bine/torutil/ed25519/internal/edwards25519"
+ "golang.org/x/crypto/ed25519"
+)
+
+const (
+ // PublicKeySize is the size, in bytes, of public keys as used in this package.
+ PublicKeySize = 32
+ // PrivateKeySize is the size, in bytes, of private keys as used in this package.
+ PrivateKeySize = 64
+ // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
+ SignatureSize = 64
+)
+
+// PrivateKey is a 64-byte Ed25519 private key. Unlike
+// golang.org/x/crypto/ed25519, this is just the digest and does not contain
+// the public key within it. Instead call PublicKey() or better, call KeyPair()
+// which stores the precomputed public key.
+type PrivateKey []byte
+
+// PublicKey is a 32-byte Ed25519 public key.
+type PublicKey []byte
+
+// FromCryptoPrivateKey converts a Go private key to the one in this package.
+func FromCryptoPrivateKey(key ed25519.PrivateKey) KeyPair {
+ digest := sha512.Sum512(key[:32])
+ digest[0] &= 248
+ digest[31] &= 127
+ digest[31] |= 64
+ return &precomputedKeyPair{PrivateKeyBytes: digest[:], PublicKeyBytes: PublicKey(key[32:])}
+}
+
+// FromCryptoPublicKey converts a Go public key to the one in this package.
+func FromCryptoPublicKey(key ed25519.PublicKey) PublicKey {
+ return PublicKey(key)
+}
+
+// KeyPair returns a new key pair with the public key precomputed.
+func (p PrivateKey) KeyPair() KeyPair {
+ return &precomputedKeyPair{PrivateKeyBytes: p, PublicKeyBytes: p.PublicKey()}
+}
+
+// PrivateKey simply returns itself. Implements KeyPair.PrivateKey.
+func (p PrivateKey) PrivateKey() PrivateKey { return p }
+
+// Public simply delegates to PublicKey() to satisfy crypto.Signer. This method
+// does a bit more work than the traditional Go ed25519's private key's Public()
+// method so developers are encouraged to reuse the result or use KeyPair()
+// which stores this value.
+func (p PrivateKey) Public() crypto.PublicKey { return p.PublicKey() }
+
+// PublicKey generates a public key for this private key. This method does a bit
+// more work than the traditional Go ed25519's private key's Public() method so
+// developers are encouraged to reuse the result or use KeyPair() which stores
+// this value. Implements KeyPair.PublicKey.
+func (p PrivateKey) PublicKey() PublicKey {
+ var A edwards25519.ExtendedGroupElement
+ var hBytes [32]byte
+ copy(hBytes[:], p[:])
+ edwards25519.GeScalarMultBase(&A, &hBytes)
+ var publicKeyBytes [32]byte
+ A.ToBytes(&publicKeyBytes)
+ return publicKeyBytes[:]
+}
+
+// Sign signs the given message with priv. Ed25519 performs two passes over
+// messages to be signed and therefore cannot handle pre-hashed messages. Thus
+// opts.HashFunc() must return zero to indicate the message hasn't been hashed.
+// This can be achieved by passing crypto.Hash(0) as the value for opts.
+func (p PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
+ if opts.HashFunc() != crypto.Hash(0) {
+ return nil, errors.New("ed25519: cannot sign hashed message")
+ }
+ return Sign(p, message), nil
+}
+
+// Verify simply calls PublicKey().Verify(). Callers are encouraged to instead
+// store a precomputed KeyPair (via KeyPair() or GenerateKey()) and call Verify
+// on that.
+func (p PrivateKey) Verify(message []byte, sig []byte) bool {
+ return p.PublicKey().Verify(message, sig)
+}
+
+// Verify simply calls the package-level function Verify().
+func (p PublicKey) Verify(message []byte, sig []byte) bool {
+ return Verify(p, message, sig)
+}
+
+// KeyPair is an interface for types with both keys. While PrivateKey does
+// implement this, it generates the PublicKey on demand. For better performance,
+// use the result of GenerateKey directly or call PrivateKey.KeyPair().
+type KeyPair interface {
+ crypto.Signer
+ PrivateKey() PrivateKey
+ PublicKey() PublicKey
+ Verify(message []byte, sig []byte) bool
+}
+
+type precomputedKeyPair struct {
+ PrivateKeyBytes PrivateKey
+ PublicKeyBytes PublicKey
+}
+
+func (p *precomputedKeyPair) PrivateKey() PrivateKey { return p.PrivateKeyBytes }
+func (p *precomputedKeyPair) PublicKey() PublicKey { return p.PublicKeyBytes }
+func (p *precomputedKeyPair) Public() crypto.PublicKey { return p.PublicKey() }
+func (p *precomputedKeyPair) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) ([]byte, error) {
+ if opts.HashFunc() != crypto.Hash(0) {
+ return nil, errors.New("ed25519: cannot sign hashed message")
+ }
+ return Sign(p, message), nil
+}
+func (p *precomputedKeyPair) Verify(message []byte, sig []byte) bool {
+ return p.PublicKeyBytes.Verify(message, sig)
+}
+
+// GenerateKey generates a public/private key pair using entropy from rand.
+// If rand is nil, crypto/rand.Reader will be used.
+func GenerateKey(rnd io.Reader) (KeyPair, error) {
+ if rnd == nil {
+ rnd = rand.Reader
+ }
+ rndByts := make([]byte, 32)
+ if _, err := io.ReadFull(rnd, rndByts); err != nil {
+ return nil, err
+ }
+ digest := sha512.Sum512(rndByts)
+ digest[0] &= 248
+ digest[31] &= 127
+ digest[31] |= 64
+ return PrivateKey(digest[:]).KeyPair(), nil
+}
+
+// Sign signs the message with the given key pair.
+func Sign(keyPair KeyPair, message []byte) []byte {
+ // Ref: https://stackoverflow.com/questions/44810708/ed25519-public-result-is-different
+
+ var privateKeyA [32]byte
+ copy(privateKeyA[:], keyPair.PrivateKey()) // we need this in an array later
+ var messageDigest, hramDigest [64]byte
+
+ h := sha512.New()
+ h.Write(keyPair.PrivateKey()[32:])
+ h.Write(message)
+ h.Sum(messageDigest[:0])
+
+ var messageDigestReduced [32]byte
+ edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
+ var R edwards25519.ExtendedGroupElement
+ edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
+
+ var encodedR [32]byte
+ R.ToBytes(&encodedR)
+
+ h.Reset()
+ h.Write(encodedR[:])
+ h.Write(keyPair.PublicKey())
+ h.Write(message)
+ h.Sum(hramDigest[:0])
+ var hramDigestReduced [32]byte
+ edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
+
+ var s [32]byte
+ edwards25519.ScMulAdd(&s, &hramDigestReduced, &privateKeyA, &messageDigestReduced)
+
+ signature := make([]byte, 64)
+ copy(signature[:], encodedR[:])
+ copy(signature[32:], s[:])
+
+ return signature
+}
+
+// Verify verifies a signed message.
+func Verify(p PublicKey, message []byte, sig []byte) bool {
+ return ed25519.Verify(ed25519.PublicKey(p), message, sig)
+}
diff --git a/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/README.md b/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/README.md
new file mode 100644
index 0000000..f8ffca5
--- /dev/null
+++ b/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/README.md
@@ -0,0 +1 @@
+This is taken from https://github.com/golang/crypto/tree/1a580b3eff7814fc9b40602fd35256c63b50f491/ed25519/internal/edwards25519 \ No newline at end of file
diff --git a/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/const.go b/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/const.go
new file mode 100644
index 0000000..e39f086
--- /dev/null
+++ b/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/const.go
@@ -0,0 +1,1422 @@
+// Copyright 2016 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.
+
+package edwards25519
+
+// These values are from the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// d is a constant in the Edwards curve equation.
+var d = FieldElement{
+ -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116,
+}
+
+// d2 is 2*d.
+var d2 = FieldElement{
+ -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199,
+}
+
+// SqrtM1 is the square-root of -1 in the field.
+var SqrtM1 = FieldElement{
+ -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482,
+}
+
+// A is a constant in the Montgomery-form of curve25519.
+var A = FieldElement{
+ 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+}
+
+// bi contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var bi = [8]PreComputedGroupElement{
+ {
+ FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+ FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+ FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+ },
+ {
+ FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+ FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+ FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+ },
+ {
+ FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+ FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+ FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+ },
+ {
+ FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+ FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+ FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+ },
+ {
+ FieldElement{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877},
+ FieldElement{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951},
+ FieldElement{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784},
+ },
+ {
+ FieldElement{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436},
+ FieldElement{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918},
+ FieldElement{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877},
+ },
+ {
+ FieldElement{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800},
+ FieldElement{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305},
+ FieldElement{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300},
+ },
+ {
+ FieldElement{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876},
+ FieldElement{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619},
+ FieldElement{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683},
+ },
+}
+
+// base contains precomputed multiples of the base-point. See the Ed25519 paper
+// for a discussion about how these values are used.
+var base = [32][8]PreComputedGroupElement{
+ {
+ {
+ FieldElement{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
+ FieldElement{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
+ FieldElement{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546},
+ },
+ {
+ FieldElement{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303},
+ FieldElement{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081},
+ FieldElement{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697},
+ },
+ {
+ FieldElement{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
+ FieldElement{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
+ FieldElement{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357},
+ },
+ {
+ FieldElement{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540},
+ FieldElement{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397},
+ FieldElement{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325},
+ },
+ {
+ FieldElement{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
+ FieldElement{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
+ FieldElement{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942},
+ },
+ {
+ FieldElement{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777},
+ FieldElement{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737},
+ FieldElement{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652},
+ },
+ {
+ FieldElement{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
+ FieldElement{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
+ FieldElement{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300},
+ },
+ {
+ FieldElement{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726},
+ FieldElement{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955},
+ FieldElement{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425},
+ },
+ },
+ {
+ {
+ FieldElement{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171},
+ FieldElement{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510},
+ FieldElement{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660},
+ },
+ {
+ FieldElement{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639},
+ FieldElement{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963},
+ FieldElement{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950},
+ },
+ {
+ FieldElement{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568},
+ FieldElement{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335},
+ FieldElement{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628},
+ },
+ {
+ FieldElement{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007},
+ FieldElement{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772},
+ FieldElement{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653},
+ },
+ {
+ FieldElement{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567},
+ FieldElement{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686},
+ FieldElement{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372},
+ },
+ {
+ FieldElement{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887},
+ FieldElement{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954},
+ FieldElement{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953},
+ },
+ {
+ FieldElement{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833},
+ FieldElement{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532},
+ FieldElement{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876},
+ },
+ {
+ FieldElement{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268},
+ FieldElement{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214},
+ FieldElement{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038},
+ },
+ },
+ {
+ {
+ FieldElement{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800},
+ FieldElement{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645},
+ FieldElement{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664},
+ },
+ {
+ FieldElement{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933},
+ FieldElement{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182},
+ FieldElement{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222},
+ },
+ {
+ FieldElement{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991},
+ FieldElement{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880},
+ FieldElement{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092},
+ },
+ {
+ FieldElement{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295},
+ FieldElement{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788},
+ FieldElement{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553},
+ },
+ {
+ FieldElement{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026},
+ FieldElement{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347},
+ FieldElement{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033},
+ },
+ {
+ FieldElement{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395},
+ FieldElement{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278},
+ FieldElement{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890},
+ },
+ {
+ FieldElement{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995},
+ FieldElement{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596},
+ FieldElement{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891},
+ },
+ {
+ FieldElement{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060},
+ FieldElement{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608},
+ FieldElement{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606},
+ },
+ },
+ {
+ {
+ FieldElement{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389},
+ FieldElement{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016},
+ FieldElement{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341},
+ },
+ {
+ FieldElement{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505},
+ FieldElement{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553},
+ FieldElement{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655},
+ },
+ {
+ FieldElement{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220},
+ FieldElement{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631},
+ FieldElement{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099},
+ },
+ {
+ FieldElement{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556},
+ FieldElement{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749},
+ FieldElement{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930},
+ },
+ {
+ FieldElement{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391},
+ FieldElement{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253},
+ FieldElement{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066},
+ },
+ {
+ FieldElement{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958},
+ FieldElement{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082},
+ FieldElement{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383},
+ },
+ {
+ FieldElement{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521},
+ FieldElement{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807},
+ FieldElement{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948},
+ },
+ {
+ FieldElement{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134},
+ FieldElement{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455},
+ FieldElement{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629},
+ },
+ },
+ {
+ {
+ FieldElement{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069},
+ FieldElement{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746},
+ FieldElement{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919},
+ },
+ {
+ FieldElement{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837},
+ FieldElement{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906},
+ FieldElement{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771},
+ },
+ {
+ FieldElement{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817},
+ FieldElement{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098},
+ FieldElement{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409},
+ },
+ {
+ FieldElement{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504},
+ FieldElement{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727},
+ FieldElement{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420},
+ },
+ {
+ FieldElement{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003},
+ FieldElement{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605},
+ FieldElement{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384},
+ },
+ {
+ FieldElement{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701},
+ FieldElement{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683},
+ FieldElement{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708},
+ },
+ {
+ FieldElement{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563},
+ FieldElement{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260},
+ FieldElement{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387},
+ },
+ {
+ FieldElement{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672},
+ FieldElement{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686},
+ FieldElement{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665},
+ },
+ },
+ {
+ {
+ FieldElement{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182},
+ FieldElement{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277},
+ FieldElement{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628},
+ },
+ {
+ FieldElement{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474},
+ FieldElement{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539},
+ FieldElement{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822},
+ },
+ {
+ FieldElement{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970},
+ FieldElement{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756},
+ FieldElement{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508},
+ },
+ {
+ FieldElement{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683},
+ FieldElement{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655},
+ FieldElement{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158},
+ },
+ {
+ FieldElement{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125},
+ FieldElement{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839},
+ FieldElement{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664},
+ },
+ {
+ FieldElement{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294},
+ FieldElement{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899},
+ FieldElement{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070},
+ },
+ {
+ FieldElement{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294},
+ FieldElement{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949},
+ FieldElement{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083},
+ },
+ {
+ FieldElement{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420},
+ FieldElement{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940},
+ FieldElement{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396},
+ },
+ },
+ {
+ {
+ FieldElement{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567},
+ FieldElement{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127},
+ FieldElement{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294},
+ },
+ {
+ FieldElement{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887},
+ FieldElement{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964},
+ FieldElement{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195},
+ },
+ {
+ FieldElement{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244},
+ FieldElement{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999},
+ FieldElement{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762},
+ },
+ {
+ FieldElement{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274},
+ FieldElement{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236},
+ FieldElement{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605},
+ },
+ {
+ FieldElement{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761},
+ FieldElement{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884},
+ FieldElement{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482},
+ },
+ {
+ FieldElement{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638},
+ FieldElement{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490},
+ FieldElement{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170},
+ },
+ {
+ FieldElement{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736},
+ FieldElement{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124},
+ FieldElement{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392},
+ },
+ {
+ FieldElement{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029},
+ FieldElement{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048},
+ FieldElement{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958},
+ },
+ },
+ {
+ {
+ FieldElement{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593},
+ FieldElement{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071},
+ FieldElement{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692},
+ },
+ {
+ FieldElement{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687},
+ FieldElement{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441},
+ FieldElement{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001},
+ },
+ {
+ FieldElement{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460},
+ FieldElement{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007},
+ FieldElement{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762},
+ },
+ {
+ FieldElement{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005},
+ FieldElement{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674},
+ FieldElement{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035},
+ },
+ {
+ FieldElement{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590},
+ FieldElement{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957},
+ FieldElement{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812},
+ },
+ {
+ FieldElement{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740},
+ FieldElement{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122},
+ FieldElement{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158},
+ },
+ {
+ FieldElement{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885},
+ FieldElement{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140},
+ FieldElement{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857},
+ },
+ {
+ FieldElement{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155},
+ FieldElement{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260},
+ FieldElement{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483},
+ },
+ },
+ {
+ {
+ FieldElement{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677},
+ FieldElement{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815},
+ FieldElement{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751},
+ },
+ {
+ FieldElement{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203},
+ FieldElement{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208},
+ FieldElement{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230},
+ },
+ {
+ FieldElement{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850},
+ FieldElement{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389},
+ FieldElement{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968},
+ },
+ {
+ FieldElement{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689},
+ FieldElement{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880},
+ FieldElement{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304},
+ },
+ {
+ FieldElement{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632},
+ FieldElement{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412},
+ FieldElement{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566},
+ },
+ {
+ FieldElement{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038},
+ FieldElement{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232},
+ FieldElement{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943},
+ },
+ {
+ FieldElement{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856},
+ FieldElement{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738},
+ FieldElement{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971},
+ },
+ {
+ FieldElement{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718},
+ FieldElement{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697},
+ FieldElement{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883},
+ },
+ },
+ {
+ {
+ FieldElement{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912},
+ FieldElement{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358},
+ FieldElement{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849},
+ },
+ {
+ FieldElement{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307},
+ FieldElement{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977},
+ FieldElement{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335},
+ },
+ {
+ FieldElement{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644},
+ FieldElement{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616},
+ FieldElement{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735},
+ },
+ {
+ FieldElement{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099},
+ FieldElement{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341},
+ FieldElement{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336},
+ },
+ {
+ FieldElement{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646},
+ FieldElement{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425},
+ FieldElement{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388},
+ },
+ {
+ FieldElement{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743},
+ FieldElement{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822},
+ FieldElement{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462},
+ },
+ {
+ FieldElement{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985},
+ FieldElement{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702},
+ FieldElement{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797},
+ },
+ {
+ FieldElement{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293},
+ FieldElement{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100},
+ FieldElement{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688},
+ },
+ },
+ {
+ {
+ FieldElement{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186},
+ FieldElement{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610},
+ FieldElement{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707},
+ },
+ {
+ FieldElement{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220},
+ FieldElement{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025},
+ FieldElement{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044},
+ },
+ {
+ FieldElement{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992},
+ FieldElement{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027},
+ FieldElement{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197},
+ },
+ {
+ FieldElement{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901},
+ FieldElement{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952},
+ FieldElement{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878},
+ },
+ {
+ FieldElement{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390},
+ FieldElement{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730},
+ FieldElement{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730},
+ },
+ {
+ FieldElement{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180},
+ FieldElement{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272},
+ FieldElement{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715},
+ },
+ {
+ FieldElement{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970},
+ FieldElement{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772},
+ FieldElement{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865},
+ },
+ {
+ FieldElement{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750},
+ FieldElement{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373},
+ FieldElement{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348},
+ },
+ },
+ {
+ {
+ FieldElement{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144},
+ FieldElement{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195},
+ FieldElement{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086},
+ },
+ {
+ FieldElement{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684},
+ FieldElement{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518},
+ FieldElement{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233},
+ },
+ {
+ FieldElement{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793},
+ FieldElement{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794},
+ FieldElement{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435},
+ },
+ {
+ FieldElement{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921},
+ FieldElement{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518},
+ FieldElement{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563},
+ },
+ {
+ FieldElement{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278},
+ FieldElement{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024},
+ FieldElement{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030},
+ },
+ {
+ FieldElement{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783},
+ FieldElement{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717},
+ FieldElement{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844},
+ },
+ {
+ FieldElement{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333},
+ FieldElement{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048},
+ FieldElement{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760},
+ },
+ {
+ FieldElement{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760},
+ FieldElement{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757},
+ FieldElement{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112},
+ },
+ },
+ {
+ {
+ FieldElement{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468},
+ FieldElement{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184},
+ FieldElement{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289},
+ },
+ {
+ FieldElement{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066},
+ FieldElement{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882},
+ FieldElement{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226},
+ },
+ {
+ FieldElement{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101},
+ FieldElement{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279},
+ FieldElement{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811},
+ },
+ {
+ FieldElement{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709},
+ FieldElement{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714},
+ FieldElement{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121},
+ },
+ {
+ FieldElement{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464},
+ FieldElement{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847},
+ FieldElement{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400},
+ },
+ {
+ FieldElement{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414},
+ FieldElement{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158},
+ FieldElement{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045},
+ },
+ {
+ FieldElement{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415},
+ FieldElement{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459},
+ FieldElement{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079},
+ },
+ {
+ FieldElement{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412},
+ FieldElement{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743},
+ FieldElement{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836},
+ },
+ },
+ {
+ {
+ FieldElement{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022},
+ FieldElement{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429},
+ FieldElement{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065},
+ },
+ {
+ FieldElement{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861},
+ FieldElement{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000},
+ FieldElement{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101},
+ },
+ {
+ FieldElement{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815},
+ FieldElement{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642},
+ FieldElement{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966},
+ },
+ {
+ FieldElement{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574},
+ FieldElement{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742},
+ FieldElement{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689},
+ },
+ {
+ FieldElement{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020},
+ FieldElement{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772},
+ FieldElement{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982},
+ },
+ {
+ FieldElement{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953},
+ FieldElement{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218},
+ FieldElement{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265},
+ },
+ {
+ FieldElement{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073},
+ FieldElement{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325},
+ FieldElement{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798},
+ },
+ {
+ FieldElement{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870},
+ FieldElement{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863},
+ FieldElement{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927},
+ },
+ },
+ {
+ {
+ FieldElement{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267},
+ FieldElement{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663},
+ FieldElement{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862},
+ },
+ {
+ FieldElement{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673},
+ FieldElement{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943},
+ FieldElement{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020},
+ },
+ {
+ FieldElement{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238},
+ FieldElement{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064},
+ FieldElement{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795},
+ },
+ {
+ FieldElement{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052},
+ FieldElement{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904},
+ FieldElement{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531},
+ },
+ {
+ FieldElement{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979},
+ FieldElement{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841},
+ FieldElement{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431},
+ },
+ {
+ FieldElement{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324},
+ FieldElement{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940},
+ FieldElement{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320},
+ },
+ {
+ FieldElement{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184},
+ FieldElement{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114},
+ FieldElement{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878},
+ },
+ {
+ FieldElement{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784},
+ FieldElement{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091},
+ FieldElement{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585},
+ },
+ },
+ {
+ {
+ FieldElement{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208},
+ FieldElement{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864},
+ FieldElement{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661},
+ },
+ {
+ FieldElement{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233},
+ FieldElement{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212},
+ FieldElement{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525},
+ },
+ {
+ FieldElement{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068},
+ FieldElement{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397},
+ FieldElement{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988},
+ },
+ {
+ FieldElement{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889},
+ FieldElement{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038},
+ FieldElement{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697},
+ },
+ {
+ FieldElement{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875},
+ FieldElement{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905},
+ FieldElement{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656},
+ },
+ {
+ FieldElement{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818},
+ FieldElement{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714},
+ FieldElement{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203},
+ },
+ {
+ FieldElement{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931},
+ FieldElement{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024},
+ FieldElement{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084},
+ },
+ {
+ FieldElement{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204},
+ FieldElement{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817},
+ FieldElement{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667},
+ },
+ },
+ {
+ {
+ FieldElement{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504},
+ FieldElement{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768},
+ FieldElement{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255},
+ },
+ {
+ FieldElement{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790},
+ FieldElement{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438},
+ FieldElement{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333},
+ },
+ {
+ FieldElement{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971},
+ FieldElement{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905},
+ FieldElement{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409},
+ },
+ {
+ FieldElement{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409},
+ FieldElement{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499},
+ FieldElement{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363},
+ },
+ {
+ FieldElement{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664},
+ FieldElement{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324},
+ FieldElement{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940},
+ },
+ {
+ FieldElement{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990},
+ FieldElement{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914},
+ FieldElement{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290},
+ },
+ {
+ FieldElement{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257},
+ FieldElement{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433},
+ FieldElement{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236},
+ },
+ {
+ FieldElement{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045},
+ FieldElement{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093},
+ FieldElement{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347},
+ },
+ },
+ {
+ {
+ FieldElement{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191},
+ FieldElement{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507},
+ FieldElement{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906},
+ },
+ {
+ FieldElement{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018},
+ FieldElement{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109},
+ FieldElement{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926},
+ },
+ {
+ FieldElement{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528},
+ FieldElement{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625},
+ FieldElement{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286},
+ },
+ {
+ FieldElement{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033},
+ FieldElement{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866},
+ FieldElement{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896},
+ },
+ {
+ FieldElement{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075},
+ FieldElement{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347},
+ FieldElement{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437},
+ },
+ {
+ FieldElement{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165},
+ FieldElement{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588},
+ FieldElement{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193},
+ },
+ {
+ FieldElement{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017},
+ FieldElement{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883},
+ FieldElement{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961},
+ },
+ {
+ FieldElement{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043},
+ FieldElement{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663},
+ FieldElement{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362},
+ },
+ },
+ {
+ {
+ FieldElement{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860},
+ FieldElement{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466},
+ FieldElement{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063},
+ },
+ {
+ FieldElement{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997},
+ FieldElement{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295},
+ FieldElement{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369},
+ },
+ {
+ FieldElement{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385},
+ FieldElement{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109},
+ FieldElement{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906},
+ },
+ {
+ FieldElement{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424},
+ FieldElement{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185},
+ FieldElement{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962},
+ },
+ {
+ FieldElement{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325},
+ FieldElement{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593},
+ FieldElement{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404},
+ },
+ {
+ FieldElement{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644},
+ FieldElement{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801},
+ FieldElement{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804},
+ },
+ {
+ FieldElement{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884},
+ FieldElement{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577},
+ FieldElement{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849},
+ },
+ {
+ FieldElement{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473},
+ FieldElement{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644},
+ FieldElement{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319},
+ },
+ },
+ {
+ {
+ FieldElement{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599},
+ FieldElement{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768},
+ FieldElement{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084},
+ },
+ {
+ FieldElement{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328},
+ FieldElement{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369},
+ FieldElement{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920},
+ },
+ {
+ FieldElement{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815},
+ FieldElement{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025},
+ FieldElement{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397},
+ },
+ {
+ FieldElement{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448},
+ FieldElement{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981},
+ FieldElement{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165},
+ },
+ {
+ FieldElement{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501},
+ FieldElement{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073},
+ FieldElement{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861},
+ },
+ {
+ FieldElement{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845},
+ FieldElement{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211},
+ FieldElement{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870},
+ },
+ {
+ FieldElement{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096},
+ FieldElement{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803},
+ FieldElement{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168},
+ },
+ {
+ FieldElement{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965},
+ FieldElement{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505},
+ FieldElement{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598},
+ },
+ },
+ {
+ {
+ FieldElement{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782},
+ FieldElement{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900},
+ FieldElement{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479},
+ },
+ {
+ FieldElement{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208},
+ FieldElement{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232},
+ FieldElement{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719},
+ },
+ {
+ FieldElement{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271},
+ FieldElement{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326},
+ FieldElement{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132},
+ },
+ {
+ FieldElement{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300},
+ FieldElement{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570},
+ FieldElement{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670},
+ },
+ {
+ FieldElement{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994},
+ FieldElement{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913},
+ FieldElement{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317},
+ },
+ {
+ FieldElement{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730},
+ FieldElement{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096},
+ FieldElement{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078},
+ },
+ {
+ FieldElement{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411},
+ FieldElement{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905},
+ FieldElement{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654},
+ },
+ {
+ FieldElement{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870},
+ FieldElement{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498},
+ FieldElement{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579},
+ },
+ },
+ {
+ {
+ FieldElement{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677},
+ FieldElement{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647},
+ FieldElement{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743},
+ },
+ {
+ FieldElement{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468},
+ FieldElement{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375},
+ FieldElement{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155},
+ },
+ {
+ FieldElement{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725},
+ FieldElement{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612},
+ FieldElement{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943},
+ },
+ {
+ FieldElement{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944},
+ FieldElement{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928},
+ FieldElement{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406},
+ },
+ {
+ FieldElement{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139},
+ FieldElement{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963},
+ FieldElement{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693},
+ },
+ {
+ FieldElement{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734},
+ FieldElement{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680},
+ FieldElement{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410},
+ },
+ {
+ FieldElement{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931},
+ FieldElement{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654},
+ FieldElement{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710},
+ },
+ {
+ FieldElement{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180},
+ FieldElement{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684},
+ FieldElement{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895},
+ },
+ },
+ {
+ {
+ FieldElement{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501},
+ FieldElement{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413},
+ FieldElement{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880},
+ },
+ {
+ FieldElement{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874},
+ FieldElement{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962},
+ FieldElement{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899},
+ },
+ {
+ FieldElement{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152},
+ FieldElement{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063},
+ FieldElement{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080},
+ },
+ {
+ FieldElement{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146},
+ FieldElement{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183},
+ FieldElement{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133},
+ },
+ {
+ FieldElement{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421},
+ FieldElement{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622},
+ FieldElement{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197},
+ },
+ {
+ FieldElement{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663},
+ FieldElement{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753},
+ FieldElement{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755},
+ },
+ {
+ FieldElement{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862},
+ FieldElement{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118},
+ FieldElement{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171},
+ },
+ {
+ FieldElement{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380},
+ FieldElement{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824},
+ FieldElement{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270},
+ },
+ },
+ {
+ {
+ FieldElement{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438},
+ FieldElement{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584},
+ FieldElement{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562},
+ },
+ {
+ FieldElement{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471},
+ FieldElement{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610},
+ FieldElement{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269},
+ },
+ {
+ FieldElement{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650},
+ FieldElement{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369},
+ FieldElement{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461},
+ },
+ {
+ FieldElement{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462},
+ FieldElement{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793},
+ FieldElement{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218},
+ },
+ {
+ FieldElement{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226},
+ FieldElement{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019},
+ FieldElement{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037},
+ },
+ {
+ FieldElement{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171},
+ FieldElement{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132},
+ FieldElement{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841},
+ },
+ {
+ FieldElement{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181},
+ FieldElement{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210},
+ FieldElement{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040},
+ },
+ {
+ FieldElement{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935},
+ FieldElement{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105},
+ FieldElement{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814},
+ },
+ },
+ {
+ {
+ FieldElement{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852},
+ FieldElement{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581},
+ FieldElement{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646},
+ },
+ {
+ FieldElement{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844},
+ FieldElement{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025},
+ FieldElement{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453},
+ },
+ {
+ FieldElement{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068},
+ FieldElement{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192},
+ FieldElement{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921},
+ },
+ {
+ FieldElement{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259},
+ FieldElement{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426},
+ FieldElement{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072},
+ },
+ {
+ FieldElement{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305},
+ FieldElement{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832},
+ FieldElement{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943},
+ },
+ {
+ FieldElement{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011},
+ FieldElement{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447},
+ FieldElement{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494},
+ },
+ {
+ FieldElement{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245},
+ FieldElement{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859},
+ FieldElement{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915},
+ },
+ {
+ FieldElement{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707},
+ FieldElement{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848},
+ FieldElement{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224},
+ },
+ },
+ {
+ {
+ FieldElement{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391},
+ FieldElement{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215},
+ FieldElement{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101},
+ },
+ {
+ FieldElement{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713},
+ FieldElement{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849},
+ FieldElement{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930},
+ },
+ {
+ FieldElement{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940},
+ FieldElement{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031},
+ FieldElement{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404},
+ },
+ {
+ FieldElement{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243},
+ FieldElement{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116},
+ FieldElement{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525},
+ },
+ {
+ FieldElement{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509},
+ FieldElement{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883},
+ FieldElement{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865},
+ },
+ {
+ FieldElement{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660},
+ FieldElement{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273},
+ FieldElement{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138},
+ },
+ {
+ FieldElement{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560},
+ FieldElement{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135},
+ FieldElement{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941},
+ },
+ {
+ FieldElement{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739},
+ FieldElement{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756},
+ FieldElement{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819},
+ },
+ },
+ {
+ {
+ FieldElement{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347},
+ FieldElement{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028},
+ FieldElement{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075},
+ },
+ {
+ FieldElement{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799},
+ FieldElement{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609},
+ FieldElement{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817},
+ },
+ {
+ FieldElement{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989},
+ FieldElement{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523},
+ FieldElement{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278},
+ },
+ {
+ FieldElement{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045},
+ FieldElement{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377},
+ FieldElement{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480},
+ },
+ {
+ FieldElement{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016},
+ FieldElement{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426},
+ FieldElement{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525},
+ },
+ {
+ FieldElement{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396},
+ FieldElement{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080},
+ FieldElement{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892},
+ },
+ {
+ FieldElement{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275},
+ FieldElement{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074},
+ FieldElement{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140},
+ },
+ {
+ FieldElement{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717},
+ FieldElement{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101},
+ FieldElement{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127},
+ },
+ },
+ {
+ {
+ FieldElement{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632},
+ FieldElement{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415},
+ FieldElement{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160},
+ },
+ {
+ FieldElement{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876},
+ FieldElement{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625},
+ FieldElement{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478},
+ },
+ {
+ FieldElement{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164},
+ FieldElement{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595},
+ FieldElement{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248},
+ },
+ {
+ FieldElement{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858},
+ FieldElement{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193},
+ FieldElement{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184},
+ },
+ {
+ FieldElement{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942},
+ FieldElement{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635},
+ FieldElement{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948},
+ },
+ {
+ FieldElement{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935},
+ FieldElement{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415},
+ FieldElement{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416},
+ },
+ {
+ FieldElement{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018},
+ FieldElement{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778},
+ FieldElement{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659},
+ },
+ {
+ FieldElement{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385},
+ FieldElement{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503},
+ FieldElement{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329},
+ },
+ },
+ {
+ {
+ FieldElement{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056},
+ FieldElement{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838},
+ FieldElement{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948},
+ },
+ {
+ FieldElement{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691},
+ FieldElement{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118},
+ FieldElement{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517},
+ },
+ {
+ FieldElement{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269},
+ FieldElement{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904},
+ FieldElement{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589},
+ },
+ {
+ FieldElement{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193},
+ FieldElement{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910},
+ FieldElement{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930},
+ },
+ {
+ FieldElement{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667},
+ FieldElement{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481},
+ FieldElement{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876},
+ },
+ {
+ FieldElement{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640},
+ FieldElement{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278},
+ FieldElement{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112},
+ },
+ {
+ FieldElement{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272},
+ FieldElement{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012},
+ FieldElement{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221},
+ },
+ {
+ FieldElement{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046},
+ FieldElement{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345},
+ FieldElement{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310},
+ },
+ },
+ {
+ {
+ FieldElement{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937},
+ FieldElement{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636},
+ FieldElement{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008},
+ },
+ {
+ FieldElement{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429},
+ FieldElement{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576},
+ FieldElement{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066},
+ },
+ {
+ FieldElement{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490},
+ FieldElement{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104},
+ FieldElement{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053},
+ },
+ {
+ FieldElement{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275},
+ FieldElement{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511},
+ FieldElement{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095},
+ },
+ {
+ FieldElement{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439},
+ FieldElement{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939},
+ FieldElement{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424},
+ },
+ {
+ FieldElement{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310},
+ FieldElement{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608},
+ FieldElement{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079},
+ },
+ {
+ FieldElement{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101},
+ FieldElement{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418},
+ FieldElement{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576},
+ },
+ {
+ FieldElement{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356},
+ FieldElement{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996},
+ FieldElement{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099},
+ },
+ },
+ {
+ {
+ FieldElement{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728},
+ FieldElement{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658},
+ FieldElement{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242},
+ },
+ {
+ FieldElement{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001},
+ FieldElement{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766},
+ FieldElement{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373},
+ },
+ {
+ FieldElement{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458},
+ FieldElement{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628},
+ FieldElement{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657},
+ },
+ {
+ FieldElement{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062},
+ FieldElement{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616},
+ FieldElement{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014},
+ },
+ {
+ FieldElement{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383},
+ FieldElement{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814},
+ FieldElement{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718},
+ },
+ {
+ FieldElement{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417},
+ FieldElement{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222},
+ FieldElement{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444},
+ },
+ {
+ FieldElement{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597},
+ FieldElement{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970},
+ FieldElement{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799},
+ },
+ {
+ FieldElement{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647},
+ FieldElement{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511},
+ FieldElement{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032},
+ },
+ },
+ {
+ {
+ FieldElement{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834},
+ FieldElement{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461},
+ FieldElement{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062},
+ },
+ {
+ FieldElement{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516},
+ FieldElement{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547},
+ FieldElement{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240},
+ },
+ {
+ FieldElement{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038},
+ FieldElement{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741},
+ FieldElement{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103},
+ },
+ {
+ FieldElement{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747},
+ FieldElement{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323},
+ FieldElement{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016},
+ },
+ {
+ FieldElement{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373},
+ FieldElement{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228},
+ FieldElement{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141},
+ },
+ {
+ FieldElement{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399},
+ FieldElement{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831},
+ FieldElement{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376},
+ },
+ {
+ FieldElement{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313},
+ FieldElement{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958},
+ FieldElement{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577},
+ },
+ {
+ FieldElement{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743},
+ FieldElement{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684},
+ FieldElement{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476},
+ },
+ },
+}
diff --git a/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/edwards25519.go b/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/edwards25519.go
new file mode 100644
index 0000000..fd03c25
--- /dev/null
+++ b/vendor/github.com/cretz/bine/torutil/ed25519/internal/edwards25519/edwards25519.go
@@ -0,0 +1,1793 @@
+// Copyright 2016 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.
+
+package edwards25519
+
+import "encoding/binary"
+
+// This code is a port of the public domain, “ref10” implementation of ed25519
+// from SUPERCOP.
+
+// FieldElement represents an element of the field GF(2^255 - 19). An element
+// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
+// context.
+type FieldElement [10]int32
+
+var zero FieldElement
+
+func FeZero(fe *FieldElement) {
+ copy(fe[:], zero[:])
+}
+
+func FeOne(fe *FieldElement) {
+ FeZero(fe)
+ fe[0] = 1
+}
+
+func FeAdd(dst, a, b *FieldElement) {
+ dst[0] = a[0] + b[0]
+ dst[1] = a[1] + b[1]
+ dst[2] = a[2] + b[2]
+ dst[3] = a[3] + b[3]
+ dst[4] = a[4] + b[4]
+ dst[5] = a[5] + b[5]
+ dst[6] = a[6] + b[6]
+ dst[7] = a[7] + b[7]
+ dst[8] = a[8] + b[8]
+ dst[9] = a[9] + b[9]
+}
+
+func FeSub(dst, a, b *FieldElement) {
+ dst[0] = a[0] - b[0]
+ dst[1] = a[1] - b[1]
+ dst[2] = a[2] - b[2]
+ dst[3] = a[3] - b[3]
+ dst[4] = a[4] - b[4]
+ dst[5] = a[5] - b[5]
+ dst[6] = a[6] - b[6]
+ dst[7] = a[7] - b[7]
+ dst[8] = a[8] - b[8]
+ dst[9] = a[9] - b[9]
+}
+
+func FeCopy(dst, src *FieldElement) {
+ copy(dst[:], src[:])
+}
+
+// Replace (f,g) with (g,g) if b == 1;
+// replace (f,g) with (f,g) if b == 0.
+//
+// Preconditions: b in {0,1}.
+func FeCMove(f, g *FieldElement, b int32) {
+ b = -b
+ f[0] ^= b & (f[0] ^ g[0])
+ f[1] ^= b & (f[1] ^ g[1])
+ f[2] ^= b & (f[2] ^ g[2])
+ f[3] ^= b & (f[3] ^ g[3])
+ f[4] ^= b & (f[4] ^ g[4])
+ f[5] ^= b & (f[5] ^ g[5])
+ f[6] ^= b & (f[6] ^ g[6])
+ f[7] ^= b & (f[7] ^ g[7])
+ f[8] ^= b & (f[8] ^ g[8])
+ f[9] ^= b & (f[9] ^ g[9])
+}
+
+func load3(in []byte) int64 {
+ var r int64
+ r = int64(in[0])
+ r |= int64(in[1]) << 8
+ r |= int64(in[2]) << 16
+ return r
+}
+
+func load4(in []byte) int64 {
+ var r int64
+ r = int64(in[0])
+ r |= int64(in[1]) << 8
+ r |= int64(in[2]) << 16
+ r |= int64(in[3]) << 24
+ return r
+}
+
+func FeFromBytes(dst *FieldElement, src *[32]byte) {
+ h0 := load4(src[:])
+ h1 := load3(src[4:]) << 6
+ h2 := load3(src[7:]) << 5
+ h3 := load3(src[10:]) << 3
+ h4 := load3(src[13:]) << 2
+ h5 := load4(src[16:])
+ h6 := load3(src[20:]) << 7
+ h7 := load3(src[23:]) << 5
+ h8 := load3(src[26:]) << 4
+ h9 := (load3(src[29:]) & 8388607) << 2
+
+ FeCombine(dst, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeToBytes marshals h to s.
+// Preconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Write p=2^255-19; q=floor(h/p).
+// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
+//
+// Proof:
+// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
+// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
+//
+// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
+// Then 0<y<1.
+//
+// Write r=h-pq.
+// Have 0<=r<=p-1=2^255-20.
+// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
+//
+// Write x=r+19(2^-255)r+y.
+// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
+//
+// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
+// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
+func FeToBytes(s *[32]byte, h *FieldElement) {
+ var carry [10]int32
+
+ q := (19*h[9] + (1 << 24)) >> 25
+ q = (h[0] + q) >> 26
+ q = (h[1] + q) >> 25
+ q = (h[2] + q) >> 26
+ q = (h[3] + q) >> 25
+ q = (h[4] + q) >> 26
+ q = (h[5] + q) >> 25
+ q = (h[6] + q) >> 26
+ q = (h[7] + q) >> 25
+ q = (h[8] + q) >> 26
+ q = (h[9] + q) >> 25
+
+ // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
+ h[0] += 19 * q
+ // Goal: Output h-2^255 q, which is between 0 and 2^255-20.
+
+ carry[0] = h[0] >> 26
+ h[1] += carry[0]
+ h[0] -= carry[0] << 26
+ carry[1] = h[1] >> 25
+ h[2] += carry[1]
+ h[1] -= carry[1] << 25
+ carry[2] = h[2] >> 26
+ h[3] += carry[2]
+ h[2] -= carry[2] << 26
+ carry[3] = h[3] >> 25
+ h[4] += carry[3]
+ h[3] -= carry[3] << 25
+ carry[4] = h[4] >> 26
+ h[5] += carry[4]
+ h[4] -= carry[4] << 26
+ carry[5] = h[5] >> 25
+ h[6] += carry[5]
+ h[5] -= carry[5] << 25
+ carry[6] = h[6] >> 26
+ h[7] += carry[6]
+ h[6] -= carry[6] << 26
+ carry[7] = h[7] >> 25
+ h[8] += carry[7]
+ h[7] -= carry[7] << 25
+ carry[8] = h[8] >> 26
+ h[9] += carry[8]
+ h[8] -= carry[8] << 26
+ carry[9] = h[9] >> 25
+ h[9] -= carry[9] << 25
+ // h10 = carry9
+
+ // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
+ // Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
+ // evidently 2^255 h10-2^255 q = 0.
+ // Goal: Output h[0]+...+2^230 h[9].
+
+ s[0] = byte(h[0] >> 0)
+ s[1] = byte(h[0] >> 8)
+ s[2] = byte(h[0] >> 16)
+ s[3] = byte((h[0] >> 24) | (h[1] << 2))
+ s[4] = byte(h[1] >> 6)
+ s[5] = byte(h[1] >> 14)
+ s[6] = byte((h[1] >> 22) | (h[2] << 3))
+ s[7] = byte(h[2] >> 5)
+ s[8] = byte(h[2] >> 13)
+ s[9] = byte((h[2] >> 21) | (h[3] << 5))
+ s[10] = byte(h[3] >> 3)
+ s[11] = byte(h[3] >> 11)
+ s[12] = byte((h[3] >> 19) | (h[4] << 6))
+ s[13] = byte(h[4] >> 2)
+ s[14] = byte(h[4] >> 10)
+ s[15] = byte(h[4] >> 18)
+ s[16] = byte(h[5] >> 0)
+ s[17] = byte(h[5] >> 8)
+ s[18] = byte(h[5] >> 16)
+ s[19] = byte((h[5] >> 24) | (h[6] << 1))
+ s[20] = byte(h[6] >> 7)
+ s[21] = byte(h[6] >> 15)
+ s[22] = byte((h[6] >> 23) | (h[7] << 3))
+ s[23] = byte(h[7] >> 5)
+ s[24] = byte(h[7] >> 13)
+ s[25] = byte((h[7] >> 21) | (h[8] << 4))
+ s[26] = byte(h[8] >> 4)
+ s[27] = byte(h[8] >> 12)
+ s[28] = byte((h[8] >> 20) | (h[9] << 6))
+ s[29] = byte(h[9] >> 2)
+ s[30] = byte(h[9] >> 10)
+ s[31] = byte(h[9] >> 18)
+}
+
+func FeIsNegative(f *FieldElement) byte {
+ var s [32]byte
+ FeToBytes(&s, f)
+ return s[0] & 1
+}
+
+func FeIsNonZero(f *FieldElement) int32 {
+ var s [32]byte
+ FeToBytes(&s, f)
+ var x uint8
+ for _, b := range s {
+ x |= b
+ }
+ x |= x >> 4
+ x |= x >> 2
+ x |= x >> 1
+ return int32(x & 1)
+}
+
+// FeNeg sets h = -f
+//
+// Preconditions:
+// |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeNeg(h, f *FieldElement) {
+ h[0] = -f[0]
+ h[1] = -f[1]
+ h[2] = -f[2]
+ h[3] = -f[3]
+ h[4] = -f[4]
+ h[5] = -f[5]
+ h[6] = -f[6]
+ h[7] = -f[7]
+ h[8] = -f[8]
+ h[9] = -f[9]
+}
+
+func FeCombine(h *FieldElement, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+ var c0, c1, c2, c3, c4, c5, c6, c7, c8, c9 int64
+
+ /*
+ |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
+ i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
+ |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
+ i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
+ */
+
+ c0 = (h0 + (1 << 25)) >> 26
+ h1 += c0
+ h0 -= c0 << 26
+ c4 = (h4 + (1 << 25)) >> 26
+ h5 += c4
+ h4 -= c4 << 26
+ /* |h0| <= 2^25 */
+ /* |h4| <= 2^25 */
+ /* |h1| <= 1.51*2^58 */
+ /* |h5| <= 1.51*2^58 */
+
+ c1 = (h1 + (1 << 24)) >> 25
+ h2 += c1
+ h1 -= c1 << 25
+ c5 = (h5 + (1 << 24)) >> 25
+ h6 += c5
+ h5 -= c5 << 25
+ /* |h1| <= 2^24; from now on fits into int32 */
+ /* |h5| <= 2^24; from now on fits into int32 */
+ /* |h2| <= 1.21*2^59 */
+ /* |h6| <= 1.21*2^59 */
+
+ c2 = (h2 + (1 << 25)) >> 26
+ h3 += c2
+ h2 -= c2 << 26
+ c6 = (h6 + (1 << 25)) >> 26
+ h7 += c6
+ h6 -= c6 << 26
+ /* |h2| <= 2^25; from now on fits into int32 unchanged */
+ /* |h6| <= 2^25; from now on fits into int32 unchanged */
+ /* |h3| <= 1.51*2^58 */
+ /* |h7| <= 1.51*2^58 */
+
+ c3 = (h3 + (1 << 24)) >> 25
+ h4 += c3
+ h3 -= c3 << 25
+ c7 = (h7 + (1 << 24)) >> 25
+ h8 += c7
+ h7 -= c7 << 25
+ /* |h3| <= 2^24; from now on fits into int32 unchanged */
+ /* |h7| <= 2^24; from now on fits into int32 unchanged */
+ /* |h4| <= 1.52*2^33 */
+ /* |h8| <= 1.52*2^33 */
+
+ c4 = (h4 + (1 << 25)) >> 26
+ h5 += c4
+ h4 -= c4 << 26
+ c8 = (h8 + (1 << 25)) >> 26
+ h9 += c8
+ h8 -= c8 << 26
+ /* |h4| <= 2^25; from now on fits into int32 unchanged */
+ /* |h8| <= 2^25; from now on fits into int32 unchanged */
+ /* |h5| <= 1.01*2^24 */
+ /* |h9| <= 1.51*2^58 */
+
+ c9 = (h9 + (1 << 24)) >> 25
+ h0 += c9 * 19
+ h9 -= c9 << 25
+ /* |h9| <= 2^24; from now on fits into int32 unchanged */
+ /* |h0| <= 1.8*2^37 */
+
+ c0 = (h0 + (1 << 25)) >> 26
+ h1 += c0
+ h0 -= c0 << 26
+ /* |h0| <= 2^25; from now on fits into int32 unchanged */
+ /* |h1| <= 1.01*2^24 */
+
+ h[0] = int32(h0)
+ h[1] = int32(h1)
+ h[2] = int32(h2)
+ h[3] = int32(h3)
+ h[4] = int32(h4)
+ h[5] = int32(h5)
+ h[6] = int32(h6)
+ h[7] = int32(h7)
+ h[8] = int32(h8)
+ h[9] = int32(h9)
+}
+
+// FeMul calculates h = f * g
+// Can overlap h with f or g.
+//
+// Preconditions:
+// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+//
+// Notes on implementation strategy:
+//
+// Using schoolbook multiplication.
+// Karatsuba would save a little in some cost models.
+//
+// Most multiplications by 2 and 19 are 32-bit precomputations;
+// cheaper than 64-bit postcomputations.
+//
+// There is one remaining multiplication by 19 in the carry chain;
+// one *19 precomputation can be merged into this,
+// but the resulting data flow is considerably less clean.
+//
+// There are 12 carries below.
+// 10 of them are 2-way parallelizable and vectorizable.
+// Can get away with 11 carries, but then data flow is much deeper.
+//
+// With tighter constraints on inputs, can squeeze carries into int32.
+func FeMul(h, f, g *FieldElement) {
+ f0 := int64(f[0])
+ f1 := int64(f[1])
+ f2 := int64(f[2])
+ f3 := int64(f[3])
+ f4 := int64(f[4])
+ f5 := int64(f[5])
+ f6 := int64(f[6])
+ f7 := int64(f[7])
+ f8 := int64(f[8])
+ f9 := int64(f[9])
+
+ f1_2 := int64(2 * f[1])
+ f3_2 := int64(2 * f[3])
+ f5_2 := int64(2 * f[5])
+ f7_2 := int64(2 * f[7])
+ f9_2 := int64(2 * f[9])
+
+ g0 := int64(g[0])
+ g1 := int64(g[1])
+ g2 := int64(g[2])
+ g3 := int64(g[3])
+ g4 := int64(g[4])
+ g5 := int64(g[5])
+ g6 := int64(g[6])
+ g7 := int64(g[7])
+ g8 := int64(g[8])
+ g9 := int64(g[9])
+
+ g1_19 := int64(19 * g[1]) /* 1.4*2^29 */
+ g2_19 := int64(19 * g[2]) /* 1.4*2^30; still ok */
+ g3_19 := int64(19 * g[3])
+ g4_19 := int64(19 * g[4])
+ g5_19 := int64(19 * g[5])
+ g6_19 := int64(19 * g[6])
+ g7_19 := int64(19 * g[7])
+ g8_19 := int64(19 * g[8])
+ g9_19 := int64(19 * g[9])
+
+ h0 := f0*g0 + f1_2*g9_19 + f2*g8_19 + f3_2*g7_19 + f4*g6_19 + f5_2*g5_19 + f6*g4_19 + f7_2*g3_19 + f8*g2_19 + f9_2*g1_19
+ h1 := f0*g1 + f1*g0 + f2*g9_19 + f3*g8_19 + f4*g7_19 + f5*g6_19 + f6*g5_19 + f7*g4_19 + f8*g3_19 + f9*g2_19
+ h2 := f0*g2 + f1_2*g1 + f2*g0 + f3_2*g9_19 + f4*g8_19 + f5_2*g7_19 + f6*g6_19 + f7_2*g5_19 + f8*g4_19 + f9_2*g3_19
+ h3 := f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4*g9_19 + f5*g8_19 + f6*g7_19 + f7*g6_19 + f8*g5_19 + f9*g4_19
+ h4 := f0*g4 + f1_2*g3 + f2*g2 + f3_2*g1 + f4*g0 + f5_2*g9_19 + f6*g8_19 + f7_2*g7_19 + f8*g6_19 + f9_2*g5_19
+ h5 := f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 + f5*g0 + f6*g9_19 + f7*g8_19 + f8*g7_19 + f9*g6_19
+ h6 := f0*g6 + f1_2*g5 + f2*g4 + f3_2*g3 + f4*g2 + f5_2*g1 + f6*g0 + f7_2*g9_19 + f8*g8_19 + f9_2*g7_19
+ h7 := f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 + f5*g2 + f6*g1 + f7*g0 + f8*g9_19 + f9*g8_19
+ h8 := f0*g8 + f1_2*g7 + f2*g6 + f3_2*g5 + f4*g4 + f5_2*g3 + f6*g2 + f7_2*g1 + f8*g0 + f9_2*g9_19
+ h9 := f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 + f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0
+
+ FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func feSquare(f *FieldElement) (h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 int64) {
+ f0 := int64(f[0])
+ f1 := int64(f[1])
+ f2 := int64(f[2])
+ f3 := int64(f[3])
+ f4 := int64(f[4])
+ f5 := int64(f[5])
+ f6 := int64(f[6])
+ f7 := int64(f[7])
+ f8 := int64(f[8])
+ f9 := int64(f[9])
+ f0_2 := int64(2 * f[0])
+ f1_2 := int64(2 * f[1])
+ f2_2 := int64(2 * f[2])
+ f3_2 := int64(2 * f[3])
+ f4_2 := int64(2 * f[4])
+ f5_2 := int64(2 * f[5])
+ f6_2 := int64(2 * f[6])
+ f7_2 := int64(2 * f[7])
+ f5_38 := 38 * f5 // 1.31*2^30
+ f6_19 := 19 * f6 // 1.31*2^30
+ f7_38 := 38 * f7 // 1.31*2^30
+ f8_19 := 19 * f8 // 1.31*2^30
+ f9_38 := 38 * f9 // 1.31*2^30
+
+ h0 = f0*f0 + f1_2*f9_38 + f2_2*f8_19 + f3_2*f7_38 + f4_2*f6_19 + f5*f5_38
+ h1 = f0_2*f1 + f2*f9_38 + f3_2*f8_19 + f4*f7_38 + f5_2*f6_19
+ h2 = f0_2*f2 + f1_2*f1 + f3_2*f9_38 + f4_2*f8_19 + f5_2*f7_38 + f6*f6_19
+ h3 = f0_2*f3 + f1_2*f2 + f4*f9_38 + f5_2*f8_19 + f6*f7_38
+ h4 = f0_2*f4 + f1_2*f3_2 + f2*f2 + f5_2*f9_38 + f6_2*f8_19 + f7*f7_38
+ h5 = f0_2*f5 + f1_2*f4 + f2_2*f3 + f6*f9_38 + f7_2*f8_19
+ h6 = f0_2*f6 + f1_2*f5_2 + f2_2*f4 + f3_2*f3 + f7_2*f9_38 + f8*f8_19
+ h7 = f0_2*f7 + f1_2*f6 + f2_2*f5 + f3_2*f4 + f8*f9_38
+ h8 = f0_2*f8 + f1_2*f7_2 + f2_2*f6 + f3_2*f5_2 + f4*f4 + f9*f9_38
+ h9 = f0_2*f9 + f1_2*f8 + f2_2*f7 + f3_2*f6 + f4_2*f5
+
+ return
+}
+
+// FeSquare calculates h = f*f. Can overlap h with f.
+//
+// Preconditions:
+// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
+func FeSquare(h, f *FieldElement) {
+ h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+ FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+// FeSquare2 sets h = 2 * f * f
+//
+// Can overlap h with f.
+//
+// Preconditions:
+// |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
+//
+// Postconditions:
+// |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
+// See fe_mul.c for discussion of implementation strategy.
+func FeSquare2(h, f *FieldElement) {
+ h0, h1, h2, h3, h4, h5, h6, h7, h8, h9 := feSquare(f)
+
+ h0 += h0
+ h1 += h1
+ h2 += h2
+ h3 += h3
+ h4 += h4
+ h5 += h5
+ h6 += h6
+ h7 += h7
+ h8 += h8
+ h9 += h9
+
+ FeCombine(h, h0, h1, h2, h3, h4, h5, h6, h7, h8, h9)
+}
+
+func FeInvert(out, z *FieldElement) {
+ var t0, t1, t2, t3 FieldElement
+ var i int
+
+ FeSquare(&t0, z) // 2^1
+ FeSquare(&t1, &t0) // 2^2
+ for i = 1; i < 2; i++ { // 2^3
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, z, &t1) // 2^3 + 2^0
+ FeMul(&t0, &t0, &t1) // 2^3 + 2^1 + 2^0
+ FeSquare(&t2, &t0) // 2^4 + 2^2 + 2^1
+ FeMul(&t1, &t1, &t2) // 2^4 + 2^3 + 2^2 + 2^1 + 2^0
+ FeSquare(&t2, &t1) // 5,4,3,2,1
+ for i = 1; i < 5; i++ { // 9,8,7,6,5
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1) // 9,8,7,6,5,4,3,2,1,0
+ FeSquare(&t2, &t1) // 10..1
+ for i = 1; i < 10; i++ { // 19..10
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t2, &t2, &t1) // 19..0
+ FeSquare(&t3, &t2) // 20..1
+ for i = 1; i < 20; i++ { // 39..20
+ FeSquare(&t3, &t3)
+ }
+ FeMul(&t2, &t3, &t2) // 39..0
+ FeSquare(&t2, &t2) // 40..1
+ for i = 1; i < 10; i++ { // 49..10
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1) // 49..0
+ FeSquare(&t2, &t1) // 50..1
+ for i = 1; i < 50; i++ { // 99..50
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t2, &t2, &t1) // 99..0
+ FeSquare(&t3, &t2) // 100..1
+ for i = 1; i < 100; i++ { // 199..100
+ FeSquare(&t3, &t3)
+ }
+ FeMul(&t2, &t3, &t2) // 199..0
+ FeSquare(&t2, &t2) // 200..1
+ for i = 1; i < 50; i++ { // 249..50
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1) // 249..0
+ FeSquare(&t1, &t1) // 250..1
+ for i = 1; i < 5; i++ { // 254..5
+ FeSquare(&t1, &t1)
+ }
+ FeMul(out, &t1, &t0) // 254..5,3,1,0
+}
+
+func fePow22523(out, z *FieldElement) {
+ var t0, t1, t2 FieldElement
+ var i int
+
+ FeSquare(&t0, z)
+ for i = 1; i < 1; i++ {
+ FeSquare(&t0, &t0)
+ }
+ FeSquare(&t1, &t0)
+ for i = 1; i < 2; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, z, &t1)
+ FeMul(&t0, &t0, &t1)
+ FeSquare(&t0, &t0)
+ for i = 1; i < 1; i++ {
+ FeSquare(&t0, &t0)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t1, &t0)
+ for i = 1; i < 5; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t1, &t0)
+ for i = 1; i < 10; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, &t1, &t0)
+ FeSquare(&t2, &t1)
+ for i = 1; i < 20; i++ {
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1)
+ FeSquare(&t1, &t1)
+ for i = 1; i < 10; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t1, &t0)
+ for i = 1; i < 50; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t1, &t1, &t0)
+ FeSquare(&t2, &t1)
+ for i = 1; i < 100; i++ {
+ FeSquare(&t2, &t2)
+ }
+ FeMul(&t1, &t2, &t1)
+ FeSquare(&t1, &t1)
+ for i = 1; i < 50; i++ {
+ FeSquare(&t1, &t1)
+ }
+ FeMul(&t0, &t1, &t0)
+ FeSquare(&t0, &t0)
+ for i = 1; i < 2; i++ {
+ FeSquare(&t0, &t0)
+ }
+ FeMul(out, &t0, z)
+}
+
+// Group elements are members of the elliptic curve -x^2 + y^2 = 1 + d * x^2 *
+// y^2 where d = -121665/121666.
+//
+// Several representations are used:
+// ProjectiveGroupElement: (X:Y:Z) satisfying x=X/Z, y=Y/Z
+// ExtendedGroupElement: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+// CompletedGroupElement: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+// PreComputedGroupElement: (y+x,y-x,2dxy)
+
+type ProjectiveGroupElement struct {
+ X, Y, Z FieldElement
+}
+
+type ExtendedGroupElement struct {
+ X, Y, Z, T FieldElement
+}
+
+type CompletedGroupElement struct {
+ X, Y, Z, T FieldElement
+}
+
+type PreComputedGroupElement struct {
+ yPlusX, yMinusX, xy2d FieldElement
+}
+
+type CachedGroupElement struct {
+ yPlusX, yMinusX, Z, T2d FieldElement
+}
+
+func (p *ProjectiveGroupElement) Zero() {
+ FeZero(&p.X)
+ FeOne(&p.Y)
+ FeOne(&p.Z)
+}
+
+func (p *ProjectiveGroupElement) Double(r *CompletedGroupElement) {
+ var t0 FieldElement
+
+ FeSquare(&r.X, &p.X)
+ FeSquare(&r.Z, &p.Y)
+ FeSquare2(&r.T, &p.Z)
+ FeAdd(&r.Y, &p.X, &p.Y)
+ FeSquare(&t0, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.X)
+ FeSub(&r.Z, &r.Z, &r.X)
+ FeSub(&r.X, &t0, &r.Y)
+ FeSub(&r.T, &r.T, &r.Z)
+}
+
+func (p *ProjectiveGroupElement) ToBytes(s *[32]byte) {
+ var recip, x, y FieldElement
+
+ FeInvert(&recip, &p.Z)
+ FeMul(&x, &p.X, &recip)
+ FeMul(&y, &p.Y, &recip)
+ FeToBytes(s, &y)
+ s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) Zero() {
+ FeZero(&p.X)
+ FeOne(&p.Y)
+ FeOne(&p.Z)
+ FeZero(&p.T)
+}
+
+func (p *ExtendedGroupElement) Double(r *CompletedGroupElement) {
+ var q ProjectiveGroupElement
+ p.ToProjective(&q)
+ q.Double(r)
+}
+
+func (p *ExtendedGroupElement) ToCached(r *CachedGroupElement) {
+ FeAdd(&r.yPlusX, &p.Y, &p.X)
+ FeSub(&r.yMinusX, &p.Y, &p.X)
+ FeCopy(&r.Z, &p.Z)
+ FeMul(&r.T2d, &p.T, &d2)
+}
+
+func (p *ExtendedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+ FeCopy(&r.X, &p.X)
+ FeCopy(&r.Y, &p.Y)
+ FeCopy(&r.Z, &p.Z)
+}
+
+func (p *ExtendedGroupElement) ToBytes(s *[32]byte) {
+ var recip, x, y FieldElement
+
+ FeInvert(&recip, &p.Z)
+ FeMul(&x, &p.X, &recip)
+ FeMul(&y, &p.Y, &recip)
+ FeToBytes(s, &y)
+ s[31] ^= FeIsNegative(&x) << 7
+}
+
+func (p *ExtendedGroupElement) FromBytes(s *[32]byte) bool {
+ var u, v, v3, vxx, check FieldElement
+
+ FeFromBytes(&p.Y, s)
+ FeOne(&p.Z)
+ FeSquare(&u, &p.Y)
+ FeMul(&v, &u, &d)
+ FeSub(&u, &u, &p.Z) // y = y^2-1
+ FeAdd(&v, &v, &p.Z) // v = dy^2+1
+
+ FeSquare(&v3, &v)
+ FeMul(&v3, &v3, &v) // v3 = v^3
+ FeSquare(&p.X, &v3)
+ FeMul(&p.X, &p.X, &v)
+ FeMul(&p.X, &p.X, &u) // x = uv^7
+
+ fePow22523(&p.X, &p.X) // x = (uv^7)^((q-5)/8)
+ FeMul(&p.X, &p.X, &v3)
+ FeMul(&p.X, &p.X, &u) // x = uv^3(uv^7)^((q-5)/8)
+
+ var tmpX, tmp2 [32]byte
+
+ FeSquare(&vxx, &p.X)
+ FeMul(&vxx, &vxx, &v)
+ FeSub(&check, &vxx, &u) // vx^2-u
+ if FeIsNonZero(&check) == 1 {
+ FeAdd(&check, &vxx, &u) // vx^2+u
+ if FeIsNonZero(&check) == 1 {
+ return false
+ }
+ FeMul(&p.X, &p.X, &SqrtM1)
+
+ FeToBytes(&tmpX, &p.X)
+ for i, v := range tmpX {
+ tmp2[31-i] = v
+ }
+ }
+
+ if FeIsNegative(&p.X) != (s[31] >> 7) {
+ FeNeg(&p.X, &p.X)
+ }
+
+ FeMul(&p.T, &p.X, &p.Y)
+ return true
+}
+
+func (p *CompletedGroupElement) ToProjective(r *ProjectiveGroupElement) {
+ FeMul(&r.X, &p.X, &p.T)
+ FeMul(&r.Y, &p.Y, &p.Z)
+ FeMul(&r.Z, &p.Z, &p.T)
+}
+
+func (p *CompletedGroupElement) ToExtended(r *ExtendedGroupElement) {
+ FeMul(&r.X, &p.X, &p.T)
+ FeMul(&r.Y, &p.Y, &p.Z)
+ FeMul(&r.Z, &p.Z, &p.T)
+ FeMul(&r.T, &p.X, &p.Y)
+}
+
+func (p *PreComputedGroupElement) Zero() {
+ FeOne(&p.yPlusX)
+ FeOne(&p.yMinusX)
+ FeZero(&p.xy2d)
+}
+
+func geAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yPlusX)
+ FeMul(&r.Y, &r.Y, &q.yMinusX)
+ FeMul(&r.T, &q.T2d, &p.T)
+ FeMul(&r.X, &p.Z, &q.Z)
+ FeAdd(&t0, &r.X, &r.X)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeAdd(&r.Z, &t0, &r.T)
+ FeSub(&r.T, &t0, &r.T)
+}
+
+func geSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *CachedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yMinusX)
+ FeMul(&r.Y, &r.Y, &q.yPlusX)
+ FeMul(&r.T, &q.T2d, &p.T)
+ FeMul(&r.X, &p.Z, &q.Z)
+ FeAdd(&t0, &r.X, &r.X)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeSub(&r.Z, &t0, &r.T)
+ FeAdd(&r.T, &t0, &r.T)
+}
+
+func geMixedAdd(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yPlusX)
+ FeMul(&r.Y, &r.Y, &q.yMinusX)
+ FeMul(&r.T, &q.xy2d, &p.T)
+ FeAdd(&t0, &p.Z, &p.Z)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeAdd(&r.Z, &t0, &r.T)
+ FeSub(&r.T, &t0, &r.T)
+}
+
+func geMixedSub(r *CompletedGroupElement, p *ExtendedGroupElement, q *PreComputedGroupElement) {
+ var t0 FieldElement
+
+ FeAdd(&r.X, &p.Y, &p.X)
+ FeSub(&r.Y, &p.Y, &p.X)
+ FeMul(&r.Z, &r.X, &q.yMinusX)
+ FeMul(&r.Y, &r.Y, &q.yPlusX)
+ FeMul(&r.T, &q.xy2d, &p.T)
+ FeAdd(&t0, &p.Z, &p.Z)
+ FeSub(&r.X, &r.Z, &r.Y)
+ FeAdd(&r.Y, &r.Z, &r.Y)
+ FeSub(&r.Z, &t0, &r.T)
+ FeAdd(&r.T, &t0, &r.T)
+}
+
+func slide(r *[256]int8, a *[32]byte) {
+ for i := range r {
+ r[i] = int8(1 & (a[i>>3] >> uint(i&7)))
+ }
+
+ for i := range r {
+ if r[i] != 0 {
+ for b := 1; b <= 6 && i+b < 256; b++ {
+ if r[i+b] != 0 {
+ if r[i]+(r[i+b]<<uint(b)) <= 15 {
+ r[i] += r[i+b] << uint(b)
+ r[i+b] = 0
+ } else if r[i]-(r[i+b]<<uint(b)) >= -15 {
+ r[i] -= r[i+b] << uint(b)
+ for k := i + b; k < 256; k++ {
+ if r[k] == 0 {
+ r[k] = 1
+ break
+ }
+ r[k] = 0
+ }
+ } else {
+ break
+ }
+ }
+ }
+ }
+ }
+}
+
+// GeDoubleScalarMultVartime sets r = a*A + b*B
+// where a = a[0]+256*a[1]+...+256^31 a[31].
+// and b = b[0]+256*b[1]+...+256^31 b[31].
+// B is the Ed25519 base point (x,4/5) with x positive.
+func GeDoubleScalarMultVartime(r *ProjectiveGroupElement, a *[32]byte, A *ExtendedGroupElement, b *[32]byte) {
+ var aSlide, bSlide [256]int8
+ var Ai [8]CachedGroupElement // A,3A,5A,7A,9A,11A,13A,15A
+ var t CompletedGroupElement
+ var u, A2 ExtendedGroupElement
+ var i int
+
+ slide(&aSlide, a)
+ slide(&bSlide, b)
+
+ A.ToCached(&Ai[0])
+ A.Double(&t)
+ t.ToExtended(&A2)
+
+ for i := 0; i < 7; i++ {
+ geAdd(&t, &A2, &Ai[i])
+ t.ToExtended(&u)
+ u.ToCached(&Ai[i+1])
+ }
+
+ r.Zero()
+
+ for i = 255; i >= 0; i-- {
+ if aSlide[i] != 0 || bSlide[i] != 0 {
+ break
+ }
+ }
+
+ for ; i >= 0; i-- {
+ r.Double(&t)
+
+ if aSlide[i] > 0 {
+ t.ToExtended(&u)
+ geAdd(&t, &u, &Ai[aSlide[i]/2])
+ } else if aSlide[i] < 0 {
+ t.ToExtended(&u)
+ geSub(&t, &u, &Ai[(-aSlide[i])/2])
+ }
+
+ if bSlide[i] > 0 {
+ t.ToExtended(&u)
+ geMixedAdd(&t, &u, &bi[bSlide[i]/2])
+ } else if bSlide[i] < 0 {
+ t.ToExtended(&u)
+ geMixedSub(&t, &u, &bi[(-bSlide[i])/2])
+ }
+
+ t.ToProjective(r)
+ }
+}
+
+// equal returns 1 if b == c and 0 otherwise, assuming that b and c are
+// non-negative.
+func equal(b, c int32) int32 {
+ x := uint32(b ^ c)
+ x--
+ return int32(x >> 31)
+}
+
+// negative returns 1 if b < 0 and 0 otherwise.
+func negative(b int32) int32 {
+ return (b >> 31) & 1
+}
+
+func PreComputedGroupElementCMove(t, u *PreComputedGroupElement, b int32) {
+ FeCMove(&t.yPlusX, &u.yPlusX, b)
+ FeCMove(&t.yMinusX, &u.yMinusX, b)
+ FeCMove(&t.xy2d, &u.xy2d, b)
+}
+
+func selectPoint(t *PreComputedGroupElement, pos int32, b int32) {
+ var minusT PreComputedGroupElement
+ bNegative := negative(b)
+ bAbs := b - (((-bNegative) & b) << 1)
+
+ t.Zero()
+ for i := int32(0); i < 8; i++ {
+ PreComputedGroupElementCMove(t, &base[pos][i], equal(bAbs, i+1))
+ }
+ FeCopy(&minusT.yPlusX, &t.yMinusX)
+ FeCopy(&minusT.yMinusX, &t.yPlusX)
+ FeNeg(&minusT.xy2d, &t.xy2d)
+ PreComputedGroupElementCMove(t, &minusT, bNegative)
+}
+
+// GeScalarMultBase computes h = a*B, where
+// a = a[0]+256*a[1]+...+256^31 a[31]
+// B is the Ed25519 base point (x,4/5) with x positive.
+//
+// Preconditions:
+// a[31] <= 127
+func GeScalarMultBase(h *ExtendedGroupElement, a *[32]byte) {
+ var e [64]int8
+
+ for i, v := range a {
+ e[2*i] = int8(v & 15)
+ e[2*i+1] = int8((v >> 4) & 15)
+ }
+
+ // each e[i] is between 0 and 15 and e[63] is between 0 and 7.
+
+ carry := int8(0)
+ for i := 0; i < 63; i++ {
+ e[i] += carry
+ carry = (e[i] + 8) >> 4
+ e[i] -= carry << 4
+ }
+ e[63] += carry
+ // each e[i] is between -8 and 8.
+
+ h.Zero()
+ var t PreComputedGroupElement
+ var r CompletedGroupElement
+ for i := int32(1); i < 64; i += 2 {
+ selectPoint(&t, i/2, int32(e[i]))
+ geMixedAdd(&r, h, &t)
+ r.ToExtended(h)
+ }
+
+ var s ProjectiveGroupElement
+
+ h.Double(&r)
+ r.ToProjective(&s)
+ s.Double(&r)
+ r.ToProjective(&s)
+ s.Double(&r)
+ r.ToProjective(&s)
+ s.Double(&r)
+ r.ToExtended(h)
+
+ for i := int32(0); i < 64; i += 2 {
+ selectPoint(&t, i/2, int32(e[i]))
+ geMixedAdd(&r, h, &t)
+ r.ToExtended(h)
+ }
+}
+
+// The scalars are GF(2^252 + 27742317777372353535851937790883648493).
+
+// Input:
+// a[0]+256*a[1]+...+256^31*a[31] = a
+// b[0]+256*b[1]+...+256^31*b[31] = b
+// c[0]+256*c[1]+...+256^31*c[31] = c
+//
+// Output:
+// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+func ScMulAdd(s, a, b, c *[32]byte) {
+ a0 := 2097151 & load3(a[:])
+ a1 := 2097151 & (load4(a[2:]) >> 5)
+ a2 := 2097151 & (load3(a[5:]) >> 2)
+ a3 := 2097151 & (load4(a[7:]) >> 7)
+ a4 := 2097151 & (load4(a[10:]) >> 4)
+ a5 := 2097151 & (load3(a[13:]) >> 1)
+ a6 := 2097151 & (load4(a[15:]) >> 6)
+ a7 := 2097151 & (load3(a[18:]) >> 3)
+ a8 := 2097151 & load3(a[21:])
+ a9 := 2097151 & (load4(a[23:]) >> 5)
+ a10 := 2097151 & (load3(a[26:]) >> 2)
+ a11 := (load4(a[28:]) >> 7)
+ b0 := 2097151 & load3(b[:])
+ b1 := 2097151 & (load4(b[2:]) >> 5)
+ b2 := 2097151 & (load3(b[5:]) >> 2)
+ b3 := 2097151 & (load4(b[7:]) >> 7)
+ b4 := 2097151 & (load4(b[10:]) >> 4)
+ b5 := 2097151 & (load3(b[13:]) >> 1)
+ b6 := 2097151 & (load4(b[15:]) >> 6)
+ b7 := 2097151 & (load3(b[18:]) >> 3)
+ b8 := 2097151 & load3(b[21:])
+ b9 := 2097151 & (load4(b[23:]) >> 5)
+ b10 := 2097151 & (load3(b[26:]) >> 2)
+ b11 := (load4(b[28:]) >> 7)
+ c0 := 2097151 & load3(c[:])
+ c1 := 2097151 & (load4(c[2:]) >> 5)
+ c2 := 2097151 & (load3(c[5:]) >> 2)
+ c3 := 2097151 & (load4(c[7:]) >> 7)
+ c4 := 2097151 & (load4(c[10:]) >> 4)
+ c5 := 2097151 & (load3(c[13:]) >> 1)
+ c6 := 2097151 & (load4(c[15:]) >> 6)
+ c7 := 2097151 & (load3(c[18:]) >> 3)
+ c8 := 2097151 & load3(c[21:])
+ c9 := 2097151 & (load4(c[23:]) >> 5)
+ c10 := 2097151 & (load3(c[26:]) >> 2)
+ c11 := (load4(c[28:]) >> 7)
+ var carry [23]int64
+
+ s0 := c0 + a0*b0
+ s1 := c1 + a0*b1 + a1*b0
+ s2 := c2 + a0*b2 + a1*b1 + a2*b0
+ s3 := c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0
+ s4 := c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0
+ s5 := c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0
+ s6 := c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0
+ s7 := c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0
+ s8 := c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0
+ s9 := c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0
+ s10 := c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0
+ s11 := c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0
+ s12 := a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1
+ s13 := a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2
+ s14 := a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3
+ s15 := a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4
+ s16 := a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5
+ s17 := a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6
+ s18 := a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7
+ s19 := a8*b11 + a9*b10 + a10*b9 + a11*b8
+ s20 := a9*b11 + a10*b10 + a11*b9
+ s21 := a10*b11 + a11*b10
+ s22 := a11 * b11
+ s23 := int64(0)
+
+ carry[0] = (s0 + (1 << 20)) >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[2] = (s2 + (1 << 20)) >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[4] = (s4 + (1 << 20)) >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[12] = (s12 + (1 << 20)) >> 21
+ s13 += carry[12]
+ s12 -= carry[12] << 21
+ carry[14] = (s14 + (1 << 20)) >> 21
+ s15 += carry[14]
+ s14 -= carry[14] << 21
+ carry[16] = (s16 + (1 << 20)) >> 21
+ s17 += carry[16]
+ s16 -= carry[16] << 21
+ carry[18] = (s18 + (1 << 20)) >> 21
+ s19 += carry[18]
+ s18 -= carry[18] << 21
+ carry[20] = (s20 + (1 << 20)) >> 21
+ s21 += carry[20]
+ s20 -= carry[20] << 21
+ carry[22] = (s22 + (1 << 20)) >> 21
+ s23 += carry[22]
+ s22 -= carry[22] << 21
+
+ carry[1] = (s1 + (1 << 20)) >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[3] = (s3 + (1 << 20)) >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[5] = (s5 + (1 << 20)) >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+ carry[13] = (s13 + (1 << 20)) >> 21
+ s14 += carry[13]
+ s13 -= carry[13] << 21
+ carry[15] = (s15 + (1 << 20)) >> 21
+ s16 += carry[15]
+ s15 -= carry[15] << 21
+ carry[17] = (s17 + (1 << 20)) >> 21
+ s18 += carry[17]
+ s17 -= carry[17] << 21
+ carry[19] = (s19 + (1 << 20)) >> 21
+ s20 += carry[19]
+ s19 -= carry[19] << 21
+ carry[21] = (s21 + (1 << 20)) >> 21
+ s22 += carry[21]
+ s21 -= carry[21] << 21
+
+ s11 += s23 * 666643
+ s12 += s23 * 470296
+ s13 += s23 * 654183
+ s14 -= s23 * 997805
+ s15 += s23 * 136657
+ s16 -= s23 * 683901
+ s23 = 0
+
+ s10 += s22 * 666643
+ s11 += s22 * 470296
+ s12 += s22 * 654183
+ s13 -= s22 * 997805
+ s14 += s22 * 136657
+ s15 -= s22 * 683901
+ s22 = 0
+
+ s9 += s21 * 666643
+ s10 += s21 * 470296
+ s11 += s21 * 654183
+ s12 -= s21 * 997805
+ s13 += s21 * 136657
+ s14 -= s21 * 683901
+ s21 = 0
+
+ s8 += s20 * 666643
+ s9 += s20 * 470296
+ s10 += s20 * 654183
+ s11 -= s20 * 997805
+ s12 += s20 * 136657
+ s13 -= s20 * 683901
+ s20 = 0
+
+ s7 += s19 * 666643
+ s8 += s19 * 470296
+ s9 += s19 * 654183
+ s10 -= s19 * 997805
+ s11 += s19 * 136657
+ s12 -= s19 * 683901
+ s19 = 0
+
+ s6 += s18 * 666643
+ s7 += s18 * 470296
+ s8 += s18 * 654183
+ s9 -= s18 * 997805
+ s10 += s18 * 136657
+ s11 -= s18 * 683901
+ s18 = 0
+
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[12] = (s12 + (1 << 20)) >> 21
+ s13 += carry[12]
+ s12 -= carry[12] << 21
+ carry[14] = (s14 + (1 << 20)) >> 21
+ s15 += carry[14]
+ s14 -= carry[14] << 21
+ carry[16] = (s16 + (1 << 20)) >> 21
+ s17 += carry[16]
+ s16 -= carry[16] << 21
+
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+ carry[13] = (s13 + (1 << 20)) >> 21
+ s14 += carry[13]
+ s13 -= carry[13] << 21
+ carry[15] = (s15 + (1 << 20)) >> 21
+ s16 += carry[15]
+ s15 -= carry[15] << 21
+
+ s5 += s17 * 666643
+ s6 += s17 * 470296
+ s7 += s17 * 654183
+ s8 -= s17 * 997805
+ s9 += s17 * 136657
+ s10 -= s17 * 683901
+ s17 = 0
+
+ s4 += s16 * 666643
+ s5 += s16 * 470296
+ s6 += s16 * 654183
+ s7 -= s16 * 997805
+ s8 += s16 * 136657
+ s9 -= s16 * 683901
+ s16 = 0
+
+ s3 += s15 * 666643
+ s4 += s15 * 470296
+ s5 += s15 * 654183
+ s6 -= s15 * 997805
+ s7 += s15 * 136657
+ s8 -= s15 * 683901
+ s15 = 0
+
+ s2 += s14 * 666643
+ s3 += s14 * 470296
+ s4 += s14 * 654183
+ s5 -= s14 * 997805
+ s6 += s14 * 136657
+ s7 -= s14 * 683901
+ s14 = 0
+
+ s1 += s13 * 666643
+ s2 += s13 * 470296
+ s3 += s13 * 654183
+ s4 -= s13 * 997805
+ s5 += s13 * 136657
+ s6 -= s13 * 683901
+ s13 = 0
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = (s0 + (1 << 20)) >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[2] = (s2 + (1 << 20)) >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[4] = (s4 + (1 << 20)) >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ carry[1] = (s1 + (1 << 20)) >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[3] = (s3 + (1 << 20)) >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[5] = (s5 + (1 << 20)) >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[11] = s11 >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ s[0] = byte(s0 >> 0)
+ s[1] = byte(s0 >> 8)
+ s[2] = byte((s0 >> 16) | (s1 << 5))
+ s[3] = byte(s1 >> 3)
+ s[4] = byte(s1 >> 11)
+ s[5] = byte((s1 >> 19) | (s2 << 2))
+ s[6] = byte(s2 >> 6)
+ s[7] = byte((s2 >> 14) | (s3 << 7))
+ s[8] = byte(s3 >> 1)
+ s[9] = byte(s3 >> 9)
+ s[10] = byte((s3 >> 17) | (s4 << 4))
+ s[11] = byte(s4 >> 4)
+ s[12] = byte(s4 >> 12)
+ s[13] = byte((s4 >> 20) | (s5 << 1))
+ s[14] = byte(s5 >> 7)
+ s[15] = byte((s5 >> 15) | (s6 << 6))
+ s[16] = byte(s6 >> 2)
+ s[17] = byte(s6 >> 10)
+ s[18] = byte((s6 >> 18) | (s7 << 3))
+ s[19] = byte(s7 >> 5)
+ s[20] = byte(s7 >> 13)
+ s[21] = byte(s8 >> 0)
+ s[22] = byte(s8 >> 8)
+ s[23] = byte((s8 >> 16) | (s9 << 5))
+ s[24] = byte(s9 >> 3)
+ s[25] = byte(s9 >> 11)
+ s[26] = byte((s9 >> 19) | (s10 << 2))
+ s[27] = byte(s10 >> 6)
+ s[28] = byte((s10 >> 14) | (s11 << 7))
+ s[29] = byte(s11 >> 1)
+ s[30] = byte(s11 >> 9)
+ s[31] = byte(s11 >> 17)
+}
+
+// Input:
+// s[0]+256*s[1]+...+256^63*s[63] = s
+//
+// Output:
+// s[0]+256*s[1]+...+256^31*s[31] = s mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
+func ScReduce(out *[32]byte, s *[64]byte) {
+ s0 := 2097151 & load3(s[:])
+ s1 := 2097151 & (load4(s[2:]) >> 5)
+ s2 := 2097151 & (load3(s[5:]) >> 2)
+ s3 := 2097151 & (load4(s[7:]) >> 7)
+ s4 := 2097151 & (load4(s[10:]) >> 4)
+ s5 := 2097151 & (load3(s[13:]) >> 1)
+ s6 := 2097151 & (load4(s[15:]) >> 6)
+ s7 := 2097151 & (load3(s[18:]) >> 3)
+ s8 := 2097151 & load3(s[21:])
+ s9 := 2097151 & (load4(s[23:]) >> 5)
+ s10 := 2097151 & (load3(s[26:]) >> 2)
+ s11 := 2097151 & (load4(s[28:]) >> 7)
+ s12 := 2097151 & (load4(s[31:]) >> 4)
+ s13 := 2097151 & (load3(s[34:]) >> 1)
+ s14 := 2097151 & (load4(s[36:]) >> 6)
+ s15 := 2097151 & (load3(s[39:]) >> 3)
+ s16 := 2097151 & load3(s[42:])
+ s17 := 2097151 & (load4(s[44:]) >> 5)
+ s18 := 2097151 & (load3(s[47:]) >> 2)
+ s19 := 2097151 & (load4(s[49:]) >> 7)
+ s20 := 2097151 & (load4(s[52:]) >> 4)
+ s21 := 2097151 & (load3(s[55:]) >> 1)
+ s22 := 2097151 & (load4(s[57:]) >> 6)
+ s23 := (load4(s[60:]) >> 3)
+
+ s11 += s23 * 666643
+ s12 += s23 * 470296
+ s13 += s23 * 654183
+ s14 -= s23 * 997805
+ s15 += s23 * 136657
+ s16 -= s23 * 683901
+ s23 = 0
+
+ s10 += s22 * 666643
+ s11 += s22 * 470296
+ s12 += s22 * 654183
+ s13 -= s22 * 997805
+ s14 += s22 * 136657
+ s15 -= s22 * 683901
+ s22 = 0
+
+ s9 += s21 * 666643
+ s10 += s21 * 470296
+ s11 += s21 * 654183
+ s12 -= s21 * 997805
+ s13 += s21 * 136657
+ s14 -= s21 * 683901
+ s21 = 0
+
+ s8 += s20 * 666643
+ s9 += s20 * 470296
+ s10 += s20 * 654183
+ s11 -= s20 * 997805
+ s12 += s20 * 136657
+ s13 -= s20 * 683901
+ s20 = 0
+
+ s7 += s19 * 666643
+ s8 += s19 * 470296
+ s9 += s19 * 654183
+ s10 -= s19 * 997805
+ s11 += s19 * 136657
+ s12 -= s19 * 683901
+ s19 = 0
+
+ s6 += s18 * 666643
+ s7 += s18 * 470296
+ s8 += s18 * 654183
+ s9 -= s18 * 997805
+ s10 += s18 * 136657
+ s11 -= s18 * 683901
+ s18 = 0
+
+ var carry [17]int64
+
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[12] = (s12 + (1 << 20)) >> 21
+ s13 += carry[12]
+ s12 -= carry[12] << 21
+ carry[14] = (s14 + (1 << 20)) >> 21
+ s15 += carry[14]
+ s14 -= carry[14] << 21
+ carry[16] = (s16 + (1 << 20)) >> 21
+ s17 += carry[16]
+ s16 -= carry[16] << 21
+
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+ carry[13] = (s13 + (1 << 20)) >> 21
+ s14 += carry[13]
+ s13 -= carry[13] << 21
+ carry[15] = (s15 + (1 << 20)) >> 21
+ s16 += carry[15]
+ s15 -= carry[15] << 21
+
+ s5 += s17 * 666643
+ s6 += s17 * 470296
+ s7 += s17 * 654183
+ s8 -= s17 * 997805
+ s9 += s17 * 136657
+ s10 -= s17 * 683901
+ s17 = 0
+
+ s4 += s16 * 666643
+ s5 += s16 * 470296
+ s6 += s16 * 654183
+ s7 -= s16 * 997805
+ s8 += s16 * 136657
+ s9 -= s16 * 683901
+ s16 = 0
+
+ s3 += s15 * 666643
+ s4 += s15 * 470296
+ s5 += s15 * 654183
+ s6 -= s15 * 997805
+ s7 += s15 * 136657
+ s8 -= s15 * 683901
+ s15 = 0
+
+ s2 += s14 * 666643
+ s3 += s14 * 470296
+ s4 += s14 * 654183
+ s5 -= s14 * 997805
+ s6 += s14 * 136657
+ s7 -= s14 * 683901
+ s14 = 0
+
+ s1 += s13 * 666643
+ s2 += s13 * 470296
+ s3 += s13 * 654183
+ s4 -= s13 * 997805
+ s5 += s13 * 136657
+ s6 -= s13 * 683901
+ s13 = 0
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = (s0 + (1 << 20)) >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[2] = (s2 + (1 << 20)) >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[4] = (s4 + (1 << 20)) >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[6] = (s6 + (1 << 20)) >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[8] = (s8 + (1 << 20)) >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[10] = (s10 + (1 << 20)) >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ carry[1] = (s1 + (1 << 20)) >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[3] = (s3 + (1 << 20)) >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[5] = (s5 + (1 << 20)) >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[7] = (s7 + (1 << 20)) >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[9] = (s9 + (1 << 20)) >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[11] = (s11 + (1 << 20)) >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+ carry[11] = s11 >> 21
+ s12 += carry[11]
+ s11 -= carry[11] << 21
+
+ s0 += s12 * 666643
+ s1 += s12 * 470296
+ s2 += s12 * 654183
+ s3 -= s12 * 997805
+ s4 += s12 * 136657
+ s5 -= s12 * 683901
+ s12 = 0
+
+ carry[0] = s0 >> 21
+ s1 += carry[0]
+ s0 -= carry[0] << 21
+ carry[1] = s1 >> 21
+ s2 += carry[1]
+ s1 -= carry[1] << 21
+ carry[2] = s2 >> 21
+ s3 += carry[2]
+ s2 -= carry[2] << 21
+ carry[3] = s3 >> 21
+ s4 += carry[3]
+ s3 -= carry[3] << 21
+ carry[4] = s4 >> 21
+ s5 += carry[4]
+ s4 -= carry[4] << 21
+ carry[5] = s5 >> 21
+ s6 += carry[5]
+ s5 -= carry[5] << 21
+ carry[6] = s6 >> 21
+ s7 += carry[6]
+ s6 -= carry[6] << 21
+ carry[7] = s7 >> 21
+ s8 += carry[7]
+ s7 -= carry[7] << 21
+ carry[8] = s8 >> 21
+ s9 += carry[8]
+ s8 -= carry[8] << 21
+ carry[9] = s9 >> 21
+ s10 += carry[9]
+ s9 -= carry[9] << 21
+ carry[10] = s10 >> 21
+ s11 += carry[10]
+ s10 -= carry[10] << 21
+
+ out[0] = byte(s0 >> 0)
+ out[1] = byte(s0 >> 8)
+ out[2] = byte((s0 >> 16) | (s1 << 5))
+ out[3] = byte(s1 >> 3)
+ out[4] = byte(s1 >> 11)
+ out[5] = byte((s1 >> 19) | (s2 << 2))
+ out[6] = byte(s2 >> 6)
+ out[7] = byte((s2 >> 14) | (s3 << 7))
+ out[8] = byte(s3 >> 1)
+ out[9] = byte(s3 >> 9)
+ out[10] = byte((s3 >> 17) | (s4 << 4))
+ out[11] = byte(s4 >> 4)
+ out[12] = byte(s4 >> 12)
+ out[13] = byte((s4 >> 20) | (s5 << 1))
+ out[14] = byte(s5 >> 7)
+ out[15] = byte((s5 >> 15) | (s6 << 6))
+ out[16] = byte(s6 >> 2)
+ out[17] = byte(s6 >> 10)
+ out[18] = byte((s6 >> 18) | (s7 << 3))
+ out[19] = byte(s7 >> 5)
+ out[20] = byte(s7 >> 13)
+ out[21] = byte(s8 >> 0)
+ out[22] = byte(s8 >> 8)
+ out[23] = byte((s8 >> 16) | (s9 << 5))
+ out[24] = byte(s9 >> 3)
+ out[25] = byte(s9 >> 11)
+ out[26] = byte((s9 >> 19) | (s10 << 2))
+ out[27] = byte(s10 >> 6)
+ out[28] = byte((s10 >> 14) | (s11 << 7))
+ out[29] = byte(s11 >> 1)
+ out[30] = byte(s11 >> 9)
+ out[31] = byte(s11 >> 17)
+}
+
+// order is the order of Curve25519 in little-endian form.
+var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000}
+
+// ScMinimal returns true if the given scalar is less than the order of the
+// curve.
+func ScMinimal(scalar *[32]byte) bool {
+ for i := 3; ; i-- {
+ v := binary.LittleEndian.Uint64(scalar[i*8:])
+ if v > order[i] {
+ return false
+ } else if v < order[i] {
+ break
+ } else if i == 0 {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/vendor/github.com/cretz/bine/torutil/key.go b/vendor/github.com/cretz/bine/torutil/key.go
new file mode 100644
index 0000000..70407e2
--- /dev/null
+++ b/vendor/github.com/cretz/bine/torutil/key.go
@@ -0,0 +1,85 @@
+package torutil
+
+import (
+ "crypto"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/x509"
+ "encoding/base32"
+ "fmt"
+ "strings"
+
+ "github.com/cretz/bine/torutil/ed25519"
+ "golang.org/x/crypto/sha3"
+)
+
+var serviceIDEncoding = base32.StdEncoding.WithPadding(base32.NoPadding)
+
+// OnionServiceIDFromPrivateKey generates the onion service ID from the given
+// private key. This panics if the private key is not a 1024-bit
+// crypto/*rsa.PrivateKey or github.com/cretz/bine/torutil/ed25519.KeyPair.
+func OnionServiceIDFromPrivateKey(key crypto.PrivateKey) string {
+ switch k := key.(type) {
+ case *rsa.PrivateKey:
+ return OnionServiceIDFromV2PublicKey(&k.PublicKey)
+ case ed25519.KeyPair:
+ return OnionServiceIDFromV3PublicKey(k.PublicKey())
+ }
+ panic(fmt.Sprintf("Unrecognized private key type: %T", key))
+}
+
+// OnionServiceIDFromPublicKey generates the onion service ID from the given
+// public key. This panics if the public key is not a 1024-bit
+// crypto/*rsa.PublicKey or github.com/cretz/bine/torutil/ed25519.PublicKey.
+func OnionServiceIDFromPublicKey(key crypto.PublicKey) string {
+ switch k := key.(type) {
+ case *rsa.PublicKey:
+ return OnionServiceIDFromV2PublicKey(k)
+ case ed25519.PublicKey:
+ return OnionServiceIDFromV3PublicKey(k)
+ }
+ panic(fmt.Sprintf("Unrecognized public key type: %T", key))
+}
+
+// OnionServiceIDFromV2PublicKey generates a V2 service ID for the given
+// RSA-1024 public key. Panics if not a 1024-bit key.
+func OnionServiceIDFromV2PublicKey(key *rsa.PublicKey) string {
+ if key.N.BitLen() != 1024 {
+ panic("RSA key not 1024 bit")
+ }
+ h := sha1.New()
+ h.Write(x509.MarshalPKCS1PublicKey(key))
+ return strings.ToLower(serviceIDEncoding.EncodeToString(h.Sum(nil)[:10]))
+}
+
+// OnionServiceIDFromV3PublicKey generates a V3 service ID for the given
+// ED25519 public key.
+func OnionServiceIDFromV3PublicKey(key ed25519.PublicKey) string {
+ checkSum := sha3.Sum256(append(append([]byte(".onion checksum"), key...), 0x03))
+ var keyBytes [35]byte
+ copy(keyBytes[:], key)
+ keyBytes[32] = checkSum[0]
+ keyBytes[33] = checkSum[1]
+ keyBytes[34] = 0x03
+ return strings.ToLower(serviceIDEncoding.EncodeToString(keyBytes[:]))
+}
+
+// PublicKeyFromV3OnionServiceID returns a public key for the given service ID
+// or an error if the service ID is invalid.
+func PublicKeyFromV3OnionServiceID(id string) (ed25519.PublicKey, error) {
+ byts, err := serviceIDEncoding.DecodeString(strings.ToUpper(id))
+ if err != nil {
+ return nil, err
+ } else if len(byts) != 35 {
+ return nil, fmt.Errorf("Invalid id length")
+ } else if byts[34] != 0x03 {
+ return nil, fmt.Errorf("Invalid version")
+ }
+ // Do a checksum check
+ key := ed25519.PublicKey(byts[:32])
+ checkSum := sha3.Sum256(append(append([]byte(".onion checksum"), key...), 0x03))
+ if byts[32] != checkSum[0] || byts[33] != checkSum[1] {
+ return nil, fmt.Errorf("Invalid checksum")
+ }
+ return key, nil
+}
diff --git a/vendor/github.com/cretz/bine/torutil/string.go b/vendor/github.com/cretz/bine/torutil/string.go
new file mode 100644
index 0000000..86cee96
--- /dev/null
+++ b/vendor/github.com/cretz/bine/torutil/string.go
@@ -0,0 +1,112 @@
+package torutil
+
+import (
+ "fmt"
+ "strings"
+)
+
+// PartitionString returns the two parts of a string delimited by the first
+// occurrence of ch. If ch does not exist, the second string is empty and the
+// resulting bool is false. Otherwise it is true.
+func PartitionString(str string, ch byte) (string, string, bool) {
+ index := strings.IndexByte(str, ch)
+ if index == -1 {
+ return str, "", false
+ }
+ return str[:index], str[index+1:], true
+}
+
+// PartitionStringFromEnd is same as PartitionString except it delimts by the
+// last occurrence of ch instead of the first.
+func PartitionStringFromEnd(str string, ch byte) (string, string, bool) {
+ index := strings.LastIndexByte(str, ch)
+ if index == -1 {
+ return str, "", false
+ }
+ return str[:index], str[index+1:], true
+}
+
+// EscapeSimpleQuotedStringIfNeeded calls EscapeSimpleQuotedString only if the
+// string contains a space, backslash, double quote, newline, or carriage return
+// character.
+func EscapeSimpleQuotedStringIfNeeded(str string) string {
+ if strings.ContainsAny(str, " \\\"\r\n") {
+ return EscapeSimpleQuotedString(str)
+ }
+ return str
+}
+
+var simpleQuotedStringEscapeReplacer = strings.NewReplacer(
+ "\\", "\\\\",
+ "\"", "\\\"",
+ "\r", "\\r",
+ "\n", "\\n",
+)
+
+// EscapeSimpleQuotedString calls EscapeSimpleQuotedStringContents and then
+// surrounds the entire string with double quotes.
+func EscapeSimpleQuotedString(str string) string {
+ return "\"" + EscapeSimpleQuotedStringContents(str) + "\""
+}
+
+// EscapeSimpleQuotedStringContents escapes backslashes, double quotes,
+// newlines, and carriage returns in str.
+func EscapeSimpleQuotedStringContents(str string) string {
+ return simpleQuotedStringEscapeReplacer.Replace(str)
+}
+
+// UnescapeSimpleQuotedStringIfNeeded calls UnescapeSimpleQuotedString only if
+// str is surrounded with double quotes.
+func UnescapeSimpleQuotedStringIfNeeded(str string) (string, error) {
+ if len(str) >= 2 && str[0] == '"' && str[len(str)-1] == '"' {
+ return UnescapeSimpleQuotedString(str)
+ }
+ return str, nil
+}
+
+// UnescapeSimpleQuotedString removes surrounding double quotes and calls
+// UnescapeSimpleQuotedStringContents.
+func UnescapeSimpleQuotedString(str string) (string, error) {
+ if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' {
+ return "", fmt.Errorf("Missing quotes")
+ }
+ return UnescapeSimpleQuotedStringContents(str[1 : len(str)-1])
+}
+
+// UnescapeSimpleQuotedStringContents unescapes backslashes, double quotes,
+// newlines, and carriage returns. Also errors if those aren't escaped.
+func UnescapeSimpleQuotedStringContents(str string) (string, error) {
+ ret := ""
+ escaping := false
+ for _, c := range str {
+ switch c {
+ case '\\':
+ if escaping {
+ ret += "\\"
+ }
+ escaping = !escaping
+ case '"':
+ if !escaping {
+ return "", fmt.Errorf("Unescaped quote")
+ }
+ ret += "\""
+ escaping = false
+ case '\r', '\n':
+ return "", fmt.Errorf("Unescaped newline or carriage return")
+ default:
+ if escaping {
+ if c == 'r' {
+ ret += "\r"
+ } else if c == 'n' {
+ ret += "\n"
+ } else {
+ return "", fmt.Errorf("Unexpected escape")
+ }
+ } else {
+ ret += string(c)
+ }
+ escaping = false
+ }
+ }
+ return ret, nil
+}
diff --git a/vendor/github.com/google/uuid/.travis.yml b/vendor/github.com/google/uuid/.travis.yml
new file mode 100644
index 0000000..d8156a6
--- /dev/null
+++ b/vendor/github.com/google/uuid/.travis.yml
@@ -0,0 +1,9 @@
+language: go
+
+go:
+ - 1.4.3
+ - 1.5.3
+ - tip
+
+script:
+ - go test -v ./...
diff --git a/vendor/github.com/google/uuid/CONTRIBUTING.md b/vendor/github.com/google/uuid/CONTRIBUTING.md
new file mode 100644
index 0000000..04fdf09
--- /dev/null
+++ b/vendor/github.com/google/uuid/CONTRIBUTING.md
@@ -0,0 +1,10 @@
+# How to contribute
+
+We definitely welcome patches and contribution to this project!
+
+### Legal requirements
+
+In order to protect both you and ourselves, you will need to sign the
+[Contributor License Agreement](https://cla.developers.google.com/clas).
+
+You may have already signed it for other Google projects.
diff --git a/vendor/github.com/google/uuid/CONTRIBUTORS b/vendor/github.com/google/uuid/CONTRIBUTORS
new file mode 100644
index 0000000..b4bb97f
--- /dev/null
+++ b/vendor/github.com/google/uuid/CONTRIBUTORS
@@ -0,0 +1,9 @@
+Paul Borman <borman@google.com>
+bmatsuo
+shawnps
+theory
+jboverfelt
+dsymonds
+cd1
+wallclockbuilder
+dansouza
diff --git a/vendor/github.com/google/uuid/LICENSE b/vendor/github.com/google/uuid/LICENSE
new file mode 100644
index 0000000..5dc6826
--- /dev/null
+++ b/vendor/github.com/google/uuid/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009,2014 Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+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
+OWNER 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.
diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md
new file mode 100644
index 0000000..f765a46
--- /dev/null
+++ b/vendor/github.com/google/uuid/README.md
@@ -0,0 +1,19 @@
+# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master)
+The uuid package generates and inspects UUIDs based on
+[RFC 4122](http://tools.ietf.org/html/rfc4122)
+and DCE 1.1: Authentication and Security Services.
+
+This package is based on the github.com/pborman/uuid package (previously named
+code.google.com/p/go-uuid). It differs from these earlier packages in that
+a UUID is a 16 byte array rather than a byte slice. One loss due to this
+change is the ability to represent an invalid UUID (vs a NIL UUID).
+
+###### Install
+`go get github.com/google/uuid`
+
+###### Documentation
+[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid)
+
+Full `go doc` style documentation for the package can be viewed online without
+installing this package by using the GoDoc site here:
+http://pkg.go.dev/github.com/google/uuid
diff --git a/vendor/github.com/google/uuid/dce.go b/vendor/github.com/google/uuid/dce.go
new file mode 100644
index 0000000..fa820b9
--- /dev/null
+++ b/vendor/github.com/google/uuid/dce.go
@@ -0,0 +1,80 @@
+// Copyright 2016 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.
+
+package uuid
+
+import (
+ "encoding/binary"
+ "fmt"
+ "os"
+)
+
+// A Domain represents a Version 2 domain
+type Domain byte
+
+// Domain constants for DCE Security (Version 2) UUIDs.
+const (
+ Person = Domain(0)
+ Group = Domain(1)
+ Org = Domain(2)
+)
+
+// NewDCESecurity returns a DCE Security (Version 2) UUID.
+//
+// The domain should be one of Person, Group or Org.
+// On a POSIX system the id should be the users UID for the Person
+// domain and the users GID for the Group. The meaning of id for
+// the domain Org or on non-POSIX systems is site defined.
+//
+// For a given domain/id pair the same token may be returned for up to
+// 7 minutes and 10 seconds.
+func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
+ uuid, err := NewUUID()
+ if err == nil {
+ uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
+ uuid[9] = byte(domain)
+ binary.BigEndian.PutUint32(uuid[0:], id)
+ }
+ return uuid, err
+}
+
+// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
+// domain with the id returned by os.Getuid.
+//
+// NewDCESecurity(Person, uint32(os.Getuid()))
+func NewDCEPerson() (UUID, error) {
+ return NewDCESecurity(Person, uint32(os.Getuid()))
+}
+
+// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
+// domain with the id returned by os.Getgid.
+//
+// NewDCESecurity(Group, uint32(os.Getgid()))
+func NewDCEGroup() (UUID, error) {
+ return NewDCESecurity(Group, uint32(os.Getgid()))
+}
+
+// Domain returns the domain for a Version 2 UUID. Domains are only defined
+// for Version 2 UUIDs.
+func (uuid UUID) Domain() Domain {
+ return Domain(uuid[9])
+}
+
+// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
+// UUIDs.
+func (uuid UUID) ID() uint32 {
+ return binary.BigEndian.Uint32(uuid[0:4])
+}
+
+func (d Domain) String() string {
+ switch d {
+ case Person:
+ return "Person"
+ case Group:
+ return "Group"
+ case Org:
+ return "Org"
+ }
+ return fmt.Sprintf("Domain%d", int(d))
+}
diff --git a/vendor/github.com/google/uuid/doc.go b/vendor/github.com/google/uuid/doc.go
new file mode 100644
index 0000000..5b8a4b9
--- /dev/null
+++ b/vendor/github.com/google/uuid/doc.go
@@ -0,0 +1,12 @@
+// Copyright 2016 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.
+
+// Package uuid generates and inspects UUIDs.
+//
+// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
+// Services.
+//
+// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
+// maps or compared directly.
+package uuid
diff --git a/vendor/github.com/google/uuid/go.mod b/vendor/github.com/google/uuid/go.mod
new file mode 100644
index 0000000..fc84cd7
--- /dev/null
+++ b/vendor/github.com/google/uuid/go.mod
@@ -0,0 +1 @@
+module github.com/google/uuid
diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go
new file mode 100644
index 0000000..b404f4b
--- /dev/null
+++ b/vendor/github.com/google/uuid/hash.go
@@ -0,0 +1,53 @@
+// Copyright 2016 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.
+
+package uuid
+
+import (
+ "crypto/md5"
+ "crypto/sha1"
+ "hash"
+)
+
+// Well known namespace IDs and UUIDs
+var (
+ NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
+ NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
+ NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
+ NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
+ Nil UUID // empty UUID, all zeros
+)
+
+// NewHash returns a new UUID derived from the hash of space concatenated with
+// data generated by h. The hash should be at least 16 byte in length. The
+// first 16 bytes of the hash are used to form the UUID. The version of the
+// UUID will be the lower 4 bits of version. NewHash is used to implement
+// NewMD5 and NewSHA1.
+func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
+ h.Reset()
+ h.Write(space[:]) //nolint:errcheck
+ h.Write(data) //nolint:errcheck
+ s := h.Sum(nil)
+ var uuid UUID
+ copy(uuid[:], s)
+ uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
+ uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
+ return uuid
+}
+
+// NewMD5 returns a new MD5 (Version 3) UUID based on the
+// supplied name space and data. It is the same as calling:
+//
+// NewHash(md5.New(), space, data, 3)
+func NewMD5(space UUID, data []byte) UUID {
+ return NewHash(md5.New(), space, data, 3)
+}
+
+// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
+// supplied name space and data. It is the same as calling:
+//
+// NewHash(sha1.New(), space, data, 5)
+func NewSHA1(space UUID, data []byte) UUID {
+ return NewHash(sha1.New(), space, data, 5)
+}
diff --git a/vendor/github.com/google/uuid/marshal.go b/vendor/github.com/google/uuid/marshal.go
new file mode 100644
index 0000000..14bd340
--- /dev/null
+++ b/vendor/github.com/google/uuid/marshal.go
@@ -0,0 +1,38 @@
+// Copyright 2016 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.
+
+package uuid
+
+import "fmt"
+
+// MarshalText implements encoding.TextMarshaler.
+func (uuid UUID) MarshalText() ([]byte, error) {
+ var js [36]byte
+ encodeHex(js[:], uuid)
+ return js[:], nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (uuid *UUID) UnmarshalText(data []byte) error {
+ id, err := ParseBytes(data)
+ if err != nil {
+ return err
+ }
+ *uuid = id
+ return nil
+}
+
+// MarshalBinary implements encoding.BinaryMarshaler.
+func (uuid UUID) MarshalBinary() ([]byte, error) {
+ return uuid[:], nil
+}
+
+// UnmarshalBinary implements encoding.BinaryUnmarshaler.
+func (uuid *UUID) UnmarshalBinary(data []byte) error {
+ if len(data) != 16 {
+ return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
+ }
+ copy(uuid[:], data)
+ return nil
+}
diff --git a/vendor/github.com/google/uuid/node.go b/vendor/github.com/google/uuid/node.go
new file mode 100644
index 0000000..d651a2b
--- /dev/null
+++ b/vendor/github.com/google/uuid/node.go
@@ -0,0 +1,90 @@
+// Copyright 2016 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.
+
+package uuid
+
+import (
+ "sync"
+)
+
+var (
+ nodeMu sync.Mutex
+ ifname string // name of interface being used
+ nodeID [6]byte // hardware for version 1 UUIDs
+ zeroID [6]byte // nodeID with only 0's
+)
+
+// NodeInterface returns the name of the interface from which the NodeID was
+// derived. The interface "user" is returned if the NodeID was set by
+// SetNodeID.
+func NodeInterface() string {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ return ifname
+}
+
+// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
+// If name is "" then the first usable interface found will be used or a random
+// Node ID will be generated. If a named interface cannot be found then false
+// is returned.
+//
+// SetNodeInterface never fails when name is "".
+func SetNodeInterface(name string) bool {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ return setNodeInterface(name)
+}
+
+func setNodeInterface(name string) bool {
+ iname, addr := getHardwareInterface(name) // null implementation for js
+ if iname != "" && addr != nil {
+ ifname = iname
+ copy(nodeID[:], addr)
+ return true
+ }
+
+ // We found no interfaces with a valid hardware address. If name
+ // does not specify a specific interface generate a random Node ID
+ // (section 4.1.6)
+ if name == "" {
+ ifname = "random"
+ randomBits(nodeID[:])
+ return true
+ }
+ return false
+}
+
+// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
+// if not already set.
+func NodeID() []byte {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ if nodeID == zeroID {
+ setNodeInterface("")
+ }
+ nid := nodeID
+ return nid[:]
+}
+
+// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
+// of id are used. If id is less than 6 bytes then false is returned and the
+// Node ID is not set.
+func SetNodeID(id []byte) bool {
+ if len(id) < 6 {
+ return false
+ }
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ copy(nodeID[:], id)
+ ifname = "user"
+ return true
+}
+
+// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
+// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) NodeID() []byte {
+ var node [6]byte
+ copy(node[:], uuid[10:])
+ return node[:]
+}
diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go
new file mode 100644
index 0000000..24b78ed
--- /dev/null
+++ b/vendor/github.com/google/uuid/node_js.go
@@ -0,0 +1,12 @@
+// Copyright 2017 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.
+
+// +build js
+
+package uuid
+
+// getHardwareInterface returns nil values for the JS version of the code.
+// This remvoves the "net" dependency, because it is not used in the browser.
+// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
+func getHardwareInterface(name string) (string, []byte) { return "", nil }
diff --git a/vendor/github.com/google/uuid/node_net.go b/vendor/github.com/google/uuid/node_net.go
new file mode 100644
index 0000000..0cbbcdd
--- /dev/null
+++ b/vendor/github.com/google/uuid/node_net.go
@@ -0,0 +1,33 @@
+// Copyright 2017 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.
+
+// +build !js
+
+package uuid
+
+import "net"
+
+var interfaces []net.Interface // cached list of interfaces
+
+// getHardwareInterface returns the name and hardware address of interface name.
+// If name is "" then the name and hardware address of one of the system's
+// interfaces is returned. If no interfaces are found (name does not exist or
+// there are no interfaces) then "", nil is returned.
+//
+// Only addresses of at least 6 bytes are returned.
+func getHardwareInterface(name string) (string, []byte) {
+ if interfaces == nil {
+ var err error
+ interfaces, err = net.Interfaces()
+ if err != nil {
+ return "", nil
+ }
+ }
+ for _, ifs := range interfaces {
+ if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
+ return ifs.Name, ifs.HardwareAddr
+ }
+ }
+ return "", nil
+}
diff --git a/vendor/github.com/google/uuid/sql.go b/vendor/github.com/google/uuid/sql.go
new file mode 100644
index 0000000..2e02ec0
--- /dev/null
+++ b/vendor/github.com/google/uuid/sql.go
@@ -0,0 +1,59 @@
+// Copyright 2016 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.
+
+package uuid
+
+import (
+ "database/sql/driver"
+ "fmt"
+)
+
+// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
+// Currently, database types that map to string and []byte are supported. Please
+// consult database-specific driver documentation for matching types.
+func (uuid *UUID) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case nil:
+ return nil
+
+ case string:
+ // if an empty UUID comes from a table, we return a null UUID
+ if src == "" {
+ return nil
+ }
+
+ // see Parse for required string format
+ u, err := Parse(src)
+ if err != nil {
+ return fmt.Errorf("Scan: %v", err)
+ }
+
+ *uuid = u
+
+ case []byte:
+ // if an empty UUID comes from a table, we return a null UUID
+ if len(src) == 0 {
+ return nil
+ }
+
+ // assumes a simple slice of bytes if 16 bytes
+ // otherwise attempts to parse
+ if len(src) != 16 {
+ return uuid.Scan(string(src))
+ }
+ copy((*uuid)[:], src)
+
+ default:
+ return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
+ }
+
+ return nil
+}
+
+// Value implements sql.Valuer so that UUIDs can be written to databases
+// transparently. Currently, UUIDs map to strings. Please consult
+// database-specific driver documentation for matching types.
+func (uuid UUID) Value() (driver.Value, error) {
+ return uuid.String(), nil
+}
diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go
new file mode 100644
index 0000000..e6ef06c
--- /dev/null
+++ b/vendor/github.com/google/uuid/time.go
@@ -0,0 +1,123 @@
+// Copyright 2016 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.
+
+package uuid
+
+import (
+ "encoding/binary"
+ "sync"
+ "time"
+)
+
+// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
+// 1582.
+type Time int64
+
+const (
+ lillian = 2299160 // Julian day of 15 Oct 1582
+ unix = 2440587 // Julian day of 1 Jan 1970
+ epoch = unix - lillian // Days between epochs
+ g1582 = epoch * 86400 // seconds between epochs
+ g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
+)
+
+var (
+ timeMu sync.Mutex
+ lasttime uint64 // last time we returned
+ clockSeq uint16 // clock sequence for this run
+
+ timeNow = time.Now // for testing
+)
+
+// UnixTime converts t the number of seconds and nanoseconds using the Unix
+// epoch of 1 Jan 1970.
+func (t Time) UnixTime() (sec, nsec int64) {
+ sec = int64(t - g1582ns100)
+ nsec = (sec % 10000000) * 100
+ sec /= 10000000
+ return sec, nsec
+}
+
+// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
+// clock sequence as well as adjusting the clock sequence as needed. An error
+// is returned if the current time cannot be determined.
+func GetTime() (Time, uint16, error) {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ return getTime()
+}
+
+func getTime() (Time, uint16, error) {
+ t := timeNow()
+
+ // If we don't have a clock sequence already, set one.
+ if clockSeq == 0 {
+ setClockSequence(-1)
+ }
+ now := uint64(t.UnixNano()/100) + g1582ns100
+
+ // If time has gone backwards with this clock sequence then we
+ // increment the clock sequence
+ if now <= lasttime {
+ clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
+ }
+ lasttime = now
+ return Time(now), clockSeq, nil
+}
+
+// ClockSequence returns the current clock sequence, generating one if not
+// already set. The clock sequence is only used for Version 1 UUIDs.
+//
+// The uuid package does not use global static storage for the clock sequence or
+// the last time a UUID was generated. Unless SetClockSequence is used, a new
+// random clock sequence is generated the first time a clock sequence is
+// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
+func ClockSequence() int {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ return clockSequence()
+}
+
+func clockSequence() int {
+ if clockSeq == 0 {
+ setClockSequence(-1)
+ }
+ return int(clockSeq & 0x3fff)
+}
+
+// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
+// -1 causes a new sequence to be generated.
+func SetClockSequence(seq int) {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ setClockSequence(seq)
+}
+
+func setClockSequence(seq int) {
+ if seq == -1 {
+ var b [2]byte
+ randomBits(b[:]) // clock sequence
+ seq = int(b[0])<<8 | int(b[1])
+ }
+ oldSeq := clockSeq
+ clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
+ if oldSeq != clockSeq {
+ lasttime = 0
+ }
+}
+
+// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
+// uuid. The time is only defined for version 1 and 2 UUIDs.
+func (uuid UUID) Time() Time {
+ time := int64(binary.BigEndian.Uint32(uuid[0:4]))
+ time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
+ time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
+ return Time(time)
+}
+
+// ClockSequence returns the clock sequence encoded in uuid.
+// The clock sequence is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) ClockSequence() int {
+ return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
+}
diff --git a/vendor/github.com/google/uuid/util.go b/vendor/github.com/google/uuid/util.go
new file mode 100644
index 0000000..5ea6c73
--- /dev/null
+++ b/vendor/github.com/google/uuid/util.go
@@ -0,0 +1,43 @@
+// Copyright 2016 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.
+
+package uuid
+
+import (
+ "io"
+)
+
+// randomBits completely fills slice b with random data.
+func randomBits(b []byte) {
+ if _, err := io.ReadFull(rander, b); err != nil {
+ panic(err.Error()) // rand should never fail
+ }
+}
+
+// xvalues returns the value of a byte as a hexadecimal digit or 255.
+var xvalues = [256]byte{
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+}
+
+// xtob converts hex characters x1 and x2 into a byte.
+func xtob(x1, x2 byte) (byte, bool) {
+ b1 := xvalues[x1]
+ b2 := xvalues[x2]
+ return (b1 << 4) | b2, b1 != 255 && b2 != 255
+}
diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go
new file mode 100644
index 0000000..60d26bb
--- /dev/null
+++ b/vendor/github.com/google/uuid/uuid.go
@@ -0,0 +1,251 @@
+// 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.
+
+package uuid
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+)
+
+// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
+// 4122.
+type UUID [16]byte
+
+// A Version represents a UUID's version.
+type Version byte
+
+// A Variant represents a UUID's variant.
+type Variant byte
+
+// Constants returned by Variant.
+const (
+ Invalid = Variant(iota) // Invalid UUID
+ RFC4122 // The variant specified in RFC4122
+ Reserved // Reserved, NCS backward compatibility.
+ Microsoft // Reserved, Microsoft Corporation backward compatibility.
+ Future // Reserved for future definition.
+)
+
+var rander = rand.Reader // random function
+
+type invalidLengthError struct{ len int }
+
+func (err invalidLengthError) Error() string {
+ return fmt.Sprintf("invalid UUID length: %d", err.len)
+}
+
+// Parse decodes s into a UUID or returns an error. Both the standard UUID
+// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
+// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
+// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
+func Parse(s string) (UUID, error) {
+ var uuid UUID
+ switch len(s) {
+ // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ case 36:
+
+ // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ case 36 + 9:
+ if strings.ToLower(s[:9]) != "urn:uuid:" {
+ return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
+ }
+ s = s[9:]
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ case 36 + 2:
+ s = s[1:]
+
+ // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ case 32:
+ var ok bool
+ for i := range uuid {
+ uuid[i], ok = xtob(s[i*2], s[i*2+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ }
+ return uuid, nil
+ default:
+ return uuid, invalidLengthError{len(s)}
+ }
+ // s is now at least 36 bytes long
+ // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
+ return uuid, errors.New("invalid UUID format")
+ }
+ for i, x := range [16]int{
+ 0, 2, 4, 6,
+ 9, 11,
+ 14, 16,
+ 19, 21,
+ 24, 26, 28, 30, 32, 34} {
+ v, ok := xtob(s[x], s[x+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ uuid[i] = v
+ }
+ return uuid, nil
+}
+
+// ParseBytes is like Parse, except it parses a byte slice instead of a string.
+func ParseBytes(b []byte) (UUID, error) {
+ var uuid UUID
+ switch len(b) {
+ case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
+ return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
+ }
+ b = b[9:]
+ case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ b = b[1:]
+ case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ var ok bool
+ for i := 0; i < 32; i += 2 {
+ uuid[i/2], ok = xtob(b[i], b[i+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ }
+ return uuid, nil
+ default:
+ return uuid, invalidLengthError{len(b)}
+ }
+ // s is now at least 36 bytes long
+ // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
+ return uuid, errors.New("invalid UUID format")
+ }
+ for i, x := range [16]int{
+ 0, 2, 4, 6,
+ 9, 11,
+ 14, 16,
+ 19, 21,
+ 24, 26, 28, 30, 32, 34} {
+ v, ok := xtob(b[x], b[x+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ uuid[i] = v
+ }
+ return uuid, nil
+}
+
+// MustParse is like Parse but panics if the string cannot be parsed.
+// It simplifies safe initialization of global variables holding compiled UUIDs.
+func MustParse(s string) UUID {
+ uuid, err := Parse(s)
+ if err != nil {
+ panic(`uuid: Parse(` + s + `): ` + err.Error())
+ }
+ return uuid
+}
+
+// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
+// does not have a length of 16. The bytes are copied from the slice.
+func FromBytes(b []byte) (uuid UUID, err error) {
+ err = uuid.UnmarshalBinary(b)
+ return uuid, err
+}
+
+// Must returns uuid if err is nil and panics otherwise.
+func Must(uuid UUID, err error) UUID {
+ if err != nil {
+ panic(err)
+ }
+ return uuid
+}
+
+// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+// , or "" if uuid is invalid.
+func (uuid UUID) String() string {
+ var buf [36]byte
+ encodeHex(buf[:], uuid)
+ return string(buf[:])
+}
+
+// URN returns the RFC 2141 URN form of uuid,
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
+func (uuid UUID) URN() string {
+ var buf [36 + 9]byte
+ copy(buf[:], "urn:uuid:")
+ encodeHex(buf[9:], uuid)
+ return string(buf[:])
+}
+
+func encodeHex(dst []byte, uuid UUID) {
+ hex.Encode(dst, uuid[:4])
+ dst[8] = '-'
+ hex.Encode(dst[9:13], uuid[4:6])
+ dst[13] = '-'
+ hex.Encode(dst[14:18], uuid[6:8])
+ dst[18] = '-'
+ hex.Encode(dst[19:23], uuid[8:10])
+ dst[23] = '-'
+ hex.Encode(dst[24:], uuid[10:])
+}
+
+// Variant returns the variant encoded in uuid.
+func (uuid UUID) Variant() Variant {
+ switch {
+ case (uuid[8] & 0xc0) == 0x80:
+ return RFC4122
+ case (uuid[8] & 0xe0) == 0xc0:
+ return Microsoft
+ case (uuid[8] & 0xe0) == 0xe0:
+ return Future
+ default:
+ return Reserved
+ }
+}
+
+// Version returns the version of uuid.
+func (uuid UUID) Version() Version {
+ return Version(uuid[6] >> 4)
+}
+
+func (v Version) String() string {
+ if v > 15 {
+ return fmt.Sprintf("BAD_VERSION_%d", v)
+ }
+ return fmt.Sprintf("VERSION_%d", v)
+}
+
+func (v Variant) String() string {
+ switch v {
+ case RFC4122:
+ return "RFC4122"
+ case Reserved:
+ return "Reserved"
+ case Microsoft:
+ return "Microsoft"
+ case Future:
+ return "Future"
+ case Invalid:
+ return "Invalid"
+ }
+ return fmt.Sprintf("BadVariant%d", int(v))
+}
+
+// SetRand sets the random number generator to r, which implements io.Reader.
+// If r.Read returns an error when the package requests random data then
+// a panic will be issued.
+//
+// Calling SetRand with nil sets the random number generator to the default
+// generator.
+func SetRand(r io.Reader) {
+ if r == nil {
+ rander = rand.Reader
+ return
+ }
+ rander = r
+}
diff --git a/vendor/github.com/google/uuid/version1.go b/vendor/github.com/google/uuid/version1.go
new file mode 100644
index 0000000..4631096
--- /dev/null
+++ b/vendor/github.com/google/uuid/version1.go
@@ -0,0 +1,44 @@
+// Copyright 2016 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.
+
+package uuid
+
+import (
+ "encoding/binary"
+)
+
+// NewUUID returns a Version 1 UUID based on the current NodeID and clock
+// sequence, and the current time. If the NodeID has not been set by SetNodeID
+// or SetNodeInterface then it will be set automatically. If the NodeID cannot
+// be set NewUUID returns nil. If clock sequence has not been set by
+// SetClockSequence then it will be set automatically. If GetTime fails to
+// return the current NewUUID returns nil and an error.
+//
+// In most cases, New should be used.
+func NewUUID() (UUID, error) {
+ var uuid UUID
+ now, seq, err := GetTime()
+ if err != nil {
+ return uuid, err
+ }
+
+ timeLow := uint32(now & 0xffffffff)
+ timeMid := uint16((now >> 32) & 0xffff)
+ timeHi := uint16((now >> 48) & 0x0fff)
+ timeHi |= 0x1000 // Version 1
+
+ binary.BigEndian.PutUint32(uuid[0:], timeLow)
+ binary.BigEndian.PutUint16(uuid[4:], timeMid)
+ binary.BigEndian.PutUint16(uuid[6:], timeHi)
+ binary.BigEndian.PutUint16(uuid[8:], seq)
+
+ nodeMu.Lock()
+ if nodeID == zeroID {
+ setNodeInterface("")
+ }
+ copy(uuid[10:], nodeID[:])
+ nodeMu.Unlock()
+
+ return uuid, nil
+}
diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go
new file mode 100644
index 0000000..86160fb
--- /dev/null
+++ b/vendor/github.com/google/uuid/version4.go
@@ -0,0 +1,51 @@
+// Copyright 2016 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.
+
+package uuid
+
+import "io"
+
+// New creates a new random UUID or panics. New is equivalent to
+// the expression
+//
+// uuid.Must(uuid.NewRandom())
+func New() UUID {
+ return Must(NewRandom())
+}
+
+// NewString creates a new random UUID and returns it as a string or panics.
+// NewString is equivalent to the expression
+//
+// uuid.New().String()
+func NewString() string {
+ return Must(NewRandom()).String()
+}
+
+// NewRandom returns a Random (Version 4) UUID.
+//
+// The strength of the UUIDs is based on the strength of the crypto/rand
+// package.
+//
+// A note about uniqueness derived from the UUID Wikipedia entry:
+//
+// Randomly generated UUIDs have 122 random bits. One's annual risk of being
+// hit by a meteorite is estimated to be one chance in 17 billion, that
+// means the probability is about 0.00000000006 (6 × 10−11),
+// equivalent to the odds of creating a few tens of trillions of UUIDs in a
+// year and having one duplicate.
+func NewRandom() (UUID, error) {
+ return NewRandomFromReader(rander)
+}
+
+// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
+func NewRandomFromReader(r io.Reader) (UUID, error) {
+ var uuid UUID
+ _, err := io.ReadFull(r, uuid[:])
+ if err != nil {
+ return Nil, err
+ }
+ uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
+ uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
+ return uuid, nil
+}
diff --git a/vendor/github.com/gopherjs/gopherjs/LICENSE b/vendor/github.com/gopherjs/gopherjs/LICENSE
new file mode 100644
index 0000000..d496fef
--- /dev/null
+++ b/vendor/github.com/gopherjs/gopherjs/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2013 Richard Musiol. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * 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
+OWNER 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.
diff --git a/vendor/github.com/gopherjs/gopherjs/js/js.go b/vendor/github.com/gopherjs/gopherjs/js/js.go
new file mode 100644
index 0000000..3fbf1d8
--- /dev/null
+++ b/vendor/github.com/gopherjs/gopherjs/js/js.go
@@ -0,0 +1,168 @@
+// Package js provides functions for interacting with native JavaScript APIs. Calls to these functions are treated specially by GopherJS and translated directly to their corresponding JavaScript syntax.
+//
+// Use MakeWrapper to expose methods to JavaScript. When passing values directly, the following type conversions are performed:
+//
+// | Go type | JavaScript type | Conversions back to interface{} |
+// | --------------------- | --------------------- | ------------------------------- |
+// | bool | Boolean | bool |
+// | integers and floats | Number | float64 |
+// | string | String | string |
+// | []int8 | Int8Array | []int8 |
+// | []int16 | Int16Array | []int16 |
+// | []int32, []int | Int32Array | []int |
+// | []uint8 | Uint8Array | []uint8 |
+// | []uint16 | Uint16Array | []uint16 |
+// | []uint32, []uint | Uint32Array | []uint |
+// | []float32 | Float32Array | []float32 |
+// | []float64 | Float64Array | []float64 |
+// | all other slices | Array | []interface{} |
+// | arrays | see slice type | see slice type |
+// | functions | Function | func(...interface{}) *js.Object |
+// | time.Time | Date | time.Time |
+// | - | instanceof Node | *js.Object |
+// | maps, structs | instanceof Object | map[string]interface{} |
+//
+// Additionally, for a struct containing a *js.Object field, only the content of the field will be passed to JavaScript and vice versa.
+package js
+
+// Object is a container for a native JavaScript object. Calls to its methods are treated specially by GopherJS and translated directly to their JavaScript syntax. A nil pointer to Object is equal to JavaScript's "null". Object can not be used as a map key.
+type Object struct{ object *Object }
+
+// Get returns the object's property with the given key.
+func (o *Object) Get(key string) *Object { return o.object.Get(key) }
+
+// Set assigns the value to the object's property with the given key.
+func (o *Object) Set(key string, value interface{}) { o.object.Set(key, value) }
+
+// Delete removes the object's property with the given key.
+func (o *Object) Delete(key string) { o.object.Delete(key) }
+
+// Length returns the object's "length" property, converted to int.
+func (o *Object) Length() int { return o.object.Length() }
+
+// Index returns the i'th element of an array.
+func (o *Object) Index(i int) *Object { return o.object.Index(i) }
+
+// SetIndex sets the i'th element of an array.
+func (o *Object) SetIndex(i int, value interface{}) { o.object.SetIndex(i, value) }
+
+// Call calls the object's method with the given name.
+func (o *Object) Call(name string, args ...interface{}) *Object { return o.object.Call(name, args...) }
+
+// Invoke calls the object itself. This will fail if it is not a function.
+func (o *Object) Invoke(args ...interface{}) *Object { return o.object.Invoke(args...) }
+
+// New creates a new instance of this type object. This will fail if it not a function (constructor).
+func (o *Object) New(args ...interface{}) *Object { return o.object.New(args...) }
+
+// Bool returns the object converted to bool according to JavaScript type conversions.
+func (o *Object) Bool() bool { return o.object.Bool() }
+
+// String returns the object converted to string according to JavaScript type conversions.
+func (o *Object) String() string { return o.object.String() }
+
+// Int returns the object converted to int according to JavaScript type conversions (parseInt).
+func (o *Object) Int() int { return o.object.Int() }
+
+// Int64 returns the object converted to int64 according to JavaScript type conversions (parseInt).
+func (o *Object) Int64() int64 { return o.object.Int64() }
+
+// Uint64 returns the object converted to uint64 according to JavaScript type conversions (parseInt).
+func (o *Object) Uint64() uint64 { return o.object.Uint64() }
+
+// Float returns the object converted to float64 according to JavaScript type conversions (parseFloat).
+func (o *Object) Float() float64 { return o.object.Float() }
+
+// Interface returns the object converted to interface{}. See table in package comment for details.
+func (o *Object) Interface() interface{} { return o.object.Interface() }
+
+// Unsafe returns the object as an uintptr, which can be converted via unsafe.Pointer. Not intended for public use.
+func (o *Object) Unsafe() uintptr { return o.object.Unsafe() }
+
+// Error encapsulates JavaScript errors. Those are turned into a Go panic and may be recovered, giving an *Error that holds the JavaScript error object.
+type Error struct {
+ *Object
+}
+
+// Error returns the message of the encapsulated JavaScript error object.
+func (err *Error) Error() string {
+ return "JavaScript error: " + err.Get("message").String()
+}
+
+// Stack returns the stack property of the encapsulated JavaScript error object.
+func (err *Error) Stack() string {
+ return err.Get("stack").String()
+}
+
+// Global gives JavaScript's global object ("window" for browsers and "GLOBAL" for Node.js).
+var Global *Object
+
+// Module gives the value of the "module" variable set by Node.js. Hint: Set a module export with 'js.Module.Get("exports").Set("exportName", ...)'.
+var Module *Object
+
+// Undefined gives the JavaScript value "undefined".
+var Undefined *Object
+
+// Debugger gets compiled to JavaScript's "debugger;" statement.
+func Debugger() {}
+
+// InternalObject returns the internal JavaScript object that represents i. Not intended for public use.
+func InternalObject(i interface{}) *Object {
+ return nil
+}
+
+// MakeFunc wraps a function and gives access to the values of JavaScript's "this" and "arguments" keywords.
+func MakeFunc(fn func(this *Object, arguments []*Object) interface{}) *Object {
+ return Global.Call("$makeFunc", InternalObject(fn))
+}
+
+// Keys returns the keys of the given JavaScript object.
+func Keys(o *Object) []string {
+ if o == nil || o == Undefined {
+ return nil
+ }
+ a := Global.Get("Object").Call("keys", o)
+ s := make([]string, a.Length())
+ for i := 0; i < a.Length(); i++ {
+ s[i] = a.Index(i).String()
+ }
+ return s
+}
+
+// MakeWrapper creates a JavaScript object which has wrappers for the exported methods of i. Use explicit getter and setter methods to expose struct fields to JavaScript.
+func MakeWrapper(i interface{}) *Object {
+ v := InternalObject(i)
+ o := Global.Get("Object").New()
+ o.Set("__internal_object__", v)
+ methods := v.Get("constructor").Get("methods")
+ for i := 0; i < methods.Length(); i++ {
+ m := methods.Index(i)
+ if m.Get("pkg").String() != "" { // not exported
+ continue
+ }
+ o.Set(m.Get("name").String(), func(args ...*Object) *Object {
+ return Global.Call("$externalizeFunction", v.Get(m.Get("prop").String()), m.Get("typ"), true).Call("apply", v, args)
+ })
+ }
+ return o
+}
+
+// NewArrayBuffer creates a JavaScript ArrayBuffer from a byte slice.
+func NewArrayBuffer(b []byte) *Object {
+ slice := InternalObject(b)
+ offset := slice.Get("$offset").Int()
+ length := slice.Get("$length").Int()
+ return slice.Get("$array").Get("buffer").Call("slice", offset, offset+length)
+}
+
+// M is a simple map type. It is intended as a shorthand for JavaScript objects (before conversion).
+type M map[string]interface{}
+
+// S is a simple slice type. It is intended as a shorthand for JavaScript arrays (before conversion).
+type S []interface{}
+
+func init() {
+ // avoid dead code elimination
+ e := Error{}
+ _ = e
+}
diff --git a/vendor/github.com/jtolds/gls/LICENSE b/vendor/github.com/jtolds/gls/LICENSE
new file mode 100644
index 0000000..9b4a822
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2013, Space Monkey, Inc.
+
+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.
diff --git a/vendor/github.com/jtolds/gls/README.md b/vendor/github.com/jtolds/gls/README.md
new file mode 100644
index 0000000..4ebb692
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/README.md
@@ -0,0 +1,89 @@
+gls
+===
+
+Goroutine local storage
+
+### IMPORTANT NOTE ###
+
+It is my duty to point you to https://blog.golang.org/context, which is how
+Google solves all of the problems you'd perhaps consider using this package
+for at scale.
+
+One downside to Google's approach is that *all* of your functions must have
+a new first argument, but after clearing that hurdle everything else is much
+better.
+
+If you aren't interested in this warning, read on.
+
+### Huhwaht? Why? ###
+
+Every so often, a thread shows up on the
+[golang-nuts](https://groups.google.com/d/forum/golang-nuts) asking for some
+form of goroutine-local-storage, or some kind of goroutine id, or some kind of
+context. There are a few valid use cases for goroutine-local-storage, one of
+the most prominent being log line context. One poster was interested in being
+able to log an HTTP request context id in every log line in the same goroutine
+as the incoming HTTP request, without having to change every library and
+function call he was interested in logging.
+
+This would be pretty useful. Provided that you could get some kind of
+goroutine-local-storage, you could call
+[log.SetOutput](http://golang.org/pkg/log/#SetOutput) with your own logging
+writer that checks goroutine-local-storage for some context information and
+adds that context to your log lines.
+
+But alas, Andrew Gerrand's typically diplomatic answer to the question of
+goroutine-local variables was:
+
+> We wouldn't even be having this discussion if thread local storage wasn't
+> useful. But every feature comes at a cost, and in my opinion the cost of
+> threadlocals far outweighs their benefits. They're just not a good fit for
+> Go.
+
+So, yeah, that makes sense. That's a pretty good reason for why the language
+won't support a specific and (relatively) unuseful feature that requires some
+runtime changes, just for the sake of a little bit of log improvement.
+
+But does Go require runtime changes?
+
+### How it works ###
+
+Go has pretty fantastic introspective and reflective features, but one thing Go
+doesn't give you is any kind of access to the stack pointer, or frame pointer,
+or goroutine id, or anything contextual about your current stack. It gives you
+access to your list of callers, but only along with program counters, which are
+fixed at compile time.
+
+But it does give you the stack.
+
+So, we define 16 special functions and embed base-16 tags into the stack using
+the call order of those 16 functions. Then, we can read our tags back out of
+the stack looking at the callers list.
+
+We then use these tags as an index into a traditional map for implementing
+this library.
+
+### What are people saying? ###
+
+"Wow, that's horrifying."
+
+"This is the most terrible thing I have seen in a very long time."
+
+"Where is it getting a context from? Is this serializing all the requests?
+What the heck is the client being bound to? What are these tags? Why does he
+need callers? Oh god no. No no no."
+
+### Docs ###
+
+Please see the docs at http://godoc.org/github.com/jtolds/gls
+
+### Related ###
+
+If you're okay relying on the string format of the current runtime stacktrace
+including a unique goroutine id (not guaranteed by the spec or anything, but
+very unlikely to change within a Go release), you might be able to squeeze
+out a bit more performance by using this similar library, inspired by some
+code Brad Fitzpatrick wrote for debugging his HTTP/2 library:
+https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require
+any knowledge of the string format of the runtime stacktrace, which
+probably adds unnecessary overhead).
diff --git a/vendor/github.com/jtolds/gls/context.go b/vendor/github.com/jtolds/gls/context.go
new file mode 100644
index 0000000..618a171
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/context.go
@@ -0,0 +1,153 @@
+// Package gls implements goroutine-local storage.
+package gls
+
+import (
+ "sync"
+)
+
+var (
+ mgrRegistry = make(map[*ContextManager]bool)
+ mgrRegistryMtx sync.RWMutex
+)
+
+// Values is simply a map of key types to value types. Used by SetValues to
+// set multiple values at once.
+type Values map[interface{}]interface{}
+
+// ContextManager is the main entrypoint for interacting with
+// Goroutine-local-storage. You can have multiple independent ContextManagers
+// at any given time. ContextManagers are usually declared globally for a given
+// class of context variables. You should use NewContextManager for
+// construction.
+type ContextManager struct {
+ mtx sync.Mutex
+ values map[uint]Values
+}
+
+// NewContextManager returns a brand new ContextManager. It also registers the
+// new ContextManager in the ContextManager registry which is used by the Go
+// method. ContextManagers are typically defined globally at package scope.
+func NewContextManager() *ContextManager {
+ mgr := &ContextManager{values: make(map[uint]Values)}
+ mgrRegistryMtx.Lock()
+ defer mgrRegistryMtx.Unlock()
+ mgrRegistry[mgr] = true
+ return mgr
+}
+
+// Unregister removes a ContextManager from the global registry, used by the
+// Go method. Only intended for use when you're completely done with a
+// ContextManager. Use of Unregister at all is rare.
+func (m *ContextManager) Unregister() {
+ mgrRegistryMtx.Lock()
+ defer mgrRegistryMtx.Unlock()
+ delete(mgrRegistry, m)
+}
+
+// SetValues takes a collection of values and a function to call for those
+// values to be set in. Anything further down the stack will have the set
+// values available through GetValue. SetValues will add new values or replace
+// existing values of the same key and will not mutate or change values for
+// previous stack frames.
+// SetValues is slow (makes a copy of all current and new values for the new
+// gls-context) in order to reduce the amount of lookups GetValue requires.
+func (m *ContextManager) SetValues(new_values Values, context_call func()) {
+ if len(new_values) == 0 {
+ context_call()
+ return
+ }
+
+ mutated_keys := make([]interface{}, 0, len(new_values))
+ mutated_vals := make(Values, len(new_values))
+
+ EnsureGoroutineId(func(gid uint) {
+ m.mtx.Lock()
+ state, found := m.values[gid]
+ if !found {
+ state = make(Values, len(new_values))
+ m.values[gid] = state
+ }
+ m.mtx.Unlock()
+
+ for key, new_val := range new_values {
+ mutated_keys = append(mutated_keys, key)
+ if old_val, ok := state[key]; ok {
+ mutated_vals[key] = old_val
+ }
+ state[key] = new_val
+ }
+
+ defer func() {
+ if !found {
+ m.mtx.Lock()
+ delete(m.values, gid)
+ m.mtx.Unlock()
+ return
+ }
+
+ for _, key := range mutated_keys {
+ if val, ok := mutated_vals[key]; ok {
+ state[key] = val
+ } else {
+ delete(state, key)
+ }
+ }
+ }()
+
+ context_call()
+ })
+}
+
+// GetValue will return a previously set value, provided that the value was set
+// by SetValues somewhere higher up the stack. If the value is not found, ok
+// will be false.
+func (m *ContextManager) GetValue(key interface{}) (
+ value interface{}, ok bool) {
+ gid, ok := GetGoroutineId()
+ if !ok {
+ return nil, false
+ }
+
+ m.mtx.Lock()
+ state, found := m.values[gid]
+ m.mtx.Unlock()
+
+ if !found {
+ return nil, false
+ }
+ value, ok = state[key]
+ return value, ok
+}
+
+func (m *ContextManager) getValues() Values {
+ gid, ok := GetGoroutineId()
+ if !ok {
+ return nil
+ }
+ m.mtx.Lock()
+ state, _ := m.values[gid]
+ m.mtx.Unlock()
+ return state
+}
+
+// Go preserves ContextManager values and Goroutine-local-storage across new
+// goroutine invocations. The Go method makes a copy of all existing values on
+// all registered context managers and makes sure they are still set after
+// kicking off the provided function in a new goroutine. If you don't use this
+// Go method instead of the standard 'go' keyword, you will lose values in
+// ContextManagers, as goroutines have brand new stacks.
+func Go(cb func()) {
+ mgrRegistryMtx.RLock()
+ defer mgrRegistryMtx.RUnlock()
+
+ for mgr := range mgrRegistry {
+ values := mgr.getValues()
+ if len(values) > 0 {
+ cb = func(mgr *ContextManager, cb func()) func() {
+ return func() { mgr.SetValues(values, cb) }
+ }(mgr, cb)
+ }
+ }
+
+ go cb()
+}
diff --git a/vendor/github.com/jtolds/gls/gen_sym.go b/vendor/github.com/jtolds/gls/gen_sym.go
new file mode 100644
index 0000000..7f615cc
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/gen_sym.go
@@ -0,0 +1,21 @@
+package gls
+
+import (
+ "sync"
+)
+
+var (
+ keyMtx sync.Mutex
+ keyCounter uint64
+)
+
+// ContextKey is a throwaway value you can use as a key to a ContextManager
+type ContextKey struct{ id uint64 }
+
+// GenSym will return a brand new, never-before-used ContextKey
+func GenSym() ContextKey {
+ keyMtx.Lock()
+ defer keyMtx.Unlock()
+ keyCounter += 1
+ return ContextKey{id: keyCounter}
+}
diff --git a/vendor/github.com/jtolds/gls/gid.go b/vendor/github.com/jtolds/gls/gid.go
new file mode 100644
index 0000000..c16bf3a
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/gid.go
@@ -0,0 +1,25 @@
+package gls
+
+var (
+ stackTagPool = &idPool{}
+)
+
+// Will return this goroutine's identifier if set. If you always need a
+// goroutine identifier, you should use EnsureGoroutineId which will make one
+// if there isn't one already.
+func GetGoroutineId() (gid uint, ok bool) {
+ return readStackTag()
+}
+
+// Will call cb with the current goroutine identifier. If one hasn't already
+// been generated, one will be created and set first. The goroutine identifier
+// might be invalid after cb returns.
+func EnsureGoroutineId(cb func(gid uint)) {
+ if gid, ok := readStackTag(); ok {
+ cb(gid)
+ return
+ }
+ gid := stackTagPool.Acquire()
+ defer stackTagPool.Release(gid)
+ addStackTag(gid, func() { cb(gid) })
+}
diff --git a/vendor/github.com/jtolds/gls/id_pool.go b/vendor/github.com/jtolds/gls/id_pool.go
new file mode 100644
index 0000000..b7974ae
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/id_pool.go
@@ -0,0 +1,34 @@
+package gls
+
+// though this could probably be better at keeping ids smaller, the goal of
+// this class is to keep a registry of the smallest unique integer ids
+// per-process possible
+
+import (
+ "sync"
+)
+
+type idPool struct {
+ mtx sync.Mutex
+ released []uint
+ max_id uint
+}
+
+func (p *idPool) Acquire() (id uint) {
+ p.mtx.Lock()
+ defer p.mtx.Unlock()
+ if len(p.released) > 0 {
+ id = p.released[len(p.released)-1]
+ p.released = p.released[:len(p.released)-1]
+ return id
+ }
+ id = p.max_id
+ p.max_id++
+ return id
+}
+
+func (p *idPool) Release(id uint) {
+ p.mtx.Lock()
+ defer p.mtx.Unlock()
+ p.released = append(p.released, id)
+}
diff --git a/vendor/github.com/jtolds/gls/stack_tags.go b/vendor/github.com/jtolds/gls/stack_tags.go
new file mode 100644
index 0000000..37bbd33
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/stack_tags.go
@@ -0,0 +1,147 @@
+package gls
+
+// so, basically, we're going to encode integer tags in base-16 on the stack
+
+const (
+ bitWidth = 4
+ stackBatchSize = 16
+)
+
+var (
+ pc_lookup = make(map[uintptr]int8, 17)
+ mark_lookup [16]func(uint, func())
+)
+
+func init() {
+ setEntries := func(f func(uint, func()), v int8) {
+ var ptr uintptr
+ f(0, func() {
+ ptr = findPtr()
+ })
+ pc_lookup[ptr] = v
+ if v >= 0 {
+ mark_lookup[v] = f
+ }
+ }
+ setEntries(github_com_jtolds_gls_markS, -0x1)
+ setEntries(github_com_jtolds_gls_mark0, 0x0)
+ setEntries(github_com_jtolds_gls_mark1, 0x1)
+ setEntries(github_com_jtolds_gls_mark2, 0x2)
+ setEntries(github_com_jtolds_gls_mark3, 0x3)
+ setEntries(github_com_jtolds_gls_mark4, 0x4)
+ setEntries(github_com_jtolds_gls_mark5, 0x5)
+ setEntries(github_com_jtolds_gls_mark6, 0x6)
+ setEntries(github_com_jtolds_gls_mark7, 0x7)
+ setEntries(github_com_jtolds_gls_mark8, 0x8)
+ setEntries(github_com_jtolds_gls_mark9, 0x9)
+ setEntries(github_com_jtolds_gls_markA, 0xa)
+ setEntries(github_com_jtolds_gls_markB, 0xb)
+ setEntries(github_com_jtolds_gls_markC, 0xc)
+ setEntries(github_com_jtolds_gls_markD, 0xd)
+ setEntries(github_com_jtolds_gls_markE, 0xe)
+ setEntries(github_com_jtolds_gls_markF, 0xf)
+}
+
+func addStackTag(tag uint, context_call func()) {
+ if context_call == nil {
+ return
+ }
+ github_com_jtolds_gls_markS(tag, context_call)
+}
+
+// these private methods are named this horrendous name so gopherjs support
+// is easier. it shouldn't add any runtime cost in non-js builds.
+
+//go:noinline
+func github_com_jtolds_gls_markS(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark0(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark1(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark2(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark3(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark4(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark5(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark6(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark7(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark8(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_mark9(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_markA(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_markB(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_markC(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_markD(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_markE(tag uint, cb func()) { _m(tag, cb) }
+
+//go:noinline
+func github_com_jtolds_gls_markF(tag uint, cb func()) { _m(tag, cb) }
+
+func _m(tag_remainder uint, cb func()) {
+ if tag_remainder == 0 {
+ cb()
+ } else {
+ mark_lookup[tag_remainder&0xf](tag_remainder>>bitWidth, cb)
+ }
+}
+
+func readStackTag() (tag uint, ok bool) {
+ var current_tag uint
+ offset := 0
+ for {
+ batch, next_offset := getStack(offset, stackBatchSize)
+ for _, pc := range batch {
+ val, ok := pc_lookup[pc]
+ if !ok {
+ continue
+ }
+ if val < 0 {
+ return current_tag, true
+ }
+ current_tag <<= bitWidth
+ current_tag += uint(val)
+ }
+ if next_offset == 0 {
+ break
+ }
+ offset = next_offset
+ }
+ return 0, false
+}
+
+func (m *ContextManager) preventInlining() {
+ // dunno if findPtr or getStack are likely to get inlined in a future release
+ // of go, but if they are inlined and their callers are inlined, that could
+ // hork some things. let's do our best to explain to the compiler that we
+ // really don't want those two functions inlined by saying they could change
+ // at any time. assumes preventInlining doesn't get compiled out.
+ // this whole thing is probably overkill.
+ findPtr = m.values[0][0].(func() uintptr)
+ getStack = m.values[0][1].(func(int, int) ([]uintptr, int))
+}
diff --git a/vendor/github.com/jtolds/gls/stack_tags_js.go b/vendor/github.com/jtolds/gls/stack_tags_js.go
new file mode 100644
index 0000000..c4e8b80
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/stack_tags_js.go
@@ -0,0 +1,75 @@
+// +build js
+
+package gls
+
+// This file is used for GopherJS builds, which don't have normal runtime
+// stack trace support
+
+import (
+ "strconv"
+ "strings"
+
+ "github.com/gopherjs/gopherjs/js"
+)
+
+const (
+ jsFuncNamePrefix = "github_com_jtolds_gls_mark"
+)
+
+func jsMarkStack() (f []uintptr) {
+ lines := strings.Split(
+ js.Global.Get("Error").New().Get("stack").String(), "\n")
+ f = make([]uintptr, 0, len(lines))
+ for i, line := range lines {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+ if i == 0 {
+ if line != "Error" {
+ panic("didn't understand js stack trace")
+ }
+ continue
+ }
+ fields := strings.Fields(line)
+ if len(fields) < 2 || fields[0] != "at" {
+ panic("didn't understand js stack trace")
+ }
+
+ pos := strings.Index(fields[1], jsFuncNamePrefix)
+ if pos < 0 {
+ continue
+ }
+ pos += len(jsFuncNamePrefix)
+ if pos >= len(fields[1]) {
+ panic("didn't understand js stack trace")
+ }
+ char := string(fields[1][pos])
+ switch char {
+ case "S":
+ f = append(f, uintptr(0))
+ default:
+ val, err := strconv.ParseUint(char, 16, 8)
+ if err != nil {
+ panic("didn't understand js stack trace")
+ }
+ f = append(f, uintptr(val)+1)
+ }
+ }
+ return f
+}
+
+// variables to prevent inlining
+var (
+ findPtr = func() uintptr {
+ funcs := jsMarkStack()
+ if len(funcs) == 0 {
+ panic("failed to find function pointer")
+ }
+ return funcs[0]
+ }
+
+ getStack = func(offset, amount int) (stack []uintptr, next_offset int) {
+ return jsMarkStack(), 0
+ }
+)
diff --git a/vendor/github.com/jtolds/gls/stack_tags_main.go b/vendor/github.com/jtolds/gls/stack_tags_main.go
new file mode 100644
index 0000000..4da89e4
--- /dev/null
+++ b/vendor/github.com/jtolds/gls/stack_tags_main.go
@@ -0,0 +1,30 @@
+// +build !js
+
+package gls
+
+// This file is used for standard Go builds, which have the expected runtime
+// support
+
+import (
+ "runtime"
+)
+
+var (
+ findPtr = func() uintptr {
+ var pc [1]uintptr
+ n := runtime.Callers(4, pc[:])
+ if n != 1 {
+ panic("failed to find function pointer")
+ }
+ return pc[0]
+ }
+
+ getStack = func(offset, amount int) (stack []uintptr, next_offset int) {
+ stack = make([]uintptr, amount)
+ stack = stack[:runtime.Callers(offset, stack)]
+ if len(stack) < amount {
+ return stack, 0
+ }
+ return stack, offset + len(stack)
+ }
+)
diff --git a/vendor/github.com/klauspost/cpuid/.gitignore b/vendor/github.com/klauspost/cpuid/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/klauspost/cpuid/.travis.yml b/vendor/github.com/klauspost/cpuid/.travis.yml
new file mode 100644
index 0000000..77d975f
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/.travis.yml
@@ -0,0 +1,46 @@
+language: go
+
+os:
+ - linux
+ - osx
+ - windows
+
+arch:
+ - amd64
+ - arm64
+
+go:
+ - 1.12.x
+ - 1.13.x
+ - 1.14.x
+ - master
+
+script:
+ - go vet ./...
+ - go test -race ./...
+ - go test -tags=noasm ./...
+
+stages:
+ - gofmt
+ - test
+
+matrix:
+ allow_failures:
+ - go: 'master'
+ fast_finish: true
+ include:
+ - stage: gofmt
+ go: 1.14.x
+ os: linux
+ arch: amd64
+ script:
+ - diff <(gofmt -d .) <(printf "")
+ - diff <(gofmt -d ./private) <(printf "")
+ - go install github.com/klauspost/asmfmt/cmd/asmfmt
+ - diff <(asmfmt -d .) <(printf "")
+ - stage: i386
+ go: 1.14.x
+ os: linux
+ arch: amd64
+ script:
+ - GOOS=linux GOARCH=386 go test .
diff --git a/vendor/github.com/klauspost/cpuid/CONTRIBUTING.txt b/vendor/github.com/klauspost/cpuid/CONTRIBUTING.txt
new file mode 100644
index 0000000..2ef4714
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/CONTRIBUTING.txt
@@ -0,0 +1,35 @@
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2015- Klaus Post & Contributors.
+Email: klauspost@gmail.com
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
diff --git a/vendor/github.com/klauspost/cpuid/LICENSE b/vendor/github.com/klauspost/cpuid/LICENSE
new file mode 100644
index 0000000..5cec7ee
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Klaus Post
+
+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.
+
diff --git a/vendor/github.com/klauspost/cpuid/README.md b/vendor/github.com/klauspost/cpuid/README.md
new file mode 100644
index 0000000..38d4a8b
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/README.md
@@ -0,0 +1,191 @@
+# cpuid
+Package cpuid provides information about the CPU running the current program.
+
+CPU features are detected on startup, and kept for fast access through the life of the application.
+Currently x86 / x64 (AMD64/i386) and ARM (ARM64) is supported, and no external C (cgo) code is used, which should make the library very easy to use.
+
+You can access the CPU information by accessing the shared CPU variable of the cpuid library.
+
+Package home: https://github.com/klauspost/cpuid
+
+[![GoDoc][1]][2] [![Build Status][3]][4]
+
+[1]: https://godoc.org/github.com/klauspost/cpuid?status.svg
+[2]: https://godoc.org/github.com/klauspost/cpuid
+[3]: https://travis-ci.org/klauspost/cpuid.svg?branch=master
+[4]: https://travis-ci.org/klauspost/cpuid
+
+# features
+
+## x86 CPU Instructions
+* **CMOV** (i686 CMOV)
+* **NX** (NX (No-Execute) bit)
+* **AMD3DNOW** (AMD 3DNOW)
+* **AMD3DNOWEXT** (AMD 3DNowExt)
+* **MMX** (standard MMX)
+* **MMXEXT** (SSE integer functions or AMD MMX ext)
+* **SSE** (SSE functions)
+* **SSE2** (P4 SSE functions)
+* **SSE3** (Prescott SSE3 functions)
+* **SSSE3** (Conroe SSSE3 functions)
+* **SSE4** (Penryn SSE4.1 functions)
+* **SSE4A** (AMD Barcelona microarchitecture SSE4a instructions)
+* **SSE42** (Nehalem SSE4.2 functions)
+* **AVX** (AVX functions)
+* **AVX2** (AVX2 functions)
+* **FMA3** (Intel FMA 3)
+* **FMA4** (Bulldozer FMA4 functions)
+* **XOP** (Bulldozer XOP functions)
+* **F16C** (Half-precision floating-point conversion)
+* **BMI1** (Bit Manipulation Instruction Set 1)
+* **BMI2** (Bit Manipulation Instruction Set 2)
+* **TBM** (AMD Trailing Bit Manipulation)
+* **LZCNT** (LZCNT instruction)
+* **POPCNT** (POPCNT instruction)
+* **AESNI** (Advanced Encryption Standard New Instructions)
+* **CLMUL** (Carry-less Multiplication)
+* **HTT** (Hyperthreading (enabled))
+* **HLE** (Hardware Lock Elision)
+* **RTM** (Restricted Transactional Memory)
+* **RDRAND** (RDRAND instruction is available)
+* **RDSEED** (RDSEED instruction is available)
+* **ADX** (Intel ADX (Multi-Precision Add-Carry Instruction Extensions))
+* **SHA** (Intel SHA Extensions)
+* **AVX512F** (AVX-512 Foundation)
+* **AVX512DQ** (AVX-512 Doubleword and Quadword Instructions)
+* **AVX512IFMA** (AVX-512 Integer Fused Multiply-Add Instructions)
+* **AVX512PF** (AVX-512 Prefetch Instructions)
+* **AVX512ER** (AVX-512 Exponential and Reciprocal Instructions)
+* **AVX512CD** (AVX-512 Conflict Detection Instructions)
+* **AVX512BW** (AVX-512 Byte and Word Instructions)
+* **AVX512VL** (AVX-512 Vector Length Extensions)
+* **AVX512VBMI** (AVX-512 Vector Bit Manipulation Instructions)
+* **AVX512VBMI2** (AVX-512 Vector Bit Manipulation Instructions, Version 2)
+* **AVX512VNNI** (AVX-512 Vector Neural Network Instructions)
+* **AVX512VPOPCNTDQ** (AVX-512 Vector Population Count Doubleword and Quadword)
+* **GFNI** (Galois Field New Instructions)
+* **VAES** (Vector AES)
+* **AVX512BITALG** (AVX-512 Bit Algorithms)
+* **VPCLMULQDQ** (Carry-Less Multiplication Quadword)
+* **AVX512BF16** (AVX-512 BFLOAT16 Instructions)
+* **AVX512VP2INTERSECT** (AVX-512 Intersect for D/Q)
+* **MPX** (Intel MPX (Memory Protection Extensions))
+* **ERMS** (Enhanced REP MOVSB/STOSB)
+* **RDTSCP** (RDTSCP Instruction)
+* **CX16** (CMPXCHG16B Instruction)
+* **SGX** (Software Guard Extensions, with activation details)
+* **VMX** (Virtual Machine Extensions)
+
+## Performance
+* **RDTSCP()** Returns current cycle count. Can be used for benchmarking.
+* **SSE2SLOW** (SSE2 is supported, but usually not faster)
+* **SSE3SLOW** (SSE3 is supported, but usually not faster)
+* **ATOM** (Atom processor, some SSSE3 instructions are slower)
+* **Cache line** (Probable size of a cache line).
+* **L1, L2, L3 Cache size** on newer Intel/AMD CPUs.
+
+## ARM CPU features
+
+# ARM FEATURE DETECTION DISABLED!
+
+See [#52](https://github.com/klauspost/cpuid/issues/52).
+
+Currently only `arm64` platforms are implemented.
+
+* **FP** Single-precision and double-precision floating point
+* **ASIMD** Advanced SIMD
+* **EVTSTRM** Generic timer
+* **AES** AES instructions
+* **PMULL** Polynomial Multiply instructions (PMULL/PMULL2)
+* **SHA1** SHA-1 instructions (SHA1C, etc)
+* **SHA2** SHA-2 instructions (SHA256H, etc)
+* **CRC32** CRC32/CRC32C instructions
+* **ATOMICS** Large System Extensions (LSE)
+* **FPHP** Half-precision floating point
+* **ASIMDHP** Advanced SIMD half-precision floating point
+* **ARMCPUID** Some CPU ID registers readable at user-level
+* **ASIMDRDM** Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH)
+* **JSCVT** Javascript-style double->int convert (FJCVTZS)
+* **FCMA** Floating point complex number addition and multiplication
+* **LRCPC** Weaker release consistency (LDAPR, etc)
+* **DCPOP** Data cache clean to Point of Persistence (DC CVAP)
+* **SHA3** SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
+* **SM3** SM3 instructions
+* **SM4** SM4 instructions
+* **ASIMDDP** SIMD Dot Product
+* **SHA512** SHA512 instructions
+* **SVE** Scalable Vector Extension
+* **GPA** Generic Pointer Authentication
+
+## Cpu Vendor/VM
+* **Intel**
+* **AMD**
+* **VIA**
+* **Transmeta**
+* **NSC**
+* **KVM** (Kernel-based Virtual Machine)
+* **MSVM** (Microsoft Hyper-V or Windows Virtual PC)
+* **VMware**
+* **XenHVM**
+* **Bhyve**
+* **Hygon**
+
+# installing
+
+```go get github.com/klauspost/cpuid```
+
+# example
+
+```Go
+package main
+
+import (
+ "fmt"
+ "github.com/klauspost/cpuid"
+)
+
+func main() {
+ // Print basic CPU information:
+ fmt.Println("Name:", cpuid.CPU.BrandName)
+ fmt.Println("PhysicalCores:", cpuid.CPU.PhysicalCores)
+ fmt.Println("ThreadsPerCore:", cpuid.CPU.ThreadsPerCore)
+ fmt.Println("LogicalCores:", cpuid.CPU.LogicalCores)
+ fmt.Println("Family", cpuid.CPU.Family, "Model:", cpuid.CPU.Model)
+ fmt.Println("Features:", cpuid.CPU.Features)
+ fmt.Println("Cacheline bytes:", cpuid.CPU.CacheLine)
+ fmt.Println("L1 Data Cache:", cpuid.CPU.Cache.L1D, "bytes")
+ fmt.Println("L1 Instruction Cache:", cpuid.CPU.Cache.L1D, "bytes")
+ fmt.Println("L2 Cache:", cpuid.CPU.Cache.L2, "bytes")
+ fmt.Println("L3 Cache:", cpuid.CPU.Cache.L3, "bytes")
+
+ // Test if we have a specific feature:
+ if cpuid.CPU.SSE() {
+ fmt.Println("We have Streaming SIMD Extensions")
+ }
+}
+```
+
+Sample output:
+```
+>go run main.go
+Name: Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz
+PhysicalCores: 2
+ThreadsPerCore: 2
+LogicalCores: 4
+Family 6 Model: 42
+Features: CMOV,MMX,MMXEXT,SSE,SSE2,SSE3,SSSE3,SSE4.1,SSE4.2,AVX,AESNI,CLMUL
+Cacheline bytes: 64
+We have Streaming SIMD Extensions
+```
+
+# private package
+
+In the "private" folder you can find an autogenerated version of the library you can include in your own packages.
+
+For this purpose all exports are removed, and functions and constants are lowercased.
+
+This is not a recommended way of using the library, but provided for convenience, if it is difficult for you to use external packages.
+
+# license
+
+This code is published under an MIT license. See LICENSE file for more information.
diff --git a/vendor/github.com/klauspost/cpuid/cpuid.go b/vendor/github.com/klauspost/cpuid/cpuid.go
new file mode 100644
index 0000000..208b3e7
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/cpuid.go
@@ -0,0 +1,1504 @@
+// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
+
+// Package cpuid provides information about the CPU running the current program.
+//
+// CPU features are detected on startup, and kept for fast access through the life of the application.
+// Currently x86 / x64 (AMD64) as well as arm64 is supported.
+//
+// You can access the CPU information by accessing the shared CPU variable of the cpuid library.
+//
+// Package home: https://github.com/klauspost/cpuid
+package cpuid
+
+import (
+ "math"
+ "strings"
+)
+
+// AMD refererence: https://www.amd.com/system/files/TechDocs/25481.pdf
+// and Processor Programming Reference (PPR)
+
+// Vendor is a representation of a CPU vendor.
+type Vendor int
+
+const (
+ Other Vendor = iota
+ Intel
+ AMD
+ VIA
+ Transmeta
+ NSC
+ KVM // Kernel-based Virtual Machine
+ MSVM // Microsoft Hyper-V or Windows Virtual PC
+ VMware
+ XenHVM
+ Bhyve
+ Hygon
+ SiS
+ RDC
+)
+
+const (
+ CMOV = 1 << iota // i686 CMOV
+ NX // NX (No-Execute) bit
+ AMD3DNOW // AMD 3DNOW
+ AMD3DNOWEXT // AMD 3DNowExt
+ MMX // standard MMX
+ MMXEXT // SSE integer functions or AMD MMX ext
+ SSE // SSE functions
+ SSE2 // P4 SSE functions
+ SSE3 // Prescott SSE3 functions
+ SSSE3 // Conroe SSSE3 functions
+ SSE4 // Penryn SSE4.1 functions
+ SSE4A // AMD Barcelona microarchitecture SSE4a instructions
+ SSE42 // Nehalem SSE4.2 functions
+ AVX // AVX functions
+ AVX2 // AVX2 functions
+ FMA3 // Intel FMA 3
+ FMA4 // Bulldozer FMA4 functions
+ XOP // Bulldozer XOP functions
+ F16C // Half-precision floating-point conversion
+ BMI1 // Bit Manipulation Instruction Set 1
+ BMI2 // Bit Manipulation Instruction Set 2
+ TBM // AMD Trailing Bit Manipulation
+ LZCNT // LZCNT instruction
+ POPCNT // POPCNT instruction
+ AESNI // Advanced Encryption Standard New Instructions
+ CLMUL // Carry-less Multiplication
+ HTT // Hyperthreading (enabled)
+ HLE // Hardware Lock Elision
+ RTM // Restricted Transactional Memory
+ RDRAND // RDRAND instruction is available
+ RDSEED // RDSEED instruction is available
+ ADX // Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
+ SHA // Intel SHA Extensions
+ AVX512F // AVX-512 Foundation
+ AVX512DQ // AVX-512 Doubleword and Quadword Instructions
+ AVX512IFMA // AVX-512 Integer Fused Multiply-Add Instructions
+ AVX512PF // AVX-512 Prefetch Instructions
+ AVX512ER // AVX-512 Exponential and Reciprocal Instructions
+ AVX512CD // AVX-512 Conflict Detection Instructions
+ AVX512BW // AVX-512 Byte and Word Instructions
+ AVX512VL // AVX-512 Vector Length Extensions
+ AVX512VBMI // AVX-512 Vector Bit Manipulation Instructions
+ AVX512VBMI2 // AVX-512 Vector Bit Manipulation Instructions, Version 2
+ AVX512VNNI // AVX-512 Vector Neural Network Instructions
+ AVX512VPOPCNTDQ // AVX-512 Vector Population Count Doubleword and Quadword
+ GFNI // Galois Field New Instructions
+ VAES // Vector AES
+ AVX512BITALG // AVX-512 Bit Algorithms
+ VPCLMULQDQ // Carry-Less Multiplication Quadword
+ AVX512BF16 // AVX-512 BFLOAT16 Instructions
+ AVX512VP2INTERSECT // AVX-512 Intersect for D/Q
+ MPX // Intel MPX (Memory Protection Extensions)
+ ERMS // Enhanced REP MOVSB/STOSB
+ RDTSCP // RDTSCP Instruction
+ CX16 // CMPXCHG16B Instruction
+ SGX // Software Guard Extensions
+ SGXLC // Software Guard Extensions Launch Control
+ IBPB // Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB)
+ STIBP // Single Thread Indirect Branch Predictors
+ VMX // Virtual Machine Extensions
+
+ // Performance indicators
+ SSE2SLOW // SSE2 is supported, but usually not faster
+ SSE3SLOW // SSE3 is supported, but usually not faster
+ ATOM // Atom processor, some SSSE3 instructions are slower
+)
+
+var flagNames = map[Flags]string{
+ CMOV: "CMOV", // i686 CMOV
+ NX: "NX", // NX (No-Execute) bit
+ AMD3DNOW: "AMD3DNOW", // AMD 3DNOW
+ AMD3DNOWEXT: "AMD3DNOWEXT", // AMD 3DNowExt
+ MMX: "MMX", // Standard MMX
+ MMXEXT: "MMXEXT", // SSE integer functions or AMD MMX ext
+ SSE: "SSE", // SSE functions
+ SSE2: "SSE2", // P4 SSE2 functions
+ SSE3: "SSE3", // Prescott SSE3 functions
+ SSSE3: "SSSE3", // Conroe SSSE3 functions
+ SSE4: "SSE4.1", // Penryn SSE4.1 functions
+ SSE4A: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions
+ SSE42: "SSE4.2", // Nehalem SSE4.2 functions
+ AVX: "AVX", // AVX functions
+ AVX2: "AVX2", // AVX functions
+ FMA3: "FMA3", // Intel FMA 3
+ FMA4: "FMA4", // Bulldozer FMA4 functions
+ XOP: "XOP", // Bulldozer XOP functions
+ F16C: "F16C", // Half-precision floating-point conversion
+ BMI1: "BMI1", // Bit Manipulation Instruction Set 1
+ BMI2: "BMI2", // Bit Manipulation Instruction Set 2
+ TBM: "TBM", // AMD Trailing Bit Manipulation
+ LZCNT: "LZCNT", // LZCNT instruction
+ POPCNT: "POPCNT", // POPCNT instruction
+ AESNI: "AESNI", // Advanced Encryption Standard New Instructions
+ CLMUL: "CLMUL", // Carry-less Multiplication
+ HTT: "HTT", // Hyperthreading (enabled)
+ HLE: "HLE", // Hardware Lock Elision
+ RTM: "RTM", // Restricted Transactional Memory
+ RDRAND: "RDRAND", // RDRAND instruction is available
+ RDSEED: "RDSEED", // RDSEED instruction is available
+ ADX: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
+ SHA: "SHA", // Intel SHA Extensions
+ AVX512F: "AVX512F", // AVX-512 Foundation
+ AVX512DQ: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions
+ AVX512IFMA: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions
+ AVX512PF: "AVX512PF", // AVX-512 Prefetch Instructions
+ AVX512ER: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions
+ AVX512CD: "AVX512CD", // AVX-512 Conflict Detection Instructions
+ AVX512BW: "AVX512BW", // AVX-512 Byte and Word Instructions
+ AVX512VL: "AVX512VL", // AVX-512 Vector Length Extensions
+ AVX512VBMI: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions
+ AVX512VBMI2: "AVX512VBMI2", // AVX-512 Vector Bit Manipulation Instructions, Version 2
+ AVX512VNNI: "AVX512VNNI", // AVX-512 Vector Neural Network Instructions
+ AVX512VPOPCNTDQ: "AVX512VPOPCNTDQ", // AVX-512 Vector Population Count Doubleword and Quadword
+ GFNI: "GFNI", // Galois Field New Instructions
+ VAES: "VAES", // Vector AES
+ AVX512BITALG: "AVX512BITALG", // AVX-512 Bit Algorithms
+ VPCLMULQDQ: "VPCLMULQDQ", // Carry-Less Multiplication Quadword
+ AVX512BF16: "AVX512BF16", // AVX-512 BFLOAT16 Instruction
+ AVX512VP2INTERSECT: "AVX512VP2INTERSECT", // AVX-512 Intersect for D/Q
+ MPX: "MPX", // Intel MPX (Memory Protection Extensions)
+ ERMS: "ERMS", // Enhanced REP MOVSB/STOSB
+ RDTSCP: "RDTSCP", // RDTSCP Instruction
+ CX16: "CX16", // CMPXCHG16B Instruction
+ SGX: "SGX", // Software Guard Extensions
+ SGXLC: "SGXLC", // Software Guard Extensions Launch Control
+ IBPB: "IBPB", // Indirect Branch Restricted Speculation and Indirect Branch Predictor Barrier
+ STIBP: "STIBP", // Single Thread Indirect Branch Predictors
+ VMX: "VMX", // Virtual Machine Extensions
+
+ // Performance indicators
+ SSE2SLOW: "SSE2SLOW", // SSE2 supported, but usually not faster
+ SSE3SLOW: "SSE3SLOW", // SSE3 supported, but usually not faster
+ ATOM: "ATOM", // Atom processor, some SSSE3 instructions are slower
+
+}
+
+/* all special features for arm64 should be defined here */
+const (
+ /* extension instructions */
+ FP ArmFlags = 1 << iota
+ ASIMD
+ EVTSTRM
+ AES
+ PMULL
+ SHA1
+ SHA2
+ CRC32
+ ATOMICS
+ FPHP
+ ASIMDHP
+ ARMCPUID
+ ASIMDRDM
+ JSCVT
+ FCMA
+ LRCPC
+ DCPOP
+ SHA3
+ SM3
+ SM4
+ ASIMDDP
+ SHA512
+ SVE
+ GPA
+)
+
+var flagNamesArm = map[ArmFlags]string{
+ FP: "FP", // Single-precision and double-precision floating point
+ ASIMD: "ASIMD", // Advanced SIMD
+ EVTSTRM: "EVTSTRM", // Generic timer
+ AES: "AES", // AES instructions
+ PMULL: "PMULL", // Polynomial Multiply instructions (PMULL/PMULL2)
+ SHA1: "SHA1", // SHA-1 instructions (SHA1C, etc)
+ SHA2: "SHA2", // SHA-2 instructions (SHA256H, etc)
+ CRC32: "CRC32", // CRC32/CRC32C instructions
+ ATOMICS: "ATOMICS", // Large System Extensions (LSE)
+ FPHP: "FPHP", // Half-precision floating point
+ ASIMDHP: "ASIMDHP", // Advanced SIMD half-precision floating point
+ ARMCPUID: "CPUID", // Some CPU ID registers readable at user-level
+ ASIMDRDM: "ASIMDRDM", // Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH)
+ JSCVT: "JSCVT", // Javascript-style double->int convert (FJCVTZS)
+ FCMA: "FCMA", // Floatin point complex number addition and multiplication
+ LRCPC: "LRCPC", // Weaker release consistency (LDAPR, etc)
+ DCPOP: "DCPOP", // Data cache clean to Point of Persistence (DC CVAP)
+ SHA3: "SHA3", // SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
+ SM3: "SM3", // SM3 instructions
+ SM4: "SM4", // SM4 instructions
+ ASIMDDP: "ASIMDDP", // SIMD Dot Product
+ SHA512: "SHA512", // SHA512 instructions
+ SVE: "SVE", // Scalable Vector Extension
+ GPA: "GPA", // Generic Pointer Authentication
+}
+
+// CPUInfo contains information about the detected system CPU.
+type CPUInfo struct {
+ BrandName string // Brand name reported by the CPU
+ VendorID Vendor // Comparable CPU vendor ID
+ VendorString string // Raw vendor string.
+ Features Flags // Features of the CPU (x64)
+ Arm ArmFlags // Features of the CPU (arm)
+ PhysicalCores int // Number of physical processor cores in your CPU. Will be 0 if undetectable.
+ ThreadsPerCore int // Number of threads per physical core. Will be 1 if undetectable.
+ 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
+ CacheLine int // Cache line size in bytes. Will be 0 if undetectable.
+ Hz int64 // Clock speed, if known
+ Cache struct {
+ L1I int // L1 Instruction Cache (per core or shared). Will be -1 if undetected
+ L1D int // L1 Data Cache (per core or shared). Will be -1 if undetected
+ L2 int // L2 Cache (per core or shared). Will be -1 if undetected
+ L3 int // L3 Cache (per core, per ccx or shared). Will be -1 if undetected
+ }
+ SGX SGXSupport
+ maxFunc uint32
+ maxExFunc uint32
+}
+
+var cpuid func(op uint32) (eax, ebx, ecx, edx uint32)
+var cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32)
+var xgetbv func(index uint32) (eax, edx uint32)
+var rdtscpAsm func() (eax, ebx, ecx, edx uint32)
+
+// CPU contains information about the CPU as detected on startup,
+// or when Detect last was called.
+//
+// Use this as the primary entry point to you data.
+var CPU CPUInfo
+
+func init() {
+ initCPU()
+ Detect()
+}
+
+// Detect will re-detect current CPU info.
+// This will replace the content of the exported CPU variable.
+//
+// Unless you expect the CPU to change while you are running your program
+// you should not need to call this function.
+// If you call this, you must ensure that no other goroutine is accessing the
+// exported CPU variable.
+func Detect() {
+ // Set defaults
+ CPU.ThreadsPerCore = 1
+ CPU.Cache.L1I = -1
+ CPU.Cache.L1D = -1
+ CPU.Cache.L2 = -1
+ CPU.Cache.L3 = -1
+ addInfo(&CPU)
+}
+
+// Generated here: http://play.golang.org/p/BxFH2Gdc0G
+
+// Cmov indicates support of CMOV instructions
+func (c CPUInfo) Cmov() bool {
+ return c.Features&CMOV != 0
+}
+
+// Amd3dnow indicates support of AMD 3DNOW! instructions
+func (c CPUInfo) Amd3dnow() bool {
+ return c.Features&AMD3DNOW != 0
+}
+
+// Amd3dnowExt indicates support of AMD 3DNOW! Extended instructions
+func (c CPUInfo) Amd3dnowExt() bool {
+ return c.Features&AMD3DNOWEXT != 0
+}
+
+// VMX indicates support of VMX
+func (c CPUInfo) VMX() bool {
+ return c.Features&VMX != 0
+}
+
+// MMX indicates support of MMX instructions
+func (c CPUInfo) MMX() bool {
+ return c.Features&MMX != 0
+}
+
+// MMXExt indicates support of MMXEXT instructions
+// (SSE integer functions or AMD MMX ext)
+func (c CPUInfo) MMXExt() bool {
+ return c.Features&MMXEXT != 0
+}
+
+// SSE indicates support of SSE instructions
+func (c CPUInfo) SSE() bool {
+ return c.Features&SSE != 0
+}
+
+// SSE2 indicates support of SSE 2 instructions
+func (c CPUInfo) SSE2() bool {
+ return c.Features&SSE2 != 0
+}
+
+// SSE3 indicates support of SSE 3 instructions
+func (c CPUInfo) SSE3() bool {
+ return c.Features&SSE3 != 0
+}
+
+// SSSE3 indicates support of SSSE 3 instructions
+func (c CPUInfo) SSSE3() bool {
+ return c.Features&SSSE3 != 0
+}
+
+// SSE4 indicates support of SSE 4 (also called SSE 4.1) instructions
+func (c CPUInfo) SSE4() bool {
+ return c.Features&SSE4 != 0
+}
+
+// SSE42 indicates support of SSE4.2 instructions
+func (c CPUInfo) SSE42() bool {
+ return c.Features&SSE42 != 0
+}
+
+// AVX indicates support of AVX instructions
+// and operating system support of AVX instructions
+func (c CPUInfo) AVX() bool {
+ return c.Features&AVX != 0
+}
+
+// AVX2 indicates support of AVX2 instructions
+func (c CPUInfo) AVX2() bool {
+ return c.Features&AVX2 != 0
+}
+
+// FMA3 indicates support of FMA3 instructions
+func (c CPUInfo) FMA3() bool {
+ return c.Features&FMA3 != 0
+}
+
+// FMA4 indicates support of FMA4 instructions
+func (c CPUInfo) FMA4() bool {
+ return c.Features&FMA4 != 0
+}
+
+// XOP indicates support of XOP instructions
+func (c CPUInfo) XOP() bool {
+ return c.Features&XOP != 0
+}
+
+// F16C indicates support of F16C instructions
+func (c CPUInfo) F16C() bool {
+ return c.Features&F16C != 0
+}
+
+// BMI1 indicates support of BMI1 instructions
+func (c CPUInfo) BMI1() bool {
+ return c.Features&BMI1 != 0
+}
+
+// BMI2 indicates support of BMI2 instructions
+func (c CPUInfo) BMI2() bool {
+ return c.Features&BMI2 != 0
+}
+
+// TBM indicates support of TBM instructions
+// (AMD Trailing Bit Manipulation)
+func (c CPUInfo) TBM() bool {
+ return c.Features&TBM != 0
+}
+
+// Lzcnt indicates support of LZCNT instruction
+func (c CPUInfo) Lzcnt() bool {
+ return c.Features&LZCNT != 0
+}
+
+// Popcnt indicates support of POPCNT instruction
+func (c CPUInfo) Popcnt() bool {
+ return c.Features&POPCNT != 0
+}
+
+// HTT indicates the processor has Hyperthreading enabled
+func (c CPUInfo) HTT() bool {
+ return c.Features&HTT != 0
+}
+
+// SSE2Slow indicates that SSE2 may be slow on this processor
+func (c CPUInfo) SSE2Slow() bool {
+ return c.Features&SSE2SLOW != 0
+}
+
+// SSE3Slow indicates that SSE3 may be slow on this processor
+func (c CPUInfo) SSE3Slow() bool {
+ return c.Features&SSE3SLOW != 0
+}
+
+// AesNi indicates support of AES-NI instructions
+// (Advanced Encryption Standard New Instructions)
+func (c CPUInfo) AesNi() bool {
+ return c.Features&AESNI != 0
+}
+
+// Clmul indicates support of CLMUL instructions
+// (Carry-less Multiplication)
+func (c CPUInfo) Clmul() bool {
+ return c.Features&CLMUL != 0
+}
+
+// NX indicates support of NX (No-Execute) bit
+func (c CPUInfo) NX() bool {
+ return c.Features&NX != 0
+}
+
+// SSE4A indicates support of AMD Barcelona microarchitecture SSE4a instructions
+func (c CPUInfo) SSE4A() bool {
+ return c.Features&SSE4A != 0
+}
+
+// HLE indicates support of Hardware Lock Elision
+func (c CPUInfo) HLE() bool {
+ return c.Features&HLE != 0
+}
+
+// RTM indicates support of Restricted Transactional Memory
+func (c CPUInfo) RTM() bool {
+ return c.Features&RTM != 0
+}
+
+// Rdrand indicates support of RDRAND instruction is available
+func (c CPUInfo) Rdrand() bool {
+ return c.Features&RDRAND != 0
+}
+
+// Rdseed indicates support of RDSEED instruction is available
+func (c CPUInfo) Rdseed() bool {
+ return c.Features&RDSEED != 0
+}
+
+// ADX indicates support of Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
+func (c CPUInfo) ADX() bool {
+ return c.Features&ADX != 0
+}
+
+// SHA indicates support of Intel SHA Extensions
+func (c CPUInfo) SHA() bool {
+ return c.Features&SHA != 0
+}
+
+// AVX512F indicates support of AVX-512 Foundation
+func (c CPUInfo) AVX512F() bool {
+ return c.Features&AVX512F != 0
+}
+
+// AVX512DQ indicates support of AVX-512 Doubleword and Quadword Instructions
+func (c CPUInfo) AVX512DQ() bool {
+ return c.Features&AVX512DQ != 0
+}
+
+// AVX512IFMA indicates support of AVX-512 Integer Fused Multiply-Add Instructions
+func (c CPUInfo) AVX512IFMA() bool {
+ return c.Features&AVX512IFMA != 0
+}
+
+// AVX512PF indicates support of AVX-512 Prefetch Instructions
+func (c CPUInfo) AVX512PF() bool {
+ return c.Features&AVX512PF != 0
+}
+
+// AVX512ER indicates support of AVX-512 Exponential and Reciprocal Instructions
+func (c CPUInfo) AVX512ER() bool {
+ return c.Features&AVX512ER != 0
+}
+
+// AVX512CD indicates support of AVX-512 Conflict Detection Instructions
+func (c CPUInfo) AVX512CD() bool {
+ return c.Features&AVX512CD != 0
+}
+
+// AVX512BW indicates support of AVX-512 Byte and Word Instructions
+func (c CPUInfo) AVX512BW() bool {
+ return c.Features&AVX512BW != 0
+}
+
+// AVX512VL indicates support of AVX-512 Vector Length Extensions
+func (c CPUInfo) AVX512VL() bool {
+ return c.Features&AVX512VL != 0
+}
+
+// AVX512VBMI indicates support of AVX-512 Vector Bit Manipulation Instructions
+func (c CPUInfo) AVX512VBMI() bool {
+ return c.Features&AVX512VBMI != 0
+}
+
+// AVX512VBMI2 indicates support of AVX-512 Vector Bit Manipulation Instructions, Version 2
+func (c CPUInfo) AVX512VBMI2() bool {
+ return c.Features&AVX512VBMI2 != 0
+}
+
+// AVX512VNNI indicates support of AVX-512 Vector Neural Network Instructions
+func (c CPUInfo) AVX512VNNI() bool {
+ return c.Features&AVX512VNNI != 0
+}
+
+// AVX512VPOPCNTDQ indicates support of AVX-512 Vector Population Count Doubleword and Quadword
+func (c CPUInfo) AVX512VPOPCNTDQ() bool {
+ return c.Features&AVX512VPOPCNTDQ != 0
+}
+
+// GFNI indicates support of Galois Field New Instructions
+func (c CPUInfo) GFNI() bool {
+ return c.Features&GFNI != 0
+}
+
+// VAES indicates support of Vector AES
+func (c CPUInfo) VAES() bool {
+ return c.Features&VAES != 0
+}
+
+// AVX512BITALG indicates support of AVX-512 Bit Algorithms
+func (c CPUInfo) AVX512BITALG() bool {
+ return c.Features&AVX512BITALG != 0
+}
+
+// VPCLMULQDQ indicates support of Carry-Less Multiplication Quadword
+func (c CPUInfo) VPCLMULQDQ() bool {
+ return c.Features&VPCLMULQDQ != 0
+}
+
+// AVX512BF16 indicates support of
+func (c CPUInfo) AVX512BF16() bool {
+ return c.Features&AVX512BF16 != 0
+}
+
+// AVX512VP2INTERSECT indicates support of
+func (c CPUInfo) AVX512VP2INTERSECT() bool {
+ return c.Features&AVX512VP2INTERSECT != 0
+}
+
+// MPX indicates support of Intel MPX (Memory Protection Extensions)
+func (c CPUInfo) MPX() bool {
+ return c.Features&MPX != 0
+}
+
+// ERMS indicates support of Enhanced REP MOVSB/STOSB
+func (c CPUInfo) ERMS() bool {
+ return c.Features&ERMS != 0
+}
+
+// RDTSCP Instruction is available.
+func (c CPUInfo) RDTSCP() bool {
+ return c.Features&RDTSCP != 0
+}
+
+// CX16 indicates if CMPXCHG16B instruction is available.
+func (c CPUInfo) CX16() bool {
+ return c.Features&CX16 != 0
+}
+
+// TSX is split into HLE (Hardware Lock Elision) and RTM (Restricted Transactional Memory) detection.
+// So TSX simply checks that.
+func (c CPUInfo) TSX() bool {
+ return c.Features&(HLE|RTM) == HLE|RTM
+}
+
+// Atom indicates an Atom processor
+func (c CPUInfo) Atom() bool {
+ return c.Features&ATOM != 0
+}
+
+// Intel returns true if vendor is recognized as Intel
+func (c CPUInfo) Intel() bool {
+ return c.VendorID == Intel
+}
+
+// AMD returns true if vendor is recognized as AMD
+func (c CPUInfo) AMD() bool {
+ return c.VendorID == AMD
+}
+
+// Hygon returns true if vendor is recognized as Hygon
+func (c CPUInfo) Hygon() bool {
+ return c.VendorID == Hygon
+}
+
+// Transmeta returns true if vendor is recognized as Transmeta
+func (c CPUInfo) Transmeta() bool {
+ return c.VendorID == Transmeta
+}
+
+// NSC returns true if vendor is recognized as National Semiconductor
+func (c CPUInfo) NSC() bool {
+ return c.VendorID == NSC
+}
+
+// VIA returns true if vendor is recognized as VIA
+func (c CPUInfo) VIA() bool {
+ return c.VendorID == VIA
+}
+
+// RTCounter returns the 64-bit time-stamp counter
+// Uses the RDTSCP instruction. The value 0 is returned
+// if the CPU does not support the instruction.
+func (c CPUInfo) RTCounter() uint64 {
+ if !c.RDTSCP() {
+ return 0
+ }
+ a, _, _, d := rdtscpAsm()
+ return uint64(a) | (uint64(d) << 32)
+}
+
+// Ia32TscAux returns the IA32_TSC_AUX part of the RDTSCP.
+// This variable is OS dependent, but on Linux contains information
+// about the current cpu/core the code is running on.
+// If the RDTSCP instruction isn't supported on the CPU, the value 0 is returned.
+func (c CPUInfo) Ia32TscAux() uint32 {
+ if !c.RDTSCP() {
+ return 0
+ }
+ _, _, ecx, _ := rdtscpAsm()
+ return ecx
+}
+
+// LogicalCPU will return the Logical CPU the code is currently executing on.
+// This is likely to change when the OS re-schedules the running thread
+// to another CPU.
+// If the current core cannot be detected, -1 will be returned.
+func (c CPUInfo) LogicalCPU() int {
+ if c.maxFunc < 1 {
+ return -1
+ }
+ _, ebx, _, _ := cpuid(1)
+ return int(ebx >> 24)
+}
+
+// hertz tries to compute the clock speed of the CPU. If leaf 15 is
+// supported, use it, otherwise parse the brand string. Yes, really.
+func hertz(model string) int64 {
+ mfi := maxFunctionID()
+ if mfi >= 0x15 {
+ eax, ebx, ecx, _ := cpuid(0x15)
+ if eax != 0 && ebx != 0 && ecx != 0 {
+ return int64((int64(ecx) * int64(ebx)) / int64(eax))
+ }
+ }
+ // computeHz determines the official rated speed of a CPU from its brand
+ // string. This insanity is *actually the official documented way to do
+ // this according to Intel*, prior to leaf 0x15 existing. The official
+ // documentation only shows this working for exactly `x.xx` or `xxxx`
+ // cases, e.g., `2.50GHz` or `1300MHz`; this parser will accept other
+ // sizes.
+ hz := strings.LastIndex(model, "Hz")
+ if hz < 3 {
+ return -1
+ }
+ var multiplier int64
+ switch model[hz-1] {
+ case 'M':
+ multiplier = 1000 * 1000
+ case 'G':
+ multiplier = 1000 * 1000 * 1000
+ case 'T':
+ multiplier = 1000 * 1000 * 1000 * 1000
+ }
+ if multiplier == 0 {
+ return -1
+ }
+ freq := int64(0)
+ divisor := int64(0)
+ decimalShift := int64(1)
+ var i int
+ for i = hz - 2; i >= 0 && model[i] != ' '; i-- {
+ if model[i] >= '0' && model[i] <= '9' {
+ freq += int64(model[i]-'0') * decimalShift
+ decimalShift *= 10
+ } else if model[i] == '.' {
+ if divisor != 0 {
+ return -1
+ }
+ divisor = decimalShift
+ } else {
+ return -1
+ }
+ }
+ // we didn't find a space
+ if i < 0 {
+ return -1
+ }
+ if divisor != 0 {
+ return (freq * multiplier) / divisor
+ }
+ return freq * multiplier
+}
+
+// VM Will return true if the cpu id indicates we are in
+// a virtual machine. This is only a hint, and will very likely
+// have many false negatives.
+func (c CPUInfo) VM() bool {
+ switch c.VendorID {
+ case MSVM, KVM, VMware, XenHVM, Bhyve:
+ return true
+ }
+ return false
+}
+
+// Flags contains detected cpu features and characteristics
+type Flags uint64
+
+// ArmFlags contains detected ARM cpu features and characteristics
+type ArmFlags uint64
+
+// String returns a string representation of the detected
+// CPU features.
+func (f Flags) String() string {
+ return strings.Join(f.Strings(), ",")
+}
+
+// Strings returns an array of the detected features.
+func (f Flags) Strings() []string {
+ r := make([]string, 0, 20)
+ for i := uint(0); i < 64; i++ {
+ key := Flags(1 << i)
+ val := flagNames[key]
+ if f&key != 0 {
+ r = append(r, val)
+ }
+ }
+ return r
+}
+
+// String returns a string representation of the detected
+// CPU features.
+func (f ArmFlags) String() string {
+ return strings.Join(f.Strings(), ",")
+}
+
+// Strings returns an array of the detected features.
+func (f ArmFlags) Strings() []string {
+ r := make([]string, 0, 20)
+ for i := uint(0); i < 64; i++ {
+ key := ArmFlags(1 << i)
+ val := flagNamesArm[key]
+ if f&key != 0 {
+ r = append(r, val)
+ }
+ }
+ return r
+}
+func maxExtendedFunction() uint32 {
+ eax, _, _, _ := cpuid(0x80000000)
+ return eax
+}
+
+func maxFunctionID() uint32 {
+ a, _, _, _ := cpuid(0)
+ return a
+}
+
+func brandName() string {
+ if maxExtendedFunction() >= 0x80000004 {
+ v := make([]uint32, 0, 48)
+ for i := uint32(0); i < 3; i++ {
+ a, b, c, d := cpuid(0x80000002 + i)
+ v = append(v, a, b, c, d)
+ }
+ return strings.Trim(string(valAsString(v...)), " ")
+ }
+ return "unknown"
+}
+
+func threadsPerCore() int {
+ mfi := maxFunctionID()
+ vend, _ := vendorID()
+
+ if mfi < 0x4 || (vend != Intel && vend != AMD) {
+ return 1
+ }
+
+ if mfi < 0xb {
+ if vend != Intel {
+ return 1
+ }
+ _, b, _, d := cpuid(1)
+ if (d & (1 << 28)) != 0 {
+ // v will contain logical core count
+ v := (b >> 16) & 255
+ if v > 1 {
+ a4, _, _, _ := cpuid(4)
+ // physical cores
+ v2 := (a4 >> 26) + 1
+ if v2 > 0 {
+ return int(v) / int(v2)
+ }
+ }
+ }
+ return 1
+ }
+ _, b, _, _ := cpuidex(0xb, 0)
+ if b&0xffff == 0 {
+ return 1
+ }
+ return int(b & 0xffff)
+}
+
+func logicalCores() int {
+ mfi := maxFunctionID()
+ v, _ := vendorID()
+ switch v {
+ case Intel:
+ // Use this on old Intel processors
+ if mfi < 0xb {
+ if mfi < 1 {
+ return 0
+ }
+ // CPUID.1:EBX[23:16] represents the maximum number of addressable IDs (initial APIC ID)
+ // that can be assigned to logical processors in a physical package.
+ // The value may not be the same as the number of logical processors that are present in the hardware of a physical package.
+ _, ebx, _, _ := cpuid(1)
+ logical := (ebx >> 16) & 0xff
+ return int(logical)
+ }
+ _, b, _, _ := cpuidex(0xb, 1)
+ return int(b & 0xffff)
+ case AMD, Hygon:
+ _, b, _, _ := cpuid(1)
+ return int((b >> 16) & 0xff)
+ default:
+ return 0
+ }
+}
+
+func familyModel() (int, int) {
+ if maxFunctionID() < 0x1 {
+ return 0, 0
+ }
+ eax, _, _, _ := cpuid(1)
+ family := ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff)
+ model := ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0)
+ return int(family), int(model)
+}
+
+func physicalCores() int {
+ v, _ := vendorID()
+ switch v {
+ case Intel:
+ return logicalCores() / threadsPerCore()
+ case AMD, Hygon:
+ lc := logicalCores()
+ tpc := threadsPerCore()
+ if lc > 0 && tpc > 0 {
+ return lc / tpc
+ }
+ // The following is inaccurate on AMD EPYC 7742 64-Core Processor
+
+ if maxExtendedFunction() >= 0x80000008 {
+ _, _, c, _ := cpuid(0x80000008)
+ return int(c&0xff) + 1
+ }
+ }
+ return 0
+}
+
+// Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID
+var vendorMapping = map[string]Vendor{
+ "AMDisbetter!": AMD,
+ "AuthenticAMD": AMD,
+ "CentaurHauls": VIA,
+ "GenuineIntel": Intel,
+ "TransmetaCPU": Transmeta,
+ "GenuineTMx86": Transmeta,
+ "Geode by NSC": NSC,
+ "VIA VIA VIA ": VIA,
+ "KVMKVMKVMKVM": KVM,
+ "Microsoft Hv": MSVM,
+ "VMwareVMware": VMware,
+ "XenVMMXenVMM": XenHVM,
+ "bhyve bhyve ": Bhyve,
+ "HygonGenuine": Hygon,
+ "Vortex86 SoC": SiS,
+ "SiS SiS SiS ": SiS,
+ "RiseRiseRise": SiS,
+ "Genuine RDC": RDC,
+}
+
+func vendorID() (Vendor, string) {
+ _, b, c, d := cpuid(0)
+ v := string(valAsString(b, d, c))
+ vend, ok := vendorMapping[v]
+ if !ok {
+ return Other, v
+ }
+ return vend, v
+}
+
+func cacheLine() int {
+ if maxFunctionID() < 0x1 {
+ return 0
+ }
+
+ _, ebx, _, _ := cpuid(1)
+ cache := (ebx & 0xff00) >> 5 // cflush size
+ if cache == 0 && maxExtendedFunction() >= 0x80000006 {
+ _, _, ecx, _ := cpuid(0x80000006)
+ cache = ecx & 0xff // cacheline size
+ }
+ // TODO: Read from Cache and TLB Information
+ return int(cache)
+}
+
+func (c *CPUInfo) cacheSize() {
+ c.Cache.L1D = -1
+ c.Cache.L1I = -1
+ c.Cache.L2 = -1
+ c.Cache.L3 = -1
+ vendor, _ := vendorID()
+ switch vendor {
+ case Intel:
+ if maxFunctionID() < 4 {
+ return
+ }
+ for i := uint32(0); ; i++ {
+ eax, ebx, ecx, _ := cpuidex(4, i)
+ cacheType := eax & 15
+ if cacheType == 0 {
+ break
+ }
+ cacheLevel := (eax >> 5) & 7
+ coherency := int(ebx&0xfff) + 1
+ partitions := int((ebx>>12)&0x3ff) + 1
+ associativity := int((ebx>>22)&0x3ff) + 1
+ sets := int(ecx) + 1
+ size := associativity * partitions * coherency * sets
+ switch cacheLevel {
+ case 1:
+ if cacheType == 1 {
+ // 1 = Data Cache
+ c.Cache.L1D = size
+ } else if cacheType == 2 {
+ // 2 = Instruction Cache
+ c.Cache.L1I = size
+ } else {
+ if c.Cache.L1D < 0 {
+ c.Cache.L1I = size
+ }
+ if c.Cache.L1I < 0 {
+ c.Cache.L1I = size
+ }
+ }
+ case 2:
+ c.Cache.L2 = size
+ case 3:
+ c.Cache.L3 = size
+ }
+ }
+ case AMD, Hygon:
+ // Untested.
+ if maxExtendedFunction() < 0x80000005 {
+ return
+ }
+ _, _, ecx, edx := cpuid(0x80000005)
+ c.Cache.L1D = int(((ecx >> 24) & 0xFF) * 1024)
+ c.Cache.L1I = int(((edx >> 24) & 0xFF) * 1024)
+
+ if maxExtendedFunction() < 0x80000006 {
+ return
+ }
+ _, _, ecx, _ = cpuid(0x80000006)
+ c.Cache.L2 = int(((ecx >> 16) & 0xFFFF) * 1024)
+
+ // CPUID Fn8000_001D_EAX_x[N:0] Cache Properties
+ if maxExtendedFunction() < 0x8000001D {
+ return
+ }
+ for i := uint32(0); i < math.MaxUint32; i++ {
+ eax, ebx, ecx, _ := cpuidex(0x8000001D, i)
+
+ level := (eax >> 5) & 7
+ cacheNumSets := ecx + 1
+ cacheLineSize := 1 + (ebx & 2047)
+ cachePhysPartitions := 1 + ((ebx >> 12) & 511)
+ cacheNumWays := 1 + ((ebx >> 22) & 511)
+
+ typ := eax & 15
+ size := int(cacheNumSets * cacheLineSize * cachePhysPartitions * cacheNumWays)
+ if typ == 0 {
+ return
+ }
+
+ switch level {
+ case 1:
+ switch typ {
+ case 1:
+ // Data cache
+ c.Cache.L1D = size
+ case 2:
+ // Inst cache
+ c.Cache.L1I = size
+ default:
+ if c.Cache.L1D < 0 {
+ c.Cache.L1I = size
+ }
+ if c.Cache.L1I < 0 {
+ c.Cache.L1I = size
+ }
+ }
+ case 2:
+ c.Cache.L2 = size
+ case 3:
+ c.Cache.L3 = size
+ }
+ }
+ }
+
+ return
+}
+
+type SGXEPCSection struct {
+ BaseAddress uint64
+ EPCSize uint64
+}
+
+type SGXSupport struct {
+ Available bool
+ LaunchControl bool
+ SGX1Supported bool
+ SGX2Supported bool
+ MaxEnclaveSizeNot64 int64
+ MaxEnclaveSize64 int64
+ EPCSections []SGXEPCSection
+}
+
+func hasSGX(available, lc bool) (rval SGXSupport) {
+ rval.Available = available
+
+ if !available {
+ return
+ }
+
+ rval.LaunchControl = lc
+
+ a, _, _, d := cpuidex(0x12, 0)
+ rval.SGX1Supported = a&0x01 != 0
+ rval.SGX2Supported = a&0x02 != 0
+ rval.MaxEnclaveSizeNot64 = 1 << (d & 0xFF) // pow 2
+ rval.MaxEnclaveSize64 = 1 << ((d >> 8) & 0xFF) // pow 2
+ rval.EPCSections = make([]SGXEPCSection, 0)
+
+ for subleaf := uint32(2); subleaf < 2+8; subleaf++ {
+ eax, ebx, ecx, edx := cpuidex(0x12, subleaf)
+ leafType := eax & 0xf
+
+ if leafType == 0 {
+ // Invalid subleaf, stop iterating
+ break
+ } else if leafType == 1 {
+ // EPC Section subleaf
+ baseAddress := uint64(eax&0xfffff000) + (uint64(ebx&0x000fffff) << 32)
+ size := uint64(ecx&0xfffff000) + (uint64(edx&0x000fffff) << 32)
+
+ section := SGXEPCSection{BaseAddress: baseAddress, EPCSize: size}
+ rval.EPCSections = append(rval.EPCSections, section)
+ }
+ }
+
+ return
+}
+
+func support() Flags {
+ mfi := maxFunctionID()
+ vend, _ := vendorID()
+ if mfi < 0x1 {
+ return 0
+ }
+ rval := uint64(0)
+ _, _, c, d := cpuid(1)
+ if (d & (1 << 15)) != 0 {
+ rval |= CMOV
+ }
+ if (d & (1 << 23)) != 0 {
+ rval |= MMX
+ }
+ if (d & (1 << 25)) != 0 {
+ rval |= MMXEXT
+ }
+ if (d & (1 << 25)) != 0 {
+ rval |= SSE
+ }
+ if (d & (1 << 26)) != 0 {
+ rval |= SSE2
+ }
+ if (c & 1) != 0 {
+ rval |= SSE3
+ }
+ if (c & (1 << 5)) != 0 {
+ rval |= VMX
+ }
+ if (c & 0x00000200) != 0 {
+ rval |= SSSE3
+ }
+ if (c & 0x00080000) != 0 {
+ rval |= SSE4
+ }
+ if (c & 0x00100000) != 0 {
+ rval |= SSE42
+ }
+ if (c & (1 << 25)) != 0 {
+ rval |= AESNI
+ }
+ if (c & (1 << 1)) != 0 {
+ rval |= CLMUL
+ }
+ if c&(1<<23) != 0 {
+ rval |= POPCNT
+ }
+ if c&(1<<30) != 0 {
+ rval |= RDRAND
+ }
+ if c&(1<<29) != 0 {
+ rval |= F16C
+ }
+ if c&(1<<13) != 0 {
+ rval |= CX16
+ }
+ if vend == Intel && (d&(1<<28)) != 0 && mfi >= 4 {
+ if threadsPerCore() > 1 {
+ rval |= HTT
+ }
+ }
+ if vend == AMD && (d&(1<<28)) != 0 && mfi >= 4 {
+ if threadsPerCore() > 1 {
+ rval |= HTT
+ }
+ }
+ // Check XGETBV, OXSAVE and AVX bits
+ if c&(1<<26) != 0 && c&(1<<27) != 0 && c&(1<<28) != 0 {
+ // Check for OS support
+ eax, _ := xgetbv(0)
+ if (eax & 0x6) == 0x6 {
+ rval |= AVX
+ if (c & 0x00001000) != 0 {
+ rval |= FMA3
+ }
+ }
+ }
+
+ // 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 (rval&AVX) != 0 && (ebx&0x00000020) != 0 {
+ rval |= AVX2
+ }
+ if (ebx & 0x00000008) != 0 {
+ rval |= BMI1
+ if (ebx & 0x00000100) != 0 {
+ rval |= BMI2
+ }
+ }
+ if ebx&(1<<2) != 0 {
+ rval |= SGX
+ }
+ if ebx&(1<<4) != 0 {
+ rval |= HLE
+ }
+ if ebx&(1<<9) != 0 {
+ rval |= ERMS
+ }
+ if ebx&(1<<11) != 0 {
+ rval |= RTM
+ }
+ if ebx&(1<<14) != 0 {
+ rval |= MPX
+ }
+ if ebx&(1<<18) != 0 {
+ rval |= RDSEED
+ }
+ if ebx&(1<<19) != 0 {
+ rval |= ADX
+ }
+ if ebx&(1<<29) != 0 {
+ rval |= SHA
+ }
+ if edx&(1<<26) != 0 {
+ rval |= IBPB
+ }
+ if ecx&(1<<30) != 0 {
+ rval |= SGXLC
+ }
+ if edx&(1<<27) != 0 {
+ rval |= STIBP
+ }
+
+ // Only detect AVX-512 features if XGETBV is supported
+ if c&((1<<26)|(1<<27)) == (1<<26)|(1<<27) {
+ // Check for OS support
+ eax, _ := xgetbv(0)
+
+ // Verify that XCR0[7:5] = ‘111b’ (OPMASK state, upper 256-bit of ZMM0-ZMM15 and
+ // ZMM16-ZMM31 state are enabled by OS)
+ /// and that XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS).
+ if (eax>>5)&7 == 7 && (eax>>1)&3 == 3 {
+ if ebx&(1<<16) != 0 {
+ rval |= AVX512F
+ }
+ if ebx&(1<<17) != 0 {
+ rval |= AVX512DQ
+ }
+ if ebx&(1<<21) != 0 {
+ rval |= AVX512IFMA
+ }
+ if ebx&(1<<26) != 0 {
+ rval |= AVX512PF
+ }
+ if ebx&(1<<27) != 0 {
+ rval |= AVX512ER
+ }
+ if ebx&(1<<28) != 0 {
+ rval |= AVX512CD
+ }
+ if ebx&(1<<30) != 0 {
+ rval |= AVX512BW
+ }
+ if ebx&(1<<31) != 0 {
+ rval |= AVX512VL
+ }
+ // ecx
+ if ecx&(1<<1) != 0 {
+ rval |= AVX512VBMI
+ }
+ if ecx&(1<<6) != 0 {
+ rval |= AVX512VBMI2
+ }
+ if ecx&(1<<8) != 0 {
+ rval |= GFNI
+ }
+ if ecx&(1<<9) != 0 {
+ rval |= VAES
+ }
+ if ecx&(1<<10) != 0 {
+ rval |= VPCLMULQDQ
+ }
+ if ecx&(1<<11) != 0 {
+ rval |= AVX512VNNI
+ }
+ if ecx&(1<<12) != 0 {
+ rval |= AVX512BITALG
+ }
+ if ecx&(1<<14) != 0 {
+ rval |= AVX512VPOPCNTDQ
+ }
+ // edx
+ if edx&(1<<8) != 0 {
+ rval |= AVX512VP2INTERSECT
+ }
+ // cpuid eax 07h,ecx=1
+ if eax1&(1<<5) != 0 {
+ rval |= AVX512BF16
+ }
+ }
+ }
+ }
+
+ if maxExtendedFunction() >= 0x80000001 {
+ _, _, c, d := cpuid(0x80000001)
+ if (c & (1 << 5)) != 0 {
+ rval |= LZCNT
+ rval |= POPCNT
+ }
+ if (d & (1 << 31)) != 0 {
+ rval |= AMD3DNOW
+ }
+ if (d & (1 << 30)) != 0 {
+ rval |= AMD3DNOWEXT
+ }
+ if (d & (1 << 23)) != 0 {
+ rval |= MMX
+ }
+ if (d & (1 << 22)) != 0 {
+ rval |= MMXEXT
+ }
+ if (c & (1 << 6)) != 0 {
+ rval |= SSE4A
+ }
+ if d&(1<<20) != 0 {
+ rval |= NX
+ }
+ if d&(1<<27) != 0 {
+ rval |= RDTSCP
+ }
+
+ /* Allow for selectively disabling SSE2 functions on AMD processors
+ with SSE2 support but not SSE4a. This includes Athlon64, some
+ Opteron, and some Sempron processors. MMX, SSE, or 3DNow! are faster
+ than SSE2 often enough to utilize this special-case flag.
+ AV_CPU_FLAG_SSE2 and AV_CPU_FLAG_SSE2SLOW are both set in this case
+ so that SSE2 is used unless explicitly disabled by checking
+ AV_CPU_FLAG_SSE2SLOW. */
+ if vend != Intel &&
+ rval&SSE2 != 0 && (c&0x00000040) == 0 {
+ rval |= SSE2SLOW
+ }
+
+ /* XOP and FMA4 use the AVX instruction coding scheme, so they can't be
+ * used unless the OS has AVX support. */
+ if (rval & AVX) != 0 {
+ if (c & 0x00000800) != 0 {
+ rval |= XOP
+ }
+ if (c & 0x00010000) != 0 {
+ rval |= FMA4
+ }
+ }
+
+ if vend == Intel {
+ family, model := familyModel()
+ if family == 6 && (model == 9 || model == 13 || model == 14) {
+ /* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and
+ * 6/14 (core1 "yonah") theoretically support sse2, but it's
+ * usually slower than mmx. */
+ if (rval & SSE2) != 0 {
+ rval |= SSE2SLOW
+ }
+ if (rval & SSE3) != 0 {
+ rval |= SSE3SLOW
+ }
+ }
+ /* The Atom processor has SSSE3 support, which is useful in many cases,
+ * but sometimes the SSSE3 version is slower than the SSE2 equivalent
+ * on the Atom, but is generally faster on other processors supporting
+ * SSSE3. This flag allows for selectively disabling certain SSSE3
+ * functions on the Atom. */
+ if family == 6 && model == 28 {
+ rval |= ATOM
+ }
+ }
+ }
+ return Flags(rval)
+}
+
+func valAsString(values ...uint32) []byte {
+ r := make([]byte, 4*len(values))
+ for i, v := range values {
+ dst := r[i*4:]
+ dst[0] = byte(v & 0xff)
+ dst[1] = byte((v >> 8) & 0xff)
+ dst[2] = byte((v >> 16) & 0xff)
+ dst[3] = byte((v >> 24) & 0xff)
+ switch {
+ case dst[0] == 0:
+ return r[:i*4]
+ case dst[1] == 0:
+ return r[:i*4+1]
+ case dst[2] == 0:
+ return r[:i*4+2]
+ case dst[3] == 0:
+ return r[:i*4+3]
+ }
+ }
+ return r
+}
+
+// Single-precision and double-precision floating point
+func (c CPUInfo) ArmFP() bool {
+ return c.Arm&FP != 0
+}
+
+// Advanced SIMD
+func (c CPUInfo) ArmASIMD() bool {
+ return c.Arm&ASIMD != 0
+}
+
+// Generic timer
+func (c CPUInfo) ArmEVTSTRM() bool {
+ return c.Arm&EVTSTRM != 0
+}
+
+// AES instructions
+func (c CPUInfo) ArmAES() bool {
+ return c.Arm&AES != 0
+}
+
+// Polynomial Multiply instructions (PMULL/PMULL2)
+func (c CPUInfo) ArmPMULL() bool {
+ return c.Arm&PMULL != 0
+}
+
+// SHA-1 instructions (SHA1C, etc)
+func (c CPUInfo) ArmSHA1() bool {
+ return c.Arm&SHA1 != 0
+}
+
+// SHA-2 instructions (SHA256H, etc)
+func (c CPUInfo) ArmSHA2() bool {
+ return c.Arm&SHA2 != 0
+}
+
+// CRC32/CRC32C instructions
+func (c CPUInfo) ArmCRC32() bool {
+ return c.Arm&CRC32 != 0
+}
+
+// Large System Extensions (LSE)
+func (c CPUInfo) ArmATOMICS() bool {
+ return c.Arm&ATOMICS != 0
+}
+
+// Half-precision floating point
+func (c CPUInfo) ArmFPHP() bool {
+ return c.Arm&FPHP != 0
+}
+
+// Advanced SIMD half-precision floating point
+func (c CPUInfo) ArmASIMDHP() bool {
+ return c.Arm&ASIMDHP != 0
+}
+
+// Rounding Double Multiply Accumulate/Subtract (SQRDMLAH/SQRDMLSH)
+func (c CPUInfo) ArmASIMDRDM() bool {
+ return c.Arm&ASIMDRDM != 0
+}
+
+// Javascript-style double->int convert (FJCVTZS)
+func (c CPUInfo) ArmJSCVT() bool {
+ return c.Arm&JSCVT != 0
+}
+
+// Floatin point complex number addition and multiplication
+func (c CPUInfo) ArmFCMA() bool {
+ return c.Arm&FCMA != 0
+}
+
+// Weaker release consistency (LDAPR, etc)
+func (c CPUInfo) ArmLRCPC() bool {
+ return c.Arm&LRCPC != 0
+}
+
+// Data cache clean to Point of Persistence (DC CVAP)
+func (c CPUInfo) ArmDCPOP() bool {
+ return c.Arm&DCPOP != 0
+}
+
+// SHA-3 instructions (EOR3, RAXI, XAR, BCAX)
+func (c CPUInfo) ArmSHA3() bool {
+ return c.Arm&SHA3 != 0
+}
+
+// SM3 instructions
+func (c CPUInfo) ArmSM3() bool {
+ return c.Arm&SM3 != 0
+}
+
+// SM4 instructions
+func (c CPUInfo) ArmSM4() bool {
+ return c.Arm&SM4 != 0
+}
+
+// SIMD Dot Product
+func (c CPUInfo) ArmASIMDDP() bool {
+ return c.Arm&ASIMDDP != 0
+}
+
+// SHA512 instructions
+func (c CPUInfo) ArmSHA512() bool {
+ return c.Arm&SHA512 != 0
+}
+
+// Scalable Vector Extension
+func (c CPUInfo) ArmSVE() bool {
+ return c.Arm&SVE != 0
+}
+
+// Generic Pointer Authentication
+func (c CPUInfo) ArmGPA() bool {
+ return c.Arm&GPA != 0
+}
diff --git a/vendor/github.com/klauspost/cpuid/cpuid_386.s b/vendor/github.com/klauspost/cpuid/cpuid_386.s
new file mode 100644
index 0000000..089638f
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/cpuid_386.s
@@ -0,0 +1,42 @@
+// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
+
+//+build 386,!gccgo,!noasm,!appengine
+
+// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
+TEXT ·asmCpuid(SB), 7, $0
+ XORL CX, CX
+ MOVL op+0(FP), AX
+ CPUID
+ MOVL AX, eax+4(FP)
+ MOVL BX, ebx+8(FP)
+ MOVL CX, ecx+12(FP)
+ MOVL DX, edx+16(FP)
+ RET
+
+// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
+TEXT ·asmCpuidex(SB), 7, $0
+ MOVL op+0(FP), AX
+ MOVL op2+4(FP), CX
+ CPUID
+ MOVL AX, eax+8(FP)
+ MOVL BX, ebx+12(FP)
+ MOVL CX, ecx+16(FP)
+ MOVL DX, edx+20(FP)
+ RET
+
+// func xgetbv(index uint32) (eax, edx uint32)
+TEXT ·asmXgetbv(SB), 7, $0
+ MOVL index+0(FP), CX
+ BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
+ MOVL AX, eax+4(FP)
+ MOVL DX, edx+8(FP)
+ RET
+
+// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
+TEXT ·asmRdtscpAsm(SB), 7, $0
+ BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
+ MOVL AX, eax+0(FP)
+ MOVL BX, ebx+4(FP)
+ MOVL CX, ecx+8(FP)
+ MOVL DX, edx+12(FP)
+ RET
diff --git a/vendor/github.com/klauspost/cpuid/cpuid_amd64.s b/vendor/github.com/klauspost/cpuid/cpuid_amd64.s
new file mode 100644
index 0000000..3ba0559
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/cpuid_amd64.s
@@ -0,0 +1,42 @@
+// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
+
+//+build amd64,!gccgo,!noasm,!appengine
+
+// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
+TEXT ·asmCpuid(SB), 7, $0
+ XORQ CX, CX
+ MOVL op+0(FP), AX
+ CPUID
+ MOVL AX, eax+8(FP)
+ MOVL BX, ebx+12(FP)
+ MOVL CX, ecx+16(FP)
+ MOVL DX, edx+20(FP)
+ RET
+
+// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
+TEXT ·asmCpuidex(SB), 7, $0
+ MOVL op+0(FP), AX
+ MOVL op2+4(FP), CX
+ CPUID
+ MOVL AX, eax+8(FP)
+ MOVL BX, ebx+12(FP)
+ MOVL CX, ecx+16(FP)
+ MOVL DX, edx+20(FP)
+ RET
+
+// func asmXgetbv(index uint32) (eax, edx uint32)
+TEXT ·asmXgetbv(SB), 7, $0
+ MOVL index+0(FP), CX
+ BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV
+ MOVL AX, eax+8(FP)
+ MOVL DX, edx+12(FP)
+ RET
+
+// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
+TEXT ·asmRdtscpAsm(SB), 7, $0
+ BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP
+ MOVL AX, eax+0(FP)
+ MOVL BX, ebx+4(FP)
+ MOVL CX, ecx+8(FP)
+ MOVL DX, edx+12(FP)
+ RET
diff --git a/vendor/github.com/klauspost/cpuid/cpuid_arm64.s b/vendor/github.com/klauspost/cpuid/cpuid_arm64.s
new file mode 100644
index 0000000..8975ee8
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/cpuid_arm64.s
@@ -0,0 +1,26 @@
+// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
+
+//+build arm64,!gccgo
+
+// See https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
+
+// func getMidr
+TEXT ·getMidr(SB), 7, $0
+ WORD $0xd5380000 // mrs x0, midr_el1 /* Main ID Register */
+ MOVD R0, midr+0(FP)
+ RET
+
+// func getProcFeatures
+TEXT ·getProcFeatures(SB), 7, $0
+ WORD $0xd5380400 // mrs x0, id_aa64pfr0_el1 /* Processor Feature Register 0 */
+ MOVD R0, procFeatures+0(FP)
+ RET
+
+// func getInstAttributes
+TEXT ·getInstAttributes(SB), 7, $0
+ WORD $0xd5380600 // mrs x0, id_aa64isar0_el1 /* Instruction Set Attribute Register 0 */
+ WORD $0xd5380621 // mrs x1, id_aa64isar1_el1 /* Instruction Set Attribute Register 1 */
+ MOVD R0, instAttrReg0+0(FP)
+ MOVD R1, instAttrReg1+8(FP)
+ RET
+
diff --git a/vendor/github.com/klauspost/cpuid/detect_arm64.go b/vendor/github.com/klauspost/cpuid/detect_arm64.go
new file mode 100644
index 0000000..923a826
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/detect_arm64.go
@@ -0,0 +1,219 @@
+// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
+
+//+build arm64,!gccgo,!noasm,!appengine
+
+package cpuid
+
+func getMidr() (midr uint64)
+func getProcFeatures() (procFeatures uint64)
+func getInstAttributes() (instAttrReg0, instAttrReg1 uint64)
+
+func initCPU() {
+ cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
+ cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
+ xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
+ rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
+}
+
+func addInfo(c *CPUInfo) {
+ // ARM64 disabled for now.
+ if true {
+ return
+ }
+ // midr := getMidr()
+
+ // MIDR_EL1 - Main ID Register
+ // x--------------------------------------------------x
+ // | Name | bits | visible |
+ // |--------------------------------------------------|
+ // | Implementer | [31-24] | y |
+ // |--------------------------------------------------|
+ // | Variant | [23-20] | y |
+ // |--------------------------------------------------|
+ // | Architecture | [19-16] | y |
+ // |--------------------------------------------------|
+ // | PartNum | [15-4] | y |
+ // |--------------------------------------------------|
+ // | Revision | [3-0] | y |
+ // x--------------------------------------------------x
+
+ // fmt.Printf(" implementer: 0x%02x\n", (midr>>24)&0xff)
+ // fmt.Printf(" variant: 0x%01x\n", (midr>>20)&0xf)
+ // fmt.Printf("architecture: 0x%01x\n", (midr>>16)&0xf)
+ // fmt.Printf(" part num: 0x%03x\n", (midr>>4)&0xfff)
+ // fmt.Printf(" revision: 0x%01x\n", (midr>>0)&0xf)
+
+ procFeatures := getProcFeatures()
+
+ // ID_AA64PFR0_EL1 - Processor Feature Register 0
+ // x--------------------------------------------------x
+ // | Name | bits | visible |
+ // |--------------------------------------------------|
+ // | DIT | [51-48] | y |
+ // |--------------------------------------------------|
+ // | SVE | [35-32] | y |
+ // |--------------------------------------------------|
+ // | GIC | [27-24] | n |
+ // |--------------------------------------------------|
+ // | AdvSIMD | [23-20] | y |
+ // |--------------------------------------------------|
+ // | FP | [19-16] | y |
+ // |--------------------------------------------------|
+ // | EL3 | [15-12] | n |
+ // |--------------------------------------------------|
+ // | EL2 | [11-8] | n |
+ // |--------------------------------------------------|
+ // | EL1 | [7-4] | n |
+ // |--------------------------------------------------|
+ // | EL0 | [3-0] | n |
+ // x--------------------------------------------------x
+
+ var f ArmFlags
+ // if procFeatures&(0xf<<48) != 0 {
+ // fmt.Println("DIT")
+ // }
+ if procFeatures&(0xf<<32) != 0 {
+ f |= SVE
+ }
+ if procFeatures&(0xf<<20) != 15<<20 {
+ f |= ASIMD
+ if procFeatures&(0xf<<20) == 1<<20 {
+ // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64pfr0_el1
+ // 0b0001 --> As for 0b0000, and also includes support for half-precision floating-point arithmetic.
+ f |= FPHP
+ f |= ASIMDHP
+ }
+ }
+ if procFeatures&(0xf<<16) != 0 {
+ f |= FP
+ }
+
+ instAttrReg0, instAttrReg1 := getInstAttributes()
+
+ // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
+ //
+ // ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
+ // x--------------------------------------------------x
+ // | Name | bits | visible |
+ // |--------------------------------------------------|
+ // | TS | [55-52] | y |
+ // |--------------------------------------------------|
+ // | FHM | [51-48] | y |
+ // |--------------------------------------------------|
+ // | DP | [47-44] | y |
+ // |--------------------------------------------------|
+ // | SM4 | [43-40] | y |
+ // |--------------------------------------------------|
+ // | SM3 | [39-36] | y |
+ // |--------------------------------------------------|
+ // | SHA3 | [35-32] | y |
+ // |--------------------------------------------------|
+ // | RDM | [31-28] | y |
+ // |--------------------------------------------------|
+ // | ATOMICS | [23-20] | y |
+ // |--------------------------------------------------|
+ // | CRC32 | [19-16] | y |
+ // |--------------------------------------------------|
+ // | SHA2 | [15-12] | y |
+ // |--------------------------------------------------|
+ // | SHA1 | [11-8] | y |
+ // |--------------------------------------------------|
+ // | AES | [7-4] | y |
+ // x--------------------------------------------------x
+
+ // if instAttrReg0&(0xf<<52) != 0 {
+ // fmt.Println("TS")
+ // }
+ // if instAttrReg0&(0xf<<48) != 0 {
+ // fmt.Println("FHM")
+ // }
+ if instAttrReg0&(0xf<<44) != 0 {
+ f |= ASIMDDP
+ }
+ if instAttrReg0&(0xf<<40) != 0 {
+ f |= SM4
+ }
+ if instAttrReg0&(0xf<<36) != 0 {
+ f |= SM3
+ }
+ if instAttrReg0&(0xf<<32) != 0 {
+ f |= SHA3
+ }
+ if instAttrReg0&(0xf<<28) != 0 {
+ f |= ASIMDRDM
+ }
+ if instAttrReg0&(0xf<<20) != 0 {
+ f |= ATOMICS
+ }
+ if instAttrReg0&(0xf<<16) != 0 {
+ f |= CRC32
+ }
+ if instAttrReg0&(0xf<<12) != 0 {
+ f |= SHA2
+ }
+ if instAttrReg0&(0xf<<12) == 2<<12 {
+ // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
+ // 0b0010 --> As 0b0001, plus SHA512H, SHA512H2, SHA512SU0, and SHA512SU1 instructions implemented.
+ f |= SHA512
+ }
+ if instAttrReg0&(0xf<<8) != 0 {
+ f |= SHA1
+ }
+ if instAttrReg0&(0xf<<4) != 0 {
+ f |= AES
+ }
+ if instAttrReg0&(0xf<<4) == 2<<4 {
+ // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar0_el1
+ // 0b0010 --> As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit data quantities.
+ f |= PMULL
+ }
+
+ // https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/id_aa64isar1_el1
+ //
+ // ID_AA64ISAR1_EL1 - Instruction set attribute register 1
+ // x--------------------------------------------------x
+ // | Name | bits | visible |
+ // |--------------------------------------------------|
+ // | GPI | [31-28] | y |
+ // |--------------------------------------------------|
+ // | GPA | [27-24] | y |
+ // |--------------------------------------------------|
+ // | LRCPC | [23-20] | y |
+ // |--------------------------------------------------|
+ // | FCMA | [19-16] | y |
+ // |--------------------------------------------------|
+ // | JSCVT | [15-12] | y |
+ // |--------------------------------------------------|
+ // | API | [11-8] | y |
+ // |--------------------------------------------------|
+ // | APA | [7-4] | y |
+ // |--------------------------------------------------|
+ // | DPB | [3-0] | y |
+ // x--------------------------------------------------x
+
+ // if instAttrReg1&(0xf<<28) != 0 {
+ // fmt.Println("GPI")
+ // }
+ if instAttrReg1&(0xf<<28) != 24 {
+ f |= GPA
+ }
+ if instAttrReg1&(0xf<<20) != 0 {
+ f |= LRCPC
+ }
+ if instAttrReg1&(0xf<<16) != 0 {
+ f |= FCMA
+ }
+ if instAttrReg1&(0xf<<12) != 0 {
+ f |= JSCVT
+ }
+ // if instAttrReg1&(0xf<<8) != 0 {
+ // fmt.Println("API")
+ // }
+ // if instAttrReg1&(0xf<<4) != 0 {
+ // fmt.Println("APA")
+ // }
+ if instAttrReg1&(0xf<<0) != 0 {
+ f |= DCPOP
+ }
+ c.Arm = f
+}
diff --git a/vendor/github.com/klauspost/cpuid/detect_intel.go b/vendor/github.com/klauspost/cpuid/detect_intel.go
new file mode 100644
index 0000000..363951b
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/detect_intel.go
@@ -0,0 +1,33 @@
+// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
+
+//+build 386,!gccgo,!noasm amd64,!gccgo,!noasm,!appengine
+
+package cpuid
+
+func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32)
+func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32)
+func asmXgetbv(index uint32) (eax, edx uint32)
+func asmRdtscpAsm() (eax, ebx, ecx, edx uint32)
+
+func initCPU() {
+ cpuid = asmCpuid
+ cpuidex = asmCpuidex
+ xgetbv = asmXgetbv
+ rdtscpAsm = asmRdtscpAsm
+}
+
+func addInfo(c *CPUInfo) {
+ c.maxFunc = maxFunctionID()
+ c.maxExFunc = maxExtendedFunction()
+ c.BrandName = brandName()
+ c.CacheLine = cacheLine()
+ c.Family, c.Model = familyModel()
+ c.Features = support()
+ c.SGX = hasSGX(c.Features&SGX != 0, c.Features&SGXLC != 0)
+ c.ThreadsPerCore = threadsPerCore()
+ c.LogicalCores = logicalCores()
+ c.PhysicalCores = physicalCores()
+ c.VendorID, c.VendorString = vendorID()
+ c.Hz = hertz(c.BrandName)
+ c.cacheSize()
+}
diff --git a/vendor/github.com/klauspost/cpuid/detect_ref.go b/vendor/github.com/klauspost/cpuid/detect_ref.go
new file mode 100644
index 0000000..970ff3d
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/detect_ref.go
@@ -0,0 +1,14 @@
+// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file.
+
+//+build !amd64,!386,!arm64 gccgo noasm appengine
+
+package cpuid
+
+func initCPU() {
+ cpuid = func(uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
+ cpuidex = func(x, y uint32) (a, b, c, d uint32) { return 0, 0, 0, 0 }
+ xgetbv = func(uint32) (a, b uint32) { return 0, 0 }
+ rdtscpAsm = func() (a, b, c, d uint32) { return 0, 0, 0, 0 }
+}
+
+func addInfo(info *CPUInfo) {}
diff --git a/vendor/github.com/klauspost/cpuid/go.mod b/vendor/github.com/klauspost/cpuid/go.mod
new file mode 100644
index 0000000..55563f2
--- /dev/null
+++ b/vendor/github.com/klauspost/cpuid/go.mod
@@ -0,0 +1,3 @@
+module github.com/klauspost/cpuid
+
+go 1.12
diff --git a/vendor/github.com/klauspost/reedsolomon/.gitignore b/vendor/github.com/klauspost/reedsolomon/.gitignore
new file mode 100644
index 0000000..59610b5
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/.gitignore
@@ -0,0 +1,26 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+
+.idea \ No newline at end of file
diff --git a/vendor/github.com/klauspost/reedsolomon/.travis.yml b/vendor/github.com/klauspost/reedsolomon/.travis.yml
new file mode 100644
index 0000000..f77b85c
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/.travis.yml
@@ -0,0 +1,77 @@
+language: go
+
+os:
+ - linux
+ - osx
+ - windows
+
+arch:
+ - amd64
+ - arm64
+ - ppc64le
+ - s390x
+
+go:
+ - 1.12.x
+ - 1.13.x
+ - 1.14.x
+ - master
+
+install:
+ - go get ./...
+
+script:
+ - go vet ./...
+ - go test -cpu=1,2 .
+ - go test -tags=noasm -cpu=1,2 .
+ - go build examples/simple-decoder.go
+ - go build examples/simple-encoder.go
+ - go build examples/stream-decoder.go
+ - go build examples/stream-encoder.go
+
+stages:
+ - gofmt
+ - test
+ - deploy
+
+jobs:
+ allow_failures:
+ - go: 'master'
+ - arch: s390x
+ fast_finish: true
+ include:
+ - stage: gofmt
+ go: 1.14.x
+ os: linux
+ arch: amd64
+ script:
+ - diff <(gofmt -d .) <(printf "")
+ - diff <(gofmt -d ./examples) <(printf "")
+ - go install github.com/klauspost/asmfmt/cmd/asmfmt
+ - diff <(asmfmt -d .) <(printf "")
+ - stage: race
+ go: 1.14.x
+ os: linux
+ arch: amd64
+ script:
+ - go test -cpu=1 -short -race .
+ - go test -cpu=2 -short -race .
+ - go test -tags=noasm -cpu=1 -short -race .
+ - go test -tags=noasm -cpu=4 -short -race .
+ - go test -no-avx512 -short -race .
+ - go test -no-avx512 -no-avx2 -short -race .
+ - go test -no-avx512 -no-avx2 -no-ssse3 -short -race .
+ - stage: amd64-noasm
+ go: 1.14.x
+ os: linux
+ arch: amd64
+ script:
+ - go test -no-avx512
+ - go test -no-avx512 -no-avx2
+ - go test -no-avx512 -no-avx2 -no-ssse3
+ - stage: i386
+ go: 1.14.x
+ os: linux
+ arch: amd64
+ script:
+ - GOOS=linux GOARCH=386 go test -short .
diff --git a/vendor/github.com/klauspost/reedsolomon/LICENSE b/vendor/github.com/klauspost/reedsolomon/LICENSE
new file mode 100644
index 0000000..a947e16
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/LICENSE
@@ -0,0 +1,23 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Klaus Post
+Copyright (c) 2015 Backblaze
+
+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.
+
diff --git a/vendor/github.com/klauspost/reedsolomon/README.md b/vendor/github.com/klauspost/reedsolomon/README.md
new file mode 100644
index 0000000..f9824cb
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/README.md
@@ -0,0 +1,395 @@
+# Reed-Solomon
+[![GoDoc][1]][2] [![Build Status][3]][4]
+
+[1]: https://godoc.org/github.com/klauspost/reedsolomon?status.svg
+[2]: https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc
+[3]: https://travis-ci.org/klauspost/reedsolomon.svg?branch=master
+[4]: https://travis-ci.org/klauspost/reedsolomon
+
+Reed-Solomon Erasure Coding in Go, with speeds exceeding 1GB/s/cpu core implemented in pure Go.
+
+This is a Go port of the [JavaReedSolomon](https://github.com/Backblaze/JavaReedSolomon) library released by
+[Backblaze](http://backblaze.com), with some additional optimizations.
+
+For an introduction on erasure coding, see the post on the [Backblaze blog](https://www.backblaze.com/blog/reed-solomon/).
+
+Package home: https://github.com/klauspost/reedsolomon
+
+Godoc: https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc
+
+# Installation
+To get the package use the standard:
+```bash
+go get -u github.com/klauspost/reedsolomon
+```
+
+# Changes
+
+## May 2020
+
+* ARM64 optimizations, up to 2.5x faster.
+* Added [WithFastOneParityMatrix](https://pkg.go.dev/github.com/klauspost/reedsolomon?tab=doc#WithFastOneParityMatrix) for faster operation with 1 parity shard.
+* Much better performance when using a limited number of goroutines.
+* AVX512 is now using multiple cores.
+* Stream processing overhaul, big speedups in most cases.
+* AVX512 optimizations
+
+## March 6, 2019
+
+The pure Go implementation is about 30% faster. Minor tweaks to assembler implementations.
+
+## February 8, 2019
+
+AVX512 accelerated version added for Intel Skylake CPUs. This can give up to a 4x speed improvement as compared to AVX2.
+See [here](https://github.com/klauspost/reedsolomon#performance-on-avx512) for more details.
+
+## December 18, 2018
+
+Assembly code for ppc64le has been contributed, this boosts performance by about 10x on this platform.
+
+## November 18, 2017
+
+Added [WithAutoGoroutines](https://godoc.org/github.com/klauspost/reedsolomon#WithAutoGoroutines) which will attempt
+to calculate the optimal number of goroutines to use based on your expected shard size and detected CPU.
+
+## October 1, 2017
+
+* [Cauchy Matrix](https://godoc.org/github.com/klauspost/reedsolomon#WithCauchyMatrix) is now an option.
+Thanks to [templexxx](https://github.com/templexxx) for the basis of this.
+
+* Default maximum number of [goroutines](https://godoc.org/github.com/klauspost/reedsolomon#WithMaxGoroutines)
+has been increased for better multi-core scaling.
+
+* After several requests the Reconstruct and ReconstructData now slices of zero length but sufficient capacity to
+be used instead of allocating new memory.
+
+## August 26, 2017
+
+* The [`Encoder()`](https://godoc.org/github.com/klauspost/reedsolomon#Encoder) now contains an `Update`
+function contributed by [chenzhongtao](https://github.com/chenzhongtao).
+
+* [Frank Wessels](https://github.com/fwessels) kindly contributed ARM 64 bit assembly,
+which gives a huge performance boost on this platform.
+
+## July 20, 2017
+
+`ReconstructData` added to [`Encoder`](https://godoc.org/github.com/klauspost/reedsolomon#Encoder) interface.
+This can cause compatibility issues if you implement your own Encoder. A simple workaround can be added:
+
+```Go
+func (e *YourEnc) ReconstructData(shards [][]byte) error {
+ return ReconstructData(shards)
+}
+```
+
+You can of course also do your own implementation.
+The [`StreamEncoder`](https://godoc.org/github.com/klauspost/reedsolomon#StreamEncoder)
+handles this without modifying the interface.
+This is a good lesson on why returning interfaces is not a good design.
+
+# Usage
+
+This section assumes you know the basics of Reed-Solomon encoding.
+A good start is this [Backblaze blog post](https://www.backblaze.com/blog/reed-solomon/).
+
+This package performs the calculation of the parity sets. The usage is therefore relatively simple.
+
+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`.
+In the example above, the top slice must have a length of 13.
+
+```Go
+ data := make([][]byte, 13)
+```
+You should then fill the 10 first slices with *equally sized* data,
+and create parity shards that will be populated with parity data. In this case we create the data in memory,
+but you could for instance also use [mmap](https://github.com/edsrzf/mmap-go) to map files.
+
+```Go
+ // Create all shards, size them at 50000 each
+ for i := range input {
+ data[i] := make([]byte, 50000)
+ }
+
+
+ // Fill some data into the data shards
+ for i, in := range data[:10] {
+ for j:= range in {
+ in[j] = byte((i+j)&0xff)
+ }
+ }
+```
+
+To populate the parity shards, you simply call `Encode()` with your data.
+```Go
+ err = enc.Encode(data)
+```
+The only cases where you should get an error is, if the data shards aren't of equal size.
+The last 3 shards now contain parity data. You can verify this by calling `Verify()`:
+
+```Go
+ ok, err = enc.Verify(data)
+```
+
+The final (and important) part is to be able to reconstruct missing shards.
+For this to work, you need to know which parts of your data is missing.
+The encoder *does not know which parts are invalid*, so if data corruption is a likely scenario,
+you need to implement a hash check for each shard.
+
+If a byte has changed in your set, and you don't know which it is, there is no way to reconstruct the data set.
+
+To indicate missing data, you set the shard to nil before calling `Reconstruct()`:
+
+```Go
+ // Delete two data shards
+ data[3] = nil
+ data[7] = nil
+
+ // Reconstruct the missing shards
+ err := enc.Reconstruct(data)
+```
+The missing data and parity shards will be recreated. If more than 3 shards are missing, the reconstruction will fail.
+
+If you are only interested in the data shards (for reading purposes) you can call `ReconstructData()`:
+
+```Go
+ // Delete two data shards
+ data[3] = nil
+ data[7] = nil
+
+ // Reconstruct just the missing data shards
+ err := enc.ReconstructData(data)
+```
+
+So to sum up reconstruction:
+* The number of data/parity shards must match the numbers used for encoding.
+* The order of shards must be the same as used when encoding.
+* You may only supply data you know is valid.
+* Invalid shards should be set to nil.
+
+For complete examples of an encoder and decoder see the
+[examples folder](https://github.com/klauspost/reedsolomon/tree/master/examples).
+
+# Splitting/Joining Data
+
+You might have a large slice of data.
+To help you split this, there are some helper functions that can split and join a single byte slice.
+
+```Go
+ bigfile, _ := ioutil.Readfile("myfile.data")
+
+ // Split the file
+ split, err := enc.Split(bigfile)
+```
+This will split the file into the number of data shards set when creating the encoder and create empty parity shards.
+
+An important thing to note is that you have to *keep track of the exact input size*.
+If the size of the input isn't divisible by the number of data shards, extra zeros will be inserted in the last shard.
+
+To join a data set, use the `Join()` function, which will join the shards and write it to the `io.Writer` you supply:
+```Go
+ // Join a data set and write it to io.Discard.
+ err = enc.Join(io.Discard, data, len(bigfile))
+```
+
+# Streaming/Merging
+
+It might seem like a limitation that all data should be in memory,
+but an important property is that *as long as the number of data/parity shards are the same,
+you can merge/split data sets*, and they will remain valid as a separate set.
+
+```Go
+ // Split the data set of 50000 elements into two of 25000
+ splitA := make([][]byte, 13)
+ splitB := make([][]byte, 13)
+
+ // Merge into a 100000 element set
+ merged := make([][]byte, 13)
+
+ for i := range data {
+ splitA[i] = data[i][:25000]
+ splitB[i] = data[i][25000:]
+
+ // Concatenate it to itself
+ merged[i] = append(make([]byte, 0, len(data[i])*2), data[i]...)
+ merged[i] = append(merged[i], data[i]...)
+ }
+
+ // Each part should still verify as ok.
+ ok, err := enc.Verify(splitA)
+ if ok && err == nil {
+ log.Println("splitA ok")
+ }
+
+ ok, err = enc.Verify(splitB)
+ if ok && err == nil {
+ log.Println("splitB ok")
+ }
+
+ ok, err = enc.Verify(merge)
+ if ok && err == nil {
+ log.Println("merge ok")
+ }
+```
+
+This means that if you have a data set that may not fit into memory, you can split processing into smaller blocks.
+For the best throughput, don't use too small blocks.
+
+This also means that you can divide big input up into smaller blocks, and do reconstruction on parts of your data.
+This doesn't give the same flexibility of a higher number of data shards, but it will be much more performant.
+
+# Streaming API
+
+There has been added support for a streaming API, to help perform fully streaming operations,
+which enables you to do the same operations, but on streams.
+To use the stream API, use [`NewStream`](https://godoc.org/github.com/klauspost/reedsolomon#NewStream) function
+to create the encoding/decoding interfaces.
+
+You can use [`WithConcurrentStreams`](https://godoc.org/github.com/klauspost/reedsolomon#WithConcurrentStreams)
+to ready an interface that reads/writes concurrently from the streams.
+
+You can specify the size of each operation using
+[`WithStreamBlockSize`](https://godoc.org/github.com/klauspost/reedsolomon#WithStreamBlockSize).
+This will set the size of each read/write operation.
+
+Input is delivered as `[]io.Reader`, output as `[]io.Writer`, and functionality corresponds to the in-memory API.
+Each stream must supply the same amount of data, similar to how each slice must be similar size with the in-memory API.
+If an error occurs in relation to a stream,
+a [`StreamReadError`](https://godoc.org/github.com/klauspost/reedsolomon#StreamReadError)
+or [`StreamWriteError`](https://godoc.org/github.com/klauspost/reedsolomon#StreamWriteError)
+will help you determine which stream was the offender.
+
+There is no buffering or timeouts/retry specified. If you want to add that, you need to add it to the Reader/Writer.
+
+For complete examples of a streaming encoder and decoder see the
+[examples folder](https://github.com/klauspost/reedsolomon/tree/master/examples).
+
+# Advanced Options
+
+You can modify internal options which affects how jobs are split between and processed by goroutines.
+
+To create options, use the WithXXX functions. You can supply options to `New`, `NewStream`.
+If no Options are supplied, default options are used.
+
+Example of how to supply options:
+
+ ```Go
+ enc, err := reedsolomon.New(10, 3, WithMaxGoroutines(25))
+ ```
+
+
+# Performance
+Performance depends mainly on the number of parity shards.
+In rough terms, doubling the number of parity shards will double the encoding time.
+
+Here are the throughput numbers with some different selections of data and parity shards.
+For reference each shard is 1MB random data, and 2 CPU cores are used for encoding.
+
+| Data | Parity | Parity | MB/s | SSSE3 MB/s | SSSE3 Speed | Rel. Speed |
+|------|--------|--------|--------|-------------|-------------|------------|
+| 5 | 2 | 40% | 576,11 | 2599,2 | 451% | 100,00% |
+| 10 | 2 | 20% | 587,73 | 3100,28 | 528% | 102,02% |
+| 10 | 4 | 40% | 298,38 | 2470,97 | 828% | 51,79% |
+| 50 | 20 | 40% | 59,81 | 713,28 | 1193% | 10,38% |
+
+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:
+```
+benchmark all MB/s data MB/s speedup
+BenchmarkReconstruct10x2x10000-8 2011.67 10530.10 5.23x
+BenchmarkReconstruct50x5x50000-8 4585.41 14301.60 3.12x
+BenchmarkReconstruct10x2x1M-8 8081.15 28216.41 3.49x
+BenchmarkReconstruct5x2x1M-8 5780.07 28015.37 4.85x
+BenchmarkReconstruct10x4x1M-8 4352.56 14367.61 3.30x
+BenchmarkReconstruct50x20x1M-8 1364.35 4189.79 3.07x
+BenchmarkReconstruct10x4x16M-8 1484.35 5779.53 3.89x
+```
+
+# Performance on AVX512
+
+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
+
+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):
+
+```
+BenchmarkGalois128K-64 119562 10028 ns/op 13070.78 MB/s
+BenchmarkGalois1M-64 14380 83424 ns/op 12569.22 MB/s
+BenchmarkGaloisXor128K-64 96508 12432 ns/op 10543.29 MB/s
+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:
+
+```
+benchmark old MB/s new MB/s speedup
+BenchmarkGalois128K-160 948.87 8878.85 9.36x
+BenchmarkGalois1M-160 968.85 9041.92 9.33x
+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/).
+* [JavaReedSolomon](https://github.com/Backblaze/JavaReedSolomon). Compatible java library by Backblaze.
+* [ocaml-reed-solomon-erasure](https://gitlab.com/darrenldl/ocaml-reed-solomon-erasure). Compatible OCaml implementation.
+* [reedsolomon-c](https://github.com/jannson/reedsolomon-c). C version, compatible with output from this package.
+* [Reed-Solomon Erasure Coding in Haskell](https://github.com/NicolasT/reedsolomon). Haskell port of the package with similar performance.
+* [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.
+
+# License
+
+This code, as the original [JavaReedSolomon](https://github.com/Backblaze/JavaReedSolomon) is published under an MIT license. See LICENSE file for more information.
diff --git a/vendor/github.com/klauspost/reedsolomon/appveyor.yml b/vendor/github.com/klauspost/reedsolomon/appveyor.yml
new file mode 100644
index 0000000..9bb067f
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/appveyor.yml
@@ -0,0 +1,20 @@
+os: Visual Studio 2015
+
+platform: x64
+
+clone_folder: c:\gopath\src\github.com\klauspost\reedsolomon
+
+# environment variables
+environment:
+ GOPATH: c:\gopath
+
+install:
+ - echo %PATH%
+ - echo %GOPATH%
+ - go version
+ - go env
+ - go get -d ./...
+
+build_script:
+ - go test -v -cpu=2 ./...
+ - go test -cpu=1,2,4 -short -race ./...
diff --git a/vendor/github.com/klauspost/reedsolomon/galois.go b/vendor/github.com/klauspost/reedsolomon/galois.go
new file mode 100644
index 0000000..76049f9
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois.go
@@ -0,0 +1,929 @@
+/**
+ * 8-bit Galois Field
+ * Copyright 2015, Klaus Post
+ * Copyright 2015, Backblaze, Inc. All rights reserved.
+ */
+
+package reedsolomon
+
+const (
+ // The number of elements in the field.
+ fieldSize = 256
+
+ // The polynomial used to generate the logarithm table.
+ //
+ // There are a number of polynomials that work to generate
+ // a Galois field of 256 elements. The choice is arbitrary,
+ // and we just use the first one.
+ //
+ // The possibilities are: 29, 43, 45, 77, 95, 99, 101, 105,
+ //* 113, 135, 141, 169, 195, 207, 231, and 245.
+ generatingPolynomial = 29
+)
+
+var logTable = [fieldSize]byte{
+ 0, 0, 1, 25, 2, 50, 26, 198,
+ 3, 223, 51, 238, 27, 104, 199, 75,
+ 4, 100, 224, 14, 52, 141, 239, 129,
+ 28, 193, 105, 248, 200, 8, 76, 113,
+ 5, 138, 101, 47, 225, 36, 15, 33,
+ 53, 147, 142, 218, 240, 18, 130, 69,
+ 29, 181, 194, 125, 106, 39, 249, 185,
+ 201, 154, 9, 120, 77, 228, 114, 166,
+ 6, 191, 139, 98, 102, 221, 48, 253,
+ 226, 152, 37, 179, 16, 145, 34, 136,
+ 54, 208, 148, 206, 143, 150, 219, 189,
+ 241, 210, 19, 92, 131, 56, 70, 64,
+ 30, 66, 182, 163, 195, 72, 126, 110,
+ 107, 58, 40, 84, 250, 133, 186, 61,
+ 202, 94, 155, 159, 10, 21, 121, 43,
+ 78, 212, 229, 172, 115, 243, 167, 87,
+ 7, 112, 192, 247, 140, 128, 99, 13,
+ 103, 74, 222, 237, 49, 197, 254, 24,
+ 227, 165, 153, 119, 38, 184, 180, 124,
+ 17, 68, 146, 217, 35, 32, 137, 46,
+ 55, 63, 209, 91, 149, 188, 207, 205,
+ 144, 135, 151, 178, 220, 252, 190, 97,
+ 242, 86, 211, 171, 20, 42, 93, 158,
+ 132, 60, 57, 83, 71, 109, 65, 162,
+ 31, 45, 67, 216, 183, 123, 164, 118,
+ 196, 23, 73, 236, 127, 12, 111, 246,
+ 108, 161, 59, 82, 41, 157, 85, 170,
+ 251, 96, 134, 177, 187, 204, 62, 90,
+ 203, 89, 95, 176, 156, 169, 160, 81,
+ 11, 245, 22, 235, 122, 117, 44, 215,
+ 79, 174, 213, 233, 230, 231, 173, 232,
+ 116, 214, 244, 234, 168, 80, 88, 175,
+}
+
+/**
+ * Inverse of the logarithm table. Maps integer logarithms
+ * to members of the field. There is no entry for 255
+ * because the highest log is 254.
+ *
+ * This table was generated by `go run gentables.go`
+ */
+var expTable = []byte{0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x3, 0x6, 0xc, 0x18, 0x30, 0x60, 0xc0, 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, 0x8c, 0x5, 0xa, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0xf, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0xd, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x7, 0xe, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x9, 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0xb, 0x16, 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x3, 0x6, 0xc, 0x18, 0x30, 0x60, 0xc0, 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 0x46, 0x8c, 0x5, 0xa, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0xf, 0x1e, 0x3c, 0x78, 0xf0, 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0xd, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x7, 0xe, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x9, 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0xb, 0x16, 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e}
+
+func galAdd(a, b byte) byte {
+ return a ^ b
+}
+
+func galSub(a, b byte) byte {
+ return a ^ b
+}
+
+// Table from https://github.com/templexxx/reedsolomon
+var invTable = [256]byte{0x0, 0x1, 0x8e, 0xf4, 0x47, 0xa7, 0x7a, 0xba, 0xad, 0x9d, 0xdd, 0x98, 0x3d, 0xaa, 0x5d, 0x96, 0xd8, 0x72, 0xc0, 0x58, 0xe0, 0x3e, 0x4c, 0x66, 0x90, 0xde, 0x55, 0x80, 0xa0, 0x83, 0x4b, 0x2a, 0x6c, 0xed, 0x39, 0x51, 0x60, 0x56, 0x2c, 0x8a, 0x70, 0xd0, 0x1f, 0x4a, 0x26, 0x8b, 0x33, 0x6e, 0x48, 0x89, 0x6f, 0x2e, 0xa4, 0xc3, 0x40, 0x5e, 0x50, 0x22, 0xcf, 0xa9, 0xab, 0xc, 0x15, 0xe1, 0x36, 0x5f, 0xf8, 0xd5, 0x92, 0x4e, 0xa6, 0x4, 0x30, 0x88, 0x2b, 0x1e, 0x16, 0x67, 0x45, 0x93, 0x38, 0x23, 0x68, 0x8c, 0x81, 0x1a, 0x25, 0x61, 0x13, 0xc1, 0xcb, 0x63, 0x97, 0xe, 0x37, 0x41, 0x24, 0x57, 0xca, 0x5b, 0xb9, 0xc4, 0x17, 0x4d, 0x52, 0x8d, 0xef, 0xb3, 0x20, 0xec, 0x2f, 0x32, 0x28, 0xd1, 0x11, 0xd9, 0xe9, 0xfb, 0xda, 0x79, 0xdb, 0x77, 0x6, 0xbb, 0x84, 0xcd, 0xfe, 0xfc, 0x1b, 0x54, 0xa1, 0x1d, 0x7c, 0xcc, 0xe4, 0xb0, 0x49, 0x31, 0x27, 0x2d, 0x53, 0x69, 0x2, 0xf5, 0x18, 0xdf, 0x44, 0x4f, 0x9b, 0xbc, 0xf, 0x5c, 0xb, 0xdc, 0xbd, 0x94, 0xac, 0x9, 0xc7, 0xa2, 0x1c, 0x82, 0x9f, 0xc6, 0x34, 0xc2, 0x46, 0x5, 0xce, 0x3b, 0xd, 0x3c, 0x9c, 0x8, 0xbe, 0xb7, 0x87, 0xe5, 0xee, 0x6b, 0xeb, 0xf2, 0xbf, 0xaf, 0xc5, 0x64, 0x7, 0x7b, 0x95, 0x9a, 0xae, 0xb6, 0x12, 0x59, 0xa5, 0x35, 0x65, 0xb8, 0xa3, 0x9e, 0xd2, 0xf7, 0x62, 0x5a, 0x85, 0x7d, 0xa8, 0x3a, 0x29, 0x71, 0xc8, 0xf6, 0xf9, 0x43, 0xd7, 0xd6, 0x10, 0x73, 0x76, 0x78, 0x99, 0xa, 0x19, 0x91, 0x14, 0x3f, 0xe6, 0xf0, 0x86, 0xb1, 0xe2, 0xf1, 0xfa, 0x74, 0xf3, 0xb4, 0x6d, 0x21, 0xb2, 0x6a, 0xe3, 0xe7, 0xb5, 0xea, 0x3, 0x8f, 0xd3, 0xc9, 0x42, 0xd4, 0xe8, 0x75, 0x7f, 0xff, 0x7e, 0xfd}
+
+var mulTable = [256][256]uint8{[256]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff},
+ {0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0xd, 0xf, 0x9, 0xb, 0x5, 0x7, 0x1, 0x3, 0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23, 0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43, 0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63, 0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83, 0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3, 0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3, 0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3},
+ {0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71, 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41, 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1, 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1, 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, 0x9d, 0x9e, 0x9b, 0x98, 0x91, 0x92, 0x97, 0x94, 0x85, 0x86, 0x83, 0x80, 0x89, 0x8a, 0x8f, 0x8c, 0xad, 0xae, 0xab, 0xa8, 0xa1, 0xa2, 0xa7, 0xa4, 0xb5, 0xb6, 0xb3, 0xb0, 0xb9, 0xba, 0xbf, 0xbc, 0xfd, 0xfe, 0xfb, 0xf8, 0xf1, 0xf2, 0xf7, 0xf4, 0xe5, 0xe6, 0xe3, 0xe0, 0xe9, 0xea, 0xef, 0xec, 0xcd, 0xce, 0xcb, 0xc8, 0xc1, 0xc2, 0xc7, 0xc4, 0xd5, 0xd6, 0xd3, 0xd0, 0xd9, 0xda, 0xdf, 0xdc, 0x5d, 0x5e, 0x5b, 0x58, 0x51, 0x52, 0x57, 0x54, 0x45, 0x46, 0x43, 0x40, 0x49, 0x4a, 0x4f, 0x4c, 0x6d, 0x6e, 0x6b, 0x68, 0x61, 0x62, 0x67, 0x64, 0x75, 0x76, 0x73, 0x70, 0x79, 0x7a, 0x7f, 0x7c, 0x3d, 0x3e, 0x3b, 0x38, 0x31, 0x32, 0x37, 0x34, 0x25, 0x26, 0x23, 0x20, 0x29, 0x2a, 0x2f, 0x2c, 0xd, 0xe, 0xb, 0x8, 0x1, 0x2, 0x7, 0x4, 0x15, 0x16, 0x13, 0x10, 0x19, 0x1a, 0x1f, 0x1c},
+ {0x0, 0x4, 0x8, 0xc, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c, 0x40, 0x44, 0x48, 0x4c, 0x50, 0x54, 0x58, 0x5c, 0x60, 0x64, 0x68, 0x6c, 0x70, 0x74, 0x78, 0x7c, 0x80, 0x84, 0x88, 0x8c, 0x90, 0x94, 0x98, 0x9c, 0xa0, 0xa4, 0xa8, 0xac, 0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc, 0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf4, 0xf8, 0xfc, 0x1d, 0x19, 0x15, 0x11, 0xd, 0x9, 0x5, 0x1, 0x3d, 0x39, 0x35, 0x31, 0x2d, 0x29, 0x25, 0x21, 0x5d, 0x59, 0x55, 0x51, 0x4d, 0x49, 0x45, 0x41, 0x7d, 0x79, 0x75, 0x71, 0x6d, 0x69, 0x65, 0x61, 0x9d, 0x99, 0x95, 0x91, 0x8d, 0x89, 0x85, 0x81, 0xbd, 0xb9, 0xb5, 0xb1, 0xad, 0xa9, 0xa5, 0xa1, 0xdd, 0xd9, 0xd5, 0xd1, 0xcd, 0xc9, 0xc5, 0xc1, 0xfd, 0xf9, 0xf5, 0xf1, 0xed, 0xe9, 0xe5, 0xe1, 0x3a, 0x3e, 0x32, 0x36, 0x2a, 0x2e, 0x22, 0x26, 0x1a, 0x1e, 0x12, 0x16, 0xa, 0xe, 0x2, 0x6, 0x7a, 0x7e, 0x72, 0x76, 0x6a, 0x6e, 0x62, 0x66, 0x5a, 0x5e, 0x52, 0x56, 0x4a, 0x4e, 0x42, 0x46, 0xba, 0xbe, 0xb2, 0xb6, 0xaa, 0xae, 0xa2, 0xa6, 0x9a, 0x9e, 0x92, 0x96, 0x8a, 0x8e, 0x82, 0x86, 0xfa, 0xfe, 0xf2, 0xf6, 0xea, 0xee, 0xe2, 0xe6, 0xda, 0xde, 0xd2, 0xd6, 0xca, 0xce, 0xc2, 0xc6, 0x27, 0x23, 0x2f, 0x2b, 0x37, 0x33, 0x3f, 0x3b, 0x7, 0x3, 0xf, 0xb, 0x17, 0x13, 0x1f, 0x1b, 0x67, 0x63, 0x6f, 0x6b, 0x77, 0x73, 0x7f, 0x7b, 0x47, 0x43, 0x4f, 0x4b, 0x57, 0x53, 0x5f, 0x5b, 0xa7, 0xa3, 0xaf, 0xab, 0xb7, 0xb3, 0xbf, 0xbb, 0x87, 0x83, 0x8f, 0x8b, 0x97, 0x93, 0x9f, 0x9b, 0xe7, 0xe3, 0xef, 0xeb, 0xf7, 0xf3, 0xff, 0xfb, 0xc7, 0xc3, 0xcf, 0xcb, 0xd7, 0xd3, 0xdf, 0xdb},
+ {0x0, 0x5, 0xa, 0xf, 0x14, 0x11, 0x1e, 0x1b, 0x28, 0x2d, 0x22, 0x27, 0x3c, 0x39, 0x36, 0x33, 0x50, 0x55, 0x5a, 0x5f, 0x44, 0x41, 0x4e, 0x4b, 0x78, 0x7d, 0x72, 0x77, 0x6c, 0x69, 0x66, 0x63, 0xa0, 0xa5, 0xaa, 0xaf, 0xb4, 0xb1, 0xbe, 0xbb, 0x88, 0x8d, 0x82, 0x87, 0x9c, 0x99, 0x96, 0x93, 0xf0, 0xf5, 0xfa, 0xff, 0xe4, 0xe1, 0xee, 0xeb, 0xd8, 0xdd, 0xd2, 0xd7, 0xcc, 0xc9, 0xc6, 0xc3, 0x5d, 0x58, 0x57, 0x52, 0x49, 0x4c, 0x43, 0x46, 0x75, 0x70, 0x7f, 0x7a, 0x61, 0x64, 0x6b, 0x6e, 0xd, 0x8, 0x7, 0x2, 0x19, 0x1c, 0x13, 0x16, 0x25, 0x20, 0x2f, 0x2a, 0x31, 0x34, 0x3b, 0x3e, 0xfd, 0xf8, 0xf7, 0xf2, 0xe9, 0xec, 0xe3, 0xe6, 0xd5, 0xd0, 0xdf, 0xda, 0xc1, 0xc4, 0xcb, 0xce, 0xad, 0xa8, 0xa7, 0xa2, 0xb9, 0xbc, 0xb3, 0xb6, 0x85, 0x80, 0x8f, 0x8a, 0x91, 0x94, 0x9b, 0x9e, 0xba, 0xbf, 0xb0, 0xb5, 0xae, 0xab, 0xa4, 0xa1, 0x92, 0x97, 0x98, 0x9d, 0x86, 0x83, 0x8c, 0x89, 0xea, 0xef, 0xe0, 0xe5, 0xfe, 0xfb, 0xf4, 0xf1, 0xc2, 0xc7, 0xc8, 0xcd, 0xd6, 0xd3, 0xdc, 0xd9, 0x1a, 0x1f, 0x10, 0x15, 0xe, 0xb, 0x4, 0x1, 0x32, 0x37, 0x38, 0x3d, 0x26, 0x23, 0x2c, 0x29, 0x4a, 0x4f, 0x40, 0x45, 0x5e, 0x5b, 0x54, 0x51, 0x62, 0x67, 0x68, 0x6d, 0x76, 0x73, 0x7c, 0x79, 0xe7, 0xe2, 0xed, 0xe8, 0xf3, 0xf6, 0xf9, 0xfc, 0xcf, 0xca, 0xc5, 0xc0, 0xdb, 0xde, 0xd1, 0xd4, 0xb7, 0xb2, 0xbd, 0xb8, 0xa3, 0xa6, 0xa9, 0xac, 0x9f, 0x9a, 0x95, 0x90, 0x8b, 0x8e, 0x81, 0x84, 0x47, 0x42, 0x4d, 0x48, 0x53, 0x56, 0x59, 0x5c, 0x6f, 0x6a, 0x65, 0x60, 0x7b, 0x7e, 0x71, 0x74, 0x17, 0x12, 0x1d, 0x18, 0x3, 0x6, 0x9, 0xc, 0x3f, 0x3a, 0x35, 0x30, 0x2b, 0x2e, 0x21, 0x24},
+ {0x0, 0x6, 0xc, 0xa, 0x18, 0x1e, 0x14, 0x12, 0x30, 0x36, 0x3c, 0x3a, 0x28, 0x2e, 0x24, 0x22, 0x60, 0x66, 0x6c, 0x6a, 0x78, 0x7e, 0x74, 0x72, 0x50, 0x56, 0x5c, 0x5a, 0x48, 0x4e, 0x44, 0x42, 0xc0, 0xc6, 0xcc, 0xca, 0xd8, 0xde, 0xd4, 0xd2, 0xf0, 0xf6, 0xfc, 0xfa, 0xe8, 0xee, 0xe4, 0xe2, 0xa0, 0xa6, 0xac, 0xaa, 0xb8, 0xbe, 0xb4, 0xb2, 0x90, 0x96, 0x9c, 0x9a, 0x88, 0x8e, 0x84, 0x82, 0x9d, 0x9b, 0x91, 0x97, 0x85, 0x83, 0x89, 0x8f, 0xad, 0xab, 0xa1, 0xa7, 0xb5, 0xb3, 0xb9, 0xbf, 0xfd, 0xfb, 0xf1, 0xf7, 0xe5, 0xe3, 0xe9, 0xef, 0xcd, 0xcb, 0xc1, 0xc7, 0xd5, 0xd3, 0xd9, 0xdf, 0x5d, 0x5b, 0x51, 0x57, 0x45, 0x43, 0x49, 0x4f, 0x6d, 0x6b, 0x61, 0x67, 0x75, 0x73, 0x79, 0x7f, 0x3d, 0x3b, 0x31, 0x37, 0x25, 0x23, 0x29, 0x2f, 0xd, 0xb, 0x1, 0x7, 0x15, 0x13, 0x19, 0x1f, 0x27, 0x21, 0x2b, 0x2d, 0x3f, 0x39, 0x33, 0x35, 0x17, 0x11, 0x1b, 0x1d, 0xf, 0x9, 0x3, 0x5, 0x47, 0x41, 0x4b, 0x4d, 0x5f, 0x59, 0x53, 0x55, 0x77, 0x71, 0x7b, 0x7d, 0x6f, 0x69, 0x63, 0x65, 0xe7, 0xe1, 0xeb, 0xed, 0xff, 0xf9, 0xf3, 0xf5, 0xd7, 0xd1, 0xdb, 0xdd, 0xcf, 0xc9, 0xc3, 0xc5, 0x87, 0x81, 0x8b, 0x8d, 0x9f, 0x99, 0x93, 0x95, 0xb7, 0xb1, 0xbb, 0xbd, 0xaf, 0xa9, 0xa3, 0xa5, 0xba, 0xbc, 0xb6, 0xb0, 0xa2, 0xa4, 0xae, 0xa8, 0x8a, 0x8c, 0x86, 0x80, 0x92, 0x94, 0x9e, 0x98, 0xda, 0xdc, 0xd6, 0xd0, 0xc2, 0xc4, 0xce, 0xc8, 0xea, 0xec, 0xe6, 0xe0, 0xf2, 0xf4, 0xfe, 0xf8, 0x7a, 0x7c, 0x76, 0x70, 0x62, 0x64, 0x6e, 0x68, 0x4a, 0x4c, 0x46, 0x40, 0x52, 0x54, 0x5e, 0x58, 0x1a, 0x1c, 0x16, 0x10, 0x2, 0x4, 0xe, 0x8, 0x2a, 0x2c, 0x26, 0x20, 0x32, 0x34, 0x3e, 0x38},
+ {0x0, 0x7, 0xe, 0x9, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xdd, 0xda, 0xd3, 0xd4, 0xc1, 0xc6, 0xcf, 0xc8, 0xe5, 0xe2, 0xeb, 0xec, 0xf9, 0xfe, 0xf7, 0xf0, 0xad, 0xaa, 0xa3, 0xa4, 0xb1, 0xb6, 0xbf, 0xb8, 0x95, 0x92, 0x9b, 0x9c, 0x89, 0x8e, 0x87, 0x80, 0x3d, 0x3a, 0x33, 0x34, 0x21, 0x26, 0x2f, 0x28, 0x5, 0x2, 0xb, 0xc, 0x19, 0x1e, 0x17, 0x10, 0x4d, 0x4a, 0x43, 0x44, 0x51, 0x56, 0x5f, 0x58, 0x75, 0x72, 0x7b, 0x7c, 0x69, 0x6e, 0x67, 0x60, 0xa7, 0xa0, 0xa9, 0xae, 0xbb, 0xbc, 0xb5, 0xb2, 0x9f, 0x98, 0x91, 0x96, 0x83, 0x84, 0x8d, 0x8a, 0xd7, 0xd0, 0xd9, 0xde, 0xcb, 0xcc, 0xc5, 0xc2, 0xef, 0xe8, 0xe1, 0xe6, 0xf3, 0xf4, 0xfd, 0xfa, 0x47, 0x40, 0x49, 0x4e, 0x5b, 0x5c, 0x55, 0x52, 0x7f, 0x78, 0x71, 0x76, 0x63, 0x64, 0x6d, 0x6a, 0x37, 0x30, 0x39, 0x3e, 0x2b, 0x2c, 0x25, 0x22, 0xf, 0x8, 0x1, 0x6, 0x13, 0x14, 0x1d, 0x1a, 0x7a, 0x7d, 0x74, 0x73, 0x66, 0x61, 0x68, 0x6f, 0x42, 0x45, 0x4c, 0x4b, 0x5e, 0x59, 0x50, 0x57, 0xa, 0xd, 0x4, 0x3, 0x16, 0x11, 0x18, 0x1f, 0x32, 0x35, 0x3c, 0x3b, 0x2e, 0x29, 0x20, 0x27, 0x9a, 0x9d, 0x94, 0x93, 0x86, 0x81, 0x88, 0x8f, 0xa2, 0xa5, 0xac, 0xab, 0xbe, 0xb9, 0xb0, 0xb7, 0xea, 0xed, 0xe4, 0xe3, 0xf6, 0xf1, 0xf8, 0xff, 0xd2, 0xd5, 0xdc, 0xdb, 0xce, 0xc9, 0xc0, 0xc7},
+ {0x0, 0x8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x1d, 0x15, 0xd, 0x5, 0x3d, 0x35, 0x2d, 0x25, 0x5d, 0x55, 0x4d, 0x45, 0x7d, 0x75, 0x6d, 0x65, 0x9d, 0x95, 0x8d, 0x85, 0xbd, 0xb5, 0xad, 0xa5, 0xdd, 0xd5, 0xcd, 0xc5, 0xfd, 0xf5, 0xed, 0xe5, 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0xa, 0x2, 0x7a, 0x72, 0x6a, 0x62, 0x5a, 0x52, 0x4a, 0x42, 0xba, 0xb2, 0xaa, 0xa2, 0x9a, 0x92, 0x8a, 0x82, 0xfa, 0xf2, 0xea, 0xe2, 0xda, 0xd2, 0xca, 0xc2, 0x27, 0x2f, 0x37, 0x3f, 0x7, 0xf, 0x17, 0x1f, 0x67, 0x6f, 0x77, 0x7f, 0x47, 0x4f, 0x57, 0x5f, 0xa7, 0xaf, 0xb7, 0xbf, 0x87, 0x8f, 0x97, 0x9f, 0xe7, 0xef, 0xf7, 0xff, 0xc7, 0xcf, 0xd7, 0xdf, 0x74, 0x7c, 0x64, 0x6c, 0x54, 0x5c, 0x44, 0x4c, 0x34, 0x3c, 0x24, 0x2c, 0x14, 0x1c, 0x4, 0xc, 0xf4, 0xfc, 0xe4, 0xec, 0xd4, 0xdc, 0xc4, 0xcc, 0xb4, 0xbc, 0xa4, 0xac, 0x94, 0x9c, 0x84, 0x8c, 0x69, 0x61, 0x79, 0x71, 0x49, 0x41, 0x59, 0x51, 0x29, 0x21, 0x39, 0x31, 0x9, 0x1, 0x19, 0x11, 0xe9, 0xe1, 0xf9, 0xf1, 0xc9, 0xc1, 0xd9, 0xd1, 0xa9, 0xa1, 0xb9, 0xb1, 0x89, 0x81, 0x99, 0x91, 0x4e, 0x46, 0x5e, 0x56, 0x6e, 0x66, 0x7e, 0x76, 0xe, 0x6, 0x1e, 0x16, 0x2e, 0x26, 0x3e, 0x36, 0xce, 0xc6, 0xde, 0xd6, 0xee, 0xe6, 0xfe, 0xf6, 0x8e, 0x86, 0x9e, 0x96, 0xae, 0xa6, 0xbe, 0xb6, 0x53, 0x5b, 0x43, 0x4b, 0x73, 0x7b, 0x63, 0x6b, 0x13, 0x1b, 0x3, 0xb, 0x33, 0x3b, 0x23, 0x2b, 0xd3, 0xdb, 0xc3, 0xcb, 0xf3, 0xfb, 0xe3, 0xeb, 0x93, 0x9b, 0x83, 0x8b, 0xb3, 0xbb, 0xa3, 0xab},
+ {0x0, 0x9, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, 0x3d, 0x34, 0x2f, 0x26, 0x19, 0x10, 0xb, 0x2, 0x75, 0x7c, 0x67, 0x6e, 0x51, 0x58, 0x43, 0x4a, 0xad, 0xa4, 0xbf, 0xb6, 0x89, 0x80, 0x9b, 0x92, 0xe5, 0xec, 0xf7, 0xfe, 0xc1, 0xc8, 0xd3, 0xda, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x4, 0xd, 0xea, 0xe3, 0xf8, 0xf1, 0xce, 0xc7, 0xdc, 0xd5, 0xa2, 0xab, 0xb0, 0xb9, 0x86, 0x8f, 0x94, 0x9d, 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0xf, 0x6, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30, 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0, 0xf4, 0xfd, 0xe6, 0xef, 0xd0, 0xd9, 0xc2, 0xcb, 0xbc, 0xb5, 0xae, 0xa7, 0x98, 0x91, 0x8a, 0x83, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, 0x2c, 0x25, 0x3e, 0x37, 0x8, 0x1, 0x1a, 0x13, 0xc9, 0xc0, 0xdb, 0xd2, 0xed, 0xe4, 0xff, 0xf6, 0x81, 0x88, 0x93, 0x9a, 0xa5, 0xac, 0xb7, 0xbe, 0x59, 0x50, 0x4b, 0x42, 0x7d, 0x74, 0x6f, 0x66, 0x11, 0x18, 0x3, 0xa, 0x35, 0x3c, 0x27, 0x2e, 0x8e, 0x87, 0x9c, 0x95, 0xaa, 0xa3, 0xb8, 0xb1, 0xc6, 0xcf, 0xd4, 0xdd, 0xe2, 0xeb, 0xf0, 0xf9, 0x1e, 0x17, 0xc, 0x5, 0x3a, 0x33, 0x28, 0x21, 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0xb3, 0xba, 0xa1, 0xa8, 0x97, 0x9e, 0x85, 0x8c, 0xfb, 0xf2, 0xe9, 0xe0, 0xdf, 0xd6, 0xcd, 0xc4, 0x23, 0x2a, 0x31, 0x38, 0x7, 0xe, 0x15, 0x1c, 0x6b, 0x62, 0x79, 0x70, 0x4f, 0x46, 0x5d, 0x54},
+ {0x0, 0xa, 0x14, 0x1e, 0x28, 0x22, 0x3c, 0x36, 0x50, 0x5a, 0x44, 0x4e, 0x78, 0x72, 0x6c, 0x66, 0xa0, 0xaa, 0xb4, 0xbe, 0x88, 0x82, 0x9c, 0x96, 0xf0, 0xfa, 0xe4, 0xee, 0xd8, 0xd2, 0xcc, 0xc6, 0x5d, 0x57, 0x49, 0x43, 0x75, 0x7f, 0x61, 0x6b, 0xd, 0x7, 0x19, 0x13, 0x25, 0x2f, 0x31, 0x3b, 0xfd, 0xf7, 0xe9, 0xe3, 0xd5, 0xdf, 0xc1, 0xcb, 0xad, 0xa7, 0xb9, 0xb3, 0x85, 0x8f, 0x91, 0x9b, 0xba, 0xb0, 0xae, 0xa4, 0x92, 0x98, 0x86, 0x8c, 0xea, 0xe0, 0xfe, 0xf4, 0xc2, 0xc8, 0xd6, 0xdc, 0x1a, 0x10, 0xe, 0x4, 0x32, 0x38, 0x26, 0x2c, 0x4a, 0x40, 0x5e, 0x54, 0x62, 0x68, 0x76, 0x7c, 0xe7, 0xed, 0xf3, 0xf9, 0xcf, 0xc5, 0xdb, 0xd1, 0xb7, 0xbd, 0xa3, 0xa9, 0x9f, 0x95, 0x8b, 0x81, 0x47, 0x4d, 0x53, 0x59, 0x6f, 0x65, 0x7b, 0x71, 0x17, 0x1d, 0x3, 0x9, 0x3f, 0x35, 0x2b, 0x21, 0x69, 0x63, 0x7d, 0x77, 0x41, 0x4b, 0x55, 0x5f, 0x39, 0x33, 0x2d, 0x27, 0x11, 0x1b, 0x5, 0xf, 0xc9, 0xc3, 0xdd, 0xd7, 0xe1, 0xeb, 0xf5, 0xff, 0x99, 0x93, 0x8d, 0x87, 0xb1, 0xbb, 0xa5, 0xaf, 0x34, 0x3e, 0x20, 0x2a, 0x1c, 0x16, 0x8, 0x2, 0x64, 0x6e, 0x70, 0x7a, 0x4c, 0x46, 0x58, 0x52, 0x94, 0x9e, 0x80, 0x8a, 0xbc, 0xb6, 0xa8, 0xa2, 0xc4, 0xce, 0xd0, 0xda, 0xec, 0xe6, 0xf8, 0xf2, 0xd3, 0xd9, 0xc7, 0xcd, 0xfb, 0xf1, 0xef, 0xe5, 0x83, 0x89, 0x97, 0x9d, 0xab, 0xa1, 0xbf, 0xb5, 0x73, 0x79, 0x67, 0x6d, 0x5b, 0x51, 0x4f, 0x45, 0x23, 0x29, 0x37, 0x3d, 0xb, 0x1, 0x1f, 0x15, 0x8e, 0x84, 0x9a, 0x90, 0xa6, 0xac, 0xb2, 0xb8, 0xde, 0xd4, 0xca, 0xc0, 0xf6, 0xfc, 0xe2, 0xe8, 0x2e, 0x24, 0x3a, 0x30, 0x6, 0xc, 0x12, 0x18, 0x7e, 0x74, 0x6a, 0x60, 0x56, 0x5c, 0x42, 0x48},
+ {0x0, 0xb, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, 0x7d, 0x76, 0x6b, 0x60, 0x51, 0x5a, 0x47, 0x4c, 0x25, 0x2e, 0x33, 0x38, 0x9, 0x2, 0x1f, 0x14, 0xcd, 0xc6, 0xdb, 0xd0, 0xe1, 0xea, 0xf7, 0xfc, 0x95, 0x9e, 0x83, 0x88, 0xb9, 0xb2, 0xaf, 0xa4, 0xfa, 0xf1, 0xec, 0xe7, 0xd6, 0xdd, 0xc0, 0xcb, 0xa2, 0xa9, 0xb4, 0xbf, 0x8e, 0x85, 0x98, 0x93, 0x4a, 0x41, 0x5c, 0x57, 0x66, 0x6d, 0x70, 0x7b, 0x12, 0x19, 0x4, 0xf, 0x3e, 0x35, 0x28, 0x23, 0x87, 0x8c, 0x91, 0x9a, 0xab, 0xa0, 0xbd, 0xb6, 0xdf, 0xd4, 0xc9, 0xc2, 0xf3, 0xf8, 0xe5, 0xee, 0x37, 0x3c, 0x21, 0x2a, 0x1b, 0x10, 0xd, 0x6, 0x6f, 0x64, 0x79, 0x72, 0x43, 0x48, 0x55, 0x5e, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68, 0x1, 0xa, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x94, 0x9f, 0x82, 0x89, 0xb8, 0xb3, 0xae, 0xa5, 0xcc, 0xc7, 0xda, 0xd1, 0xe0, 0xeb, 0xf6, 0xfd, 0x24, 0x2f, 0x32, 0x39, 0x8, 0x3, 0x1e, 0x15, 0x7c, 0x77, 0x6a, 0x61, 0x50, 0x5b, 0x46, 0x4d, 0x13, 0x18, 0x5, 0xe, 0x3f, 0x34, 0x29, 0x22, 0x4b, 0x40, 0x5d, 0x56, 0x67, 0x6c, 0x71, 0x7a, 0xa3, 0xa8, 0xb5, 0xbe, 0x8f, 0x84, 0x99, 0x92, 0xfb, 0xf0, 0xed, 0xe6, 0xd7, 0xdc, 0xc1, 0xca, 0x6e, 0x65, 0x78, 0x73, 0x42, 0x49, 0x54, 0x5f, 0x36, 0x3d, 0x20, 0x2b, 0x1a, 0x11, 0xc, 0x7, 0xde, 0xd5, 0xc8, 0xc3, 0xf2, 0xf9, 0xe4, 0xef, 0x86, 0x8d, 0x90, 0x9b, 0xaa, 0xa1, 0xbc, 0xb7},
+ {0x0, 0xc, 0x18, 0x14, 0x30, 0x3c, 0x28, 0x24, 0x60, 0x6c, 0x78, 0x74, 0x50, 0x5c, 0x48, 0x44, 0xc0, 0xcc, 0xd8, 0xd4, 0xf0, 0xfc, 0xe8, 0xe4, 0xa0, 0xac, 0xb8, 0xb4, 0x90, 0x9c, 0x88, 0x84, 0x9d, 0x91, 0x85, 0x89, 0xad, 0xa1, 0xb5, 0xb9, 0xfd, 0xf1, 0xe5, 0xe9, 0xcd, 0xc1, 0xd5, 0xd9, 0x5d, 0x51, 0x45, 0x49, 0x6d, 0x61, 0x75, 0x79, 0x3d, 0x31, 0x25, 0x29, 0xd, 0x1, 0x15, 0x19, 0x27, 0x2b, 0x3f, 0x33, 0x17, 0x1b, 0xf, 0x3, 0x47, 0x4b, 0x5f, 0x53, 0x77, 0x7b, 0x6f, 0x63, 0xe7, 0xeb, 0xff, 0xf3, 0xd7, 0xdb, 0xcf, 0xc3, 0x87, 0x8b, 0x9f, 0x93, 0xb7, 0xbb, 0xaf, 0xa3, 0xba, 0xb6, 0xa2, 0xae, 0x8a, 0x86, 0x92, 0x9e, 0xda, 0xd6, 0xc2, 0xce, 0xea, 0xe6, 0xf2, 0xfe, 0x7a, 0x76, 0x62, 0x6e, 0x4a, 0x46, 0x52, 0x5e, 0x1a, 0x16, 0x2, 0xe, 0x2a, 0x26, 0x32, 0x3e, 0x4e, 0x42, 0x56, 0x5a, 0x7e, 0x72, 0x66, 0x6a, 0x2e, 0x22, 0x36, 0x3a, 0x1e, 0x12, 0x6, 0xa, 0x8e, 0x82, 0x96, 0x9a, 0xbe, 0xb2, 0xa6, 0xaa, 0xee, 0xe2, 0xf6, 0xfa, 0xde, 0xd2, 0xc6, 0xca, 0xd3, 0xdf, 0xcb, 0xc7, 0xe3, 0xef, 0xfb, 0xf7, 0xb3, 0xbf, 0xab, 0xa7, 0x83, 0x8f, 0x9b, 0x97, 0x13, 0x1f, 0xb, 0x7, 0x23, 0x2f, 0x3b, 0x37, 0x73, 0x7f, 0x6b, 0x67, 0x43, 0x4f, 0x5b, 0x57, 0x69, 0x65, 0x71, 0x7d, 0x59, 0x55, 0x41, 0x4d, 0x9, 0x5, 0x11, 0x1d, 0x39, 0x35, 0x21, 0x2d, 0xa9, 0xa5, 0xb1, 0xbd, 0x99, 0x95, 0x81, 0x8d, 0xc9, 0xc5, 0xd1, 0xdd, 0xf9, 0xf5, 0xe1, 0xed, 0xf4, 0xf8, 0xec, 0xe0, 0xc4, 0xc8, 0xdc, 0xd0, 0x94, 0x98, 0x8c, 0x80, 0xa4, 0xa8, 0xbc, 0xb0, 0x34, 0x38, 0x2c, 0x20, 0x4, 0x8, 0x1c, 0x10, 0x54, 0x58, 0x4c, 0x40, 0x64, 0x68, 0x7c, 0x70},
+ {0x0, 0xd, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, 0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6, 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x5, 0x8, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26, 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0xf, 0x2, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc, 0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91, 0xa, 0x7, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41, 0xce, 0xc3, 0xd4, 0xd9, 0xfa, 0xf7, 0xe0, 0xed, 0xa6, 0xab, 0xbc, 0xb1, 0x92, 0x9f, 0x88, 0x85, 0x1e, 0x13, 0x4, 0x9, 0x2a, 0x27, 0x30, 0x3d, 0x76, 0x7b, 0x6c, 0x61, 0x42, 0x4f, 0x58, 0x55, 0x73, 0x7e, 0x69, 0x64, 0x47, 0x4a, 0x5d, 0x50, 0x1b, 0x16, 0x1, 0xc, 0x2f, 0x22, 0x35, 0x38, 0xa3, 0xae, 0xb9, 0xb4, 0x97, 0x9a, 0x8d, 0x80, 0xcb, 0xc6, 0xd1, 0xdc, 0xff, 0xf2, 0xe5, 0xe8, 0xa9, 0xa4, 0xb3, 0xbe, 0x9d, 0x90, 0x87, 0x8a, 0xc1, 0xcc, 0xdb, 0xd6, 0xf5, 0xf8, 0xef, 0xe2, 0x79, 0x74, 0x63, 0x6e, 0x4d, 0x40, 0x57, 0x5a, 0x11, 0x1c, 0xb, 0x6, 0x25, 0x28, 0x3f, 0x32, 0x14, 0x19, 0xe, 0x3, 0x20, 0x2d, 0x3a, 0x37, 0x7c, 0x71, 0x66, 0x6b, 0x48, 0x45, 0x52, 0x5f, 0xc4, 0xc9, 0xde, 0xd3, 0xf0, 0xfd, 0xea, 0xe7, 0xac, 0xa1, 0xb6, 0xbb, 0x98, 0x95, 0x82, 0x8f},
+ {0x0, 0xe, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0x3d, 0x33, 0x21, 0x2f, 0x5, 0xb, 0x19, 0x17, 0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d, 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d, 0x37, 0x39, 0x2b, 0x25, 0xf, 0x1, 0x13, 0x1d, 0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0xa, 0x4, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20, 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0, 0x53, 0x5d, 0x4f, 0x41, 0x6b, 0x65, 0x77, 0x79, 0x23, 0x2d, 0x3f, 0x31, 0x1b, 0x15, 0x7, 0x9, 0xb3, 0xbd, 0xaf, 0xa1, 0x8b, 0x85, 0x97, 0x99, 0xc3, 0xcd, 0xdf, 0xd1, 0xfb, 0xf5, 0xe7, 0xe9, 0x8e, 0x80, 0x92, 0x9c, 0xb6, 0xb8, 0xaa, 0xa4, 0xfe, 0xf0, 0xe2, 0xec, 0xc6, 0xc8, 0xda, 0xd4, 0x6e, 0x60, 0x72, 0x7c, 0x56, 0x58, 0x4a, 0x44, 0x1e, 0x10, 0x2, 0xc, 0x26, 0x28, 0x3a, 0x34, 0xf4, 0xfa, 0xe8, 0xe6, 0xcc, 0xc2, 0xd0, 0xde, 0x84, 0x8a, 0x98, 0x96, 0xbc, 0xb2, 0xa0, 0xae, 0x14, 0x1a, 0x8, 0x6, 0x2c, 0x22, 0x30, 0x3e, 0x64, 0x6a, 0x78, 0x76, 0x5c, 0x52, 0x40, 0x4e, 0x29, 0x27, 0x35, 0x3b, 0x11, 0x1f, 0xd, 0x3, 0x59, 0x57, 0x45, 0x4b, 0x61, 0x6f, 0x7d, 0x73, 0xc9, 0xc7, 0xd5, 0xdb, 0xf1, 0xff, 0xed, 0xe3, 0xb9, 0xb7, 0xa5, 0xab, 0x81, 0x8f, 0x9d, 0x93},
+ {0x0, 0xf, 0x1e, 0x11, 0x3c, 0x33, 0x22, 0x2d, 0x78, 0x77, 0x66, 0x69, 0x44, 0x4b, 0x5a, 0x55, 0xf0, 0xff, 0xee, 0xe1, 0xcc, 0xc3, 0xd2, 0xdd, 0x88, 0x87, 0x96, 0x99, 0xb4, 0xbb, 0xaa, 0xa5, 0xfd, 0xf2, 0xe3, 0xec, 0xc1, 0xce, 0xdf, 0xd0, 0x85, 0x8a, 0x9b, 0x94, 0xb9, 0xb6, 0xa7, 0xa8, 0xd, 0x2, 0x13, 0x1c, 0x31, 0x3e, 0x2f, 0x20, 0x75, 0x7a, 0x6b, 0x64, 0x49, 0x46, 0x57, 0x58, 0xe7, 0xe8, 0xf9, 0xf6, 0xdb, 0xd4, 0xc5, 0xca, 0x9f, 0x90, 0x81, 0x8e, 0xa3, 0xac, 0xbd, 0xb2, 0x17, 0x18, 0x9, 0x6, 0x2b, 0x24, 0x35, 0x3a, 0x6f, 0x60, 0x71, 0x7e, 0x53, 0x5c, 0x4d, 0x42, 0x1a, 0x15, 0x4, 0xb, 0x26, 0x29, 0x38, 0x37, 0x62, 0x6d, 0x7c, 0x73, 0x5e, 0x51, 0x40, 0x4f, 0xea, 0xe5, 0xf4, 0xfb, 0xd6, 0xd9, 0xc8, 0xc7, 0x92, 0x9d, 0x8c, 0x83, 0xae, 0xa1, 0xb0, 0xbf, 0xd3, 0xdc, 0xcd, 0xc2, 0xef, 0xe0, 0xf1, 0xfe, 0xab, 0xa4, 0xb5, 0xba, 0x97, 0x98, 0x89, 0x86, 0x23, 0x2c, 0x3d, 0x32, 0x1f, 0x10, 0x1, 0xe, 0x5b, 0x54, 0x45, 0x4a, 0x67, 0x68, 0x79, 0x76, 0x2e, 0x21, 0x30, 0x3f, 0x12, 0x1d, 0xc, 0x3, 0x56, 0x59, 0x48, 0x47, 0x6a, 0x65, 0x74, 0x7b, 0xde, 0xd1, 0xc0, 0xcf, 0xe2, 0xed, 0xfc, 0xf3, 0xa6, 0xa9, 0xb8, 0xb7, 0x9a, 0x95, 0x84, 0x8b, 0x34, 0x3b, 0x2a, 0x25, 0x8, 0x7, 0x16, 0x19, 0x4c, 0x43, 0x52, 0x5d, 0x70, 0x7f, 0x6e, 0x61, 0xc4, 0xcb, 0xda, 0xd5, 0xf8, 0xf7, 0xe6, 0xe9, 0xbc, 0xb3, 0xa2, 0xad, 0x80, 0x8f, 0x9e, 0x91, 0xc9, 0xc6, 0xd7, 0xd8, 0xf5, 0xfa, 0xeb, 0xe4, 0xb1, 0xbe, 0xaf, 0xa0, 0x8d, 0x82, 0x93, 0x9c, 0x39, 0x36, 0x27, 0x28, 0x5, 0xa, 0x1b, 0x14, 0x41, 0x4e, 0x5f, 0x50, 0x7d, 0x72, 0x63, 0x6c},
+ {0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x1d, 0xd, 0x3d, 0x2d, 0x5d, 0x4d, 0x7d, 0x6d, 0x9d, 0x8d, 0xbd, 0xad, 0xdd, 0xcd, 0xfd, 0xed, 0x3a, 0x2a, 0x1a, 0xa, 0x7a, 0x6a, 0x5a, 0x4a, 0xba, 0xaa, 0x9a, 0x8a, 0xfa, 0xea, 0xda, 0xca, 0x27, 0x37, 0x7, 0x17, 0x67, 0x77, 0x47, 0x57, 0xa7, 0xb7, 0x87, 0x97, 0xe7, 0xf7, 0xc7, 0xd7, 0x74, 0x64, 0x54, 0x44, 0x34, 0x24, 0x14, 0x4, 0xf4, 0xe4, 0xd4, 0xc4, 0xb4, 0xa4, 0x94, 0x84, 0x69, 0x79, 0x49, 0x59, 0x29, 0x39, 0x9, 0x19, 0xe9, 0xf9, 0xc9, 0xd9, 0xa9, 0xb9, 0x89, 0x99, 0x4e, 0x5e, 0x6e, 0x7e, 0xe, 0x1e, 0x2e, 0x3e, 0xce, 0xde, 0xee, 0xfe, 0x8e, 0x9e, 0xae, 0xbe, 0x53, 0x43, 0x73, 0x63, 0x13, 0x3, 0x33, 0x23, 0xd3, 0xc3, 0xf3, 0xe3, 0x93, 0x83, 0xb3, 0xa3, 0xe8, 0xf8, 0xc8, 0xd8, 0xa8, 0xb8, 0x88, 0x98, 0x68, 0x78, 0x48, 0x58, 0x28, 0x38, 0x8, 0x18, 0xf5, 0xe5, 0xd5, 0xc5, 0xb5, 0xa5, 0x95, 0x85, 0x75, 0x65, 0x55, 0x45, 0x35, 0x25, 0x15, 0x5, 0xd2, 0xc2, 0xf2, 0xe2, 0x92, 0x82, 0xb2, 0xa2, 0x52, 0x42, 0x72, 0x62, 0x12, 0x2, 0x32, 0x22, 0xcf, 0xdf, 0xef, 0xff, 0x8f, 0x9f, 0xaf, 0xbf, 0x4f, 0x5f, 0x6f, 0x7f, 0xf, 0x1f, 0x2f, 0x3f, 0x9c, 0x8c, 0xbc, 0xac, 0xdc, 0xcc, 0xfc, 0xec, 0x1c, 0xc, 0x3c, 0x2c, 0x5c, 0x4c, 0x7c, 0x6c, 0x81, 0x91, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1, 0xf1, 0x1, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0xa6, 0xb6, 0x86, 0x96, 0xe6, 0xf6, 0xc6, 0xd6, 0x26, 0x36, 0x6, 0x16, 0x66, 0x76, 0x46, 0x56, 0xbb, 0xab, 0x9b, 0x8b, 0xfb, 0xeb, 0xdb, 0xcb, 0x3b, 0x2b, 0x1b, 0xb, 0x7b, 0x6b, 0x5b, 0x4b},
+ {0x0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xd, 0x1c, 0x2f, 0x3e, 0x49, 0x58, 0x6b, 0x7a, 0x85, 0x94, 0xa7, 0xb6, 0xc1, 0xd0, 0xe3, 0xf2, 0x1a, 0xb, 0x38, 0x29, 0x5e, 0x4f, 0x7c, 0x6d, 0x92, 0x83, 0xb0, 0xa1, 0xd6, 0xc7, 0xf4, 0xe5, 0x17, 0x6, 0x35, 0x24, 0x53, 0x42, 0x71, 0x60, 0x9f, 0x8e, 0xbd, 0xac, 0xdb, 0xca, 0xf9, 0xe8, 0x34, 0x25, 0x16, 0x7, 0x70, 0x61, 0x52, 0x43, 0xbc, 0xad, 0x9e, 0x8f, 0xf8, 0xe9, 0xda, 0xcb, 0x39, 0x28, 0x1b, 0xa, 0x7d, 0x6c, 0x5f, 0x4e, 0xb1, 0xa0, 0x93, 0x82, 0xf5, 0xe4, 0xd7, 0xc6, 0x2e, 0x3f, 0xc, 0x1d, 0x6a, 0x7b, 0x48, 0x59, 0xa6, 0xb7, 0x84, 0x95, 0xe2, 0xf3, 0xc0, 0xd1, 0x23, 0x32, 0x1, 0x10, 0x67, 0x76, 0x45, 0x54, 0xab, 0xba, 0x89, 0x98, 0xef, 0xfe, 0xcd, 0xdc, 0x68, 0x79, 0x4a, 0x5b, 0x2c, 0x3d, 0xe, 0x1f, 0xe0, 0xf1, 0xc2, 0xd3, 0xa4, 0xb5, 0x86, 0x97, 0x65, 0x74, 0x47, 0x56, 0x21, 0x30, 0x3, 0x12, 0xed, 0xfc, 0xcf, 0xde, 0xa9, 0xb8, 0x8b, 0x9a, 0x72, 0x63, 0x50, 0x41, 0x36, 0x27, 0x14, 0x5, 0xfa, 0xeb, 0xd8, 0xc9, 0xbe, 0xaf, 0x9c, 0x8d, 0x7f, 0x6e, 0x5d, 0x4c, 0x3b, 0x2a, 0x19, 0x8, 0xf7, 0xe6, 0xd5, 0xc4, 0xb3, 0xa2, 0x91, 0x80, 0x5c, 0x4d, 0x7e, 0x6f, 0x18, 0x9, 0x3a, 0x2b, 0xd4, 0xc5, 0xf6, 0xe7, 0x90, 0x81, 0xb2, 0xa3, 0x51, 0x40, 0x73, 0x62, 0x15, 0x4, 0x37, 0x26, 0xd9, 0xc8, 0xfb, 0xea, 0x9d, 0x8c, 0xbf, 0xae, 0x46, 0x57, 0x64, 0x75, 0x2, 0x13, 0x20, 0x31, 0xce, 0xdf, 0xec, 0xfd, 0x8a, 0x9b, 0xa8, 0xb9, 0x4b, 0x5a, 0x69, 0x78, 0xf, 0x1e, 0x2d, 0x3c, 0xc3, 0xd2, 0xe1, 0xf0, 0x87, 0x96, 0xa5, 0xb4},
+ {0x0, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee, 0x3d, 0x2f, 0x19, 0xb, 0x75, 0x67, 0x51, 0x43, 0xad, 0xbf, 0x89, 0x9b, 0xe5, 0xf7, 0xc1, 0xd3, 0x7a, 0x68, 0x5e, 0x4c, 0x32, 0x20, 0x16, 0x4, 0xea, 0xf8, 0xce, 0xdc, 0xa2, 0xb0, 0x86, 0x94, 0x47, 0x55, 0x63, 0x71, 0xf, 0x1d, 0x2b, 0x39, 0xd7, 0xc5, 0xf3, 0xe1, 0x9f, 0x8d, 0xbb, 0xa9, 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a, 0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x8, 0x1a, 0xc9, 0xdb, 0xed, 0xff, 0x81, 0x93, 0xa5, 0xb7, 0x59, 0x4b, 0x7d, 0x6f, 0x11, 0x3, 0x35, 0x27, 0x8e, 0x9c, 0xaa, 0xb8, 0xc6, 0xd4, 0xe2, 0xf0, 0x1e, 0xc, 0x3a, 0x28, 0x56, 0x44, 0x72, 0x60, 0xb3, 0xa1, 0x97, 0x85, 0xfb, 0xe9, 0xdf, 0xcd, 0x23, 0x31, 0x7, 0x15, 0x6b, 0x79, 0x4f, 0x5d, 0xf5, 0xe7, 0xd1, 0xc3, 0xbd, 0xaf, 0x99, 0x8b, 0x65, 0x77, 0x41, 0x53, 0x2d, 0x3f, 0x9, 0x1b, 0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x2, 0x34, 0x26, 0x8f, 0x9d, 0xab, 0xb9, 0xc7, 0xd5, 0xe3, 0xf1, 0x1f, 0xd, 0x3b, 0x29, 0x57, 0x45, 0x73, 0x61, 0xb2, 0xa0, 0x96, 0x84, 0xfa, 0xe8, 0xde, 0xcc, 0x22, 0x30, 0x6, 0x14, 0x6a, 0x78, 0x4e, 0x5c, 0x1, 0x13, 0x25, 0x37, 0x49, 0x5b, 0x6d, 0x7f, 0x91, 0x83, 0xb5, 0xa7, 0xd9, 0xcb, 0xfd, 0xef, 0x3c, 0x2e, 0x18, 0xa, 0x74, 0x66, 0x50, 0x42, 0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, 0x7b, 0x69, 0x5f, 0x4d, 0x33, 0x21, 0x17, 0x5, 0xeb, 0xf9, 0xcf, 0xdd, 0xa3, 0xb1, 0x87, 0x95, 0x46, 0x54, 0x62, 0x70, 0xe, 0x1c, 0x2a, 0x38, 0xd6, 0xc4, 0xf2, 0xe0, 0x9e, 0x8c, 0xba, 0xa8},
+ {0x0, 0x13, 0x26, 0x35, 0x4c, 0x5f, 0x6a, 0x79, 0x98, 0x8b, 0xbe, 0xad, 0xd4, 0xc7, 0xf2, 0xe1, 0x2d, 0x3e, 0xb, 0x18, 0x61, 0x72, 0x47, 0x54, 0xb5, 0xa6, 0x93, 0x80, 0xf9, 0xea, 0xdf, 0xcc, 0x5a, 0x49, 0x7c, 0x6f, 0x16, 0x5, 0x30, 0x23, 0xc2, 0xd1, 0xe4, 0xf7, 0x8e, 0x9d, 0xa8, 0xbb, 0x77, 0x64, 0x51, 0x42, 0x3b, 0x28, 0x1d, 0xe, 0xef, 0xfc, 0xc9, 0xda, 0xa3, 0xb0, 0x85, 0x96, 0xb4, 0xa7, 0x92, 0x81, 0xf8, 0xeb, 0xde, 0xcd, 0x2c, 0x3f, 0xa, 0x19, 0x60, 0x73, 0x46, 0x55, 0x99, 0x8a, 0xbf, 0xac, 0xd5, 0xc6, 0xf3, 0xe0, 0x1, 0x12, 0x27, 0x34, 0x4d, 0x5e, 0x6b, 0x78, 0xee, 0xfd, 0xc8, 0xdb, 0xa2, 0xb1, 0x84, 0x97, 0x76, 0x65, 0x50, 0x43, 0x3a, 0x29, 0x1c, 0xf, 0xc3, 0xd0, 0xe5, 0xf6, 0x8f, 0x9c, 0xa9, 0xba, 0x5b, 0x48, 0x7d, 0x6e, 0x17, 0x4, 0x31, 0x22, 0x75, 0x66, 0x53, 0x40, 0x39, 0x2a, 0x1f, 0xc, 0xed, 0xfe, 0xcb, 0xd8, 0xa1, 0xb2, 0x87, 0x94, 0x58, 0x4b, 0x7e, 0x6d, 0x14, 0x7, 0x32, 0x21, 0xc0, 0xd3, 0xe6, 0xf5, 0x8c, 0x9f, 0xaa, 0xb9, 0x2f, 0x3c, 0x9, 0x1a, 0x63, 0x70, 0x45, 0x56, 0xb7, 0xa4, 0x91, 0x82, 0xfb, 0xe8, 0xdd, 0xce, 0x2, 0x11, 0x24, 0x37, 0x4e, 0x5d, 0x68, 0x7b, 0x9a, 0x89, 0xbc, 0xaf, 0xd6, 0xc5, 0xf0, 0xe3, 0xc1, 0xd2, 0xe7, 0xf4, 0x8d, 0x9e, 0xab, 0xb8, 0x59, 0x4a, 0x7f, 0x6c, 0x15, 0x6, 0x33, 0x20, 0xec, 0xff, 0xca, 0xd9, 0xa0, 0xb3, 0x86, 0x95, 0x74, 0x67, 0x52, 0x41, 0x38, 0x2b, 0x1e, 0xd, 0x9b, 0x88, 0xbd, 0xae, 0xd7, 0xc4, 0xf1, 0xe2, 0x3, 0x10, 0x25, 0x36, 0x4f, 0x5c, 0x69, 0x7a, 0xb6, 0xa5, 0x90, 0x83, 0xfa, 0xe9, 0xdc, 0xcf, 0x2e, 0x3d, 0x8, 0x1b, 0x62, 0x71, 0x44, 0x57},
+ {0x0, 0x14, 0x28, 0x3c, 0x50, 0x44, 0x78, 0x6c, 0xa0, 0xb4, 0x88, 0x9c, 0xf0, 0xe4, 0xd8, 0xcc, 0x5d, 0x49, 0x75, 0x61, 0xd, 0x19, 0x25, 0x31, 0xfd, 0xe9, 0xd5, 0xc1, 0xad, 0xb9, 0x85, 0x91, 0xba, 0xae, 0x92, 0x86, 0xea, 0xfe, 0xc2, 0xd6, 0x1a, 0xe, 0x32, 0x26, 0x4a, 0x5e, 0x62, 0x76, 0xe7, 0xf3, 0xcf, 0xdb, 0xb7, 0xa3, 0x9f, 0x8b, 0x47, 0x53, 0x6f, 0x7b, 0x17, 0x3, 0x3f, 0x2b, 0x69, 0x7d, 0x41, 0x55, 0x39, 0x2d, 0x11, 0x5, 0xc9, 0xdd, 0xe1, 0xf5, 0x99, 0x8d, 0xb1, 0xa5, 0x34, 0x20, 0x1c, 0x8, 0x64, 0x70, 0x4c, 0x58, 0x94, 0x80, 0xbc, 0xa8, 0xc4, 0xd0, 0xec, 0xf8, 0xd3, 0xc7, 0xfb, 0xef, 0x83, 0x97, 0xab, 0xbf, 0x73, 0x67, 0x5b, 0x4f, 0x23, 0x37, 0xb, 0x1f, 0x8e, 0x9a, 0xa6, 0xb2, 0xde, 0xca, 0xf6, 0xe2, 0x2e, 0x3a, 0x6, 0x12, 0x7e, 0x6a, 0x56, 0x42, 0xd2, 0xc6, 0xfa, 0xee, 0x82, 0x96, 0xaa, 0xbe, 0x72, 0x66, 0x5a, 0x4e, 0x22, 0x36, 0xa, 0x1e, 0x8f, 0x9b, 0xa7, 0xb3, 0xdf, 0xcb, 0xf7, 0xe3, 0x2f, 0x3b, 0x7, 0x13, 0x7f, 0x6b, 0x57, 0x43, 0x68, 0x7c, 0x40, 0x54, 0x38, 0x2c, 0x10, 0x4, 0xc8, 0xdc, 0xe0, 0xf4, 0x98, 0x8c, 0xb0, 0xa4, 0x35, 0x21, 0x1d, 0x9, 0x65, 0x71, 0x4d, 0x59, 0x95, 0x81, 0xbd, 0xa9, 0xc5, 0xd1, 0xed, 0xf9, 0xbb, 0xaf, 0x93, 0x87, 0xeb, 0xff, 0xc3, 0xd7, 0x1b, 0xf, 0x33, 0x27, 0x4b, 0x5f, 0x63, 0x77, 0xe6, 0xf2, 0xce, 0xda, 0xb6, 0xa2, 0x9e, 0x8a, 0x46, 0x52, 0x6e, 0x7a, 0x16, 0x2, 0x3e, 0x2a, 0x1, 0x15, 0x29, 0x3d, 0x51, 0x45, 0x79, 0x6d, 0xa1, 0xb5, 0x89, 0x9d, 0xf1, 0xe5, 0xd9, 0xcd, 0x5c, 0x48, 0x74, 0x60, 0xc, 0x18, 0x24, 0x30, 0xfc, 0xe8, 0xd4, 0xc0, 0xac, 0xb8, 0x84, 0x90},
+ {0x0, 0x15, 0x2a, 0x3f, 0x54, 0x41, 0x7e, 0x6b, 0xa8, 0xbd, 0x82, 0x97, 0xfc, 0xe9, 0xd6, 0xc3, 0x4d, 0x58, 0x67, 0x72, 0x19, 0xc, 0x33, 0x26, 0xe5, 0xf0, 0xcf, 0xda, 0xb1, 0xa4, 0x9b, 0x8e, 0x9a, 0x8f, 0xb0, 0xa5, 0xce, 0xdb, 0xe4, 0xf1, 0x32, 0x27, 0x18, 0xd, 0x66, 0x73, 0x4c, 0x59, 0xd7, 0xc2, 0xfd, 0xe8, 0x83, 0x96, 0xa9, 0xbc, 0x7f, 0x6a, 0x55, 0x40, 0x2b, 0x3e, 0x1, 0x14, 0x29, 0x3c, 0x3, 0x16, 0x7d, 0x68, 0x57, 0x42, 0x81, 0x94, 0xab, 0xbe, 0xd5, 0xc0, 0xff, 0xea, 0x64, 0x71, 0x4e, 0x5b, 0x30, 0x25, 0x1a, 0xf, 0xcc, 0xd9, 0xe6, 0xf3, 0x98, 0x8d, 0xb2, 0xa7, 0xb3, 0xa6, 0x99, 0x8c, 0xe7, 0xf2, 0xcd, 0xd8, 0x1b, 0xe, 0x31, 0x24, 0x4f, 0x5a, 0x65, 0x70, 0xfe, 0xeb, 0xd4, 0xc1, 0xaa, 0xbf, 0x80, 0x95, 0x56, 0x43, 0x7c, 0x69, 0x2, 0x17, 0x28, 0x3d, 0x52, 0x47, 0x78, 0x6d, 0x6, 0x13, 0x2c, 0x39, 0xfa, 0xef, 0xd0, 0xc5, 0xae, 0xbb, 0x84, 0x91, 0x1f, 0xa, 0x35, 0x20, 0x4b, 0x5e, 0x61, 0x74, 0xb7, 0xa2, 0x9d, 0x88, 0xe3, 0xf6, 0xc9, 0xdc, 0xc8, 0xdd, 0xe2, 0xf7, 0x9c, 0x89, 0xb6, 0xa3, 0x60, 0x75, 0x4a, 0x5f, 0x34, 0x21, 0x1e, 0xb, 0x85, 0x90, 0xaf, 0xba, 0xd1, 0xc4, 0xfb, 0xee, 0x2d, 0x38, 0x7, 0x12, 0x79, 0x6c, 0x53, 0x46, 0x7b, 0x6e, 0x51, 0x44, 0x2f, 0x3a, 0x5, 0x10, 0xd3, 0xc6, 0xf9, 0xec, 0x87, 0x92, 0xad, 0xb8, 0x36, 0x23, 0x1c, 0x9, 0x62, 0x77, 0x48, 0x5d, 0x9e, 0x8b, 0xb4, 0xa1, 0xca, 0xdf, 0xe0, 0xf5, 0xe1, 0xf4, 0xcb, 0xde, 0xb5, 0xa0, 0x9f, 0x8a, 0x49, 0x5c, 0x63, 0x76, 0x1d, 0x8, 0x37, 0x22, 0xac, 0xb9, 0x86, 0x93, 0xf8, 0xed, 0xd2, 0xc7, 0x4, 0x11, 0x2e, 0x3b, 0x50, 0x45, 0x7a, 0x6f},
+ {0x0, 0x16, 0x2c, 0x3a, 0x58, 0x4e, 0x74, 0x62, 0xb0, 0xa6, 0x9c, 0x8a, 0xe8, 0xfe, 0xc4, 0xd2, 0x7d, 0x6b, 0x51, 0x47, 0x25, 0x33, 0x9, 0x1f, 0xcd, 0xdb, 0xe1, 0xf7, 0x95, 0x83, 0xb9, 0xaf, 0xfa, 0xec, 0xd6, 0xc0, 0xa2, 0xb4, 0x8e, 0x98, 0x4a, 0x5c, 0x66, 0x70, 0x12, 0x4, 0x3e, 0x28, 0x87, 0x91, 0xab, 0xbd, 0xdf, 0xc9, 0xf3, 0xe5, 0x37, 0x21, 0x1b, 0xd, 0x6f, 0x79, 0x43, 0x55, 0xe9, 0xff, 0xc5, 0xd3, 0xb1, 0xa7, 0x9d, 0x8b, 0x59, 0x4f, 0x75, 0x63, 0x1, 0x17, 0x2d, 0x3b, 0x94, 0x82, 0xb8, 0xae, 0xcc, 0xda, 0xe0, 0xf6, 0x24, 0x32, 0x8, 0x1e, 0x7c, 0x6a, 0x50, 0x46, 0x13, 0x5, 0x3f, 0x29, 0x4b, 0x5d, 0x67, 0x71, 0xa3, 0xb5, 0x8f, 0x99, 0xfb, 0xed, 0xd7, 0xc1, 0x6e, 0x78, 0x42, 0x54, 0x36, 0x20, 0x1a, 0xc, 0xde, 0xc8, 0xf2, 0xe4, 0x86, 0x90, 0xaa, 0xbc, 0xcf, 0xd9, 0xe3, 0xf5, 0x97, 0x81, 0xbb, 0xad, 0x7f, 0x69, 0x53, 0x45, 0x27, 0x31, 0xb, 0x1d, 0xb2, 0xa4, 0x9e, 0x88, 0xea, 0xfc, 0xc6, 0xd0, 0x2, 0x14, 0x2e, 0x38, 0x5a, 0x4c, 0x76, 0x60, 0x35, 0x23, 0x19, 0xf, 0x6d, 0x7b, 0x41, 0x57, 0x85, 0x93, 0xa9, 0xbf, 0xdd, 0xcb, 0xf1, 0xe7, 0x48, 0x5e, 0x64, 0x72, 0x10, 0x6, 0x3c, 0x2a, 0xf8, 0xee, 0xd4, 0xc2, 0xa0, 0xb6, 0x8c, 0x9a, 0x26, 0x30, 0xa, 0x1c, 0x7e, 0x68, 0x52, 0x44, 0x96, 0x80, 0xba, 0xac, 0xce, 0xd8, 0xe2, 0xf4, 0x5b, 0x4d, 0x77, 0x61, 0x3, 0x15, 0x2f, 0x39, 0xeb, 0xfd, 0xc7, 0xd1, 0xb3, 0xa5, 0x9f, 0x89, 0xdc, 0xca, 0xf0, 0xe6, 0x84, 0x92, 0xa8, 0xbe, 0x6c, 0x7a, 0x40, 0x56, 0x34, 0x22, 0x18, 0xe, 0xa1, 0xb7, 0x8d, 0x9b, 0xf9, 0xef, 0xd5, 0xc3, 0x11, 0x7, 0x3d, 0x2b, 0x49, 0x5f, 0x65, 0x73},
+ {0x0, 0x17, 0x2e, 0x39, 0x5c, 0x4b, 0x72, 0x65, 0xb8, 0xaf, 0x96, 0x81, 0xe4, 0xf3, 0xca, 0xdd, 0x6d, 0x7a, 0x43, 0x54, 0x31, 0x26, 0x1f, 0x8, 0xd5, 0xc2, 0xfb, 0xec, 0x89, 0x9e, 0xa7, 0xb0, 0xda, 0xcd, 0xf4, 0xe3, 0x86, 0x91, 0xa8, 0xbf, 0x62, 0x75, 0x4c, 0x5b, 0x3e, 0x29, 0x10, 0x7, 0xb7, 0xa0, 0x99, 0x8e, 0xeb, 0xfc, 0xc5, 0xd2, 0xf, 0x18, 0x21, 0x36, 0x53, 0x44, 0x7d, 0x6a, 0xa9, 0xbe, 0x87, 0x90, 0xf5, 0xe2, 0xdb, 0xcc, 0x11, 0x6, 0x3f, 0x28, 0x4d, 0x5a, 0x63, 0x74, 0xc4, 0xd3, 0xea, 0xfd, 0x98, 0x8f, 0xb6, 0xa1, 0x7c, 0x6b, 0x52, 0x45, 0x20, 0x37, 0xe, 0x19, 0x73, 0x64, 0x5d, 0x4a, 0x2f, 0x38, 0x1, 0x16, 0xcb, 0xdc, 0xe5, 0xf2, 0x97, 0x80, 0xb9, 0xae, 0x1e, 0x9, 0x30, 0x27, 0x42, 0x55, 0x6c, 0x7b, 0xa6, 0xb1, 0x88, 0x9f, 0xfa, 0xed, 0xd4, 0xc3, 0x4f, 0x58, 0x61, 0x76, 0x13, 0x4, 0x3d, 0x2a, 0xf7, 0xe0, 0xd9, 0xce, 0xab, 0xbc, 0x85, 0x92, 0x22, 0x35, 0xc, 0x1b, 0x7e, 0x69, 0x50, 0x47, 0x9a, 0x8d, 0xb4, 0xa3, 0xc6, 0xd1, 0xe8, 0xff, 0x95, 0x82, 0xbb, 0xac, 0xc9, 0xde, 0xe7, 0xf0, 0x2d, 0x3a, 0x3, 0x14, 0x71, 0x66, 0x5f, 0x48, 0xf8, 0xef, 0xd6, 0xc1, 0xa4, 0xb3, 0x8a, 0x9d, 0x40, 0x57, 0x6e, 0x79, 0x1c, 0xb, 0x32, 0x25, 0xe6, 0xf1, 0xc8, 0xdf, 0xba, 0xad, 0x94, 0x83, 0x5e, 0x49, 0x70, 0x67, 0x2, 0x15, 0x2c, 0x3b, 0x8b, 0x9c, 0xa5, 0xb2, 0xd7, 0xc0, 0xf9, 0xee, 0x33, 0x24, 0x1d, 0xa, 0x6f, 0x78, 0x41, 0x56, 0x3c, 0x2b, 0x12, 0x5, 0x60, 0x77, 0x4e, 0x59, 0x84, 0x93, 0xaa, 0xbd, 0xd8, 0xcf, 0xf6, 0xe1, 0x51, 0x46, 0x7f, 0x68, 0xd, 0x1a, 0x23, 0x34, 0xe9, 0xfe, 0xc7, 0xd0, 0xb5, 0xa2, 0x9b, 0x8c},
+ {0x0, 0x18, 0x30, 0x28, 0x60, 0x78, 0x50, 0x48, 0xc0, 0xd8, 0xf0, 0xe8, 0xa0, 0xb8, 0x90, 0x88, 0x9d, 0x85, 0xad, 0xb5, 0xfd, 0xe5, 0xcd, 0xd5, 0x5d, 0x45, 0x6d, 0x75, 0x3d, 0x25, 0xd, 0x15, 0x27, 0x3f, 0x17, 0xf, 0x47, 0x5f, 0x77, 0x6f, 0xe7, 0xff, 0xd7, 0xcf, 0x87, 0x9f, 0xb7, 0xaf, 0xba, 0xa2, 0x8a, 0x92, 0xda, 0xc2, 0xea, 0xf2, 0x7a, 0x62, 0x4a, 0x52, 0x1a, 0x2, 0x2a, 0x32, 0x4e, 0x56, 0x7e, 0x66, 0x2e, 0x36, 0x1e, 0x6, 0x8e, 0x96, 0xbe, 0xa6, 0xee, 0xf6, 0xde, 0xc6, 0xd3, 0xcb, 0xe3, 0xfb, 0xb3, 0xab, 0x83, 0x9b, 0x13, 0xb, 0x23, 0x3b, 0x73, 0x6b, 0x43, 0x5b, 0x69, 0x71, 0x59, 0x41, 0x9, 0x11, 0x39, 0x21, 0xa9, 0xb1, 0x99, 0x81, 0xc9, 0xd1, 0xf9, 0xe1, 0xf4, 0xec, 0xc4, 0xdc, 0x94, 0x8c, 0xa4, 0xbc, 0x34, 0x2c, 0x4, 0x1c, 0x54, 0x4c, 0x64, 0x7c, 0x9c, 0x84, 0xac, 0xb4, 0xfc, 0xe4, 0xcc, 0xd4, 0x5c, 0x44, 0x6c, 0x74, 0x3c, 0x24, 0xc, 0x14, 0x1, 0x19, 0x31, 0x29, 0x61, 0x79, 0x51, 0x49, 0xc1, 0xd9, 0xf1, 0xe9, 0xa1, 0xb9, 0x91, 0x89, 0xbb, 0xa3, 0x8b, 0x93, 0xdb, 0xc3, 0xeb, 0xf3, 0x7b, 0x63, 0x4b, 0x53, 0x1b, 0x3, 0x2b, 0x33, 0x26, 0x3e, 0x16, 0xe, 0x46, 0x5e, 0x76, 0x6e, 0xe6, 0xfe, 0xd6, 0xce, 0x86, 0x9e, 0xb6, 0xae, 0xd2, 0xca, 0xe2, 0xfa, 0xb2, 0xaa, 0x82, 0x9a, 0x12, 0xa, 0x22, 0x3a, 0x72, 0x6a, 0x42, 0x5a, 0x4f, 0x57, 0x7f, 0x67, 0x2f, 0x37, 0x1f, 0x7, 0x8f, 0x97, 0xbf, 0xa7, 0xef, 0xf7, 0xdf, 0xc7, 0xf5, 0xed, 0xc5, 0xdd, 0x95, 0x8d, 0xa5, 0xbd, 0x35, 0x2d, 0x5, 0x1d, 0x55, 0x4d, 0x65, 0x7d, 0x68, 0x70, 0x58, 0x40, 0x8, 0x10, 0x38, 0x20, 0xa8, 0xb0, 0x98, 0x80, 0xc8, 0xd0, 0xf8, 0xe0},
+ {0x0, 0x19, 0x32, 0x2b, 0x64, 0x7d, 0x56, 0x4f, 0xc8, 0xd1, 0xfa, 0xe3, 0xac, 0xb5, 0x9e, 0x87, 0x8d, 0x94, 0xbf, 0xa6, 0xe9, 0xf0, 0xdb, 0xc2, 0x45, 0x5c, 0x77, 0x6e, 0x21, 0x38, 0x13, 0xa, 0x7, 0x1e, 0x35, 0x2c, 0x63, 0x7a, 0x51, 0x48, 0xcf, 0xd6, 0xfd, 0xe4, 0xab, 0xb2, 0x99, 0x80, 0x8a, 0x93, 0xb8, 0xa1, 0xee, 0xf7, 0xdc, 0xc5, 0x42, 0x5b, 0x70, 0x69, 0x26, 0x3f, 0x14, 0xd, 0xe, 0x17, 0x3c, 0x25, 0x6a, 0x73, 0x58, 0x41, 0xc6, 0xdf, 0xf4, 0xed, 0xa2, 0xbb, 0x90, 0x89, 0x83, 0x9a, 0xb1, 0xa8, 0xe7, 0xfe, 0xd5, 0xcc, 0x4b, 0x52, 0x79, 0x60, 0x2f, 0x36, 0x1d, 0x4, 0x9, 0x10, 0x3b, 0x22, 0x6d, 0x74, 0x5f, 0x46, 0xc1, 0xd8, 0xf3, 0xea, 0xa5, 0xbc, 0x97, 0x8e, 0x84, 0x9d, 0xb6, 0xaf, 0xe0, 0xf9, 0xd2, 0xcb, 0x4c, 0x55, 0x7e, 0x67, 0x28, 0x31, 0x1a, 0x3, 0x1c, 0x5, 0x2e, 0x37, 0x78, 0x61, 0x4a, 0x53, 0xd4, 0xcd, 0xe6, 0xff, 0xb0, 0xa9, 0x82, 0x9b, 0x91, 0x88, 0xa3, 0xba, 0xf5, 0xec, 0xc7, 0xde, 0x59, 0x40, 0x6b, 0x72, 0x3d, 0x24, 0xf, 0x16, 0x1b, 0x2, 0x29, 0x30, 0x7f, 0x66, 0x4d, 0x54, 0xd3, 0xca, 0xe1, 0xf8, 0xb7, 0xae, 0x85, 0x9c, 0x96, 0x8f, 0xa4, 0xbd, 0xf2, 0xeb, 0xc0, 0xd9, 0x5e, 0x47, 0x6c, 0x75, 0x3a, 0x23, 0x8, 0x11, 0x12, 0xb, 0x20, 0x39, 0x76, 0x6f, 0x44, 0x5d, 0xda, 0xc3, 0xe8, 0xf1, 0xbe, 0xa7, 0x8c, 0x95, 0x9f, 0x86, 0xad, 0xb4, 0xfb, 0xe2, 0xc9, 0xd0, 0x57, 0x4e, 0x65, 0x7c, 0x33, 0x2a, 0x1, 0x18, 0x15, 0xc, 0x27, 0x3e, 0x71, 0x68, 0x43, 0x5a, 0xdd, 0xc4, 0xef, 0xf6, 0xb9, 0xa0, 0x8b, 0x92, 0x98, 0x81, 0xaa, 0xb3, 0xfc, 0xe5, 0xce, 0xd7, 0x50, 0x49, 0x62, 0x7b, 0x34, 0x2d, 0x6, 0x1f},
+ {0x0, 0x1a, 0x34, 0x2e, 0x68, 0x72, 0x5c, 0x46, 0xd0, 0xca, 0xe4, 0xfe, 0xb8, 0xa2, 0x8c, 0x96, 0xbd, 0xa7, 0x89, 0x93, 0xd5, 0xcf, 0xe1, 0xfb, 0x6d, 0x77, 0x59, 0x43, 0x5, 0x1f, 0x31, 0x2b, 0x67, 0x7d, 0x53, 0x49, 0xf, 0x15, 0x3b, 0x21, 0xb7, 0xad, 0x83, 0x99, 0xdf, 0xc5, 0xeb, 0xf1, 0xda, 0xc0, 0xee, 0xf4, 0xb2, 0xa8, 0x86, 0x9c, 0xa, 0x10, 0x3e, 0x24, 0x62, 0x78, 0x56, 0x4c, 0xce, 0xd4, 0xfa, 0xe0, 0xa6, 0xbc, 0x92, 0x88, 0x1e, 0x4, 0x2a, 0x30, 0x76, 0x6c, 0x42, 0x58, 0x73, 0x69, 0x47, 0x5d, 0x1b, 0x1, 0x2f, 0x35, 0xa3, 0xb9, 0x97, 0x8d, 0xcb, 0xd1, 0xff, 0xe5, 0xa9, 0xb3, 0x9d, 0x87, 0xc1, 0xdb, 0xf5, 0xef, 0x79, 0x63, 0x4d, 0x57, 0x11, 0xb, 0x25, 0x3f, 0x14, 0xe, 0x20, 0x3a, 0x7c, 0x66, 0x48, 0x52, 0xc4, 0xde, 0xf0, 0xea, 0xac, 0xb6, 0x98, 0x82, 0x81, 0x9b, 0xb5, 0xaf, 0xe9, 0xf3, 0xdd, 0xc7, 0x51, 0x4b, 0x65, 0x7f, 0x39, 0x23, 0xd, 0x17, 0x3c, 0x26, 0x8, 0x12, 0x54, 0x4e, 0x60, 0x7a, 0xec, 0xf6, 0xd8, 0xc2, 0x84, 0x9e, 0xb0, 0xaa, 0xe6, 0xfc, 0xd2, 0xc8, 0x8e, 0x94, 0xba, 0xa0, 0x36, 0x2c, 0x2, 0x18, 0x5e, 0x44, 0x6a, 0x70, 0x5b, 0x41, 0x6f, 0x75, 0x33, 0x29, 0x7, 0x1d, 0x8b, 0x91, 0xbf, 0xa5, 0xe3, 0xf9, 0xd7, 0xcd, 0x4f, 0x55, 0x7b, 0x61, 0x27, 0x3d, 0x13, 0x9, 0x9f, 0x85, 0xab, 0xb1, 0xf7, 0xed, 0xc3, 0xd9, 0xf2, 0xe8, 0xc6, 0xdc, 0x9a, 0x80, 0xae, 0xb4, 0x22, 0x38, 0x16, 0xc, 0x4a, 0x50, 0x7e, 0x64, 0x28, 0x32, 0x1c, 0x6, 0x40, 0x5a, 0x74, 0x6e, 0xf8, 0xe2, 0xcc, 0xd6, 0x90, 0x8a, 0xa4, 0xbe, 0x95, 0x8f, 0xa1, 0xbb, 0xfd, 0xe7, 0xc9, 0xd3, 0x45, 0x5f, 0x71, 0x6b, 0x2d, 0x37, 0x19, 0x3},
+ {0x0, 0x1b, 0x36, 0x2d, 0x6c, 0x77, 0x5a, 0x41, 0xd8, 0xc3, 0xee, 0xf5, 0xb4, 0xaf, 0x82, 0x99, 0xad, 0xb6, 0x9b, 0x80, 0xc1, 0xda, 0xf7, 0xec, 0x75, 0x6e, 0x43, 0x58, 0x19, 0x2, 0x2f, 0x34, 0x47, 0x5c, 0x71, 0x6a, 0x2b, 0x30, 0x1d, 0x6, 0x9f, 0x84, 0xa9, 0xb2, 0xf3, 0xe8, 0xc5, 0xde, 0xea, 0xf1, 0xdc, 0xc7, 0x86, 0x9d, 0xb0, 0xab, 0x32, 0x29, 0x4, 0x1f, 0x5e, 0x45, 0x68, 0x73, 0x8e, 0x95, 0xb8, 0xa3, 0xe2, 0xf9, 0xd4, 0xcf, 0x56, 0x4d, 0x60, 0x7b, 0x3a, 0x21, 0xc, 0x17, 0x23, 0x38, 0x15, 0xe, 0x4f, 0x54, 0x79, 0x62, 0xfb, 0xe0, 0xcd, 0xd6, 0x97, 0x8c, 0xa1, 0xba, 0xc9, 0xd2, 0xff, 0xe4, 0xa5, 0xbe, 0x93, 0x88, 0x11, 0xa, 0x27, 0x3c, 0x7d, 0x66, 0x4b, 0x50, 0x64, 0x7f, 0x52, 0x49, 0x8, 0x13, 0x3e, 0x25, 0xbc, 0xa7, 0x8a, 0x91, 0xd0, 0xcb, 0xe6, 0xfd, 0x1, 0x1a, 0x37, 0x2c, 0x6d, 0x76, 0x5b, 0x40, 0xd9, 0xc2, 0xef, 0xf4, 0xb5, 0xae, 0x83, 0x98, 0xac, 0xb7, 0x9a, 0x81, 0xc0, 0xdb, 0xf6, 0xed, 0x74, 0x6f, 0x42, 0x59, 0x18, 0x3, 0x2e, 0x35, 0x46, 0x5d, 0x70, 0x6b, 0x2a, 0x31, 0x1c, 0x7, 0x9e, 0x85, 0xa8, 0xb3, 0xf2, 0xe9, 0xc4, 0xdf, 0xeb, 0xf0, 0xdd, 0xc6, 0x87, 0x9c, 0xb1, 0xaa, 0x33, 0x28, 0x5, 0x1e, 0x5f, 0x44, 0x69, 0x72, 0x8f, 0x94, 0xb9, 0xa2, 0xe3, 0xf8, 0xd5, 0xce, 0x57, 0x4c, 0x61, 0x7a, 0x3b, 0x20, 0xd, 0x16, 0x22, 0x39, 0x14, 0xf, 0x4e, 0x55, 0x78, 0x63, 0xfa, 0xe1, 0xcc, 0xd7, 0x96, 0x8d, 0xa0, 0xbb, 0xc8, 0xd3, 0xfe, 0xe5, 0xa4, 0xbf, 0x92, 0x89, 0x10, 0xb, 0x26, 0x3d, 0x7c, 0x67, 0x4a, 0x51, 0x65, 0x7e, 0x53, 0x48, 0x9, 0x12, 0x3f, 0x24, 0xbd, 0xa6, 0x8b, 0x90, 0xd1, 0xca, 0xe7, 0xfc},
+ {0x0, 0x1c, 0x38, 0x24, 0x70, 0x6c, 0x48, 0x54, 0xe0, 0xfc, 0xd8, 0xc4, 0x90, 0x8c, 0xa8, 0xb4, 0xdd, 0xc1, 0xe5, 0xf9, 0xad, 0xb1, 0x95, 0x89, 0x3d, 0x21, 0x5, 0x19, 0x4d, 0x51, 0x75, 0x69, 0xa7, 0xbb, 0x9f, 0x83, 0xd7, 0xcb, 0xef, 0xf3, 0x47, 0x5b, 0x7f, 0x63, 0x37, 0x2b, 0xf, 0x13, 0x7a, 0x66, 0x42, 0x5e, 0xa, 0x16, 0x32, 0x2e, 0x9a, 0x86, 0xa2, 0xbe, 0xea, 0xf6, 0xd2, 0xce, 0x53, 0x4f, 0x6b, 0x77, 0x23, 0x3f, 0x1b, 0x7, 0xb3, 0xaf, 0x8b, 0x97, 0xc3, 0xdf, 0xfb, 0xe7, 0x8e, 0x92, 0xb6, 0xaa, 0xfe, 0xe2, 0xc6, 0xda, 0x6e, 0x72, 0x56, 0x4a, 0x1e, 0x2, 0x26, 0x3a, 0xf4, 0xe8, 0xcc, 0xd0, 0x84, 0x98, 0xbc, 0xa0, 0x14, 0x8, 0x2c, 0x30, 0x64, 0x78, 0x5c, 0x40, 0x29, 0x35, 0x11, 0xd, 0x59, 0x45, 0x61, 0x7d, 0xc9, 0xd5, 0xf1, 0xed, 0xb9, 0xa5, 0x81, 0x9d, 0xa6, 0xba, 0x9e, 0x82, 0xd6, 0xca, 0xee, 0xf2, 0x46, 0x5a, 0x7e, 0x62, 0x36, 0x2a, 0xe, 0x12, 0x7b, 0x67, 0x43, 0x5f, 0xb, 0x17, 0x33, 0x2f, 0x9b, 0x87, 0xa3, 0xbf, 0xeb, 0xf7, 0xd3, 0xcf, 0x1, 0x1d, 0x39, 0x25, 0x71, 0x6d, 0x49, 0x55, 0xe1, 0xfd, 0xd9, 0xc5, 0x91, 0x8d, 0xa9, 0xb5, 0xdc, 0xc0, 0xe4, 0xf8, 0xac, 0xb0, 0x94, 0x88, 0x3c, 0x20, 0x4, 0x18, 0x4c, 0x50, 0x74, 0x68, 0xf5, 0xe9, 0xcd, 0xd1, 0x85, 0x99, 0xbd, 0xa1, 0x15, 0x9, 0x2d, 0x31, 0x65, 0x79, 0x5d, 0x41, 0x28, 0x34, 0x10, 0xc, 0x58, 0x44, 0x60, 0x7c, 0xc8, 0xd4, 0xf0, 0xec, 0xb8, 0xa4, 0x80, 0x9c, 0x52, 0x4e, 0x6a, 0x76, 0x22, 0x3e, 0x1a, 0x6, 0xb2, 0xae, 0x8a, 0x96, 0xc2, 0xde, 0xfa, 0xe6, 0x8f, 0x93, 0xb7, 0xab, 0xff, 0xe3, 0xc7, 0xdb, 0x6f, 0x73, 0x57, 0x4b, 0x1f, 0x3, 0x27, 0x3b},
+ {0x0, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb, 0xcd, 0xd0, 0xf7, 0xea, 0xb9, 0xa4, 0x83, 0x9e, 0x25, 0x38, 0x1f, 0x2, 0x51, 0x4c, 0x6b, 0x76, 0x87, 0x9a, 0xbd, 0xa0, 0xf3, 0xee, 0xc9, 0xd4, 0x6f, 0x72, 0x55, 0x48, 0x1b, 0x6, 0x21, 0x3c, 0x4a, 0x57, 0x70, 0x6d, 0x3e, 0x23, 0x4, 0x19, 0xa2, 0xbf, 0x98, 0x85, 0xd6, 0xcb, 0xec, 0xf1, 0x13, 0xe, 0x29, 0x34, 0x67, 0x7a, 0x5d, 0x40, 0xfb, 0xe6, 0xc1, 0xdc, 0x8f, 0x92, 0xb5, 0xa8, 0xde, 0xc3, 0xe4, 0xf9, 0xaa, 0xb7, 0x90, 0x8d, 0x36, 0x2b, 0xc, 0x11, 0x42, 0x5f, 0x78, 0x65, 0x94, 0x89, 0xae, 0xb3, 0xe0, 0xfd, 0xda, 0xc7, 0x7c, 0x61, 0x46, 0x5b, 0x8, 0x15, 0x32, 0x2f, 0x59, 0x44, 0x63, 0x7e, 0x2d, 0x30, 0x17, 0xa, 0xb1, 0xac, 0x8b, 0x96, 0xc5, 0xd8, 0xff, 0xe2, 0x26, 0x3b, 0x1c, 0x1, 0x52, 0x4f, 0x68, 0x75, 0xce, 0xd3, 0xf4, 0xe9, 0xba, 0xa7, 0x80, 0x9d, 0xeb, 0xf6, 0xd1, 0xcc, 0x9f, 0x82, 0xa5, 0xb8, 0x3, 0x1e, 0x39, 0x24, 0x77, 0x6a, 0x4d, 0x50, 0xa1, 0xbc, 0x9b, 0x86, 0xd5, 0xc8, 0xef, 0xf2, 0x49, 0x54, 0x73, 0x6e, 0x3d, 0x20, 0x7, 0x1a, 0x6c, 0x71, 0x56, 0x4b, 0x18, 0x5, 0x22, 0x3f, 0x84, 0x99, 0xbe, 0xa3, 0xf0, 0xed, 0xca, 0xd7, 0x35, 0x28, 0xf, 0x12, 0x41, 0x5c, 0x7b, 0x66, 0xdd, 0xc0, 0xe7, 0xfa, 0xa9, 0xb4, 0x93, 0x8e, 0xf8, 0xe5, 0xc2, 0xdf, 0x8c, 0x91, 0xb6, 0xab, 0x10, 0xd, 0x2a, 0x37, 0x64, 0x79, 0x5e, 0x43, 0xb2, 0xaf, 0x88, 0x95, 0xc6, 0xdb, 0xfc, 0xe1, 0x5a, 0x47, 0x60, 0x7d, 0x2e, 0x33, 0x14, 0x9, 0x7f, 0x62, 0x45, 0x58, 0xb, 0x16, 0x31, 0x2c, 0x97, 0x8a, 0xad, 0xb0, 0xe3, 0xfe, 0xd9, 0xc4},
+ {0x0, 0x1e, 0x3c, 0x22, 0x78, 0x66, 0x44, 0x5a, 0xf0, 0xee, 0xcc, 0xd2, 0x88, 0x96, 0xb4, 0xaa, 0xfd, 0xe3, 0xc1, 0xdf, 0x85, 0x9b, 0xb9, 0xa7, 0xd, 0x13, 0x31, 0x2f, 0x75, 0x6b, 0x49, 0x57, 0xe7, 0xf9, 0xdb, 0xc5, 0x9f, 0x81, 0xa3, 0xbd, 0x17, 0x9, 0x2b, 0x35, 0x6f, 0x71, 0x53, 0x4d, 0x1a, 0x4, 0x26, 0x38, 0x62, 0x7c, 0x5e, 0x40, 0xea, 0xf4, 0xd6, 0xc8, 0x92, 0x8c, 0xae, 0xb0, 0xd3, 0xcd, 0xef, 0xf1, 0xab, 0xb5, 0x97, 0x89, 0x23, 0x3d, 0x1f, 0x1, 0x5b, 0x45, 0x67, 0x79, 0x2e, 0x30, 0x12, 0xc, 0x56, 0x48, 0x6a, 0x74, 0xde, 0xc0, 0xe2, 0xfc, 0xa6, 0xb8, 0x9a, 0x84, 0x34, 0x2a, 0x8, 0x16, 0x4c, 0x52, 0x70, 0x6e, 0xc4, 0xda, 0xf8, 0xe6, 0xbc, 0xa2, 0x80, 0x9e, 0xc9, 0xd7, 0xf5, 0xeb, 0xb1, 0xaf, 0x8d, 0x93, 0x39, 0x27, 0x5, 0x1b, 0x41, 0x5f, 0x7d, 0x63, 0xbb, 0xa5, 0x87, 0x99, 0xc3, 0xdd, 0xff, 0xe1, 0x4b, 0x55, 0x77, 0x69, 0x33, 0x2d, 0xf, 0x11, 0x46, 0x58, 0x7a, 0x64, 0x3e, 0x20, 0x2, 0x1c, 0xb6, 0xa8, 0x8a, 0x94, 0xce, 0xd0, 0xf2, 0xec, 0x5c, 0x42, 0x60, 0x7e, 0x24, 0x3a, 0x18, 0x6, 0xac, 0xb2, 0x90, 0x8e, 0xd4, 0xca, 0xe8, 0xf6, 0xa1, 0xbf, 0x9d, 0x83, 0xd9, 0xc7, 0xe5, 0xfb, 0x51, 0x4f, 0x6d, 0x73, 0x29, 0x37, 0x15, 0xb, 0x68, 0x76, 0x54, 0x4a, 0x10, 0xe, 0x2c, 0x32, 0x98, 0x86, 0xa4, 0xba, 0xe0, 0xfe, 0xdc, 0xc2, 0x95, 0x8b, 0xa9, 0xb7, 0xed, 0xf3, 0xd1, 0xcf, 0x65, 0x7b, 0x59, 0x47, 0x1d, 0x3, 0x21, 0x3f, 0x8f, 0x91, 0xb3, 0xad, 0xf7, 0xe9, 0xcb, 0xd5, 0x7f, 0x61, 0x43, 0x5d, 0x7, 0x19, 0x3b, 0x25, 0x72, 0x6c, 0x4e, 0x50, 0xa, 0x14, 0x36, 0x28, 0x82, 0x9c, 0xbe, 0xa0, 0xfa, 0xe4, 0xc6, 0xd8},
+ {0x0, 0x1f, 0x3e, 0x21, 0x7c, 0x63, 0x42, 0x5d, 0xf8, 0xe7, 0xc6, 0xd9, 0x84, 0x9b, 0xba, 0xa5, 0xed, 0xf2, 0xd3, 0xcc, 0x91, 0x8e, 0xaf, 0xb0, 0x15, 0xa, 0x2b, 0x34, 0x69, 0x76, 0x57, 0x48, 0xc7, 0xd8, 0xf9, 0xe6, 0xbb, 0xa4, 0x85, 0x9a, 0x3f, 0x20, 0x1, 0x1e, 0x43, 0x5c, 0x7d, 0x62, 0x2a, 0x35, 0x14, 0xb, 0x56, 0x49, 0x68, 0x77, 0xd2, 0xcd, 0xec, 0xf3, 0xae, 0xb1, 0x90, 0x8f, 0x93, 0x8c, 0xad, 0xb2, 0xef, 0xf0, 0xd1, 0xce, 0x6b, 0x74, 0x55, 0x4a, 0x17, 0x8, 0x29, 0x36, 0x7e, 0x61, 0x40, 0x5f, 0x2, 0x1d, 0x3c, 0x23, 0x86, 0x99, 0xb8, 0xa7, 0xfa, 0xe5, 0xc4, 0xdb, 0x54, 0x4b, 0x6a, 0x75, 0x28, 0x37, 0x16, 0x9, 0xac, 0xb3, 0x92, 0x8d, 0xd0, 0xcf, 0xee, 0xf1, 0xb9, 0xa6, 0x87, 0x98, 0xc5, 0xda, 0xfb, 0xe4, 0x41, 0x5e, 0x7f, 0x60, 0x3d, 0x22, 0x3, 0x1c, 0x3b, 0x24, 0x5, 0x1a, 0x47, 0x58, 0x79, 0x66, 0xc3, 0xdc, 0xfd, 0xe2, 0xbf, 0xa0, 0x81, 0x9e, 0xd6, 0xc9, 0xe8, 0xf7, 0xaa, 0xb5, 0x94, 0x8b, 0x2e, 0x31, 0x10, 0xf, 0x52, 0x4d, 0x6c, 0x73, 0xfc, 0xe3, 0xc2, 0xdd, 0x80, 0x9f, 0xbe, 0xa1, 0x4, 0x1b, 0x3a, 0x25, 0x78, 0x67, 0x46, 0x59, 0x11, 0xe, 0x2f, 0x30, 0x6d, 0x72, 0x53, 0x4c, 0xe9, 0xf6, 0xd7, 0xc8, 0x95, 0x8a, 0xab, 0xb4, 0xa8, 0xb7, 0x96, 0x89, 0xd4, 0xcb, 0xea, 0xf5, 0x50, 0x4f, 0x6e, 0x71, 0x2c, 0x33, 0x12, 0xd, 0x45, 0x5a, 0x7b, 0x64, 0x39, 0x26, 0x7, 0x18, 0xbd, 0xa2, 0x83, 0x9c, 0xc1, 0xde, 0xff, 0xe0, 0x6f, 0x70, 0x51, 0x4e, 0x13, 0xc, 0x2d, 0x32, 0x97, 0x88, 0xa9, 0xb6, 0xeb, 0xf4, 0xd5, 0xca, 0x82, 0x9d, 0xbc, 0xa3, 0xfe, 0xe1, 0xc0, 0xdf, 0x7a, 0x65, 0x44, 0x5b, 0x6, 0x19, 0x38, 0x27},
+ {0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x1d, 0x3d, 0x5d, 0x7d, 0x9d, 0xbd, 0xdd, 0xfd, 0x3a, 0x1a, 0x7a, 0x5a, 0xba, 0x9a, 0xfa, 0xda, 0x27, 0x7, 0x67, 0x47, 0xa7, 0x87, 0xe7, 0xc7, 0x74, 0x54, 0x34, 0x14, 0xf4, 0xd4, 0xb4, 0x94, 0x69, 0x49, 0x29, 0x9, 0xe9, 0xc9, 0xa9, 0x89, 0x4e, 0x6e, 0xe, 0x2e, 0xce, 0xee, 0x8e, 0xae, 0x53, 0x73, 0x13, 0x33, 0xd3, 0xf3, 0x93, 0xb3, 0xe8, 0xc8, 0xa8, 0x88, 0x68, 0x48, 0x28, 0x8, 0xf5, 0xd5, 0xb5, 0x95, 0x75, 0x55, 0x35, 0x15, 0xd2, 0xf2, 0x92, 0xb2, 0x52, 0x72, 0x12, 0x32, 0xcf, 0xef, 0x8f, 0xaf, 0x4f, 0x6f, 0xf, 0x2f, 0x9c, 0xbc, 0xdc, 0xfc, 0x1c, 0x3c, 0x5c, 0x7c, 0x81, 0xa1, 0xc1, 0xe1, 0x1, 0x21, 0x41, 0x61, 0xa6, 0x86, 0xe6, 0xc6, 0x26, 0x6, 0x66, 0x46, 0xbb, 0x9b, 0xfb, 0xdb, 0x3b, 0x1b, 0x7b, 0x5b, 0xcd, 0xed, 0x8d, 0xad, 0x4d, 0x6d, 0xd, 0x2d, 0xd0, 0xf0, 0x90, 0xb0, 0x50, 0x70, 0x10, 0x30, 0xf7, 0xd7, 0xb7, 0x97, 0x77, 0x57, 0x37, 0x17, 0xea, 0xca, 0xaa, 0x8a, 0x6a, 0x4a, 0x2a, 0xa, 0xb9, 0x99, 0xf9, 0xd9, 0x39, 0x19, 0x79, 0x59, 0xa4, 0x84, 0xe4, 0xc4, 0x24, 0x4, 0x64, 0x44, 0x83, 0xa3, 0xc3, 0xe3, 0x3, 0x23, 0x43, 0x63, 0x9e, 0xbe, 0xde, 0xfe, 0x1e, 0x3e, 0x5e, 0x7e, 0x25, 0x5, 0x65, 0x45, 0xa5, 0x85, 0xe5, 0xc5, 0x38, 0x18, 0x78, 0x58, 0xb8, 0x98, 0xf8, 0xd8, 0x1f, 0x3f, 0x5f, 0x7f, 0x9f, 0xbf, 0xdf, 0xff, 0x2, 0x22, 0x42, 0x62, 0x82, 0xa2, 0xc2, 0xe2, 0x51, 0x71, 0x11, 0x31, 0xd1, 0xf1, 0x91, 0xb1, 0x4c, 0x6c, 0xc, 0x2c, 0xcc, 0xec, 0x8c, 0xac, 0x6b, 0x4b, 0x2b, 0xb, 0xeb, 0xcb, 0xab, 0x8b, 0x76, 0x56, 0x36, 0x16, 0xf6, 0xd6, 0xb6, 0x96},
+ {0x0, 0x21, 0x42, 0x63, 0x84, 0xa5, 0xc6, 0xe7, 0x15, 0x34, 0x57, 0x76, 0x91, 0xb0, 0xd3, 0xf2, 0x2a, 0xb, 0x68, 0x49, 0xae, 0x8f, 0xec, 0xcd, 0x3f, 0x1e, 0x7d, 0x5c, 0xbb, 0x9a, 0xf9, 0xd8, 0x54, 0x75, 0x16, 0x37, 0xd0, 0xf1, 0x92, 0xb3, 0x41, 0x60, 0x3, 0x22, 0xc5, 0xe4, 0x87, 0xa6, 0x7e, 0x5f, 0x3c, 0x1d, 0xfa, 0xdb, 0xb8, 0x99, 0x6b, 0x4a, 0x29, 0x8, 0xef, 0xce, 0xad, 0x8c, 0xa8, 0x89, 0xea, 0xcb, 0x2c, 0xd, 0x6e, 0x4f, 0xbd, 0x9c, 0xff, 0xde, 0x39, 0x18, 0x7b, 0x5a, 0x82, 0xa3, 0xc0, 0xe1, 0x6, 0x27, 0x44, 0x65, 0x97, 0xb6, 0xd5, 0xf4, 0x13, 0x32, 0x51, 0x70, 0xfc, 0xdd, 0xbe, 0x9f, 0x78, 0x59, 0x3a, 0x1b, 0xe9, 0xc8, 0xab, 0x8a, 0x6d, 0x4c, 0x2f, 0xe, 0xd6, 0xf7, 0x94, 0xb5, 0x52, 0x73, 0x10, 0x31, 0xc3, 0xe2, 0x81, 0xa0, 0x47, 0x66, 0x5, 0x24, 0x4d, 0x6c, 0xf, 0x2e, 0xc9, 0xe8, 0x8b, 0xaa, 0x58, 0x79, 0x1a, 0x3b, 0xdc, 0xfd, 0x9e, 0xbf, 0x67, 0x46, 0x25, 0x4, 0xe3, 0xc2, 0xa1, 0x80, 0x72, 0x53, 0x30, 0x11, 0xf6, 0xd7, 0xb4, 0x95, 0x19, 0x38, 0x5b, 0x7a, 0x9d, 0xbc, 0xdf, 0xfe, 0xc, 0x2d, 0x4e, 0x6f, 0x88, 0xa9, 0xca, 0xeb, 0x33, 0x12, 0x71, 0x50, 0xb7, 0x96, 0xf5, 0xd4, 0x26, 0x7, 0x64, 0x45, 0xa2, 0x83, 0xe0, 0xc1, 0xe5, 0xc4, 0xa7, 0x86, 0x61, 0x40, 0x23, 0x2, 0xf0, 0xd1, 0xb2, 0x93, 0x74, 0x55, 0x36, 0x17, 0xcf, 0xee, 0x8d, 0xac, 0x4b, 0x6a, 0x9, 0x28, 0xda, 0xfb, 0x98, 0xb9, 0x5e, 0x7f, 0x1c, 0x3d, 0xb1, 0x90, 0xf3, 0xd2, 0x35, 0x14, 0x77, 0x56, 0xa4, 0x85, 0xe6, 0xc7, 0x20, 0x1, 0x62, 0x43, 0x9b, 0xba, 0xd9, 0xf8, 0x1f, 0x3e, 0x5d, 0x7c, 0x8e, 0xaf, 0xcc, 0xed, 0xa, 0x2b, 0x48, 0x69},
+ {0x0, 0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0xd, 0x2f, 0x49, 0x6b, 0x85, 0xa7, 0xc1, 0xe3, 0x1a, 0x38, 0x5e, 0x7c, 0x92, 0xb0, 0xd6, 0xf4, 0x17, 0x35, 0x53, 0x71, 0x9f, 0xbd, 0xdb, 0xf9, 0x34, 0x16, 0x70, 0x52, 0xbc, 0x9e, 0xf8, 0xda, 0x39, 0x1b, 0x7d, 0x5f, 0xb1, 0x93, 0xf5, 0xd7, 0x2e, 0xc, 0x6a, 0x48, 0xa6, 0x84, 0xe2, 0xc0, 0x23, 0x1, 0x67, 0x45, 0xab, 0x89, 0xef, 0xcd, 0x68, 0x4a, 0x2c, 0xe, 0xe0, 0xc2, 0xa4, 0x86, 0x65, 0x47, 0x21, 0x3, 0xed, 0xcf, 0xa9, 0x8b, 0x72, 0x50, 0x36, 0x14, 0xfa, 0xd8, 0xbe, 0x9c, 0x7f, 0x5d, 0x3b, 0x19, 0xf7, 0xd5, 0xb3, 0x91, 0x5c, 0x7e, 0x18, 0x3a, 0xd4, 0xf6, 0x90, 0xb2, 0x51, 0x73, 0x15, 0x37, 0xd9, 0xfb, 0x9d, 0xbf, 0x46, 0x64, 0x2, 0x20, 0xce, 0xec, 0x8a, 0xa8, 0x4b, 0x69, 0xf, 0x2d, 0xc3, 0xe1, 0x87, 0xa5, 0xd0, 0xf2, 0x94, 0xb6, 0x58, 0x7a, 0x1c, 0x3e, 0xdd, 0xff, 0x99, 0xbb, 0x55, 0x77, 0x11, 0x33, 0xca, 0xe8, 0x8e, 0xac, 0x42, 0x60, 0x6, 0x24, 0xc7, 0xe5, 0x83, 0xa1, 0x4f, 0x6d, 0xb, 0x29, 0xe4, 0xc6, 0xa0, 0x82, 0x6c, 0x4e, 0x28, 0xa, 0xe9, 0xcb, 0xad, 0x8f, 0x61, 0x43, 0x25, 0x7, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xf3, 0xd1, 0xb7, 0x95, 0x7b, 0x59, 0x3f, 0x1d, 0xb8, 0x9a, 0xfc, 0xde, 0x30, 0x12, 0x74, 0x56, 0xb5, 0x97, 0xf1, 0xd3, 0x3d, 0x1f, 0x79, 0x5b, 0xa2, 0x80, 0xe6, 0xc4, 0x2a, 0x8, 0x6e, 0x4c, 0xaf, 0x8d, 0xeb, 0xc9, 0x27, 0x5, 0x63, 0x41, 0x8c, 0xae, 0xc8, 0xea, 0x4, 0x26, 0x40, 0x62, 0x81, 0xa3, 0xc5, 0xe7, 0x9, 0x2b, 0x4d, 0x6f, 0x96, 0xb4, 0xd2, 0xf0, 0x1e, 0x3c, 0x5a, 0x78, 0x9b, 0xb9, 0xdf, 0xfd, 0x13, 0x31, 0x57, 0x75},
+ {0x0, 0x23, 0x46, 0x65, 0x8c, 0xaf, 0xca, 0xe9, 0x5, 0x26, 0x43, 0x60, 0x89, 0xaa, 0xcf, 0xec, 0xa, 0x29, 0x4c, 0x6f, 0x86, 0xa5, 0xc0, 0xe3, 0xf, 0x2c, 0x49, 0x6a, 0x83, 0xa0, 0xc5, 0xe6, 0x14, 0x37, 0x52, 0x71, 0x98, 0xbb, 0xde, 0xfd, 0x11, 0x32, 0x57, 0x74, 0x9d, 0xbe, 0xdb, 0xf8, 0x1e, 0x3d, 0x58, 0x7b, 0x92, 0xb1, 0xd4, 0xf7, 0x1b, 0x38, 0x5d, 0x7e, 0x97, 0xb4, 0xd1, 0xf2, 0x28, 0xb, 0x6e, 0x4d, 0xa4, 0x87, 0xe2, 0xc1, 0x2d, 0xe, 0x6b, 0x48, 0xa1, 0x82, 0xe7, 0xc4, 0x22, 0x1, 0x64, 0x47, 0xae, 0x8d, 0xe8, 0xcb, 0x27, 0x4, 0x61, 0x42, 0xab, 0x88, 0xed, 0xce, 0x3c, 0x1f, 0x7a, 0x59, 0xb0, 0x93, 0xf6, 0xd5, 0x39, 0x1a, 0x7f, 0x5c, 0xb5, 0x96, 0xf3, 0xd0, 0x36, 0x15, 0x70, 0x53, 0xba, 0x99, 0xfc, 0xdf, 0x33, 0x10, 0x75, 0x56, 0xbf, 0x9c, 0xf9, 0xda, 0x50, 0x73, 0x16, 0x35, 0xdc, 0xff, 0x9a, 0xb9, 0x55, 0x76, 0x13, 0x30, 0xd9, 0xfa, 0x9f, 0xbc, 0x5a, 0x79, 0x1c, 0x3f, 0xd6, 0xf5, 0x90, 0xb3, 0x5f, 0x7c, 0x19, 0x3a, 0xd3, 0xf0, 0x95, 0xb6, 0x44, 0x67, 0x2, 0x21, 0xc8, 0xeb, 0x8e, 0xad, 0x41, 0x62, 0x7, 0x24, 0xcd, 0xee, 0x8b, 0xa8, 0x4e, 0x6d, 0x8, 0x2b, 0xc2, 0xe1, 0x84, 0xa7, 0x4b, 0x68, 0xd, 0x2e, 0xc7, 0xe4, 0x81, 0xa2, 0x78, 0x5b, 0x3e, 0x1d, 0xf4, 0xd7, 0xb2, 0x91, 0x7d, 0x5e, 0x3b, 0x18, 0xf1, 0xd2, 0xb7, 0x94, 0x72, 0x51, 0x34, 0x17, 0xfe, 0xdd, 0xb8, 0x9b, 0x77, 0x54, 0x31, 0x12, 0xfb, 0xd8, 0xbd, 0x9e, 0x6c, 0x4f, 0x2a, 0x9, 0xe0, 0xc3, 0xa6, 0x85, 0x69, 0x4a, 0x2f, 0xc, 0xe5, 0xc6, 0xa3, 0x80, 0x66, 0x45, 0x20, 0x3, 0xea, 0xc9, 0xac, 0x8f, 0x63, 0x40, 0x25, 0x6, 0xef, 0xcc, 0xa9, 0x8a},
+ {0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0xd8, 0xfc, 0x3d, 0x19, 0x75, 0x51, 0xad, 0x89, 0xe5, 0xc1, 0x7a, 0x5e, 0x32, 0x16, 0xea, 0xce, 0xa2, 0x86, 0x47, 0x63, 0xf, 0x2b, 0xd7, 0xf3, 0x9f, 0xbb, 0xf4, 0xd0, 0xbc, 0x98, 0x64, 0x40, 0x2c, 0x8, 0xc9, 0xed, 0x81, 0xa5, 0x59, 0x7d, 0x11, 0x35, 0x8e, 0xaa, 0xc6, 0xe2, 0x1e, 0x3a, 0x56, 0x72, 0xb3, 0x97, 0xfb, 0xdf, 0x23, 0x7, 0x6b, 0x4f, 0xf5, 0xd1, 0xbd, 0x99, 0x65, 0x41, 0x2d, 0x9, 0xc8, 0xec, 0x80, 0xa4, 0x58, 0x7c, 0x10, 0x34, 0x8f, 0xab, 0xc7, 0xe3, 0x1f, 0x3b, 0x57, 0x73, 0xb2, 0x96, 0xfa, 0xde, 0x22, 0x6, 0x6a, 0x4e, 0x1, 0x25, 0x49, 0x6d, 0x91, 0xb5, 0xd9, 0xfd, 0x3c, 0x18, 0x74, 0x50, 0xac, 0x88, 0xe4, 0xc0, 0x7b, 0x5f, 0x33, 0x17, 0xeb, 0xcf, 0xa3, 0x87, 0x46, 0x62, 0xe, 0x2a, 0xd6, 0xf2, 0x9e, 0xba, 0xf7, 0xd3, 0xbf, 0x9b, 0x67, 0x43, 0x2f, 0xb, 0xca, 0xee, 0x82, 0xa6, 0x5a, 0x7e, 0x12, 0x36, 0x8d, 0xa9, 0xc5, 0xe1, 0x1d, 0x39, 0x55, 0x71, 0xb0, 0x94, 0xf8, 0xdc, 0x20, 0x4, 0x68, 0x4c, 0x3, 0x27, 0x4b, 0x6f, 0x93, 0xb7, 0xdb, 0xff, 0x3e, 0x1a, 0x76, 0x52, 0xae, 0x8a, 0xe6, 0xc2, 0x79, 0x5d, 0x31, 0x15, 0xe9, 0xcd, 0xa1, 0x85, 0x44, 0x60, 0xc, 0x28, 0xd4, 0xf0, 0x9c, 0xb8, 0x2, 0x26, 0x4a, 0x6e, 0x92, 0xb6, 0xda, 0xfe, 0x3f, 0x1b, 0x77, 0x53, 0xaf, 0x8b, 0xe7, 0xc3, 0x78, 0x5c, 0x30, 0x14, 0xe8, 0xcc, 0xa0, 0x84, 0x45, 0x61, 0xd, 0x29, 0xd5, 0xf1, 0x9d, 0xb9, 0xf6, 0xd2, 0xbe, 0x9a, 0x66, 0x42, 0x2e, 0xa, 0xcb, 0xef, 0x83, 0xa7, 0x5b, 0x7f, 0x13, 0x37, 0x8c, 0xa8, 0xc4, 0xe0, 0x1c, 0x38, 0x54, 0x70, 0xb1, 0x95, 0xf9, 0xdd, 0x21, 0x5, 0x69, 0x4d},
+ {0x0, 0x25, 0x4a, 0x6f, 0x94, 0xb1, 0xde, 0xfb, 0x35, 0x10, 0x7f, 0x5a, 0xa1, 0x84, 0xeb, 0xce, 0x6a, 0x4f, 0x20, 0x5, 0xfe, 0xdb, 0xb4, 0x91, 0x5f, 0x7a, 0x15, 0x30, 0xcb, 0xee, 0x81, 0xa4, 0xd4, 0xf1, 0x9e, 0xbb, 0x40, 0x65, 0xa, 0x2f, 0xe1, 0xc4, 0xab, 0x8e, 0x75, 0x50, 0x3f, 0x1a, 0xbe, 0x9b, 0xf4, 0xd1, 0x2a, 0xf, 0x60, 0x45, 0x8b, 0xae, 0xc1, 0xe4, 0x1f, 0x3a, 0x55, 0x70, 0xb5, 0x90, 0xff, 0xda, 0x21, 0x4, 0x6b, 0x4e, 0x80, 0xa5, 0xca, 0xef, 0x14, 0x31, 0x5e, 0x7b, 0xdf, 0xfa, 0x95, 0xb0, 0x4b, 0x6e, 0x1, 0x24, 0xea, 0xcf, 0xa0, 0x85, 0x7e, 0x5b, 0x34, 0x11, 0x61, 0x44, 0x2b, 0xe, 0xf5, 0xd0, 0xbf, 0x9a, 0x54, 0x71, 0x1e, 0x3b, 0xc0, 0xe5, 0x8a, 0xaf, 0xb, 0x2e, 0x41, 0x64, 0x9f, 0xba, 0xd5, 0xf0, 0x3e, 0x1b, 0x74, 0x51, 0xaa, 0x8f, 0xe0, 0xc5, 0x77, 0x52, 0x3d, 0x18, 0xe3, 0xc6, 0xa9, 0x8c, 0x42, 0x67, 0x8, 0x2d, 0xd6, 0xf3, 0x9c, 0xb9, 0x1d, 0x38, 0x57, 0x72, 0x89, 0xac, 0xc3, 0xe6, 0x28, 0xd, 0x62, 0x47, 0xbc, 0x99, 0xf6, 0xd3, 0xa3, 0x86, 0xe9, 0xcc, 0x37, 0x12, 0x7d, 0x58, 0x96, 0xb3, 0xdc, 0xf9, 0x2, 0x27, 0x48, 0x6d, 0xc9, 0xec, 0x83, 0xa6, 0x5d, 0x78, 0x17, 0x32, 0xfc, 0xd9, 0xb6, 0x93, 0x68, 0x4d, 0x22, 0x7, 0xc2, 0xe7, 0x88, 0xad, 0x56, 0x73, 0x1c, 0x39, 0xf7, 0xd2, 0xbd, 0x98, 0x63, 0x46, 0x29, 0xc, 0xa8, 0x8d, 0xe2, 0xc7, 0x3c, 0x19, 0x76, 0x53, 0x9d, 0xb8, 0xd7, 0xf2, 0x9, 0x2c, 0x43, 0x66, 0x16, 0x33, 0x5c, 0x79, 0x82, 0xa7, 0xc8, 0xed, 0x23, 0x6, 0x69, 0x4c, 0xb7, 0x92, 0xfd, 0xd8, 0x7c, 0x59, 0x36, 0x13, 0xe8, 0xcd, 0xa2, 0x87, 0x49, 0x6c, 0x3, 0x26, 0xdd, 0xf8, 0x97, 0xb2},
+ {0x0, 0x26, 0x4c, 0x6a, 0x98, 0xbe, 0xd4, 0xf2, 0x2d, 0xb, 0x61, 0x47, 0xb5, 0x93, 0xf9, 0xdf, 0x5a, 0x7c, 0x16, 0x30, 0xc2, 0xe4, 0x8e, 0xa8, 0x77, 0x51, 0x3b, 0x1d, 0xef, 0xc9, 0xa3, 0x85, 0xb4, 0x92, 0xf8, 0xde, 0x2c, 0xa, 0x60, 0x46, 0x99, 0xbf, 0xd5, 0xf3, 0x1, 0x27, 0x4d, 0x6b, 0xee, 0xc8, 0xa2, 0x84, 0x76, 0x50, 0x3a, 0x1c, 0xc3, 0xe5, 0x8f, 0xa9, 0x5b, 0x7d, 0x17, 0x31, 0x75, 0x53, 0x39, 0x1f, 0xed, 0xcb, 0xa1, 0x87, 0x58, 0x7e, 0x14, 0x32, 0xc0, 0xe6, 0x8c, 0xaa, 0x2f, 0x9, 0x63, 0x45, 0xb7, 0x91, 0xfb, 0xdd, 0x2, 0x24, 0x4e, 0x68, 0x9a, 0xbc, 0xd6, 0xf0, 0xc1, 0xe7, 0x8d, 0xab, 0x59, 0x7f, 0x15, 0x33, 0xec, 0xca, 0xa0, 0x86, 0x74, 0x52, 0x38, 0x1e, 0x9b, 0xbd, 0xd7, 0xf1, 0x3, 0x25, 0x4f, 0x69, 0xb6, 0x90, 0xfa, 0xdc, 0x2e, 0x8, 0x62, 0x44, 0xea, 0xcc, 0xa6, 0x80, 0x72, 0x54, 0x3e, 0x18, 0xc7, 0xe1, 0x8b, 0xad, 0x5f, 0x79, 0x13, 0x35, 0xb0, 0x96, 0xfc, 0xda, 0x28, 0xe, 0x64, 0x42, 0x9d, 0xbb, 0xd1, 0xf7, 0x5, 0x23, 0x49, 0x6f, 0x5e, 0x78, 0x12, 0x34, 0xc6, 0xe0, 0x8a, 0xac, 0x73, 0x55, 0x3f, 0x19, 0xeb, 0xcd, 0xa7, 0x81, 0x4, 0x22, 0x48, 0x6e, 0x9c, 0xba, 0xd0, 0xf6, 0x29, 0xf, 0x65, 0x43, 0xb1, 0x97, 0xfd, 0xdb, 0x9f, 0xb9, 0xd3, 0xf5, 0x7, 0x21, 0x4b, 0x6d, 0xb2, 0x94, 0xfe, 0xd8, 0x2a, 0xc, 0x66, 0x40, 0xc5, 0xe3, 0x89, 0xaf, 0x5d, 0x7b, 0x11, 0x37, 0xe8, 0xce, 0xa4, 0x82, 0x70, 0x56, 0x3c, 0x1a, 0x2b, 0xd, 0x67, 0x41, 0xb3, 0x95, 0xff, 0xd9, 0x6, 0x20, 0x4a, 0x6c, 0x9e, 0xb8, 0xd2, 0xf4, 0x71, 0x57, 0x3d, 0x1b, 0xe9, 0xcf, 0xa5, 0x83, 0x5c, 0x7a, 0x10, 0x36, 0xc4, 0xe2, 0x88, 0xae},
+ {0x0, 0x27, 0x4e, 0x69, 0x9c, 0xbb, 0xd2, 0xf5, 0x25, 0x2, 0x6b, 0x4c, 0xb9, 0x9e, 0xf7, 0xd0, 0x4a, 0x6d, 0x4, 0x23, 0xd6, 0xf1, 0x98, 0xbf, 0x6f, 0x48, 0x21, 0x6, 0xf3, 0xd4, 0xbd, 0x9a, 0x94, 0xb3, 0xda, 0xfd, 0x8, 0x2f, 0x46, 0x61, 0xb1, 0x96, 0xff, 0xd8, 0x2d, 0xa, 0x63, 0x44, 0xde, 0xf9, 0x90, 0xb7, 0x42, 0x65, 0xc, 0x2b, 0xfb, 0xdc, 0xb5, 0x92, 0x67, 0x40, 0x29, 0xe, 0x35, 0x12, 0x7b, 0x5c, 0xa9, 0x8e, 0xe7, 0xc0, 0x10, 0x37, 0x5e, 0x79, 0x8c, 0xab, 0xc2, 0xe5, 0x7f, 0x58, 0x31, 0x16, 0xe3, 0xc4, 0xad, 0x8a, 0x5a, 0x7d, 0x14, 0x33, 0xc6, 0xe1, 0x88, 0xaf, 0xa1, 0x86, 0xef, 0xc8, 0x3d, 0x1a, 0x73, 0x54, 0x84, 0xa3, 0xca, 0xed, 0x18, 0x3f, 0x56, 0x71, 0xeb, 0xcc, 0xa5, 0x82, 0x77, 0x50, 0x39, 0x1e, 0xce, 0xe9, 0x80, 0xa7, 0x52, 0x75, 0x1c, 0x3b, 0x6a, 0x4d, 0x24, 0x3, 0xf6, 0xd1, 0xb8, 0x9f, 0x4f, 0x68, 0x1, 0x26, 0xd3, 0xf4, 0x9d, 0xba, 0x20, 0x7, 0x6e, 0x49, 0xbc, 0x9b, 0xf2, 0xd5, 0x5, 0x22, 0x4b, 0x6c, 0x99, 0xbe, 0xd7, 0xf0, 0xfe, 0xd9, 0xb0, 0x97, 0x62, 0x45, 0x2c, 0xb, 0xdb, 0xfc, 0x95, 0xb2, 0x47, 0x60, 0x9, 0x2e, 0xb4, 0x93, 0xfa, 0xdd, 0x28, 0xf, 0x66, 0x41, 0x91, 0xb6, 0xdf, 0xf8, 0xd, 0x2a, 0x43, 0x64, 0x5f, 0x78, 0x11, 0x36, 0xc3, 0xe4, 0x8d, 0xaa, 0x7a, 0x5d, 0x34, 0x13, 0xe6, 0xc1, 0xa8, 0x8f, 0x15, 0x32, 0x5b, 0x7c, 0x89, 0xae, 0xc7, 0xe0, 0x30, 0x17, 0x7e, 0x59, 0xac, 0x8b, 0xe2, 0xc5, 0xcb, 0xec, 0x85, 0xa2, 0x57, 0x70, 0x19, 0x3e, 0xee, 0xc9, 0xa0, 0x87, 0x72, 0x55, 0x3c, 0x1b, 0x81, 0xa6, 0xcf, 0xe8, 0x1d, 0x3a, 0x53, 0x74, 0xa4, 0x83, 0xea, 0xcd, 0x38, 0x1f, 0x76, 0x51},
+ {0x0, 0x28, 0x50, 0x78, 0xa0, 0x88, 0xf0, 0xd8, 0x5d, 0x75, 0xd, 0x25, 0xfd, 0xd5, 0xad, 0x85, 0xba, 0x92, 0xea, 0xc2, 0x1a, 0x32, 0x4a, 0x62, 0xe7, 0xcf, 0xb7, 0x9f, 0x47, 0x6f, 0x17, 0x3f, 0x69, 0x41, 0x39, 0x11, 0xc9, 0xe1, 0x99, 0xb1, 0x34, 0x1c, 0x64, 0x4c, 0x94, 0xbc, 0xc4, 0xec, 0xd3, 0xfb, 0x83, 0xab, 0x73, 0x5b, 0x23, 0xb, 0x8e, 0xa6, 0xde, 0xf6, 0x2e, 0x6, 0x7e, 0x56, 0xd2, 0xfa, 0x82, 0xaa, 0x72, 0x5a, 0x22, 0xa, 0x8f, 0xa7, 0xdf, 0xf7, 0x2f, 0x7, 0x7f, 0x57, 0x68, 0x40, 0x38, 0x10, 0xc8, 0xe0, 0x98, 0xb0, 0x35, 0x1d, 0x65, 0x4d, 0x95, 0xbd, 0xc5, 0xed, 0xbb, 0x93, 0xeb, 0xc3, 0x1b, 0x33, 0x4b, 0x63, 0xe6, 0xce, 0xb6, 0x9e, 0x46, 0x6e, 0x16, 0x3e, 0x1, 0x29, 0x51, 0x79, 0xa1, 0x89, 0xf1, 0xd9, 0x5c, 0x74, 0xc, 0x24, 0xfc, 0xd4, 0xac, 0x84, 0xb9, 0x91, 0xe9, 0xc1, 0x19, 0x31, 0x49, 0x61, 0xe4, 0xcc, 0xb4, 0x9c, 0x44, 0x6c, 0x14, 0x3c, 0x3, 0x2b, 0x53, 0x7b, 0xa3, 0x8b, 0xf3, 0xdb, 0x5e, 0x76, 0xe, 0x26, 0xfe, 0xd6, 0xae, 0x86, 0xd0, 0xf8, 0x80, 0xa8, 0x70, 0x58, 0x20, 0x8, 0x8d, 0xa5, 0xdd, 0xf5, 0x2d, 0x5, 0x7d, 0x55, 0x6a, 0x42, 0x3a, 0x12, 0xca, 0xe2, 0x9a, 0xb2, 0x37, 0x1f, 0x67, 0x4f, 0x97, 0xbf, 0xc7, 0xef, 0x6b, 0x43, 0x3b, 0x13, 0xcb, 0xe3, 0x9b, 0xb3, 0x36, 0x1e, 0x66, 0x4e, 0x96, 0xbe, 0xc6, 0xee, 0xd1, 0xf9, 0x81, 0xa9, 0x71, 0x59, 0x21, 0x9, 0x8c, 0xa4, 0xdc, 0xf4, 0x2c, 0x4, 0x7c, 0x54, 0x2, 0x2a, 0x52, 0x7a, 0xa2, 0x8a, 0xf2, 0xda, 0x5f, 0x77, 0xf, 0x27, 0xff, 0xd7, 0xaf, 0x87, 0xb8, 0x90, 0xe8, 0xc0, 0x18, 0x30, 0x48, 0x60, 0xe5, 0xcd, 0xb5, 0x9d, 0x45, 0x6d, 0x15, 0x3d},
+ {0x0, 0x29, 0x52, 0x7b, 0xa4, 0x8d, 0xf6, 0xdf, 0x55, 0x7c, 0x7, 0x2e, 0xf1, 0xd8, 0xa3, 0x8a, 0xaa, 0x83, 0xf8, 0xd1, 0xe, 0x27, 0x5c, 0x75, 0xff, 0xd6, 0xad, 0x84, 0x5b, 0x72, 0x9, 0x20, 0x49, 0x60, 0x1b, 0x32, 0xed, 0xc4, 0xbf, 0x96, 0x1c, 0x35, 0x4e, 0x67, 0xb8, 0x91, 0xea, 0xc3, 0xe3, 0xca, 0xb1, 0x98, 0x47, 0x6e, 0x15, 0x3c, 0xb6, 0x9f, 0xe4, 0xcd, 0x12, 0x3b, 0x40, 0x69, 0x92, 0xbb, 0xc0, 0xe9, 0x36, 0x1f, 0x64, 0x4d, 0xc7, 0xee, 0x95, 0xbc, 0x63, 0x4a, 0x31, 0x18, 0x38, 0x11, 0x6a, 0x43, 0x9c, 0xb5, 0xce, 0xe7, 0x6d, 0x44, 0x3f, 0x16, 0xc9, 0xe0, 0x9b, 0xb2, 0xdb, 0xf2, 0x89, 0xa0, 0x7f, 0x56, 0x2d, 0x4, 0x8e, 0xa7, 0xdc, 0xf5, 0x2a, 0x3, 0x78, 0x51, 0x71, 0x58, 0x23, 0xa, 0xd5, 0xfc, 0x87, 0xae, 0x24, 0xd, 0x76, 0x5f, 0x80, 0xa9, 0xd2, 0xfb, 0x39, 0x10, 0x6b, 0x42, 0x9d, 0xb4, 0xcf, 0xe6, 0x6c, 0x45, 0x3e, 0x17, 0xc8, 0xe1, 0x9a, 0xb3, 0x93, 0xba, 0xc1, 0xe8, 0x37, 0x1e, 0x65, 0x4c, 0xc6, 0xef, 0x94, 0xbd, 0x62, 0x4b, 0x30, 0x19, 0x70, 0x59, 0x22, 0xb, 0xd4, 0xfd, 0x86, 0xaf, 0x25, 0xc, 0x77, 0x5e, 0x81, 0xa8, 0xd3, 0xfa, 0xda, 0xf3, 0x88, 0xa1, 0x7e, 0x57, 0x2c, 0x5, 0x8f, 0xa6, 0xdd, 0xf4, 0x2b, 0x2, 0x79, 0x50, 0xab, 0x82, 0xf9, 0xd0, 0xf, 0x26, 0x5d, 0x74, 0xfe, 0xd7, 0xac, 0x85, 0x5a, 0x73, 0x8, 0x21, 0x1, 0x28, 0x53, 0x7a, 0xa5, 0x8c, 0xf7, 0xde, 0x54, 0x7d, 0x6, 0x2f, 0xf0, 0xd9, 0xa2, 0x8b, 0xe2, 0xcb, 0xb0, 0x99, 0x46, 0x6f, 0x14, 0x3d, 0xb7, 0x9e, 0xe5, 0xcc, 0x13, 0x3a, 0x41, 0x68, 0x48, 0x61, 0x1a, 0x33, 0xec, 0xc5, 0xbe, 0x97, 0x1d, 0x34, 0x4f, 0x66, 0xb9, 0x90, 0xeb, 0xc2},
+ {0x0, 0x2a, 0x54, 0x7e, 0xa8, 0x82, 0xfc, 0xd6, 0x4d, 0x67, 0x19, 0x33, 0xe5, 0xcf, 0xb1, 0x9b, 0x9a, 0xb0, 0xce, 0xe4, 0x32, 0x18, 0x66, 0x4c, 0xd7, 0xfd, 0x83, 0xa9, 0x7f, 0x55, 0x2b, 0x1, 0x29, 0x3, 0x7d, 0x57, 0x81, 0xab, 0xd5, 0xff, 0x64, 0x4e, 0x30, 0x1a, 0xcc, 0xe6, 0x98, 0xb2, 0xb3, 0x99, 0xe7, 0xcd, 0x1b, 0x31, 0x4f, 0x65, 0xfe, 0xd4, 0xaa, 0x80, 0x56, 0x7c, 0x2, 0x28, 0x52, 0x78, 0x6, 0x2c, 0xfa, 0xd0, 0xae, 0x84, 0x1f, 0x35, 0x4b, 0x61, 0xb7, 0x9d, 0xe3, 0xc9, 0xc8, 0xe2, 0x9c, 0xb6, 0x60, 0x4a, 0x34, 0x1e, 0x85, 0xaf, 0xd1, 0xfb, 0x2d, 0x7, 0x79, 0x53, 0x7b, 0x51, 0x2f, 0x5, 0xd3, 0xf9, 0x87, 0xad, 0x36, 0x1c, 0x62, 0x48, 0x9e, 0xb4, 0xca, 0xe0, 0xe1, 0xcb, 0xb5, 0x9f, 0x49, 0x63, 0x1d, 0x37, 0xac, 0x86, 0xf8, 0xd2, 0x4, 0x2e, 0x50, 0x7a, 0xa4, 0x8e, 0xf0, 0xda, 0xc, 0x26, 0x58, 0x72, 0xe9, 0xc3, 0xbd, 0x97, 0x41, 0x6b, 0x15, 0x3f, 0x3e, 0x14, 0x6a, 0x40, 0x96, 0xbc, 0xc2, 0xe8, 0x73, 0x59, 0x27, 0xd, 0xdb, 0xf1, 0x8f, 0xa5, 0x8d, 0xa7, 0xd9, 0xf3, 0x25, 0xf, 0x71, 0x5b, 0xc0, 0xea, 0x94, 0xbe, 0x68, 0x42, 0x3c, 0x16, 0x17, 0x3d, 0x43, 0x69, 0xbf, 0x95, 0xeb, 0xc1, 0x5a, 0x70, 0xe, 0x24, 0xf2, 0xd8, 0xa6, 0x8c, 0xf6, 0xdc, 0xa2, 0x88, 0x5e, 0x74, 0xa, 0x20, 0xbb, 0x91, 0xef, 0xc5, 0x13, 0x39, 0x47, 0x6d, 0x6c, 0x46, 0x38, 0x12, 0xc4, 0xee, 0x90, 0xba, 0x21, 0xb, 0x75, 0x5f, 0x89, 0xa3, 0xdd, 0xf7, 0xdf, 0xf5, 0x8b, 0xa1, 0x77, 0x5d, 0x23, 0x9, 0x92, 0xb8, 0xc6, 0xec, 0x3a, 0x10, 0x6e, 0x44, 0x45, 0x6f, 0x11, 0x3b, 0xed, 0xc7, 0xb9, 0x93, 0x8, 0x22, 0x5c, 0x76, 0xa0, 0x8a, 0xf4, 0xde},
+ {0x0, 0x2b, 0x56, 0x7d, 0xac, 0x87, 0xfa, 0xd1, 0x45, 0x6e, 0x13, 0x38, 0xe9, 0xc2, 0xbf, 0x94, 0x8a, 0xa1, 0xdc, 0xf7, 0x26, 0xd, 0x70, 0x5b, 0xcf, 0xe4, 0x99, 0xb2, 0x63, 0x48, 0x35, 0x1e, 0x9, 0x22, 0x5f, 0x74, 0xa5, 0x8e, 0xf3, 0xd8, 0x4c, 0x67, 0x1a, 0x31, 0xe0, 0xcb, 0xb6, 0x9d, 0x83, 0xa8, 0xd5, 0xfe, 0x2f, 0x4, 0x79, 0x52, 0xc6, 0xed, 0x90, 0xbb, 0x6a, 0x41, 0x3c, 0x17, 0x12, 0x39, 0x44, 0x6f, 0xbe, 0x95, 0xe8, 0xc3, 0x57, 0x7c, 0x1, 0x2a, 0xfb, 0xd0, 0xad, 0x86, 0x98, 0xb3, 0xce, 0xe5, 0x34, 0x1f, 0x62, 0x49, 0xdd, 0xf6, 0x8b, 0xa0, 0x71, 0x5a, 0x27, 0xc, 0x1b, 0x30, 0x4d, 0x66, 0xb7, 0x9c, 0xe1, 0xca, 0x5e, 0x75, 0x8, 0x23, 0xf2, 0xd9, 0xa4, 0x8f, 0x91, 0xba, 0xc7, 0xec, 0x3d, 0x16, 0x6b, 0x40, 0xd4, 0xff, 0x82, 0xa9, 0x78, 0x53, 0x2e, 0x5, 0x24, 0xf, 0x72, 0x59, 0x88, 0xa3, 0xde, 0xf5, 0x61, 0x4a, 0x37, 0x1c, 0xcd, 0xe6, 0x9b, 0xb0, 0xae, 0x85, 0xf8, 0xd3, 0x2, 0x29, 0x54, 0x7f, 0xeb, 0xc0, 0xbd, 0x96, 0x47, 0x6c, 0x11, 0x3a, 0x2d, 0x6, 0x7b, 0x50, 0x81, 0xaa, 0xd7, 0xfc, 0x68, 0x43, 0x3e, 0x15, 0xc4, 0xef, 0x92, 0xb9, 0xa7, 0x8c, 0xf1, 0xda, 0xb, 0x20, 0x5d, 0x76, 0xe2, 0xc9, 0xb4, 0x9f, 0x4e, 0x65, 0x18, 0x33, 0x36, 0x1d, 0x60, 0x4b, 0x9a, 0xb1, 0xcc, 0xe7, 0x73, 0x58, 0x25, 0xe, 0xdf, 0xf4, 0x89, 0xa2, 0xbc, 0x97, 0xea, 0xc1, 0x10, 0x3b, 0x46, 0x6d, 0xf9, 0xd2, 0xaf, 0x84, 0x55, 0x7e, 0x3, 0x28, 0x3f, 0x14, 0x69, 0x42, 0x93, 0xb8, 0xc5, 0xee, 0x7a, 0x51, 0x2c, 0x7, 0xd6, 0xfd, 0x80, 0xab, 0xb5, 0x9e, 0xe3, 0xc8, 0x19, 0x32, 0x4f, 0x64, 0xf0, 0xdb, 0xa6, 0x8d, 0x5c, 0x77, 0xa, 0x21},
+ {0x0, 0x2c, 0x58, 0x74, 0xb0, 0x9c, 0xe8, 0xc4, 0x7d, 0x51, 0x25, 0x9, 0xcd, 0xe1, 0x95, 0xb9, 0xfa, 0xd6, 0xa2, 0x8e, 0x4a, 0x66, 0x12, 0x3e, 0x87, 0xab, 0xdf, 0xf3, 0x37, 0x1b, 0x6f, 0x43, 0xe9, 0xc5, 0xb1, 0x9d, 0x59, 0x75, 0x1, 0x2d, 0x94, 0xb8, 0xcc, 0xe0, 0x24, 0x8, 0x7c, 0x50, 0x13, 0x3f, 0x4b, 0x67, 0xa3, 0x8f, 0xfb, 0xd7, 0x6e, 0x42, 0x36, 0x1a, 0xde, 0xf2, 0x86, 0xaa, 0xcf, 0xe3, 0x97, 0xbb, 0x7f, 0x53, 0x27, 0xb, 0xb2, 0x9e, 0xea, 0xc6, 0x2, 0x2e, 0x5a, 0x76, 0x35, 0x19, 0x6d, 0x41, 0x85, 0xa9, 0xdd, 0xf1, 0x48, 0x64, 0x10, 0x3c, 0xf8, 0xd4, 0xa0, 0x8c, 0x26, 0xa, 0x7e, 0x52, 0x96, 0xba, 0xce, 0xe2, 0x5b, 0x77, 0x3, 0x2f, 0xeb, 0xc7, 0xb3, 0x9f, 0xdc, 0xf0, 0x84, 0xa8, 0x6c, 0x40, 0x34, 0x18, 0xa1, 0x8d, 0xf9, 0xd5, 0x11, 0x3d, 0x49, 0x65, 0x83, 0xaf, 0xdb, 0xf7, 0x33, 0x1f, 0x6b, 0x47, 0xfe, 0xd2, 0xa6, 0x8a, 0x4e, 0x62, 0x16, 0x3a, 0x79, 0x55, 0x21, 0xd, 0xc9, 0xe5, 0x91, 0xbd, 0x4, 0x28, 0x5c, 0x70, 0xb4, 0x98, 0xec, 0xc0, 0x6a, 0x46, 0x32, 0x1e, 0xda, 0xf6, 0x82, 0xae, 0x17, 0x3b, 0x4f, 0x63, 0xa7, 0x8b, 0xff, 0xd3, 0x90, 0xbc, 0xc8, 0xe4, 0x20, 0xc, 0x78, 0x54, 0xed, 0xc1, 0xb5, 0x99, 0x5d, 0x71, 0x5, 0x29, 0x4c, 0x60, 0x14, 0x38, 0xfc, 0xd0, 0xa4, 0x88, 0x31, 0x1d, 0x69, 0x45, 0x81, 0xad, 0xd9, 0xf5, 0xb6, 0x9a, 0xee, 0xc2, 0x6, 0x2a, 0x5e, 0x72, 0xcb, 0xe7, 0x93, 0xbf, 0x7b, 0x57, 0x23, 0xf, 0xa5, 0x89, 0xfd, 0xd1, 0x15, 0x39, 0x4d, 0x61, 0xd8, 0xf4, 0x80, 0xac, 0x68, 0x44, 0x30, 0x1c, 0x5f, 0x73, 0x7, 0x2b, 0xef, 0xc3, 0xb7, 0x9b, 0x22, 0xe, 0x7a, 0x56, 0x92, 0xbe, 0xca, 0xe6},
+ {0x0, 0x2d, 0x5a, 0x77, 0xb4, 0x99, 0xee, 0xc3, 0x75, 0x58, 0x2f, 0x2, 0xc1, 0xec, 0x9b, 0xb6, 0xea, 0xc7, 0xb0, 0x9d, 0x5e, 0x73, 0x4, 0x29, 0x9f, 0xb2, 0xc5, 0xe8, 0x2b, 0x6, 0x71, 0x5c, 0xc9, 0xe4, 0x93, 0xbe, 0x7d, 0x50, 0x27, 0xa, 0xbc, 0x91, 0xe6, 0xcb, 0x8, 0x25, 0x52, 0x7f, 0x23, 0xe, 0x79, 0x54, 0x97, 0xba, 0xcd, 0xe0, 0x56, 0x7b, 0xc, 0x21, 0xe2, 0xcf, 0xb8, 0x95, 0x8f, 0xa2, 0xd5, 0xf8, 0x3b, 0x16, 0x61, 0x4c, 0xfa, 0xd7, 0xa0, 0x8d, 0x4e, 0x63, 0x14, 0x39, 0x65, 0x48, 0x3f, 0x12, 0xd1, 0xfc, 0x8b, 0xa6, 0x10, 0x3d, 0x4a, 0x67, 0xa4, 0x89, 0xfe, 0xd3, 0x46, 0x6b, 0x1c, 0x31, 0xf2, 0xdf, 0xa8, 0x85, 0x33, 0x1e, 0x69, 0x44, 0x87, 0xaa, 0xdd, 0xf0, 0xac, 0x81, 0xf6, 0xdb, 0x18, 0x35, 0x42, 0x6f, 0xd9, 0xf4, 0x83, 0xae, 0x6d, 0x40, 0x37, 0x1a, 0x3, 0x2e, 0x59, 0x74, 0xb7, 0x9a, 0xed, 0xc0, 0x76, 0x5b, 0x2c, 0x1, 0xc2, 0xef, 0x98, 0xb5, 0xe9, 0xc4, 0xb3, 0x9e, 0x5d, 0x70, 0x7, 0x2a, 0x9c, 0xb1, 0xc6, 0xeb, 0x28, 0x5, 0x72, 0x5f, 0xca, 0xe7, 0x90, 0xbd, 0x7e, 0x53, 0x24, 0x9, 0xbf, 0x92, 0xe5, 0xc8, 0xb, 0x26, 0x51, 0x7c, 0x20, 0xd, 0x7a, 0x57, 0x94, 0xb9, 0xce, 0xe3, 0x55, 0x78, 0xf, 0x22, 0xe1, 0xcc, 0xbb, 0x96, 0x8c, 0xa1, 0xd6, 0xfb, 0x38, 0x15, 0x62, 0x4f, 0xf9, 0xd4, 0xa3, 0x8e, 0x4d, 0x60, 0x17, 0x3a, 0x66, 0x4b, 0x3c, 0x11, 0xd2, 0xff, 0x88, 0xa5, 0x13, 0x3e, 0x49, 0x64, 0xa7, 0x8a, 0xfd, 0xd0, 0x45, 0x68, 0x1f, 0x32, 0xf1, 0xdc, 0xab, 0x86, 0x30, 0x1d, 0x6a, 0x47, 0x84, 0xa9, 0xde, 0xf3, 0xaf, 0x82, 0xf5, 0xd8, 0x1b, 0x36, 0x41, 0x6c, 0xda, 0xf7, 0x80, 0xad, 0x6e, 0x43, 0x34, 0x19},
+ {0x0, 0x2e, 0x5c, 0x72, 0xb8, 0x96, 0xe4, 0xca, 0x6d, 0x43, 0x31, 0x1f, 0xd5, 0xfb, 0x89, 0xa7, 0xda, 0xf4, 0x86, 0xa8, 0x62, 0x4c, 0x3e, 0x10, 0xb7, 0x99, 0xeb, 0xc5, 0xf, 0x21, 0x53, 0x7d, 0xa9, 0x87, 0xf5, 0xdb, 0x11, 0x3f, 0x4d, 0x63, 0xc4, 0xea, 0x98, 0xb6, 0x7c, 0x52, 0x20, 0xe, 0x73, 0x5d, 0x2f, 0x1, 0xcb, 0xe5, 0x97, 0xb9, 0x1e, 0x30, 0x42, 0x6c, 0xa6, 0x88, 0xfa, 0xd4, 0x4f, 0x61, 0x13, 0x3d, 0xf7, 0xd9, 0xab, 0x85, 0x22, 0xc, 0x7e, 0x50, 0x9a, 0xb4, 0xc6, 0xe8, 0x95, 0xbb, 0xc9, 0xe7, 0x2d, 0x3, 0x71, 0x5f, 0xf8, 0xd6, 0xa4, 0x8a, 0x40, 0x6e, 0x1c, 0x32, 0xe6, 0xc8, 0xba, 0x94, 0x5e, 0x70, 0x2, 0x2c, 0x8b, 0xa5, 0xd7, 0xf9, 0x33, 0x1d, 0x6f, 0x41, 0x3c, 0x12, 0x60, 0x4e, 0x84, 0xaa, 0xd8, 0xf6, 0x51, 0x7f, 0xd, 0x23, 0xe9, 0xc7, 0xb5, 0x9b, 0x9e, 0xb0, 0xc2, 0xec, 0x26, 0x8, 0x7a, 0x54, 0xf3, 0xdd, 0xaf, 0x81, 0x4b, 0x65, 0x17, 0x39, 0x44, 0x6a, 0x18, 0x36, 0xfc, 0xd2, 0xa0, 0x8e, 0x29, 0x7, 0x75, 0x5b, 0x91, 0xbf, 0xcd, 0xe3, 0x37, 0x19, 0x6b, 0x45, 0x8f, 0xa1, 0xd3, 0xfd, 0x5a, 0x74, 0x6, 0x28, 0xe2, 0xcc, 0xbe, 0x90, 0xed, 0xc3, 0xb1, 0x9f, 0x55, 0x7b, 0x9, 0x27, 0x80, 0xae, 0xdc, 0xf2, 0x38, 0x16, 0x64, 0x4a, 0xd1, 0xff, 0x8d, 0xa3, 0x69, 0x47, 0x35, 0x1b, 0xbc, 0x92, 0xe0, 0xce, 0x4, 0x2a, 0x58, 0x76, 0xb, 0x25, 0x57, 0x79, 0xb3, 0x9d, 0xef, 0xc1, 0x66, 0x48, 0x3a, 0x14, 0xde, 0xf0, 0x82, 0xac, 0x78, 0x56, 0x24, 0xa, 0xc0, 0xee, 0x9c, 0xb2, 0x15, 0x3b, 0x49, 0x67, 0xad, 0x83, 0xf1, 0xdf, 0xa2, 0x8c, 0xfe, 0xd0, 0x1a, 0x34, 0x46, 0x68, 0xcf, 0xe1, 0x93, 0xbd, 0x77, 0x59, 0x2b, 0x5},
+ {0x0, 0x2f, 0x5e, 0x71, 0xbc, 0x93, 0xe2, 0xcd, 0x65, 0x4a, 0x3b, 0x14, 0xd9, 0xf6, 0x87, 0xa8, 0xca, 0xe5, 0x94, 0xbb, 0x76, 0x59, 0x28, 0x7, 0xaf, 0x80, 0xf1, 0xde, 0x13, 0x3c, 0x4d, 0x62, 0x89, 0xa6, 0xd7, 0xf8, 0x35, 0x1a, 0x6b, 0x44, 0xec, 0xc3, 0xb2, 0x9d, 0x50, 0x7f, 0xe, 0x21, 0x43, 0x6c, 0x1d, 0x32, 0xff, 0xd0, 0xa1, 0x8e, 0x26, 0x9, 0x78, 0x57, 0x9a, 0xb5, 0xc4, 0xeb, 0xf, 0x20, 0x51, 0x7e, 0xb3, 0x9c, 0xed, 0xc2, 0x6a, 0x45, 0x34, 0x1b, 0xd6, 0xf9, 0x88, 0xa7, 0xc5, 0xea, 0x9b, 0xb4, 0x79, 0x56, 0x27, 0x8, 0xa0, 0x8f, 0xfe, 0xd1, 0x1c, 0x33, 0x42, 0x6d, 0x86, 0xa9, 0xd8, 0xf7, 0x3a, 0x15, 0x64, 0x4b, 0xe3, 0xcc, 0xbd, 0x92, 0x5f, 0x70, 0x1, 0x2e, 0x4c, 0x63, 0x12, 0x3d, 0xf0, 0xdf, 0xae, 0x81, 0x29, 0x6, 0x77, 0x58, 0x95, 0xba, 0xcb, 0xe4, 0x1e, 0x31, 0x40, 0x6f, 0xa2, 0x8d, 0xfc, 0xd3, 0x7b, 0x54, 0x25, 0xa, 0xc7, 0xe8, 0x99, 0xb6, 0xd4, 0xfb, 0x8a, 0xa5, 0x68, 0x47, 0x36, 0x19, 0xb1, 0x9e, 0xef, 0xc0, 0xd, 0x22, 0x53, 0x7c, 0x97, 0xb8, 0xc9, 0xe6, 0x2b, 0x4, 0x75, 0x5a, 0xf2, 0xdd, 0xac, 0x83, 0x4e, 0x61, 0x10, 0x3f, 0x5d, 0x72, 0x3, 0x2c, 0xe1, 0xce, 0xbf, 0x90, 0x38, 0x17, 0x66, 0x49, 0x84, 0xab, 0xda, 0xf5, 0x11, 0x3e, 0x4f, 0x60, 0xad, 0x82, 0xf3, 0xdc, 0x74, 0x5b, 0x2a, 0x5, 0xc8, 0xe7, 0x96, 0xb9, 0xdb, 0xf4, 0x85, 0xaa, 0x67, 0x48, 0x39, 0x16, 0xbe, 0x91, 0xe0, 0xcf, 0x2, 0x2d, 0x5c, 0x73, 0x98, 0xb7, 0xc6, 0xe9, 0x24, 0xb, 0x7a, 0x55, 0xfd, 0xd2, 0xa3, 0x8c, 0x41, 0x6e, 0x1f, 0x30, 0x52, 0x7d, 0xc, 0x23, 0xee, 0xc1, 0xb0, 0x9f, 0x37, 0x18, 0x69, 0x46, 0x8b, 0xa4, 0xd5, 0xfa},
+ {0x0, 0x30, 0x60, 0x50, 0xc0, 0xf0, 0xa0, 0x90, 0x9d, 0xad, 0xfd, 0xcd, 0x5d, 0x6d, 0x3d, 0xd, 0x27, 0x17, 0x47, 0x77, 0xe7, 0xd7, 0x87, 0xb7, 0xba, 0x8a, 0xda, 0xea, 0x7a, 0x4a, 0x1a, 0x2a, 0x4e, 0x7e, 0x2e, 0x1e, 0x8e, 0xbe, 0xee, 0xde, 0xd3, 0xe3, 0xb3, 0x83, 0x13, 0x23, 0x73, 0x43, 0x69, 0x59, 0x9, 0x39, 0xa9, 0x99, 0xc9, 0xf9, 0xf4, 0xc4, 0x94, 0xa4, 0x34, 0x4, 0x54, 0x64, 0x9c, 0xac, 0xfc, 0xcc, 0x5c, 0x6c, 0x3c, 0xc, 0x1, 0x31, 0x61, 0x51, 0xc1, 0xf1, 0xa1, 0x91, 0xbb, 0x8b, 0xdb, 0xeb, 0x7b, 0x4b, 0x1b, 0x2b, 0x26, 0x16, 0x46, 0x76, 0xe6, 0xd6, 0x86, 0xb6, 0xd2, 0xe2, 0xb2, 0x82, 0x12, 0x22, 0x72, 0x42, 0x4f, 0x7f, 0x2f, 0x1f, 0x8f, 0xbf, 0xef, 0xdf, 0xf5, 0xc5, 0x95, 0xa5, 0x35, 0x5, 0x55, 0x65, 0x68, 0x58, 0x8, 0x38, 0xa8, 0x98, 0xc8, 0xf8, 0x25, 0x15, 0x45, 0x75, 0xe5, 0xd5, 0x85, 0xb5, 0xb8, 0x88, 0xd8, 0xe8, 0x78, 0x48, 0x18, 0x28, 0x2, 0x32, 0x62, 0x52, 0xc2, 0xf2, 0xa2, 0x92, 0x9f, 0xaf, 0xff, 0xcf, 0x5f, 0x6f, 0x3f, 0xf, 0x6b, 0x5b, 0xb, 0x3b, 0xab, 0x9b, 0xcb, 0xfb, 0xf6, 0xc6, 0x96, 0xa6, 0x36, 0x6, 0x56, 0x66, 0x4c, 0x7c, 0x2c, 0x1c, 0x8c, 0xbc, 0xec, 0xdc, 0xd1, 0xe1, 0xb1, 0x81, 0x11, 0x21, 0x71, 0x41, 0xb9, 0x89, 0xd9, 0xe9, 0x79, 0x49, 0x19, 0x29, 0x24, 0x14, 0x44, 0x74, 0xe4, 0xd4, 0x84, 0xb4, 0x9e, 0xae, 0xfe, 0xce, 0x5e, 0x6e, 0x3e, 0xe, 0x3, 0x33, 0x63, 0x53, 0xc3, 0xf3, 0xa3, 0x93, 0xf7, 0xc7, 0x97, 0xa7, 0x37, 0x7, 0x57, 0x67, 0x6a, 0x5a, 0xa, 0x3a, 0xaa, 0x9a, 0xca, 0xfa, 0xd0, 0xe0, 0xb0, 0x80, 0x10, 0x20, 0x70, 0x40, 0x4d, 0x7d, 0x2d, 0x1d, 0x8d, 0xbd, 0xed, 0xdd},
+ {0x0, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, 0x95, 0xa4, 0xf7, 0xc6, 0x51, 0x60, 0x33, 0x2, 0x37, 0x6, 0x55, 0x64, 0xf3, 0xc2, 0x91, 0xa0, 0xa2, 0x93, 0xc0, 0xf1, 0x66, 0x57, 0x4, 0x35, 0x6e, 0x5f, 0xc, 0x3d, 0xaa, 0x9b, 0xc8, 0xf9, 0xfb, 0xca, 0x99, 0xa8, 0x3f, 0xe, 0x5d, 0x6c, 0x59, 0x68, 0x3b, 0xa, 0x9d, 0xac, 0xff, 0xce, 0xcc, 0xfd, 0xae, 0x9f, 0x8, 0x39, 0x6a, 0x5b, 0xdc, 0xed, 0xbe, 0x8f, 0x18, 0x29, 0x7a, 0x4b, 0x49, 0x78, 0x2b, 0x1a, 0x8d, 0xbc, 0xef, 0xde, 0xeb, 0xda, 0x89, 0xb8, 0x2f, 0x1e, 0x4d, 0x7c, 0x7e, 0x4f, 0x1c, 0x2d, 0xba, 0x8b, 0xd8, 0xe9, 0xb2, 0x83, 0xd0, 0xe1, 0x76, 0x47, 0x14, 0x25, 0x27, 0x16, 0x45, 0x74, 0xe3, 0xd2, 0x81, 0xb0, 0x85, 0xb4, 0xe7, 0xd6, 0x41, 0x70, 0x23, 0x12, 0x10, 0x21, 0x72, 0x43, 0xd4, 0xe5, 0xb6, 0x87, 0xa5, 0x94, 0xc7, 0xf6, 0x61, 0x50, 0x3, 0x32, 0x30, 0x1, 0x52, 0x63, 0xf4, 0xc5, 0x96, 0xa7, 0x92, 0xa3, 0xf0, 0xc1, 0x56, 0x67, 0x34, 0x5, 0x7, 0x36, 0x65, 0x54, 0xc3, 0xf2, 0xa1, 0x90, 0xcb, 0xfa, 0xa9, 0x98, 0xf, 0x3e, 0x6d, 0x5c, 0x5e, 0x6f, 0x3c, 0xd, 0x9a, 0xab, 0xf8, 0xc9, 0xfc, 0xcd, 0x9e, 0xaf, 0x38, 0x9, 0x5a, 0x6b, 0x69, 0x58, 0xb, 0x3a, 0xad, 0x9c, 0xcf, 0xfe, 0x79, 0x48, 0x1b, 0x2a, 0xbd, 0x8c, 0xdf, 0xee, 0xec, 0xdd, 0x8e, 0xbf, 0x28, 0x19, 0x4a, 0x7b, 0x4e, 0x7f, 0x2c, 0x1d, 0x8a, 0xbb, 0xe8, 0xd9, 0xdb, 0xea, 0xb9, 0x88, 0x1f, 0x2e, 0x7d, 0x4c, 0x17, 0x26, 0x75, 0x44, 0xd3, 0xe2, 0xb1, 0x80, 0x82, 0xb3, 0xe0, 0xd1, 0x46, 0x77, 0x24, 0x15, 0x20, 0x11, 0x42, 0x73, 0xe4, 0xd5, 0x86, 0xb7, 0xb5, 0x84, 0xd7, 0xe6, 0x71, 0x40, 0x13, 0x22},
+ {0x0, 0x32, 0x64, 0x56, 0xc8, 0xfa, 0xac, 0x9e, 0x8d, 0xbf, 0xe9, 0xdb, 0x45, 0x77, 0x21, 0x13, 0x7, 0x35, 0x63, 0x51, 0xcf, 0xfd, 0xab, 0x99, 0x8a, 0xb8, 0xee, 0xdc, 0x42, 0x70, 0x26, 0x14, 0xe, 0x3c, 0x6a, 0x58, 0xc6, 0xf4, 0xa2, 0x90, 0x83, 0xb1, 0xe7, 0xd5, 0x4b, 0x79, 0x2f, 0x1d, 0x9, 0x3b, 0x6d, 0x5f, 0xc1, 0xf3, 0xa5, 0x97, 0x84, 0xb6, 0xe0, 0xd2, 0x4c, 0x7e, 0x28, 0x1a, 0x1c, 0x2e, 0x78, 0x4a, 0xd4, 0xe6, 0xb0, 0x82, 0x91, 0xa3, 0xf5, 0xc7, 0x59, 0x6b, 0x3d, 0xf, 0x1b, 0x29, 0x7f, 0x4d, 0xd3, 0xe1, 0xb7, 0x85, 0x96, 0xa4, 0xf2, 0xc0, 0x5e, 0x6c, 0x3a, 0x8, 0x12, 0x20, 0x76, 0x44, 0xda, 0xe8, 0xbe, 0x8c, 0x9f, 0xad, 0xfb, 0xc9, 0x57, 0x65, 0x33, 0x1, 0x15, 0x27, 0x71, 0x43, 0xdd, 0xef, 0xb9, 0x8b, 0x98, 0xaa, 0xfc, 0xce, 0x50, 0x62, 0x34, 0x6, 0x38, 0xa, 0x5c, 0x6e, 0xf0, 0xc2, 0x94, 0xa6, 0xb5, 0x87, 0xd1, 0xe3, 0x7d, 0x4f, 0x19, 0x2b, 0x3f, 0xd, 0x5b, 0x69, 0xf7, 0xc5, 0x93, 0xa1, 0xb2, 0x80, 0xd6, 0xe4, 0x7a, 0x48, 0x1e, 0x2c, 0x36, 0x4, 0x52, 0x60, 0xfe, 0xcc, 0x9a, 0xa8, 0xbb, 0x89, 0xdf, 0xed, 0x73, 0x41, 0x17, 0x25, 0x31, 0x3, 0x55, 0x67, 0xf9, 0xcb, 0x9d, 0xaf, 0xbc, 0x8e, 0xd8, 0xea, 0x74, 0x46, 0x10, 0x22, 0x24, 0x16, 0x40, 0x72, 0xec, 0xde, 0x88, 0xba, 0xa9, 0x9b, 0xcd, 0xff, 0x61, 0x53, 0x5, 0x37, 0x23, 0x11, 0x47, 0x75, 0xeb, 0xd9, 0x8f, 0xbd, 0xae, 0x9c, 0xca, 0xf8, 0x66, 0x54, 0x2, 0x30, 0x2a, 0x18, 0x4e, 0x7c, 0xe2, 0xd0, 0x86, 0xb4, 0xa7, 0x95, 0xc3, 0xf1, 0x6f, 0x5d, 0xb, 0x39, 0x2d, 0x1f, 0x49, 0x7b, 0xe5, 0xd7, 0x81, 0xb3, 0xa0, 0x92, 0xc4, 0xf6, 0x68, 0x5a, 0xc, 0x3e},
+ {0x0, 0x33, 0x66, 0x55, 0xcc, 0xff, 0xaa, 0x99, 0x85, 0xb6, 0xe3, 0xd0, 0x49, 0x7a, 0x2f, 0x1c, 0x17, 0x24, 0x71, 0x42, 0xdb, 0xe8, 0xbd, 0x8e, 0x92, 0xa1, 0xf4, 0xc7, 0x5e, 0x6d, 0x38, 0xb, 0x2e, 0x1d, 0x48, 0x7b, 0xe2, 0xd1, 0x84, 0xb7, 0xab, 0x98, 0xcd, 0xfe, 0x67, 0x54, 0x1, 0x32, 0x39, 0xa, 0x5f, 0x6c, 0xf5, 0xc6, 0x93, 0xa0, 0xbc, 0x8f, 0xda, 0xe9, 0x70, 0x43, 0x16, 0x25, 0x5c, 0x6f, 0x3a, 0x9, 0x90, 0xa3, 0xf6, 0xc5, 0xd9, 0xea, 0xbf, 0x8c, 0x15, 0x26, 0x73, 0x40, 0x4b, 0x78, 0x2d, 0x1e, 0x87, 0xb4, 0xe1, 0xd2, 0xce, 0xfd, 0xa8, 0x9b, 0x2, 0x31, 0x64, 0x57, 0x72, 0x41, 0x14, 0x27, 0xbe, 0x8d, 0xd8, 0xeb, 0xf7, 0xc4, 0x91, 0xa2, 0x3b, 0x8, 0x5d, 0x6e, 0x65, 0x56, 0x3, 0x30, 0xa9, 0x9a, 0xcf, 0xfc, 0xe0, 0xd3, 0x86, 0xb5, 0x2c, 0x1f, 0x4a, 0x79, 0xb8, 0x8b, 0xde, 0xed, 0x74, 0x47, 0x12, 0x21, 0x3d, 0xe, 0x5b, 0x68, 0xf1, 0xc2, 0x97, 0xa4, 0xaf, 0x9c, 0xc9, 0xfa, 0x63, 0x50, 0x5, 0x36, 0x2a, 0x19, 0x4c, 0x7f, 0xe6, 0xd5, 0x80, 0xb3, 0x96, 0xa5, 0xf0, 0xc3, 0x5a, 0x69, 0x3c, 0xf, 0x13, 0x20, 0x75, 0x46, 0xdf, 0xec, 0xb9, 0x8a, 0x81, 0xb2, 0xe7, 0xd4, 0x4d, 0x7e, 0x2b, 0x18, 0x4, 0x37, 0x62, 0x51, 0xc8, 0xfb, 0xae, 0x9d, 0xe4, 0xd7, 0x82, 0xb1, 0x28, 0x1b, 0x4e, 0x7d, 0x61, 0x52, 0x7, 0x34, 0xad, 0x9e, 0xcb, 0xf8, 0xf3, 0xc0, 0x95, 0xa6, 0x3f, 0xc, 0x59, 0x6a, 0x76, 0x45, 0x10, 0x23, 0xba, 0x89, 0xdc, 0xef, 0xca, 0xf9, 0xac, 0x9f, 0x6, 0x35, 0x60, 0x53, 0x4f, 0x7c, 0x29, 0x1a, 0x83, 0xb0, 0xe5, 0xd6, 0xdd, 0xee, 0xbb, 0x88, 0x11, 0x22, 0x77, 0x44, 0x58, 0x6b, 0x3e, 0xd, 0x94, 0xa7, 0xf2, 0xc1},
+ {0x0, 0x34, 0x68, 0x5c, 0xd0, 0xe4, 0xb8, 0x8c, 0xbd, 0x89, 0xd5, 0xe1, 0x6d, 0x59, 0x5, 0x31, 0x67, 0x53, 0xf, 0x3b, 0xb7, 0x83, 0xdf, 0xeb, 0xda, 0xee, 0xb2, 0x86, 0xa, 0x3e, 0x62, 0x56, 0xce, 0xfa, 0xa6, 0x92, 0x1e, 0x2a, 0x76, 0x42, 0x73, 0x47, 0x1b, 0x2f, 0xa3, 0x97, 0xcb, 0xff, 0xa9, 0x9d, 0xc1, 0xf5, 0x79, 0x4d, 0x11, 0x25, 0x14, 0x20, 0x7c, 0x48, 0xc4, 0xf0, 0xac, 0x98, 0x81, 0xb5, 0xe9, 0xdd, 0x51, 0x65, 0x39, 0xd, 0x3c, 0x8, 0x54, 0x60, 0xec, 0xd8, 0x84, 0xb0, 0xe6, 0xd2, 0x8e, 0xba, 0x36, 0x2, 0x5e, 0x6a, 0x5b, 0x6f, 0x33, 0x7, 0x8b, 0xbf, 0xe3, 0xd7, 0x4f, 0x7b, 0x27, 0x13, 0x9f, 0xab, 0xf7, 0xc3, 0xf2, 0xc6, 0x9a, 0xae, 0x22, 0x16, 0x4a, 0x7e, 0x28, 0x1c, 0x40, 0x74, 0xf8, 0xcc, 0x90, 0xa4, 0x95, 0xa1, 0xfd, 0xc9, 0x45, 0x71, 0x2d, 0x19, 0x1f, 0x2b, 0x77, 0x43, 0xcf, 0xfb, 0xa7, 0x93, 0xa2, 0x96, 0xca, 0xfe, 0x72, 0x46, 0x1a, 0x2e, 0x78, 0x4c, 0x10, 0x24, 0xa8, 0x9c, 0xc0, 0xf4, 0xc5, 0xf1, 0xad, 0x99, 0x15, 0x21, 0x7d, 0x49, 0xd1, 0xe5, 0xb9, 0x8d, 0x1, 0x35, 0x69, 0x5d, 0x6c, 0x58, 0x4, 0x30, 0xbc, 0x88, 0xd4, 0xe0, 0xb6, 0x82, 0xde, 0xea, 0x66, 0x52, 0xe, 0x3a, 0xb, 0x3f, 0x63, 0x57, 0xdb, 0xef, 0xb3, 0x87, 0x9e, 0xaa, 0xf6, 0xc2, 0x4e, 0x7a, 0x26, 0x12, 0x23, 0x17, 0x4b, 0x7f, 0xf3, 0xc7, 0x9b, 0xaf, 0xf9, 0xcd, 0x91, 0xa5, 0x29, 0x1d, 0x41, 0x75, 0x44, 0x70, 0x2c, 0x18, 0x94, 0xa0, 0xfc, 0xc8, 0x50, 0x64, 0x38, 0xc, 0x80, 0xb4, 0xe8, 0xdc, 0xed, 0xd9, 0x85, 0xb1, 0x3d, 0x9, 0x55, 0x61, 0x37, 0x3, 0x5f, 0x6b, 0xe7, 0xd3, 0x8f, 0xbb, 0x8a, 0xbe, 0xe2, 0xd6, 0x5a, 0x6e, 0x32, 0x6},
+ {0x0, 0x35, 0x6a, 0x5f, 0xd4, 0xe1, 0xbe, 0x8b, 0xb5, 0x80, 0xdf, 0xea, 0x61, 0x54, 0xb, 0x3e, 0x77, 0x42, 0x1d, 0x28, 0xa3, 0x96, 0xc9, 0xfc, 0xc2, 0xf7, 0xa8, 0x9d, 0x16, 0x23, 0x7c, 0x49, 0xee, 0xdb, 0x84, 0xb1, 0x3a, 0xf, 0x50, 0x65, 0x5b, 0x6e, 0x31, 0x4, 0x8f, 0xba, 0xe5, 0xd0, 0x99, 0xac, 0xf3, 0xc6, 0x4d, 0x78, 0x27, 0x12, 0x2c, 0x19, 0x46, 0x73, 0xf8, 0xcd, 0x92, 0xa7, 0xc1, 0xf4, 0xab, 0x9e, 0x15, 0x20, 0x7f, 0x4a, 0x74, 0x41, 0x1e, 0x2b, 0xa0, 0x95, 0xca, 0xff, 0xb6, 0x83, 0xdc, 0xe9, 0x62, 0x57, 0x8, 0x3d, 0x3, 0x36, 0x69, 0x5c, 0xd7, 0xe2, 0xbd, 0x88, 0x2f, 0x1a, 0x45, 0x70, 0xfb, 0xce, 0x91, 0xa4, 0x9a, 0xaf, 0xf0, 0xc5, 0x4e, 0x7b, 0x24, 0x11, 0x58, 0x6d, 0x32, 0x7, 0x8c, 0xb9, 0xe6, 0xd3, 0xed, 0xd8, 0x87, 0xb2, 0x39, 0xc, 0x53, 0x66, 0x9f, 0xaa, 0xf5, 0xc0, 0x4b, 0x7e, 0x21, 0x14, 0x2a, 0x1f, 0x40, 0x75, 0xfe, 0xcb, 0x94, 0xa1, 0xe8, 0xdd, 0x82, 0xb7, 0x3c, 0x9, 0x56, 0x63, 0x5d, 0x68, 0x37, 0x2, 0x89, 0xbc, 0xe3, 0xd6, 0x71, 0x44, 0x1b, 0x2e, 0xa5, 0x90, 0xcf, 0xfa, 0xc4, 0xf1, 0xae, 0x9b, 0x10, 0x25, 0x7a, 0x4f, 0x6, 0x33, 0x6c, 0x59, 0xd2, 0xe7, 0xb8, 0x8d, 0xb3, 0x86, 0xd9, 0xec, 0x67, 0x52, 0xd, 0x38, 0x5e, 0x6b, 0x34, 0x1, 0x8a, 0xbf, 0xe0, 0xd5, 0xeb, 0xde, 0x81, 0xb4, 0x3f, 0xa, 0x55, 0x60, 0x29, 0x1c, 0x43, 0x76, 0xfd, 0xc8, 0x97, 0xa2, 0x9c, 0xa9, 0xf6, 0xc3, 0x48, 0x7d, 0x22, 0x17, 0xb0, 0x85, 0xda, 0xef, 0x64, 0x51, 0xe, 0x3b, 0x5, 0x30, 0x6f, 0x5a, 0xd1, 0xe4, 0xbb, 0x8e, 0xc7, 0xf2, 0xad, 0x98, 0x13, 0x26, 0x79, 0x4c, 0x72, 0x47, 0x18, 0x2d, 0xa6, 0x93, 0xcc, 0xf9},
+ {0x0, 0x36, 0x6c, 0x5a, 0xd8, 0xee, 0xb4, 0x82, 0xad, 0x9b, 0xc1, 0xf7, 0x75, 0x43, 0x19, 0x2f, 0x47, 0x71, 0x2b, 0x1d, 0x9f, 0xa9, 0xf3, 0xc5, 0xea, 0xdc, 0x86, 0xb0, 0x32, 0x4, 0x5e, 0x68, 0x8e, 0xb8, 0xe2, 0xd4, 0x56, 0x60, 0x3a, 0xc, 0x23, 0x15, 0x4f, 0x79, 0xfb, 0xcd, 0x97, 0xa1, 0xc9, 0xff, 0xa5, 0x93, 0x11, 0x27, 0x7d, 0x4b, 0x64, 0x52, 0x8, 0x3e, 0xbc, 0x8a, 0xd0, 0xe6, 0x1, 0x37, 0x6d, 0x5b, 0xd9, 0xef, 0xb5, 0x83, 0xac, 0x9a, 0xc0, 0xf6, 0x74, 0x42, 0x18, 0x2e, 0x46, 0x70, 0x2a, 0x1c, 0x9e, 0xa8, 0xf2, 0xc4, 0xeb, 0xdd, 0x87, 0xb1, 0x33, 0x5, 0x5f, 0x69, 0x8f, 0xb9, 0xe3, 0xd5, 0x57, 0x61, 0x3b, 0xd, 0x22, 0x14, 0x4e, 0x78, 0xfa, 0xcc, 0x96, 0xa0, 0xc8, 0xfe, 0xa4, 0x92, 0x10, 0x26, 0x7c, 0x4a, 0x65, 0x53, 0x9, 0x3f, 0xbd, 0x8b, 0xd1, 0xe7, 0x2, 0x34, 0x6e, 0x58, 0xda, 0xec, 0xb6, 0x80, 0xaf, 0x99, 0xc3, 0xf5, 0x77, 0x41, 0x1b, 0x2d, 0x45, 0x73, 0x29, 0x1f, 0x9d, 0xab, 0xf1, 0xc7, 0xe8, 0xde, 0x84, 0xb2, 0x30, 0x6, 0x5c, 0x6a, 0x8c, 0xba, 0xe0, 0xd6, 0x54, 0x62, 0x38, 0xe, 0x21, 0x17, 0x4d, 0x7b, 0xf9, 0xcf, 0x95, 0xa3, 0xcb, 0xfd, 0xa7, 0x91, 0x13, 0x25, 0x7f, 0x49, 0x66, 0x50, 0xa, 0x3c, 0xbe, 0x88, 0xd2, 0xe4, 0x3, 0x35, 0x6f, 0x59, 0xdb, 0xed, 0xb7, 0x81, 0xae, 0x98, 0xc2, 0xf4, 0x76, 0x40, 0x1a, 0x2c, 0x44, 0x72, 0x28, 0x1e, 0x9c, 0xaa, 0xf0, 0xc6, 0xe9, 0xdf, 0x85, 0xb3, 0x31, 0x7, 0x5d, 0x6b, 0x8d, 0xbb, 0xe1, 0xd7, 0x55, 0x63, 0x39, 0xf, 0x20, 0x16, 0x4c, 0x7a, 0xf8, 0xce, 0x94, 0xa2, 0xca, 0xfc, 0xa6, 0x90, 0x12, 0x24, 0x7e, 0x48, 0x67, 0x51, 0xb, 0x3d, 0xbf, 0x89, 0xd3, 0xe5},
+ {0x0, 0x37, 0x6e, 0x59, 0xdc, 0xeb, 0xb2, 0x85, 0xa5, 0x92, 0xcb, 0xfc, 0x79, 0x4e, 0x17, 0x20, 0x57, 0x60, 0x39, 0xe, 0x8b, 0xbc, 0xe5, 0xd2, 0xf2, 0xc5, 0x9c, 0xab, 0x2e, 0x19, 0x40, 0x77, 0xae, 0x99, 0xc0, 0xf7, 0x72, 0x45, 0x1c, 0x2b, 0xb, 0x3c, 0x65, 0x52, 0xd7, 0xe0, 0xb9, 0x8e, 0xf9, 0xce, 0x97, 0xa0, 0x25, 0x12, 0x4b, 0x7c, 0x5c, 0x6b, 0x32, 0x5, 0x80, 0xb7, 0xee, 0xd9, 0x41, 0x76, 0x2f, 0x18, 0x9d, 0xaa, 0xf3, 0xc4, 0xe4, 0xd3, 0x8a, 0xbd, 0x38, 0xf, 0x56, 0x61, 0x16, 0x21, 0x78, 0x4f, 0xca, 0xfd, 0xa4, 0x93, 0xb3, 0x84, 0xdd, 0xea, 0x6f, 0x58, 0x1, 0x36, 0xef, 0xd8, 0x81, 0xb6, 0x33, 0x4, 0x5d, 0x6a, 0x4a, 0x7d, 0x24, 0x13, 0x96, 0xa1, 0xf8, 0xcf, 0xb8, 0x8f, 0xd6, 0xe1, 0x64, 0x53, 0xa, 0x3d, 0x1d, 0x2a, 0x73, 0x44, 0xc1, 0xf6, 0xaf, 0x98, 0x82, 0xb5, 0xec, 0xdb, 0x5e, 0x69, 0x30, 0x7, 0x27, 0x10, 0x49, 0x7e, 0xfb, 0xcc, 0x95, 0xa2, 0xd5, 0xe2, 0xbb, 0x8c, 0x9, 0x3e, 0x67, 0x50, 0x70, 0x47, 0x1e, 0x29, 0xac, 0x9b, 0xc2, 0xf5, 0x2c, 0x1b, 0x42, 0x75, 0xf0, 0xc7, 0x9e, 0xa9, 0x89, 0xbe, 0xe7, 0xd0, 0x55, 0x62, 0x3b, 0xc, 0x7b, 0x4c, 0x15, 0x22, 0xa7, 0x90, 0xc9, 0xfe, 0xde, 0xe9, 0xb0, 0x87, 0x2, 0x35, 0x6c, 0x5b, 0xc3, 0xf4, 0xad, 0x9a, 0x1f, 0x28, 0x71, 0x46, 0x66, 0x51, 0x8, 0x3f, 0xba, 0x8d, 0xd4, 0xe3, 0x94, 0xa3, 0xfa, 0xcd, 0x48, 0x7f, 0x26, 0x11, 0x31, 0x6, 0x5f, 0x68, 0xed, 0xda, 0x83, 0xb4, 0x6d, 0x5a, 0x3, 0x34, 0xb1, 0x86, 0xdf, 0xe8, 0xc8, 0xff, 0xa6, 0x91, 0x14, 0x23, 0x7a, 0x4d, 0x3a, 0xd, 0x54, 0x63, 0xe6, 0xd1, 0x88, 0xbf, 0x9f, 0xa8, 0xf1, 0xc6, 0x43, 0x74, 0x2d, 0x1a},
+ {0x0, 0x38, 0x70, 0x48, 0xe0, 0xd8, 0x90, 0xa8, 0xdd, 0xe5, 0xad, 0x95, 0x3d, 0x5, 0x4d, 0x75, 0xa7, 0x9f, 0xd7, 0xef, 0x47, 0x7f, 0x37, 0xf, 0x7a, 0x42, 0xa, 0x32, 0x9a, 0xa2, 0xea, 0xd2, 0x53, 0x6b, 0x23, 0x1b, 0xb3, 0x8b, 0xc3, 0xfb, 0x8e, 0xb6, 0xfe, 0xc6, 0x6e, 0x56, 0x1e, 0x26, 0xf4, 0xcc, 0x84, 0xbc, 0x14, 0x2c, 0x64, 0x5c, 0x29, 0x11, 0x59, 0x61, 0xc9, 0xf1, 0xb9, 0x81, 0xa6, 0x9e, 0xd6, 0xee, 0x46, 0x7e, 0x36, 0xe, 0x7b, 0x43, 0xb, 0x33, 0x9b, 0xa3, 0xeb, 0xd3, 0x1, 0x39, 0x71, 0x49, 0xe1, 0xd9, 0x91, 0xa9, 0xdc, 0xe4, 0xac, 0x94, 0x3c, 0x4, 0x4c, 0x74, 0xf5, 0xcd, 0x85, 0xbd, 0x15, 0x2d, 0x65, 0x5d, 0x28, 0x10, 0x58, 0x60, 0xc8, 0xf0, 0xb8, 0x80, 0x52, 0x6a, 0x22, 0x1a, 0xb2, 0x8a, 0xc2, 0xfa, 0x8f, 0xb7, 0xff, 0xc7, 0x6f, 0x57, 0x1f, 0x27, 0x51, 0x69, 0x21, 0x19, 0xb1, 0x89, 0xc1, 0xf9, 0x8c, 0xb4, 0xfc, 0xc4, 0x6c, 0x54, 0x1c, 0x24, 0xf6, 0xce, 0x86, 0xbe, 0x16, 0x2e, 0x66, 0x5e, 0x2b, 0x13, 0x5b, 0x63, 0xcb, 0xf3, 0xbb, 0x83, 0x2, 0x3a, 0x72, 0x4a, 0xe2, 0xda, 0x92, 0xaa, 0xdf, 0xe7, 0xaf, 0x97, 0x3f, 0x7, 0x4f, 0x77, 0xa5, 0x9d, 0xd5, 0xed, 0x45, 0x7d, 0x35, 0xd, 0x78, 0x40, 0x8, 0x30, 0x98, 0xa0, 0xe8, 0xd0, 0xf7, 0xcf, 0x87, 0xbf, 0x17, 0x2f, 0x67, 0x5f, 0x2a, 0x12, 0x5a, 0x62, 0xca, 0xf2, 0xba, 0x82, 0x50, 0x68, 0x20, 0x18, 0xb0, 0x88, 0xc0, 0xf8, 0x8d, 0xb5, 0xfd, 0xc5, 0x6d, 0x55, 0x1d, 0x25, 0xa4, 0x9c, 0xd4, 0xec, 0x44, 0x7c, 0x34, 0xc, 0x79, 0x41, 0x9, 0x31, 0x99, 0xa1, 0xe9, 0xd1, 0x3, 0x3b, 0x73, 0x4b, 0xe3, 0xdb, 0x93, 0xab, 0xde, 0xe6, 0xae, 0x96, 0x3e, 0x6, 0x4e, 0x76},
+ {0x0, 0x39, 0x72, 0x4b, 0xe4, 0xdd, 0x96, 0xaf, 0xd5, 0xec, 0xa7, 0x9e, 0x31, 0x8, 0x43, 0x7a, 0xb7, 0x8e, 0xc5, 0xfc, 0x53, 0x6a, 0x21, 0x18, 0x62, 0x5b, 0x10, 0x29, 0x86, 0xbf, 0xf4, 0xcd, 0x73, 0x4a, 0x1, 0x38, 0x97, 0xae, 0xe5, 0xdc, 0xa6, 0x9f, 0xd4, 0xed, 0x42, 0x7b, 0x30, 0x9, 0xc4, 0xfd, 0xb6, 0x8f, 0x20, 0x19, 0x52, 0x6b, 0x11, 0x28, 0x63, 0x5a, 0xf5, 0xcc, 0x87, 0xbe, 0xe6, 0xdf, 0x94, 0xad, 0x2, 0x3b, 0x70, 0x49, 0x33, 0xa, 0x41, 0x78, 0xd7, 0xee, 0xa5, 0x9c, 0x51, 0x68, 0x23, 0x1a, 0xb5, 0x8c, 0xc7, 0xfe, 0x84, 0xbd, 0xf6, 0xcf, 0x60, 0x59, 0x12, 0x2b, 0x95, 0xac, 0xe7, 0xde, 0x71, 0x48, 0x3, 0x3a, 0x40, 0x79, 0x32, 0xb, 0xa4, 0x9d, 0xd6, 0xef, 0x22, 0x1b, 0x50, 0x69, 0xc6, 0xff, 0xb4, 0x8d, 0xf7, 0xce, 0x85, 0xbc, 0x13, 0x2a, 0x61, 0x58, 0xd1, 0xe8, 0xa3, 0x9a, 0x35, 0xc, 0x47, 0x7e, 0x4, 0x3d, 0x76, 0x4f, 0xe0, 0xd9, 0x92, 0xab, 0x66, 0x5f, 0x14, 0x2d, 0x82, 0xbb, 0xf0, 0xc9, 0xb3, 0x8a, 0xc1, 0xf8, 0x57, 0x6e, 0x25, 0x1c, 0xa2, 0x9b, 0xd0, 0xe9, 0x46, 0x7f, 0x34, 0xd, 0x77, 0x4e, 0x5, 0x3c, 0x93, 0xaa, 0xe1, 0xd8, 0x15, 0x2c, 0x67, 0x5e, 0xf1, 0xc8, 0x83, 0xba, 0xc0, 0xf9, 0xb2, 0x8b, 0x24, 0x1d, 0x56, 0x6f, 0x37, 0xe, 0x45, 0x7c, 0xd3, 0xea, 0xa1, 0x98, 0xe2, 0xdb, 0x90, 0xa9, 0x6, 0x3f, 0x74, 0x4d, 0x80, 0xb9, 0xf2, 0xcb, 0x64, 0x5d, 0x16, 0x2f, 0x55, 0x6c, 0x27, 0x1e, 0xb1, 0x88, 0xc3, 0xfa, 0x44, 0x7d, 0x36, 0xf, 0xa0, 0x99, 0xd2, 0xeb, 0x91, 0xa8, 0xe3, 0xda, 0x75, 0x4c, 0x7, 0x3e, 0xf3, 0xca, 0x81, 0xb8, 0x17, 0x2e, 0x65, 0x5c, 0x26, 0x1f, 0x54, 0x6d, 0xc2, 0xfb, 0xb0, 0x89},
+ {0x0, 0x3a, 0x74, 0x4e, 0xe8, 0xd2, 0x9c, 0xa6, 0xcd, 0xf7, 0xb9, 0x83, 0x25, 0x1f, 0x51, 0x6b, 0x87, 0xbd, 0xf3, 0xc9, 0x6f, 0x55, 0x1b, 0x21, 0x4a, 0x70, 0x3e, 0x4, 0xa2, 0x98, 0xd6, 0xec, 0x13, 0x29, 0x67, 0x5d, 0xfb, 0xc1, 0x8f, 0xb5, 0xde, 0xe4, 0xaa, 0x90, 0x36, 0xc, 0x42, 0x78, 0x94, 0xae, 0xe0, 0xda, 0x7c, 0x46, 0x8, 0x32, 0x59, 0x63, 0x2d, 0x17, 0xb1, 0x8b, 0xc5, 0xff, 0x26, 0x1c, 0x52, 0x68, 0xce, 0xf4, 0xba, 0x80, 0xeb, 0xd1, 0x9f, 0xa5, 0x3, 0x39, 0x77, 0x4d, 0xa1, 0x9b, 0xd5, 0xef, 0x49, 0x73, 0x3d, 0x7, 0x6c, 0x56, 0x18, 0x22, 0x84, 0xbe, 0xf0, 0xca, 0x35, 0xf, 0x41, 0x7b, 0xdd, 0xe7, 0xa9, 0x93, 0xf8, 0xc2, 0x8c, 0xb6, 0x10, 0x2a, 0x64, 0x5e, 0xb2, 0x88, 0xc6, 0xfc, 0x5a, 0x60, 0x2e, 0x14, 0x7f, 0x45, 0xb, 0x31, 0x97, 0xad, 0xe3, 0xd9, 0x4c, 0x76, 0x38, 0x2, 0xa4, 0x9e, 0xd0, 0xea, 0x81, 0xbb, 0xf5, 0xcf, 0x69, 0x53, 0x1d, 0x27, 0xcb, 0xf1, 0xbf, 0x85, 0x23, 0x19, 0x57, 0x6d, 0x6, 0x3c, 0x72, 0x48, 0xee, 0xd4, 0x9a, 0xa0, 0x5f, 0x65, 0x2b, 0x11, 0xb7, 0x8d, 0xc3, 0xf9, 0x92, 0xa8, 0xe6, 0xdc, 0x7a, 0x40, 0xe, 0x34, 0xd8, 0xe2, 0xac, 0x96, 0x30, 0xa, 0x44, 0x7e, 0x15, 0x2f, 0x61, 0x5b, 0xfd, 0xc7, 0x89, 0xb3, 0x6a, 0x50, 0x1e, 0x24, 0x82, 0xb8, 0xf6, 0xcc, 0xa7, 0x9d, 0xd3, 0xe9, 0x4f, 0x75, 0x3b, 0x1, 0xed, 0xd7, 0x99, 0xa3, 0x5, 0x3f, 0x71, 0x4b, 0x20, 0x1a, 0x54, 0x6e, 0xc8, 0xf2, 0xbc, 0x86, 0x79, 0x43, 0xd, 0x37, 0x91, 0xab, 0xe5, 0xdf, 0xb4, 0x8e, 0xc0, 0xfa, 0x5c, 0x66, 0x28, 0x12, 0xfe, 0xc4, 0x8a, 0xb0, 0x16, 0x2c, 0x62, 0x58, 0x33, 0x9, 0x47, 0x7d, 0xdb, 0xe1, 0xaf, 0x95},
+ {0x0, 0x3b, 0x76, 0x4d, 0xec, 0xd7, 0x9a, 0xa1, 0xc5, 0xfe, 0xb3, 0x88, 0x29, 0x12, 0x5f, 0x64, 0x97, 0xac, 0xe1, 0xda, 0x7b, 0x40, 0xd, 0x36, 0x52, 0x69, 0x24, 0x1f, 0xbe, 0x85, 0xc8, 0xf3, 0x33, 0x8, 0x45, 0x7e, 0xdf, 0xe4, 0xa9, 0x92, 0xf6, 0xcd, 0x80, 0xbb, 0x1a, 0x21, 0x6c, 0x57, 0xa4, 0x9f, 0xd2, 0xe9, 0x48, 0x73, 0x3e, 0x5, 0x61, 0x5a, 0x17, 0x2c, 0x8d, 0xb6, 0xfb, 0xc0, 0x66, 0x5d, 0x10, 0x2b, 0x8a, 0xb1, 0xfc, 0xc7, 0xa3, 0x98, 0xd5, 0xee, 0x4f, 0x74, 0x39, 0x2, 0xf1, 0xca, 0x87, 0xbc, 0x1d, 0x26, 0x6b, 0x50, 0x34, 0xf, 0x42, 0x79, 0xd8, 0xe3, 0xae, 0x95, 0x55, 0x6e, 0x23, 0x18, 0xb9, 0x82, 0xcf, 0xf4, 0x90, 0xab, 0xe6, 0xdd, 0x7c, 0x47, 0xa, 0x31, 0xc2, 0xf9, 0xb4, 0x8f, 0x2e, 0x15, 0x58, 0x63, 0x7, 0x3c, 0x71, 0x4a, 0xeb, 0xd0, 0x9d, 0xa6, 0xcc, 0xf7, 0xba, 0x81, 0x20, 0x1b, 0x56, 0x6d, 0x9, 0x32, 0x7f, 0x44, 0xe5, 0xde, 0x93, 0xa8, 0x5b, 0x60, 0x2d, 0x16, 0xb7, 0x8c, 0xc1, 0xfa, 0x9e, 0xa5, 0xe8, 0xd3, 0x72, 0x49, 0x4, 0x3f, 0xff, 0xc4, 0x89, 0xb2, 0x13, 0x28, 0x65, 0x5e, 0x3a, 0x1, 0x4c, 0x77, 0xd6, 0xed, 0xa0, 0x9b, 0x68, 0x53, 0x1e, 0x25, 0x84, 0xbf, 0xf2, 0xc9, 0xad, 0x96, 0xdb, 0xe0, 0x41, 0x7a, 0x37, 0xc, 0xaa, 0x91, 0xdc, 0xe7, 0x46, 0x7d, 0x30, 0xb, 0x6f, 0x54, 0x19, 0x22, 0x83, 0xb8, 0xf5, 0xce, 0x3d, 0x6, 0x4b, 0x70, 0xd1, 0xea, 0xa7, 0x9c, 0xf8, 0xc3, 0x8e, 0xb5, 0x14, 0x2f, 0x62, 0x59, 0x99, 0xa2, 0xef, 0xd4, 0x75, 0x4e, 0x3, 0x38, 0x5c, 0x67, 0x2a, 0x11, 0xb0, 0x8b, 0xc6, 0xfd, 0xe, 0x35, 0x78, 0x43, 0xe2, 0xd9, 0x94, 0xaf, 0xcb, 0xf0, 0xbd, 0x86, 0x27, 0x1c, 0x51, 0x6a},
+ {0x0, 0x3c, 0x78, 0x44, 0xf0, 0xcc, 0x88, 0xb4, 0xfd, 0xc1, 0x85, 0xb9, 0xd, 0x31, 0x75, 0x49, 0xe7, 0xdb, 0x9f, 0xa3, 0x17, 0x2b, 0x6f, 0x53, 0x1a, 0x26, 0x62, 0x5e, 0xea, 0xd6, 0x92, 0xae, 0xd3, 0xef, 0xab, 0x97, 0x23, 0x1f, 0x5b, 0x67, 0x2e, 0x12, 0x56, 0x6a, 0xde, 0xe2, 0xa6, 0x9a, 0x34, 0x8, 0x4c, 0x70, 0xc4, 0xf8, 0xbc, 0x80, 0xc9, 0xf5, 0xb1, 0x8d, 0x39, 0x5, 0x41, 0x7d, 0xbb, 0x87, 0xc3, 0xff, 0x4b, 0x77, 0x33, 0xf, 0x46, 0x7a, 0x3e, 0x2, 0xb6, 0x8a, 0xce, 0xf2, 0x5c, 0x60, 0x24, 0x18, 0xac, 0x90, 0xd4, 0xe8, 0xa1, 0x9d, 0xd9, 0xe5, 0x51, 0x6d, 0x29, 0x15, 0x68, 0x54, 0x10, 0x2c, 0x98, 0xa4, 0xe0, 0xdc, 0x95, 0xa9, 0xed, 0xd1, 0x65, 0x59, 0x1d, 0x21, 0x8f, 0xb3, 0xf7, 0xcb, 0x7f, 0x43, 0x7, 0x3b, 0x72, 0x4e, 0xa, 0x36, 0x82, 0xbe, 0xfa, 0xc6, 0x6b, 0x57, 0x13, 0x2f, 0x9b, 0xa7, 0xe3, 0xdf, 0x96, 0xaa, 0xee, 0xd2, 0x66, 0x5a, 0x1e, 0x22, 0x8c, 0xb0, 0xf4, 0xc8, 0x7c, 0x40, 0x4, 0x38, 0x71, 0x4d, 0x9, 0x35, 0x81, 0xbd, 0xf9, 0xc5, 0xb8, 0x84, 0xc0, 0xfc, 0x48, 0x74, 0x30, 0xc, 0x45, 0x79, 0x3d, 0x1, 0xb5, 0x89, 0xcd, 0xf1, 0x5f, 0x63, 0x27, 0x1b, 0xaf, 0x93, 0xd7, 0xeb, 0xa2, 0x9e, 0xda, 0xe6, 0x52, 0x6e, 0x2a, 0x16, 0xd0, 0xec, 0xa8, 0x94, 0x20, 0x1c, 0x58, 0x64, 0x2d, 0x11, 0x55, 0x69, 0xdd, 0xe1, 0xa5, 0x99, 0x37, 0xb, 0x4f, 0x73, 0xc7, 0xfb, 0xbf, 0x83, 0xca, 0xf6, 0xb2, 0x8e, 0x3a, 0x6, 0x42, 0x7e, 0x3, 0x3f, 0x7b, 0x47, 0xf3, 0xcf, 0x8b, 0xb7, 0xfe, 0xc2, 0x86, 0xba, 0xe, 0x32, 0x76, 0x4a, 0xe4, 0xd8, 0x9c, 0xa0, 0x14, 0x28, 0x6c, 0x50, 0x19, 0x25, 0x61, 0x5d, 0xe9, 0xd5, 0x91, 0xad},
+ {0x0, 0x3d, 0x7a, 0x47, 0xf4, 0xc9, 0x8e, 0xb3, 0xf5, 0xc8, 0x8f, 0xb2, 0x1, 0x3c, 0x7b, 0x46, 0xf7, 0xca, 0x8d, 0xb0, 0x3, 0x3e, 0x79, 0x44, 0x2, 0x3f, 0x78, 0x45, 0xf6, 0xcb, 0x8c, 0xb1, 0xf3, 0xce, 0x89, 0xb4, 0x7, 0x3a, 0x7d, 0x40, 0x6, 0x3b, 0x7c, 0x41, 0xf2, 0xcf, 0x88, 0xb5, 0x4, 0x39, 0x7e, 0x43, 0xf0, 0xcd, 0x8a, 0xb7, 0xf1, 0xcc, 0x8b, 0xb6, 0x5, 0x38, 0x7f, 0x42, 0xfb, 0xc6, 0x81, 0xbc, 0xf, 0x32, 0x75, 0x48, 0xe, 0x33, 0x74, 0x49, 0xfa, 0xc7, 0x80, 0xbd, 0xc, 0x31, 0x76, 0x4b, 0xf8, 0xc5, 0x82, 0xbf, 0xf9, 0xc4, 0x83, 0xbe, 0xd, 0x30, 0x77, 0x4a, 0x8, 0x35, 0x72, 0x4f, 0xfc, 0xc1, 0x86, 0xbb, 0xfd, 0xc0, 0x87, 0xba, 0x9, 0x34, 0x73, 0x4e, 0xff, 0xc2, 0x85, 0xb8, 0xb, 0x36, 0x71, 0x4c, 0xa, 0x37, 0x70, 0x4d, 0xfe, 0xc3, 0x84, 0xb9, 0xeb, 0xd6, 0x91, 0xac, 0x1f, 0x22, 0x65, 0x58, 0x1e, 0x23, 0x64, 0x59, 0xea, 0xd7, 0x90, 0xad, 0x1c, 0x21, 0x66, 0x5b, 0xe8, 0xd5, 0x92, 0xaf, 0xe9, 0xd4, 0x93, 0xae, 0x1d, 0x20, 0x67, 0x5a, 0x18, 0x25, 0x62, 0x5f, 0xec, 0xd1, 0x96, 0xab, 0xed, 0xd0, 0x97, 0xaa, 0x19, 0x24, 0x63, 0x5e, 0xef, 0xd2, 0x95, 0xa8, 0x1b, 0x26, 0x61, 0x5c, 0x1a, 0x27, 0x60, 0x5d, 0xee, 0xd3, 0x94, 0xa9, 0x10, 0x2d, 0x6a, 0x57, 0xe4, 0xd9, 0x9e, 0xa3, 0xe5, 0xd8, 0x9f, 0xa2, 0x11, 0x2c, 0x6b, 0x56, 0xe7, 0xda, 0x9d, 0xa0, 0x13, 0x2e, 0x69, 0x54, 0x12, 0x2f, 0x68, 0x55, 0xe6, 0xdb, 0x9c, 0xa1, 0xe3, 0xde, 0x99, 0xa4, 0x17, 0x2a, 0x6d, 0x50, 0x16, 0x2b, 0x6c, 0x51, 0xe2, 0xdf, 0x98, 0xa5, 0x14, 0x29, 0x6e, 0x53, 0xe0, 0xdd, 0x9a, 0xa7, 0xe1, 0xdc, 0x9b, 0xa6, 0x15, 0x28, 0x6f, 0x52},
+ {0x0, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0xed, 0xd3, 0x91, 0xaf, 0x15, 0x2b, 0x69, 0x57, 0xc7, 0xf9, 0xbb, 0x85, 0x3f, 0x1, 0x43, 0x7d, 0x2a, 0x14, 0x56, 0x68, 0xd2, 0xec, 0xae, 0x90, 0x93, 0xad, 0xef, 0xd1, 0x6b, 0x55, 0x17, 0x29, 0x7e, 0x40, 0x2, 0x3c, 0x86, 0xb8, 0xfa, 0xc4, 0x54, 0x6a, 0x28, 0x16, 0xac, 0x92, 0xd0, 0xee, 0xb9, 0x87, 0xc5, 0xfb, 0x41, 0x7f, 0x3d, 0x3, 0x3b, 0x5, 0x47, 0x79, 0xc3, 0xfd, 0xbf, 0x81, 0xd6, 0xe8, 0xaa, 0x94, 0x2e, 0x10, 0x52, 0x6c, 0xfc, 0xc2, 0x80, 0xbe, 0x4, 0x3a, 0x78, 0x46, 0x11, 0x2f, 0x6d, 0x53, 0xe9, 0xd7, 0x95, 0xab, 0xa8, 0x96, 0xd4, 0xea, 0x50, 0x6e, 0x2c, 0x12, 0x45, 0x7b, 0x39, 0x7, 0xbd, 0x83, 0xc1, 0xff, 0x6f, 0x51, 0x13, 0x2d, 0x97, 0xa9, 0xeb, 0xd5, 0x82, 0xbc, 0xfe, 0xc0, 0x7a, 0x44, 0x6, 0x38, 0x76, 0x48, 0xa, 0x34, 0x8e, 0xb0, 0xf2, 0xcc, 0x9b, 0xa5, 0xe7, 0xd9, 0x63, 0x5d, 0x1f, 0x21, 0xb1, 0x8f, 0xcd, 0xf3, 0x49, 0x77, 0x35, 0xb, 0x5c, 0x62, 0x20, 0x1e, 0xa4, 0x9a, 0xd8, 0xe6, 0xe5, 0xdb, 0x99, 0xa7, 0x1d, 0x23, 0x61, 0x5f, 0x8, 0x36, 0x74, 0x4a, 0xf0, 0xce, 0x8c, 0xb2, 0x22, 0x1c, 0x5e, 0x60, 0xda, 0xe4, 0xa6, 0x98, 0xcf, 0xf1, 0xb3, 0x8d, 0x37, 0x9, 0x4b, 0x75, 0x4d, 0x73, 0x31, 0xf, 0xb5, 0x8b, 0xc9, 0xf7, 0xa0, 0x9e, 0xdc, 0xe2, 0x58, 0x66, 0x24, 0x1a, 0x8a, 0xb4, 0xf6, 0xc8, 0x72, 0x4c, 0xe, 0x30, 0x67, 0x59, 0x1b, 0x25, 0x9f, 0xa1, 0xe3, 0xdd, 0xde, 0xe0, 0xa2, 0x9c, 0x26, 0x18, 0x5a, 0x64, 0x33, 0xd, 0x4f, 0x71, 0xcb, 0xf5, 0xb7, 0x89, 0x19, 0x27, 0x65, 0x5b, 0xe1, 0xdf, 0x9d, 0xa3, 0xf4, 0xca, 0x88, 0xb6, 0xc, 0x32, 0x70, 0x4e},
+ {0x0, 0x3f, 0x7e, 0x41, 0xfc, 0xc3, 0x82, 0xbd, 0xe5, 0xda, 0x9b, 0xa4, 0x19, 0x26, 0x67, 0x58, 0xd7, 0xe8, 0xa9, 0x96, 0x2b, 0x14, 0x55, 0x6a, 0x32, 0xd, 0x4c, 0x73, 0xce, 0xf1, 0xb0, 0x8f, 0xb3, 0x8c, 0xcd, 0xf2, 0x4f, 0x70, 0x31, 0xe, 0x56, 0x69, 0x28, 0x17, 0xaa, 0x95, 0xd4, 0xeb, 0x64, 0x5b, 0x1a, 0x25, 0x98, 0xa7, 0xe6, 0xd9, 0x81, 0xbe, 0xff, 0xc0, 0x7d, 0x42, 0x3, 0x3c, 0x7b, 0x44, 0x5, 0x3a, 0x87, 0xb8, 0xf9, 0xc6, 0x9e, 0xa1, 0xe0, 0xdf, 0x62, 0x5d, 0x1c, 0x23, 0xac, 0x93, 0xd2, 0xed, 0x50, 0x6f, 0x2e, 0x11, 0x49, 0x76, 0x37, 0x8, 0xb5, 0x8a, 0xcb, 0xf4, 0xc8, 0xf7, 0xb6, 0x89, 0x34, 0xb, 0x4a, 0x75, 0x2d, 0x12, 0x53, 0x6c, 0xd1, 0xee, 0xaf, 0x90, 0x1f, 0x20, 0x61, 0x5e, 0xe3, 0xdc, 0x9d, 0xa2, 0xfa, 0xc5, 0x84, 0xbb, 0x6, 0x39, 0x78, 0x47, 0xf6, 0xc9, 0x88, 0xb7, 0xa, 0x35, 0x74, 0x4b, 0x13, 0x2c, 0x6d, 0x52, 0xef, 0xd0, 0x91, 0xae, 0x21, 0x1e, 0x5f, 0x60, 0xdd, 0xe2, 0xa3, 0x9c, 0xc4, 0xfb, 0xba, 0x85, 0x38, 0x7, 0x46, 0x79, 0x45, 0x7a, 0x3b, 0x4, 0xb9, 0x86, 0xc7, 0xf8, 0xa0, 0x9f, 0xde, 0xe1, 0x5c, 0x63, 0x22, 0x1d, 0x92, 0xad, 0xec, 0xd3, 0x6e, 0x51, 0x10, 0x2f, 0x77, 0x48, 0x9, 0x36, 0x8b, 0xb4, 0xf5, 0xca, 0x8d, 0xb2, 0xf3, 0xcc, 0x71, 0x4e, 0xf, 0x30, 0x68, 0x57, 0x16, 0x29, 0x94, 0xab, 0xea, 0xd5, 0x5a, 0x65, 0x24, 0x1b, 0xa6, 0x99, 0xd8, 0xe7, 0xbf, 0x80, 0xc1, 0xfe, 0x43, 0x7c, 0x3d, 0x2, 0x3e, 0x1, 0x40, 0x7f, 0xc2, 0xfd, 0xbc, 0x83, 0xdb, 0xe4, 0xa5, 0x9a, 0x27, 0x18, 0x59, 0x66, 0xe9, 0xd6, 0x97, 0xa8, 0x15, 0x2a, 0x6b, 0x54, 0xc, 0x33, 0x72, 0x4d, 0xf0, 0xcf, 0x8e, 0xb1},
+ {0x0, 0x40, 0x80, 0xc0, 0x1d, 0x5d, 0x9d, 0xdd, 0x3a, 0x7a, 0xba, 0xfa, 0x27, 0x67, 0xa7, 0xe7, 0x74, 0x34, 0xf4, 0xb4, 0x69, 0x29, 0xe9, 0xa9, 0x4e, 0xe, 0xce, 0x8e, 0x53, 0x13, 0xd3, 0x93, 0xe8, 0xa8, 0x68, 0x28, 0xf5, 0xb5, 0x75, 0x35, 0xd2, 0x92, 0x52, 0x12, 0xcf, 0x8f, 0x4f, 0xf, 0x9c, 0xdc, 0x1c, 0x5c, 0x81, 0xc1, 0x1, 0x41, 0xa6, 0xe6, 0x26, 0x66, 0xbb, 0xfb, 0x3b, 0x7b, 0xcd, 0x8d, 0x4d, 0xd, 0xd0, 0x90, 0x50, 0x10, 0xf7, 0xb7, 0x77, 0x37, 0xea, 0xaa, 0x6a, 0x2a, 0xb9, 0xf9, 0x39, 0x79, 0xa4, 0xe4, 0x24, 0x64, 0x83, 0xc3, 0x3, 0x43, 0x9e, 0xde, 0x1e, 0x5e, 0x25, 0x65, 0xa5, 0xe5, 0x38, 0x78, 0xb8, 0xf8, 0x1f, 0x5f, 0x9f, 0xdf, 0x2, 0x42, 0x82, 0xc2, 0x51, 0x11, 0xd1, 0x91, 0x4c, 0xc, 0xcc, 0x8c, 0x6b, 0x2b, 0xeb, 0xab, 0x76, 0x36, 0xf6, 0xb6, 0x87, 0xc7, 0x7, 0x47, 0x9a, 0xda, 0x1a, 0x5a, 0xbd, 0xfd, 0x3d, 0x7d, 0xa0, 0xe0, 0x20, 0x60, 0xf3, 0xb3, 0x73, 0x33, 0xee, 0xae, 0x6e, 0x2e, 0xc9, 0x89, 0x49, 0x9, 0xd4, 0x94, 0x54, 0x14, 0x6f, 0x2f, 0xef, 0xaf, 0x72, 0x32, 0xf2, 0xb2, 0x55, 0x15, 0xd5, 0x95, 0x48, 0x8, 0xc8, 0x88, 0x1b, 0x5b, 0x9b, 0xdb, 0x6, 0x46, 0x86, 0xc6, 0x21, 0x61, 0xa1, 0xe1, 0x3c, 0x7c, 0xbc, 0xfc, 0x4a, 0xa, 0xca, 0x8a, 0x57, 0x17, 0xd7, 0x97, 0x70, 0x30, 0xf0, 0xb0, 0x6d, 0x2d, 0xed, 0xad, 0x3e, 0x7e, 0xbe, 0xfe, 0x23, 0x63, 0xa3, 0xe3, 0x4, 0x44, 0x84, 0xc4, 0x19, 0x59, 0x99, 0xd9, 0xa2, 0xe2, 0x22, 0x62, 0xbf, 0xff, 0x3f, 0x7f, 0x98, 0xd8, 0x18, 0x58, 0x85, 0xc5, 0x5, 0x45, 0xd6, 0x96, 0x56, 0x16, 0xcb, 0x8b, 0x4b, 0xb, 0xec, 0xac, 0x6c, 0x2c, 0xf1, 0xb1, 0x71, 0x31},
+ {0x0, 0x41, 0x82, 0xc3, 0x19, 0x58, 0x9b, 0xda, 0x32, 0x73, 0xb0, 0xf1, 0x2b, 0x6a, 0xa9, 0xe8, 0x64, 0x25, 0xe6, 0xa7, 0x7d, 0x3c, 0xff, 0xbe, 0x56, 0x17, 0xd4, 0x95, 0x4f, 0xe, 0xcd, 0x8c, 0xc8, 0x89, 0x4a, 0xb, 0xd1, 0x90, 0x53, 0x12, 0xfa, 0xbb, 0x78, 0x39, 0xe3, 0xa2, 0x61, 0x20, 0xac, 0xed, 0x2e, 0x6f, 0xb5, 0xf4, 0x37, 0x76, 0x9e, 0xdf, 0x1c, 0x5d, 0x87, 0xc6, 0x5, 0x44, 0x8d, 0xcc, 0xf, 0x4e, 0x94, 0xd5, 0x16, 0x57, 0xbf, 0xfe, 0x3d, 0x7c, 0xa6, 0xe7, 0x24, 0x65, 0xe9, 0xa8, 0x6b, 0x2a, 0xf0, 0xb1, 0x72, 0x33, 0xdb, 0x9a, 0x59, 0x18, 0xc2, 0x83, 0x40, 0x1, 0x45, 0x4, 0xc7, 0x86, 0x5c, 0x1d, 0xde, 0x9f, 0x77, 0x36, 0xf5, 0xb4, 0x6e, 0x2f, 0xec, 0xad, 0x21, 0x60, 0xa3, 0xe2, 0x38, 0x79, 0xba, 0xfb, 0x13, 0x52, 0x91, 0xd0, 0xa, 0x4b, 0x88, 0xc9, 0x7, 0x46, 0x85, 0xc4, 0x1e, 0x5f, 0x9c, 0xdd, 0x35, 0x74, 0xb7, 0xf6, 0x2c, 0x6d, 0xae, 0xef, 0x63, 0x22, 0xe1, 0xa0, 0x7a, 0x3b, 0xf8, 0xb9, 0x51, 0x10, 0xd3, 0x92, 0x48, 0x9, 0xca, 0x8b, 0xcf, 0x8e, 0x4d, 0xc, 0xd6, 0x97, 0x54, 0x15, 0xfd, 0xbc, 0x7f, 0x3e, 0xe4, 0xa5, 0x66, 0x27, 0xab, 0xea, 0x29, 0x68, 0xb2, 0xf3, 0x30, 0x71, 0x99, 0xd8, 0x1b, 0x5a, 0x80, 0xc1, 0x2, 0x43, 0x8a, 0xcb, 0x8, 0x49, 0x93, 0xd2, 0x11, 0x50, 0xb8, 0xf9, 0x3a, 0x7b, 0xa1, 0xe0, 0x23, 0x62, 0xee, 0xaf, 0x6c, 0x2d, 0xf7, 0xb6, 0x75, 0x34, 0xdc, 0x9d, 0x5e, 0x1f, 0xc5, 0x84, 0x47, 0x6, 0x42, 0x3, 0xc0, 0x81, 0x5b, 0x1a, 0xd9, 0x98, 0x70, 0x31, 0xf2, 0xb3, 0x69, 0x28, 0xeb, 0xaa, 0x26, 0x67, 0xa4, 0xe5, 0x3f, 0x7e, 0xbd, 0xfc, 0x14, 0x55, 0x96, 0xd7, 0xd, 0x4c, 0x8f, 0xce},
+ {0x0, 0x42, 0x84, 0xc6, 0x15, 0x57, 0x91, 0xd3, 0x2a, 0x68, 0xae, 0xec, 0x3f, 0x7d, 0xbb, 0xf9, 0x54, 0x16, 0xd0, 0x92, 0x41, 0x3, 0xc5, 0x87, 0x7e, 0x3c, 0xfa, 0xb8, 0x6b, 0x29, 0xef, 0xad, 0xa8, 0xea, 0x2c, 0x6e, 0xbd, 0xff, 0x39, 0x7b, 0x82, 0xc0, 0x6, 0x44, 0x97, 0xd5, 0x13, 0x51, 0xfc, 0xbe, 0x78, 0x3a, 0xe9, 0xab, 0x6d, 0x2f, 0xd6, 0x94, 0x52, 0x10, 0xc3, 0x81, 0x47, 0x5, 0x4d, 0xf, 0xc9, 0x8b, 0x58, 0x1a, 0xdc, 0x9e, 0x67, 0x25, 0xe3, 0xa1, 0x72, 0x30, 0xf6, 0xb4, 0x19, 0x5b, 0x9d, 0xdf, 0xc, 0x4e, 0x88, 0xca, 0x33, 0x71, 0xb7, 0xf5, 0x26, 0x64, 0xa2, 0xe0, 0xe5, 0xa7, 0x61, 0x23, 0xf0, 0xb2, 0x74, 0x36, 0xcf, 0x8d, 0x4b, 0x9, 0xda, 0x98, 0x5e, 0x1c, 0xb1, 0xf3, 0x35, 0x77, 0xa4, 0xe6, 0x20, 0x62, 0x9b, 0xd9, 0x1f, 0x5d, 0x8e, 0xcc, 0xa, 0x48, 0x9a, 0xd8, 0x1e, 0x5c, 0x8f, 0xcd, 0xb, 0x49, 0xb0, 0xf2, 0x34, 0x76, 0xa5, 0xe7, 0x21, 0x63, 0xce, 0x8c, 0x4a, 0x8, 0xdb, 0x99, 0x5f, 0x1d, 0xe4, 0xa6, 0x60, 0x22, 0xf1, 0xb3, 0x75, 0x37, 0x32, 0x70, 0xb6, 0xf4, 0x27, 0x65, 0xa3, 0xe1, 0x18, 0x5a, 0x9c, 0xde, 0xd, 0x4f, 0x89, 0xcb, 0x66, 0x24, 0xe2, 0xa0, 0x73, 0x31, 0xf7, 0xb5, 0x4c, 0xe, 0xc8, 0x8a, 0x59, 0x1b, 0xdd, 0x9f, 0xd7, 0x95, 0x53, 0x11, 0xc2, 0x80, 0x46, 0x4, 0xfd, 0xbf, 0x79, 0x3b, 0xe8, 0xaa, 0x6c, 0x2e, 0x83, 0xc1, 0x7, 0x45, 0x96, 0xd4, 0x12, 0x50, 0xa9, 0xeb, 0x2d, 0x6f, 0xbc, 0xfe, 0x38, 0x7a, 0x7f, 0x3d, 0xfb, 0xb9, 0x6a, 0x28, 0xee, 0xac, 0x55, 0x17, 0xd1, 0x93, 0x40, 0x2, 0xc4, 0x86, 0x2b, 0x69, 0xaf, 0xed, 0x3e, 0x7c, 0xba, 0xf8, 0x1, 0x43, 0x85, 0xc7, 0x14, 0x56, 0x90, 0xd2},
+ {0x0, 0x43, 0x86, 0xc5, 0x11, 0x52, 0x97, 0xd4, 0x22, 0x61, 0xa4, 0xe7, 0x33, 0x70, 0xb5, 0xf6, 0x44, 0x7, 0xc2, 0x81, 0x55, 0x16, 0xd3, 0x90, 0x66, 0x25, 0xe0, 0xa3, 0x77, 0x34, 0xf1, 0xb2, 0x88, 0xcb, 0xe, 0x4d, 0x99, 0xda, 0x1f, 0x5c, 0xaa, 0xe9, 0x2c, 0x6f, 0xbb, 0xf8, 0x3d, 0x7e, 0xcc, 0x8f, 0x4a, 0x9, 0xdd, 0x9e, 0x5b, 0x18, 0xee, 0xad, 0x68, 0x2b, 0xff, 0xbc, 0x79, 0x3a, 0xd, 0x4e, 0x8b, 0xc8, 0x1c, 0x5f, 0x9a, 0xd9, 0x2f, 0x6c, 0xa9, 0xea, 0x3e, 0x7d, 0xb8, 0xfb, 0x49, 0xa, 0xcf, 0x8c, 0x58, 0x1b, 0xde, 0x9d, 0x6b, 0x28, 0xed, 0xae, 0x7a, 0x39, 0xfc, 0xbf, 0x85, 0xc6, 0x3, 0x40, 0x94, 0xd7, 0x12, 0x51, 0xa7, 0xe4, 0x21, 0x62, 0xb6, 0xf5, 0x30, 0x73, 0xc1, 0x82, 0x47, 0x4, 0xd0, 0x93, 0x56, 0x15, 0xe3, 0xa0, 0x65, 0x26, 0xf2, 0xb1, 0x74, 0x37, 0x1a, 0x59, 0x9c, 0xdf, 0xb, 0x48, 0x8d, 0xce, 0x38, 0x7b, 0xbe, 0xfd, 0x29, 0x6a, 0xaf, 0xec, 0x5e, 0x1d, 0xd8, 0x9b, 0x4f, 0xc, 0xc9, 0x8a, 0x7c, 0x3f, 0xfa, 0xb9, 0x6d, 0x2e, 0xeb, 0xa8, 0x92, 0xd1, 0x14, 0x57, 0x83, 0xc0, 0x5, 0x46, 0xb0, 0xf3, 0x36, 0x75, 0xa1, 0xe2, 0x27, 0x64, 0xd6, 0x95, 0x50, 0x13, 0xc7, 0x84, 0x41, 0x2, 0xf4, 0xb7, 0x72, 0x31, 0xe5, 0xa6, 0x63, 0x20, 0x17, 0x54, 0x91, 0xd2, 0x6, 0x45, 0x80, 0xc3, 0x35, 0x76, 0xb3, 0xf0, 0x24, 0x67, 0xa2, 0xe1, 0x53, 0x10, 0xd5, 0x96, 0x42, 0x1, 0xc4, 0x87, 0x71, 0x32, 0xf7, 0xb4, 0x60, 0x23, 0xe6, 0xa5, 0x9f, 0xdc, 0x19, 0x5a, 0x8e, 0xcd, 0x8, 0x4b, 0xbd, 0xfe, 0x3b, 0x78, 0xac, 0xef, 0x2a, 0x69, 0xdb, 0x98, 0x5d, 0x1e, 0xca, 0x89, 0x4c, 0xf, 0xf9, 0xba, 0x7f, 0x3c, 0xe8, 0xab, 0x6e, 0x2d},
+ {0x0, 0x44, 0x88, 0xcc, 0xd, 0x49, 0x85, 0xc1, 0x1a, 0x5e, 0x92, 0xd6, 0x17, 0x53, 0x9f, 0xdb, 0x34, 0x70, 0xbc, 0xf8, 0x39, 0x7d, 0xb1, 0xf5, 0x2e, 0x6a, 0xa6, 0xe2, 0x23, 0x67, 0xab, 0xef, 0x68, 0x2c, 0xe0, 0xa4, 0x65, 0x21, 0xed, 0xa9, 0x72, 0x36, 0xfa, 0xbe, 0x7f, 0x3b, 0xf7, 0xb3, 0x5c, 0x18, 0xd4, 0x90, 0x51, 0x15, 0xd9, 0x9d, 0x46, 0x2, 0xce, 0x8a, 0x4b, 0xf, 0xc3, 0x87, 0xd0, 0x94, 0x58, 0x1c, 0xdd, 0x99, 0x55, 0x11, 0xca, 0x8e, 0x42, 0x6, 0xc7, 0x83, 0x4f, 0xb, 0xe4, 0xa0, 0x6c, 0x28, 0xe9, 0xad, 0x61, 0x25, 0xfe, 0xba, 0x76, 0x32, 0xf3, 0xb7, 0x7b, 0x3f, 0xb8, 0xfc, 0x30, 0x74, 0xb5, 0xf1, 0x3d, 0x79, 0xa2, 0xe6, 0x2a, 0x6e, 0xaf, 0xeb, 0x27, 0x63, 0x8c, 0xc8, 0x4, 0x40, 0x81, 0xc5, 0x9, 0x4d, 0x96, 0xd2, 0x1e, 0x5a, 0x9b, 0xdf, 0x13, 0x57, 0xbd, 0xf9, 0x35, 0x71, 0xb0, 0xf4, 0x38, 0x7c, 0xa7, 0xe3, 0x2f, 0x6b, 0xaa, 0xee, 0x22, 0x66, 0x89, 0xcd, 0x1, 0x45, 0x84, 0xc0, 0xc, 0x48, 0x93, 0xd7, 0x1b, 0x5f, 0x9e, 0xda, 0x16, 0x52, 0xd5, 0x91, 0x5d, 0x19, 0xd8, 0x9c, 0x50, 0x14, 0xcf, 0x8b, 0x47, 0x3, 0xc2, 0x86, 0x4a, 0xe, 0xe1, 0xa5, 0x69, 0x2d, 0xec, 0xa8, 0x64, 0x20, 0xfb, 0xbf, 0x73, 0x37, 0xf6, 0xb2, 0x7e, 0x3a, 0x6d, 0x29, 0xe5, 0xa1, 0x60, 0x24, 0xe8, 0xac, 0x77, 0x33, 0xff, 0xbb, 0x7a, 0x3e, 0xf2, 0xb6, 0x59, 0x1d, 0xd1, 0x95, 0x54, 0x10, 0xdc, 0x98, 0x43, 0x7, 0xcb, 0x8f, 0x4e, 0xa, 0xc6, 0x82, 0x5, 0x41, 0x8d, 0xc9, 0x8, 0x4c, 0x80, 0xc4, 0x1f, 0x5b, 0x97, 0xd3, 0x12, 0x56, 0x9a, 0xde, 0x31, 0x75, 0xb9, 0xfd, 0x3c, 0x78, 0xb4, 0xf0, 0x2b, 0x6f, 0xa3, 0xe7, 0x26, 0x62, 0xae, 0xea},
+ {0x0, 0x45, 0x8a, 0xcf, 0x9, 0x4c, 0x83, 0xc6, 0x12, 0x57, 0x98, 0xdd, 0x1b, 0x5e, 0x91, 0xd4, 0x24, 0x61, 0xae, 0xeb, 0x2d, 0x68, 0xa7, 0xe2, 0x36, 0x73, 0xbc, 0xf9, 0x3f, 0x7a, 0xb5, 0xf0, 0x48, 0xd, 0xc2, 0x87, 0x41, 0x4, 0xcb, 0x8e, 0x5a, 0x1f, 0xd0, 0x95, 0x53, 0x16, 0xd9, 0x9c, 0x6c, 0x29, 0xe6, 0xa3, 0x65, 0x20, 0xef, 0xaa, 0x7e, 0x3b, 0xf4, 0xb1, 0x77, 0x32, 0xfd, 0xb8, 0x90, 0xd5, 0x1a, 0x5f, 0x99, 0xdc, 0x13, 0x56, 0x82, 0xc7, 0x8, 0x4d, 0x8b, 0xce, 0x1, 0x44, 0xb4, 0xf1, 0x3e, 0x7b, 0xbd, 0xf8, 0x37, 0x72, 0xa6, 0xe3, 0x2c, 0x69, 0xaf, 0xea, 0x25, 0x60, 0xd8, 0x9d, 0x52, 0x17, 0xd1, 0x94, 0x5b, 0x1e, 0xca, 0x8f, 0x40, 0x5, 0xc3, 0x86, 0x49, 0xc, 0xfc, 0xb9, 0x76, 0x33, 0xf5, 0xb0, 0x7f, 0x3a, 0xee, 0xab, 0x64, 0x21, 0xe7, 0xa2, 0x6d, 0x28, 0x3d, 0x78, 0xb7, 0xf2, 0x34, 0x71, 0xbe, 0xfb, 0x2f, 0x6a, 0xa5, 0xe0, 0x26, 0x63, 0xac, 0xe9, 0x19, 0x5c, 0x93, 0xd6, 0x10, 0x55, 0x9a, 0xdf, 0xb, 0x4e, 0x81, 0xc4, 0x2, 0x47, 0x88, 0xcd, 0x75, 0x30, 0xff, 0xba, 0x7c, 0x39, 0xf6, 0xb3, 0x67, 0x22, 0xed, 0xa8, 0x6e, 0x2b, 0xe4, 0xa1, 0x51, 0x14, 0xdb, 0x9e, 0x58, 0x1d, 0xd2, 0x97, 0x43, 0x6, 0xc9, 0x8c, 0x4a, 0xf, 0xc0, 0x85, 0xad, 0xe8, 0x27, 0x62, 0xa4, 0xe1, 0x2e, 0x6b, 0xbf, 0xfa, 0x35, 0x70, 0xb6, 0xf3, 0x3c, 0x79, 0x89, 0xcc, 0x3, 0x46, 0x80, 0xc5, 0xa, 0x4f, 0x9b, 0xde, 0x11, 0x54, 0x92, 0xd7, 0x18, 0x5d, 0xe5, 0xa0, 0x6f, 0x2a, 0xec, 0xa9, 0x66, 0x23, 0xf7, 0xb2, 0x7d, 0x38, 0xfe, 0xbb, 0x74, 0x31, 0xc1, 0x84, 0x4b, 0xe, 0xc8, 0x8d, 0x42, 0x7, 0xd3, 0x96, 0x59, 0x1c, 0xda, 0x9f, 0x50, 0x15},
+ {0x0, 0x46, 0x8c, 0xca, 0x5, 0x43, 0x89, 0xcf, 0xa, 0x4c, 0x86, 0xc0, 0xf, 0x49, 0x83, 0xc5, 0x14, 0x52, 0x98, 0xde, 0x11, 0x57, 0x9d, 0xdb, 0x1e, 0x58, 0x92, 0xd4, 0x1b, 0x5d, 0x97, 0xd1, 0x28, 0x6e, 0xa4, 0xe2, 0x2d, 0x6b, 0xa1, 0xe7, 0x22, 0x64, 0xae, 0xe8, 0x27, 0x61, 0xab, 0xed, 0x3c, 0x7a, 0xb0, 0xf6, 0x39, 0x7f, 0xb5, 0xf3, 0x36, 0x70, 0xba, 0xfc, 0x33, 0x75, 0xbf, 0xf9, 0x50, 0x16, 0xdc, 0x9a, 0x55, 0x13, 0xd9, 0x9f, 0x5a, 0x1c, 0xd6, 0x90, 0x5f, 0x19, 0xd3, 0x95, 0x44, 0x2, 0xc8, 0x8e, 0x41, 0x7, 0xcd, 0x8b, 0x4e, 0x8, 0xc2, 0x84, 0x4b, 0xd, 0xc7, 0x81, 0x78, 0x3e, 0xf4, 0xb2, 0x7d, 0x3b, 0xf1, 0xb7, 0x72, 0x34, 0xfe, 0xb8, 0x77, 0x31, 0xfb, 0xbd, 0x6c, 0x2a, 0xe0, 0xa6, 0x69, 0x2f, 0xe5, 0xa3, 0x66, 0x20, 0xea, 0xac, 0x63, 0x25, 0xef, 0xa9, 0xa0, 0xe6, 0x2c, 0x6a, 0xa5, 0xe3, 0x29, 0x6f, 0xaa, 0xec, 0x26, 0x60, 0xaf, 0xe9, 0x23, 0x65, 0xb4, 0xf2, 0x38, 0x7e, 0xb1, 0xf7, 0x3d, 0x7b, 0xbe, 0xf8, 0x32, 0x74, 0xbb, 0xfd, 0x37, 0x71, 0x88, 0xce, 0x4, 0x42, 0x8d, 0xcb, 0x1, 0x47, 0x82, 0xc4, 0xe, 0x48, 0x87, 0xc1, 0xb, 0x4d, 0x9c, 0xda, 0x10, 0x56, 0x99, 0xdf, 0x15, 0x53, 0x96, 0xd0, 0x1a, 0x5c, 0x93, 0xd5, 0x1f, 0x59, 0xf0, 0xb6, 0x7c, 0x3a, 0xf5, 0xb3, 0x79, 0x3f, 0xfa, 0xbc, 0x76, 0x30, 0xff, 0xb9, 0x73, 0x35, 0xe4, 0xa2, 0x68, 0x2e, 0xe1, 0xa7, 0x6d, 0x2b, 0xee, 0xa8, 0x62, 0x24, 0xeb, 0xad, 0x67, 0x21, 0xd8, 0x9e, 0x54, 0x12, 0xdd, 0x9b, 0x51, 0x17, 0xd2, 0x94, 0x5e, 0x18, 0xd7, 0x91, 0x5b, 0x1d, 0xcc, 0x8a, 0x40, 0x6, 0xc9, 0x8f, 0x45, 0x3, 0xc6, 0x80, 0x4a, 0xc, 0xc3, 0x85, 0x4f, 0x9},
+ {0x0, 0x47, 0x8e, 0xc9, 0x1, 0x46, 0x8f, 0xc8, 0x2, 0x45, 0x8c, 0xcb, 0x3, 0x44, 0x8d, 0xca, 0x4, 0x43, 0x8a, 0xcd, 0x5, 0x42, 0x8b, 0xcc, 0x6, 0x41, 0x88, 0xcf, 0x7, 0x40, 0x89, 0xce, 0x8, 0x4f, 0x86, 0xc1, 0x9, 0x4e, 0x87, 0xc0, 0xa, 0x4d, 0x84, 0xc3, 0xb, 0x4c, 0x85, 0xc2, 0xc, 0x4b, 0x82, 0xc5, 0xd, 0x4a, 0x83, 0xc4, 0xe, 0x49, 0x80, 0xc7, 0xf, 0x48, 0x81, 0xc6, 0x10, 0x57, 0x9e, 0xd9, 0x11, 0x56, 0x9f, 0xd8, 0x12, 0x55, 0x9c, 0xdb, 0x13, 0x54, 0x9d, 0xda, 0x14, 0x53, 0x9a, 0xdd, 0x15, 0x52, 0x9b, 0xdc, 0x16, 0x51, 0x98, 0xdf, 0x17, 0x50, 0x99, 0xde, 0x18, 0x5f, 0x96, 0xd1, 0x19, 0x5e, 0x97, 0xd0, 0x1a, 0x5d, 0x94, 0xd3, 0x1b, 0x5c, 0x95, 0xd2, 0x1c, 0x5b, 0x92, 0xd5, 0x1d, 0x5a, 0x93, 0xd4, 0x1e, 0x59, 0x90, 0xd7, 0x1f, 0x58, 0x91, 0xd6, 0x20, 0x67, 0xae, 0xe9, 0x21, 0x66, 0xaf, 0xe8, 0x22, 0x65, 0xac, 0xeb, 0x23, 0x64, 0xad, 0xea, 0x24, 0x63, 0xaa, 0xed, 0x25, 0x62, 0xab, 0xec, 0x26, 0x61, 0xa8, 0xef, 0x27, 0x60, 0xa9, 0xee, 0x28, 0x6f, 0xa6, 0xe1, 0x29, 0x6e, 0xa7, 0xe0, 0x2a, 0x6d, 0xa4, 0xe3, 0x2b, 0x6c, 0xa5, 0xe2, 0x2c, 0x6b, 0xa2, 0xe5, 0x2d, 0x6a, 0xa3, 0xe4, 0x2e, 0x69, 0xa0, 0xe7, 0x2f, 0x68, 0xa1, 0xe6, 0x30, 0x77, 0xbe, 0xf9, 0x31, 0x76, 0xbf, 0xf8, 0x32, 0x75, 0xbc, 0xfb, 0x33, 0x74, 0xbd, 0xfa, 0x34, 0x73, 0xba, 0xfd, 0x35, 0x72, 0xbb, 0xfc, 0x36, 0x71, 0xb8, 0xff, 0x37, 0x70, 0xb9, 0xfe, 0x38, 0x7f, 0xb6, 0xf1, 0x39, 0x7e, 0xb7, 0xf0, 0x3a, 0x7d, 0xb4, 0xf3, 0x3b, 0x7c, 0xb5, 0xf2, 0x3c, 0x7b, 0xb2, 0xf5, 0x3d, 0x7a, 0xb3, 0xf4, 0x3e, 0x79, 0xb0, 0xf7, 0x3f, 0x78, 0xb1, 0xf6},
+ {0x0, 0x48, 0x90, 0xd8, 0x3d, 0x75, 0xad, 0xe5, 0x7a, 0x32, 0xea, 0xa2, 0x47, 0xf, 0xd7, 0x9f, 0xf4, 0xbc, 0x64, 0x2c, 0xc9, 0x81, 0x59, 0x11, 0x8e, 0xc6, 0x1e, 0x56, 0xb3, 0xfb, 0x23, 0x6b, 0xf5, 0xbd, 0x65, 0x2d, 0xc8, 0x80, 0x58, 0x10, 0x8f, 0xc7, 0x1f, 0x57, 0xb2, 0xfa, 0x22, 0x6a, 0x1, 0x49, 0x91, 0xd9, 0x3c, 0x74, 0xac, 0xe4, 0x7b, 0x33, 0xeb, 0xa3, 0x46, 0xe, 0xd6, 0x9e, 0xf7, 0xbf, 0x67, 0x2f, 0xca, 0x82, 0x5a, 0x12, 0x8d, 0xc5, 0x1d, 0x55, 0xb0, 0xf8, 0x20, 0x68, 0x3, 0x4b, 0x93, 0xdb, 0x3e, 0x76, 0xae, 0xe6, 0x79, 0x31, 0xe9, 0xa1, 0x44, 0xc, 0xd4, 0x9c, 0x2, 0x4a, 0x92, 0xda, 0x3f, 0x77, 0xaf, 0xe7, 0x78, 0x30, 0xe8, 0xa0, 0x45, 0xd, 0xd5, 0x9d, 0xf6, 0xbe, 0x66, 0x2e, 0xcb, 0x83, 0x5b, 0x13, 0x8c, 0xc4, 0x1c, 0x54, 0xb1, 0xf9, 0x21, 0x69, 0xf3, 0xbb, 0x63, 0x2b, 0xce, 0x86, 0x5e, 0x16, 0x89, 0xc1, 0x19, 0x51, 0xb4, 0xfc, 0x24, 0x6c, 0x7, 0x4f, 0x97, 0xdf, 0x3a, 0x72, 0xaa, 0xe2, 0x7d, 0x35, 0xed, 0xa5, 0x40, 0x8, 0xd0, 0x98, 0x6, 0x4e, 0x96, 0xde, 0x3b, 0x73, 0xab, 0xe3, 0x7c, 0x34, 0xec, 0xa4, 0x41, 0x9, 0xd1, 0x99, 0xf2, 0xba, 0x62, 0x2a, 0xcf, 0x87, 0x5f, 0x17, 0x88, 0xc0, 0x18, 0x50, 0xb5, 0xfd, 0x25, 0x6d, 0x4, 0x4c, 0x94, 0xdc, 0x39, 0x71, 0xa9, 0xe1, 0x7e, 0x36, 0xee, 0xa6, 0x43, 0xb, 0xd3, 0x9b, 0xf0, 0xb8, 0x60, 0x28, 0xcd, 0x85, 0x5d, 0x15, 0x8a, 0xc2, 0x1a, 0x52, 0xb7, 0xff, 0x27, 0x6f, 0xf1, 0xb9, 0x61, 0x29, 0xcc, 0x84, 0x5c, 0x14, 0x8b, 0xc3, 0x1b, 0x53, 0xb6, 0xfe, 0x26, 0x6e, 0x5, 0x4d, 0x95, 0xdd, 0x38, 0x70, 0xa8, 0xe0, 0x7f, 0x37, 0xef, 0xa7, 0x42, 0xa, 0xd2, 0x9a},
+ {0x0, 0x49, 0x92, 0xdb, 0x39, 0x70, 0xab, 0xe2, 0x72, 0x3b, 0xe0, 0xa9, 0x4b, 0x2, 0xd9, 0x90, 0xe4, 0xad, 0x76, 0x3f, 0xdd, 0x94, 0x4f, 0x6, 0x96, 0xdf, 0x4, 0x4d, 0xaf, 0xe6, 0x3d, 0x74, 0xd5, 0x9c, 0x47, 0xe, 0xec, 0xa5, 0x7e, 0x37, 0xa7, 0xee, 0x35, 0x7c, 0x9e, 0xd7, 0xc, 0x45, 0x31, 0x78, 0xa3, 0xea, 0x8, 0x41, 0x9a, 0xd3, 0x43, 0xa, 0xd1, 0x98, 0x7a, 0x33, 0xe8, 0xa1, 0xb7, 0xfe, 0x25, 0x6c, 0x8e, 0xc7, 0x1c, 0x55, 0xc5, 0x8c, 0x57, 0x1e, 0xfc, 0xb5, 0x6e, 0x27, 0x53, 0x1a, 0xc1, 0x88, 0x6a, 0x23, 0xf8, 0xb1, 0x21, 0x68, 0xb3, 0xfa, 0x18, 0x51, 0x8a, 0xc3, 0x62, 0x2b, 0xf0, 0xb9, 0x5b, 0x12, 0xc9, 0x80, 0x10, 0x59, 0x82, 0xcb, 0x29, 0x60, 0xbb, 0xf2, 0x86, 0xcf, 0x14, 0x5d, 0xbf, 0xf6, 0x2d, 0x64, 0xf4, 0xbd, 0x66, 0x2f, 0xcd, 0x84, 0x5f, 0x16, 0x73, 0x3a, 0xe1, 0xa8, 0x4a, 0x3, 0xd8, 0x91, 0x1, 0x48, 0x93, 0xda, 0x38, 0x71, 0xaa, 0xe3, 0x97, 0xde, 0x5, 0x4c, 0xae, 0xe7, 0x3c, 0x75, 0xe5, 0xac, 0x77, 0x3e, 0xdc, 0x95, 0x4e, 0x7, 0xa6, 0xef, 0x34, 0x7d, 0x9f, 0xd6, 0xd, 0x44, 0xd4, 0x9d, 0x46, 0xf, 0xed, 0xa4, 0x7f, 0x36, 0x42, 0xb, 0xd0, 0x99, 0x7b, 0x32, 0xe9, 0xa0, 0x30, 0x79, 0xa2, 0xeb, 0x9, 0x40, 0x9b, 0xd2, 0xc4, 0x8d, 0x56, 0x1f, 0xfd, 0xb4, 0x6f, 0x26, 0xb6, 0xff, 0x24, 0x6d, 0x8f, 0xc6, 0x1d, 0x54, 0x20, 0x69, 0xb2, 0xfb, 0x19, 0x50, 0x8b, 0xc2, 0x52, 0x1b, 0xc0, 0x89, 0x6b, 0x22, 0xf9, 0xb0, 0x11, 0x58, 0x83, 0xca, 0x28, 0x61, 0xba, 0xf3, 0x63, 0x2a, 0xf1, 0xb8, 0x5a, 0x13, 0xc8, 0x81, 0xf5, 0xbc, 0x67, 0x2e, 0xcc, 0x85, 0x5e, 0x17, 0x87, 0xce, 0x15, 0x5c, 0xbe, 0xf7, 0x2c, 0x65},
+ {0x0, 0x4a, 0x94, 0xde, 0x35, 0x7f, 0xa1, 0xeb, 0x6a, 0x20, 0xfe, 0xb4, 0x5f, 0x15, 0xcb, 0x81, 0xd4, 0x9e, 0x40, 0xa, 0xe1, 0xab, 0x75, 0x3f, 0xbe, 0xf4, 0x2a, 0x60, 0x8b, 0xc1, 0x1f, 0x55, 0xb5, 0xff, 0x21, 0x6b, 0x80, 0xca, 0x14, 0x5e, 0xdf, 0x95, 0x4b, 0x1, 0xea, 0xa0, 0x7e, 0x34, 0x61, 0x2b, 0xf5, 0xbf, 0x54, 0x1e, 0xc0, 0x8a, 0xb, 0x41, 0x9f, 0xd5, 0x3e, 0x74, 0xaa, 0xe0, 0x77, 0x3d, 0xe3, 0xa9, 0x42, 0x8, 0xd6, 0x9c, 0x1d, 0x57, 0x89, 0xc3, 0x28, 0x62, 0xbc, 0xf6, 0xa3, 0xe9, 0x37, 0x7d, 0x96, 0xdc, 0x2, 0x48, 0xc9, 0x83, 0x5d, 0x17, 0xfc, 0xb6, 0x68, 0x22, 0xc2, 0x88, 0x56, 0x1c, 0xf7, 0xbd, 0x63, 0x29, 0xa8, 0xe2, 0x3c, 0x76, 0x9d, 0xd7, 0x9, 0x43, 0x16, 0x5c, 0x82, 0xc8, 0x23, 0x69, 0xb7, 0xfd, 0x7c, 0x36, 0xe8, 0xa2, 0x49, 0x3, 0xdd, 0x97, 0xee, 0xa4, 0x7a, 0x30, 0xdb, 0x91, 0x4f, 0x5, 0x84, 0xce, 0x10, 0x5a, 0xb1, 0xfb, 0x25, 0x6f, 0x3a, 0x70, 0xae, 0xe4, 0xf, 0x45, 0x9b, 0xd1, 0x50, 0x1a, 0xc4, 0x8e, 0x65, 0x2f, 0xf1, 0xbb, 0x5b, 0x11, 0xcf, 0x85, 0x6e, 0x24, 0xfa, 0xb0, 0x31, 0x7b, 0xa5, 0xef, 0x4, 0x4e, 0x90, 0xda, 0x8f, 0xc5, 0x1b, 0x51, 0xba, 0xf0, 0x2e, 0x64, 0xe5, 0xaf, 0x71, 0x3b, 0xd0, 0x9a, 0x44, 0xe, 0x99, 0xd3, 0xd, 0x47, 0xac, 0xe6, 0x38, 0x72, 0xf3, 0xb9, 0x67, 0x2d, 0xc6, 0x8c, 0x52, 0x18, 0x4d, 0x7, 0xd9, 0x93, 0x78, 0x32, 0xec, 0xa6, 0x27, 0x6d, 0xb3, 0xf9, 0x12, 0x58, 0x86, 0xcc, 0x2c, 0x66, 0xb8, 0xf2, 0x19, 0x53, 0x8d, 0xc7, 0x46, 0xc, 0xd2, 0x98, 0x73, 0x39, 0xe7, 0xad, 0xf8, 0xb2, 0x6c, 0x26, 0xcd, 0x87, 0x59, 0x13, 0x92, 0xd8, 0x6, 0x4c, 0xa7, 0xed, 0x33, 0x79},
+ {0x0, 0x4b, 0x96, 0xdd, 0x31, 0x7a, 0xa7, 0xec, 0x62, 0x29, 0xf4, 0xbf, 0x53, 0x18, 0xc5, 0x8e, 0xc4, 0x8f, 0x52, 0x19, 0xf5, 0xbe, 0x63, 0x28, 0xa6, 0xed, 0x30, 0x7b, 0x97, 0xdc, 0x1, 0x4a, 0x95, 0xde, 0x3, 0x48, 0xa4, 0xef, 0x32, 0x79, 0xf7, 0xbc, 0x61, 0x2a, 0xc6, 0x8d, 0x50, 0x1b, 0x51, 0x1a, 0xc7, 0x8c, 0x60, 0x2b, 0xf6, 0xbd, 0x33, 0x78, 0xa5, 0xee, 0x2, 0x49, 0x94, 0xdf, 0x37, 0x7c, 0xa1, 0xea, 0x6, 0x4d, 0x90, 0xdb, 0x55, 0x1e, 0xc3, 0x88, 0x64, 0x2f, 0xf2, 0xb9, 0xf3, 0xb8, 0x65, 0x2e, 0xc2, 0x89, 0x54, 0x1f, 0x91, 0xda, 0x7, 0x4c, 0xa0, 0xeb, 0x36, 0x7d, 0xa2, 0xe9, 0x34, 0x7f, 0x93, 0xd8, 0x5, 0x4e, 0xc0, 0x8b, 0x56, 0x1d, 0xf1, 0xba, 0x67, 0x2c, 0x66, 0x2d, 0xf0, 0xbb, 0x57, 0x1c, 0xc1, 0x8a, 0x4, 0x4f, 0x92, 0xd9, 0x35, 0x7e, 0xa3, 0xe8, 0x6e, 0x25, 0xf8, 0xb3, 0x5f, 0x14, 0xc9, 0x82, 0xc, 0x47, 0x9a, 0xd1, 0x3d, 0x76, 0xab, 0xe0, 0xaa, 0xe1, 0x3c, 0x77, 0x9b, 0xd0, 0xd, 0x46, 0xc8, 0x83, 0x5e, 0x15, 0xf9, 0xb2, 0x6f, 0x24, 0xfb, 0xb0, 0x6d, 0x26, 0xca, 0x81, 0x5c, 0x17, 0x99, 0xd2, 0xf, 0x44, 0xa8, 0xe3, 0x3e, 0x75, 0x3f, 0x74, 0xa9, 0xe2, 0xe, 0x45, 0x98, 0xd3, 0x5d, 0x16, 0xcb, 0x80, 0x6c, 0x27, 0xfa, 0xb1, 0x59, 0x12, 0xcf, 0x84, 0x68, 0x23, 0xfe, 0xb5, 0x3b, 0x70, 0xad, 0xe6, 0xa, 0x41, 0x9c, 0xd7, 0x9d, 0xd6, 0xb, 0x40, 0xac, 0xe7, 0x3a, 0x71, 0xff, 0xb4, 0x69, 0x22, 0xce, 0x85, 0x58, 0x13, 0xcc, 0x87, 0x5a, 0x11, 0xfd, 0xb6, 0x6b, 0x20, 0xae, 0xe5, 0x38, 0x73, 0x9f, 0xd4, 0x9, 0x42, 0x8, 0x43, 0x9e, 0xd5, 0x39, 0x72, 0xaf, 0xe4, 0x6a, 0x21, 0xfc, 0xb7, 0x5b, 0x10, 0xcd, 0x86},
+ {0x0, 0x4c, 0x98, 0xd4, 0x2d, 0x61, 0xb5, 0xf9, 0x5a, 0x16, 0xc2, 0x8e, 0x77, 0x3b, 0xef, 0xa3, 0xb4, 0xf8, 0x2c, 0x60, 0x99, 0xd5, 0x1, 0x4d, 0xee, 0xa2, 0x76, 0x3a, 0xc3, 0x8f, 0x5b, 0x17, 0x75, 0x39, 0xed, 0xa1, 0x58, 0x14, 0xc0, 0x8c, 0x2f, 0x63, 0xb7, 0xfb, 0x2, 0x4e, 0x9a, 0xd6, 0xc1, 0x8d, 0x59, 0x15, 0xec, 0xa0, 0x74, 0x38, 0x9b, 0xd7, 0x3, 0x4f, 0xb6, 0xfa, 0x2e, 0x62, 0xea, 0xa6, 0x72, 0x3e, 0xc7, 0x8b, 0x5f, 0x13, 0xb0, 0xfc, 0x28, 0x64, 0x9d, 0xd1, 0x5, 0x49, 0x5e, 0x12, 0xc6, 0x8a, 0x73, 0x3f, 0xeb, 0xa7, 0x4, 0x48, 0x9c, 0xd0, 0x29, 0x65, 0xb1, 0xfd, 0x9f, 0xd3, 0x7, 0x4b, 0xb2, 0xfe, 0x2a, 0x66, 0xc5, 0x89, 0x5d, 0x11, 0xe8, 0xa4, 0x70, 0x3c, 0x2b, 0x67, 0xb3, 0xff, 0x6, 0x4a, 0x9e, 0xd2, 0x71, 0x3d, 0xe9, 0xa5, 0x5c, 0x10, 0xc4, 0x88, 0xc9, 0x85, 0x51, 0x1d, 0xe4, 0xa8, 0x7c, 0x30, 0x93, 0xdf, 0xb, 0x47, 0xbe, 0xf2, 0x26, 0x6a, 0x7d, 0x31, 0xe5, 0xa9, 0x50, 0x1c, 0xc8, 0x84, 0x27, 0x6b, 0xbf, 0xf3, 0xa, 0x46, 0x92, 0xde, 0xbc, 0xf0, 0x24, 0x68, 0x91, 0xdd, 0x9, 0x45, 0xe6, 0xaa, 0x7e, 0x32, 0xcb, 0x87, 0x53, 0x1f, 0x8, 0x44, 0x90, 0xdc, 0x25, 0x69, 0xbd, 0xf1, 0x52, 0x1e, 0xca, 0x86, 0x7f, 0x33, 0xe7, 0xab, 0x23, 0x6f, 0xbb, 0xf7, 0xe, 0x42, 0x96, 0xda, 0x79, 0x35, 0xe1, 0xad, 0x54, 0x18, 0xcc, 0x80, 0x97, 0xdb, 0xf, 0x43, 0xba, 0xf6, 0x22, 0x6e, 0xcd, 0x81, 0x55, 0x19, 0xe0, 0xac, 0x78, 0x34, 0x56, 0x1a, 0xce, 0x82, 0x7b, 0x37, 0xe3, 0xaf, 0xc, 0x40, 0x94, 0xd8, 0x21, 0x6d, 0xb9, 0xf5, 0xe2, 0xae, 0x7a, 0x36, 0xcf, 0x83, 0x57, 0x1b, 0xb8, 0xf4, 0x20, 0x6c, 0x95, 0xd9, 0xd, 0x41},
+ {0x0, 0x4d, 0x9a, 0xd7, 0x29, 0x64, 0xb3, 0xfe, 0x52, 0x1f, 0xc8, 0x85, 0x7b, 0x36, 0xe1, 0xac, 0xa4, 0xe9, 0x3e, 0x73, 0x8d, 0xc0, 0x17, 0x5a, 0xf6, 0xbb, 0x6c, 0x21, 0xdf, 0x92, 0x45, 0x8, 0x55, 0x18, 0xcf, 0x82, 0x7c, 0x31, 0xe6, 0xab, 0x7, 0x4a, 0x9d, 0xd0, 0x2e, 0x63, 0xb4, 0xf9, 0xf1, 0xbc, 0x6b, 0x26, 0xd8, 0x95, 0x42, 0xf, 0xa3, 0xee, 0x39, 0x74, 0x8a, 0xc7, 0x10, 0x5d, 0xaa, 0xe7, 0x30, 0x7d, 0x83, 0xce, 0x19, 0x54, 0xf8, 0xb5, 0x62, 0x2f, 0xd1, 0x9c, 0x4b, 0x6, 0xe, 0x43, 0x94, 0xd9, 0x27, 0x6a, 0xbd, 0xf0, 0x5c, 0x11, 0xc6, 0x8b, 0x75, 0x38, 0xef, 0xa2, 0xff, 0xb2, 0x65, 0x28, 0xd6, 0x9b, 0x4c, 0x1, 0xad, 0xe0, 0x37, 0x7a, 0x84, 0xc9, 0x1e, 0x53, 0x5b, 0x16, 0xc1, 0x8c, 0x72, 0x3f, 0xe8, 0xa5, 0x9, 0x44, 0x93, 0xde, 0x20, 0x6d, 0xba, 0xf7, 0x49, 0x4, 0xd3, 0x9e, 0x60, 0x2d, 0xfa, 0xb7, 0x1b, 0x56, 0x81, 0xcc, 0x32, 0x7f, 0xa8, 0xe5, 0xed, 0xa0, 0x77, 0x3a, 0xc4, 0x89, 0x5e, 0x13, 0xbf, 0xf2, 0x25, 0x68, 0x96, 0xdb, 0xc, 0x41, 0x1c, 0x51, 0x86, 0xcb, 0x35, 0x78, 0xaf, 0xe2, 0x4e, 0x3, 0xd4, 0x99, 0x67, 0x2a, 0xfd, 0xb0, 0xb8, 0xf5, 0x22, 0x6f, 0x91, 0xdc, 0xb, 0x46, 0xea, 0xa7, 0x70, 0x3d, 0xc3, 0x8e, 0x59, 0x14, 0xe3, 0xae, 0x79, 0x34, 0xca, 0x87, 0x50, 0x1d, 0xb1, 0xfc, 0x2b, 0x66, 0x98, 0xd5, 0x2, 0x4f, 0x47, 0xa, 0xdd, 0x90, 0x6e, 0x23, 0xf4, 0xb9, 0x15, 0x58, 0x8f, 0xc2, 0x3c, 0x71, 0xa6, 0xeb, 0xb6, 0xfb, 0x2c, 0x61, 0x9f, 0xd2, 0x5, 0x48, 0xe4, 0xa9, 0x7e, 0x33, 0xcd, 0x80, 0x57, 0x1a, 0x12, 0x5f, 0x88, 0xc5, 0x3b, 0x76, 0xa1, 0xec, 0x40, 0xd, 0xda, 0x97, 0x69, 0x24, 0xf3, 0xbe},
+ {0x0, 0x4e, 0x9c, 0xd2, 0x25, 0x6b, 0xb9, 0xf7, 0x4a, 0x4, 0xd6, 0x98, 0x6f, 0x21, 0xf3, 0xbd, 0x94, 0xda, 0x8, 0x46, 0xb1, 0xff, 0x2d, 0x63, 0xde, 0x90, 0x42, 0xc, 0xfb, 0xb5, 0x67, 0x29, 0x35, 0x7b, 0xa9, 0xe7, 0x10, 0x5e, 0x8c, 0xc2, 0x7f, 0x31, 0xe3, 0xad, 0x5a, 0x14, 0xc6, 0x88, 0xa1, 0xef, 0x3d, 0x73, 0x84, 0xca, 0x18, 0x56, 0xeb, 0xa5, 0x77, 0x39, 0xce, 0x80, 0x52, 0x1c, 0x6a, 0x24, 0xf6, 0xb8, 0x4f, 0x1, 0xd3, 0x9d, 0x20, 0x6e, 0xbc, 0xf2, 0x5, 0x4b, 0x99, 0xd7, 0xfe, 0xb0, 0x62, 0x2c, 0xdb, 0x95, 0x47, 0x9, 0xb4, 0xfa, 0x28, 0x66, 0x91, 0xdf, 0xd, 0x43, 0x5f, 0x11, 0xc3, 0x8d, 0x7a, 0x34, 0xe6, 0xa8, 0x15, 0x5b, 0x89, 0xc7, 0x30, 0x7e, 0xac, 0xe2, 0xcb, 0x85, 0x57, 0x19, 0xee, 0xa0, 0x72, 0x3c, 0x81, 0xcf, 0x1d, 0x53, 0xa4, 0xea, 0x38, 0x76, 0xd4, 0x9a, 0x48, 0x6, 0xf1, 0xbf, 0x6d, 0x23, 0x9e, 0xd0, 0x2, 0x4c, 0xbb, 0xf5, 0x27, 0x69, 0x40, 0xe, 0xdc, 0x92, 0x65, 0x2b, 0xf9, 0xb7, 0xa, 0x44, 0x96, 0xd8, 0x2f, 0x61, 0xb3, 0xfd, 0xe1, 0xaf, 0x7d, 0x33, 0xc4, 0x8a, 0x58, 0x16, 0xab, 0xe5, 0x37, 0x79, 0x8e, 0xc0, 0x12, 0x5c, 0x75, 0x3b, 0xe9, 0xa7, 0x50, 0x1e, 0xcc, 0x82, 0x3f, 0x71, 0xa3, 0xed, 0x1a, 0x54, 0x86, 0xc8, 0xbe, 0xf0, 0x22, 0x6c, 0x9b, 0xd5, 0x7, 0x49, 0xf4, 0xba, 0x68, 0x26, 0xd1, 0x9f, 0x4d, 0x3, 0x2a, 0x64, 0xb6, 0xf8, 0xf, 0x41, 0x93, 0xdd, 0x60, 0x2e, 0xfc, 0xb2, 0x45, 0xb, 0xd9, 0x97, 0x8b, 0xc5, 0x17, 0x59, 0xae, 0xe0, 0x32, 0x7c, 0xc1, 0x8f, 0x5d, 0x13, 0xe4, 0xaa, 0x78, 0x36, 0x1f, 0x51, 0x83, 0xcd, 0x3a, 0x74, 0xa6, 0xe8, 0x55, 0x1b, 0xc9, 0x87, 0x70, 0x3e, 0xec, 0xa2},
+ {0x0, 0x4f, 0x9e, 0xd1, 0x21, 0x6e, 0xbf, 0xf0, 0x42, 0xd, 0xdc, 0x93, 0x63, 0x2c, 0xfd, 0xb2, 0x84, 0xcb, 0x1a, 0x55, 0xa5, 0xea, 0x3b, 0x74, 0xc6, 0x89, 0x58, 0x17, 0xe7, 0xa8, 0x79, 0x36, 0x15, 0x5a, 0x8b, 0xc4, 0x34, 0x7b, 0xaa, 0xe5, 0x57, 0x18, 0xc9, 0x86, 0x76, 0x39, 0xe8, 0xa7, 0x91, 0xde, 0xf, 0x40, 0xb0, 0xff, 0x2e, 0x61, 0xd3, 0x9c, 0x4d, 0x2, 0xf2, 0xbd, 0x6c, 0x23, 0x2a, 0x65, 0xb4, 0xfb, 0xb, 0x44, 0x95, 0xda, 0x68, 0x27, 0xf6, 0xb9, 0x49, 0x6, 0xd7, 0x98, 0xae, 0xe1, 0x30, 0x7f, 0x8f, 0xc0, 0x11, 0x5e, 0xec, 0xa3, 0x72, 0x3d, 0xcd, 0x82, 0x53, 0x1c, 0x3f, 0x70, 0xa1, 0xee, 0x1e, 0x51, 0x80, 0xcf, 0x7d, 0x32, 0xe3, 0xac, 0x5c, 0x13, 0xc2, 0x8d, 0xbb, 0xf4, 0x25, 0x6a, 0x9a, 0xd5, 0x4, 0x4b, 0xf9, 0xb6, 0x67, 0x28, 0xd8, 0x97, 0x46, 0x9, 0x54, 0x1b, 0xca, 0x85, 0x75, 0x3a, 0xeb, 0xa4, 0x16, 0x59, 0x88, 0xc7, 0x37, 0x78, 0xa9, 0xe6, 0xd0, 0x9f, 0x4e, 0x1, 0xf1, 0xbe, 0x6f, 0x20, 0x92, 0xdd, 0xc, 0x43, 0xb3, 0xfc, 0x2d, 0x62, 0x41, 0xe, 0xdf, 0x90, 0x60, 0x2f, 0xfe, 0xb1, 0x3, 0x4c, 0x9d, 0xd2, 0x22, 0x6d, 0xbc, 0xf3, 0xc5, 0x8a, 0x5b, 0x14, 0xe4, 0xab, 0x7a, 0x35, 0x87, 0xc8, 0x19, 0x56, 0xa6, 0xe9, 0x38, 0x77, 0x7e, 0x31, 0xe0, 0xaf, 0x5f, 0x10, 0xc1, 0x8e, 0x3c, 0x73, 0xa2, 0xed, 0x1d, 0x52, 0x83, 0xcc, 0xfa, 0xb5, 0x64, 0x2b, 0xdb, 0x94, 0x45, 0xa, 0xb8, 0xf7, 0x26, 0x69, 0x99, 0xd6, 0x7, 0x48, 0x6b, 0x24, 0xf5, 0xba, 0x4a, 0x5, 0xd4, 0x9b, 0x29, 0x66, 0xb7, 0xf8, 0x8, 0x47, 0x96, 0xd9, 0xef, 0xa0, 0x71, 0x3e, 0xce, 0x81, 0x50, 0x1f, 0xad, 0xe2, 0x33, 0x7c, 0x8c, 0xc3, 0x12, 0x5d},
+ {0x0, 0x50, 0xa0, 0xf0, 0x5d, 0xd, 0xfd, 0xad, 0xba, 0xea, 0x1a, 0x4a, 0xe7, 0xb7, 0x47, 0x17, 0x69, 0x39, 0xc9, 0x99, 0x34, 0x64, 0x94, 0xc4, 0xd3, 0x83, 0x73, 0x23, 0x8e, 0xde, 0x2e, 0x7e, 0xd2, 0x82, 0x72, 0x22, 0x8f, 0xdf, 0x2f, 0x7f, 0x68, 0x38, 0xc8, 0x98, 0x35, 0x65, 0x95, 0xc5, 0xbb, 0xeb, 0x1b, 0x4b, 0xe6, 0xb6, 0x46, 0x16, 0x1, 0x51, 0xa1, 0xf1, 0x5c, 0xc, 0xfc, 0xac, 0xb9, 0xe9, 0x19, 0x49, 0xe4, 0xb4, 0x44, 0x14, 0x3, 0x53, 0xa3, 0xf3, 0x5e, 0xe, 0xfe, 0xae, 0xd0, 0x80, 0x70, 0x20, 0x8d, 0xdd, 0x2d, 0x7d, 0x6a, 0x3a, 0xca, 0x9a, 0x37, 0x67, 0x97, 0xc7, 0x6b, 0x3b, 0xcb, 0x9b, 0x36, 0x66, 0x96, 0xc6, 0xd1, 0x81, 0x71, 0x21, 0x8c, 0xdc, 0x2c, 0x7c, 0x2, 0x52, 0xa2, 0xf2, 0x5f, 0xf, 0xff, 0xaf, 0xb8, 0xe8, 0x18, 0x48, 0xe5, 0xb5, 0x45, 0x15, 0x6f, 0x3f, 0xcf, 0x9f, 0x32, 0x62, 0x92, 0xc2, 0xd5, 0x85, 0x75, 0x25, 0x88, 0xd8, 0x28, 0x78, 0x6, 0x56, 0xa6, 0xf6, 0x5b, 0xb, 0xfb, 0xab, 0xbc, 0xec, 0x1c, 0x4c, 0xe1, 0xb1, 0x41, 0x11, 0xbd, 0xed, 0x1d, 0x4d, 0xe0, 0xb0, 0x40, 0x10, 0x7, 0x57, 0xa7, 0xf7, 0x5a, 0xa, 0xfa, 0xaa, 0xd4, 0x84, 0x74, 0x24, 0x89, 0xd9, 0x29, 0x79, 0x6e, 0x3e, 0xce, 0x9e, 0x33, 0x63, 0x93, 0xc3, 0xd6, 0x86, 0x76, 0x26, 0x8b, 0xdb, 0x2b, 0x7b, 0x6c, 0x3c, 0xcc, 0x9c, 0x31, 0x61, 0x91, 0xc1, 0xbf, 0xef, 0x1f, 0x4f, 0xe2, 0xb2, 0x42, 0x12, 0x5, 0x55, 0xa5, 0xf5, 0x58, 0x8, 0xf8, 0xa8, 0x4, 0x54, 0xa4, 0xf4, 0x59, 0x9, 0xf9, 0xa9, 0xbe, 0xee, 0x1e, 0x4e, 0xe3, 0xb3, 0x43, 0x13, 0x6d, 0x3d, 0xcd, 0x9d, 0x30, 0x60, 0x90, 0xc0, 0xd7, 0x87, 0x77, 0x27, 0x8a, 0xda, 0x2a, 0x7a},
+ {0x0, 0x51, 0xa2, 0xf3, 0x59, 0x8, 0xfb, 0xaa, 0xb2, 0xe3, 0x10, 0x41, 0xeb, 0xba, 0x49, 0x18, 0x79, 0x28, 0xdb, 0x8a, 0x20, 0x71, 0x82, 0xd3, 0xcb, 0x9a, 0x69, 0x38, 0x92, 0xc3, 0x30, 0x61, 0xf2, 0xa3, 0x50, 0x1, 0xab, 0xfa, 0x9, 0x58, 0x40, 0x11, 0xe2, 0xb3, 0x19, 0x48, 0xbb, 0xea, 0x8b, 0xda, 0x29, 0x78, 0xd2, 0x83, 0x70, 0x21, 0x39, 0x68, 0x9b, 0xca, 0x60, 0x31, 0xc2, 0x93, 0xf9, 0xa8, 0x5b, 0xa, 0xa0, 0xf1, 0x2, 0x53, 0x4b, 0x1a, 0xe9, 0xb8, 0x12, 0x43, 0xb0, 0xe1, 0x80, 0xd1, 0x22, 0x73, 0xd9, 0x88, 0x7b, 0x2a, 0x32, 0x63, 0x90, 0xc1, 0x6b, 0x3a, 0xc9, 0x98, 0xb, 0x5a, 0xa9, 0xf8, 0x52, 0x3, 0xf0, 0xa1, 0xb9, 0xe8, 0x1b, 0x4a, 0xe0, 0xb1, 0x42, 0x13, 0x72, 0x23, 0xd0, 0x81, 0x2b, 0x7a, 0x89, 0xd8, 0xc0, 0x91, 0x62, 0x33, 0x99, 0xc8, 0x3b, 0x6a, 0xef, 0xbe, 0x4d, 0x1c, 0xb6, 0xe7, 0x14, 0x45, 0x5d, 0xc, 0xff, 0xae, 0x4, 0x55, 0xa6, 0xf7, 0x96, 0xc7, 0x34, 0x65, 0xcf, 0x9e, 0x6d, 0x3c, 0x24, 0x75, 0x86, 0xd7, 0x7d, 0x2c, 0xdf, 0x8e, 0x1d, 0x4c, 0xbf, 0xee, 0x44, 0x15, 0xe6, 0xb7, 0xaf, 0xfe, 0xd, 0x5c, 0xf6, 0xa7, 0x54, 0x5, 0x64, 0x35, 0xc6, 0x97, 0x3d, 0x6c, 0x9f, 0xce, 0xd6, 0x87, 0x74, 0x25, 0x8f, 0xde, 0x2d, 0x7c, 0x16, 0x47, 0xb4, 0xe5, 0x4f, 0x1e, 0xed, 0xbc, 0xa4, 0xf5, 0x6, 0x57, 0xfd, 0xac, 0x5f, 0xe, 0x6f, 0x3e, 0xcd, 0x9c, 0x36, 0x67, 0x94, 0xc5, 0xdd, 0x8c, 0x7f, 0x2e, 0x84, 0xd5, 0x26, 0x77, 0xe4, 0xb5, 0x46, 0x17, 0xbd, 0xec, 0x1f, 0x4e, 0x56, 0x7, 0xf4, 0xa5, 0xf, 0x5e, 0xad, 0xfc, 0x9d, 0xcc, 0x3f, 0x6e, 0xc4, 0x95, 0x66, 0x37, 0x2f, 0x7e, 0x8d, 0xdc, 0x76, 0x27, 0xd4, 0x85},
+ {0x0, 0x52, 0xa4, 0xf6, 0x55, 0x7, 0xf1, 0xa3, 0xaa, 0xf8, 0xe, 0x5c, 0xff, 0xad, 0x5b, 0x9, 0x49, 0x1b, 0xed, 0xbf, 0x1c, 0x4e, 0xb8, 0xea, 0xe3, 0xb1, 0x47, 0x15, 0xb6, 0xe4, 0x12, 0x40, 0x92, 0xc0, 0x36, 0x64, 0xc7, 0x95, 0x63, 0x31, 0x38, 0x6a, 0x9c, 0xce, 0x6d, 0x3f, 0xc9, 0x9b, 0xdb, 0x89, 0x7f, 0x2d, 0x8e, 0xdc, 0x2a, 0x78, 0x71, 0x23, 0xd5, 0x87, 0x24, 0x76, 0x80, 0xd2, 0x39, 0x6b, 0x9d, 0xcf, 0x6c, 0x3e, 0xc8, 0x9a, 0x93, 0xc1, 0x37, 0x65, 0xc6, 0x94, 0x62, 0x30, 0x70, 0x22, 0xd4, 0x86, 0x25, 0x77, 0x81, 0xd3, 0xda, 0x88, 0x7e, 0x2c, 0x8f, 0xdd, 0x2b, 0x79, 0xab, 0xf9, 0xf, 0x5d, 0xfe, 0xac, 0x5a, 0x8, 0x1, 0x53, 0xa5, 0xf7, 0x54, 0x6, 0xf0, 0xa2, 0xe2, 0xb0, 0x46, 0x14, 0xb7, 0xe5, 0x13, 0x41, 0x48, 0x1a, 0xec, 0xbe, 0x1d, 0x4f, 0xb9, 0xeb, 0x72, 0x20, 0xd6, 0x84, 0x27, 0x75, 0x83, 0xd1, 0xd8, 0x8a, 0x7c, 0x2e, 0x8d, 0xdf, 0x29, 0x7b, 0x3b, 0x69, 0x9f, 0xcd, 0x6e, 0x3c, 0xca, 0x98, 0x91, 0xc3, 0x35, 0x67, 0xc4, 0x96, 0x60, 0x32, 0xe0, 0xb2, 0x44, 0x16, 0xb5, 0xe7, 0x11, 0x43, 0x4a, 0x18, 0xee, 0xbc, 0x1f, 0x4d, 0xbb, 0xe9, 0xa9, 0xfb, 0xd, 0x5f, 0xfc, 0xae, 0x58, 0xa, 0x3, 0x51, 0xa7, 0xf5, 0x56, 0x4, 0xf2, 0xa0, 0x4b, 0x19, 0xef, 0xbd, 0x1e, 0x4c, 0xba, 0xe8, 0xe1, 0xb3, 0x45, 0x17, 0xb4, 0xe6, 0x10, 0x42, 0x2, 0x50, 0xa6, 0xf4, 0x57, 0x5, 0xf3, 0xa1, 0xa8, 0xfa, 0xc, 0x5e, 0xfd, 0xaf, 0x59, 0xb, 0xd9, 0x8b, 0x7d, 0x2f, 0x8c, 0xde, 0x28, 0x7a, 0x73, 0x21, 0xd7, 0x85, 0x26, 0x74, 0x82, 0xd0, 0x90, 0xc2, 0x34, 0x66, 0xc5, 0x97, 0x61, 0x33, 0x3a, 0x68, 0x9e, 0xcc, 0x6f, 0x3d, 0xcb, 0x99},
+ {0x0, 0x53, 0xa6, 0xf5, 0x51, 0x2, 0xf7, 0xa4, 0xa2, 0xf1, 0x4, 0x57, 0xf3, 0xa0, 0x55, 0x6, 0x59, 0xa, 0xff, 0xac, 0x8, 0x5b, 0xae, 0xfd, 0xfb, 0xa8, 0x5d, 0xe, 0xaa, 0xf9, 0xc, 0x5f, 0xb2, 0xe1, 0x14, 0x47, 0xe3, 0xb0, 0x45, 0x16, 0x10, 0x43, 0xb6, 0xe5, 0x41, 0x12, 0xe7, 0xb4, 0xeb, 0xb8, 0x4d, 0x1e, 0xba, 0xe9, 0x1c, 0x4f, 0x49, 0x1a, 0xef, 0xbc, 0x18, 0x4b, 0xbe, 0xed, 0x79, 0x2a, 0xdf, 0x8c, 0x28, 0x7b, 0x8e, 0xdd, 0xdb, 0x88, 0x7d, 0x2e, 0x8a, 0xd9, 0x2c, 0x7f, 0x20, 0x73, 0x86, 0xd5, 0x71, 0x22, 0xd7, 0x84, 0x82, 0xd1, 0x24, 0x77, 0xd3, 0x80, 0x75, 0x26, 0xcb, 0x98, 0x6d, 0x3e, 0x9a, 0xc9, 0x3c, 0x6f, 0x69, 0x3a, 0xcf, 0x9c, 0x38, 0x6b, 0x9e, 0xcd, 0x92, 0xc1, 0x34, 0x67, 0xc3, 0x90, 0x65, 0x36, 0x30, 0x63, 0x96, 0xc5, 0x61, 0x32, 0xc7, 0x94, 0xf2, 0xa1, 0x54, 0x7, 0xa3, 0xf0, 0x5, 0x56, 0x50, 0x3, 0xf6, 0xa5, 0x1, 0x52, 0xa7, 0xf4, 0xab, 0xf8, 0xd, 0x5e, 0xfa, 0xa9, 0x5c, 0xf, 0x9, 0x5a, 0xaf, 0xfc, 0x58, 0xb, 0xfe, 0xad, 0x40, 0x13, 0xe6, 0xb5, 0x11, 0x42, 0xb7, 0xe4, 0xe2, 0xb1, 0x44, 0x17, 0xb3, 0xe0, 0x15, 0x46, 0x19, 0x4a, 0xbf, 0xec, 0x48, 0x1b, 0xee, 0xbd, 0xbb, 0xe8, 0x1d, 0x4e, 0xea, 0xb9, 0x4c, 0x1f, 0x8b, 0xd8, 0x2d, 0x7e, 0xda, 0x89, 0x7c, 0x2f, 0x29, 0x7a, 0x8f, 0xdc, 0x78, 0x2b, 0xde, 0x8d, 0xd2, 0x81, 0x74, 0x27, 0x83, 0xd0, 0x25, 0x76, 0x70, 0x23, 0xd6, 0x85, 0x21, 0x72, 0x87, 0xd4, 0x39, 0x6a, 0x9f, 0xcc, 0x68, 0x3b, 0xce, 0x9d, 0x9b, 0xc8, 0x3d, 0x6e, 0xca, 0x99, 0x6c, 0x3f, 0x60, 0x33, 0xc6, 0x95, 0x31, 0x62, 0x97, 0xc4, 0xc2, 0x91, 0x64, 0x37, 0x93, 0xc0, 0x35, 0x66},
+ {0x0, 0x54, 0xa8, 0xfc, 0x4d, 0x19, 0xe5, 0xb1, 0x9a, 0xce, 0x32, 0x66, 0xd7, 0x83, 0x7f, 0x2b, 0x29, 0x7d, 0x81, 0xd5, 0x64, 0x30, 0xcc, 0x98, 0xb3, 0xe7, 0x1b, 0x4f, 0xfe, 0xaa, 0x56, 0x2, 0x52, 0x6, 0xfa, 0xae, 0x1f, 0x4b, 0xb7, 0xe3, 0xc8, 0x9c, 0x60, 0x34, 0x85, 0xd1, 0x2d, 0x79, 0x7b, 0x2f, 0xd3, 0x87, 0x36, 0x62, 0x9e, 0xca, 0xe1, 0xb5, 0x49, 0x1d, 0xac, 0xf8, 0x4, 0x50, 0xa4, 0xf0, 0xc, 0x58, 0xe9, 0xbd, 0x41, 0x15, 0x3e, 0x6a, 0x96, 0xc2, 0x73, 0x27, 0xdb, 0x8f, 0x8d, 0xd9, 0x25, 0x71, 0xc0, 0x94, 0x68, 0x3c, 0x17, 0x43, 0xbf, 0xeb, 0x5a, 0xe, 0xf2, 0xa6, 0xf6, 0xa2, 0x5e, 0xa, 0xbb, 0xef, 0x13, 0x47, 0x6c, 0x38, 0xc4, 0x90, 0x21, 0x75, 0x89, 0xdd, 0xdf, 0x8b, 0x77, 0x23, 0x92, 0xc6, 0x3a, 0x6e, 0x45, 0x11, 0xed, 0xb9, 0x8, 0x5c, 0xa0, 0xf4, 0x55, 0x1, 0xfd, 0xa9, 0x18, 0x4c, 0xb0, 0xe4, 0xcf, 0x9b, 0x67, 0x33, 0x82, 0xd6, 0x2a, 0x7e, 0x7c, 0x28, 0xd4, 0x80, 0x31, 0x65, 0x99, 0xcd, 0xe6, 0xb2, 0x4e, 0x1a, 0xab, 0xff, 0x3, 0x57, 0x7, 0x53, 0xaf, 0xfb, 0x4a, 0x1e, 0xe2, 0xb6, 0x9d, 0xc9, 0x35, 0x61, 0xd0, 0x84, 0x78, 0x2c, 0x2e, 0x7a, 0x86, 0xd2, 0x63, 0x37, 0xcb, 0x9f, 0xb4, 0xe0, 0x1c, 0x48, 0xf9, 0xad, 0x51, 0x5, 0xf1, 0xa5, 0x59, 0xd, 0xbc, 0xe8, 0x14, 0x40, 0x6b, 0x3f, 0xc3, 0x97, 0x26, 0x72, 0x8e, 0xda, 0xd8, 0x8c, 0x70, 0x24, 0x95, 0xc1, 0x3d, 0x69, 0x42, 0x16, 0xea, 0xbe, 0xf, 0x5b, 0xa7, 0xf3, 0xa3, 0xf7, 0xb, 0x5f, 0xee, 0xba, 0x46, 0x12, 0x39, 0x6d, 0x91, 0xc5, 0x74, 0x20, 0xdc, 0x88, 0x8a, 0xde, 0x22, 0x76, 0xc7, 0x93, 0x6f, 0x3b, 0x10, 0x44, 0xb8, 0xec, 0x5d, 0x9, 0xf5, 0xa1},
+ {0x0, 0x55, 0xaa, 0xff, 0x49, 0x1c, 0xe3, 0xb6, 0x92, 0xc7, 0x38, 0x6d, 0xdb, 0x8e, 0x71, 0x24, 0x39, 0x6c, 0x93, 0xc6, 0x70, 0x25, 0xda, 0x8f, 0xab, 0xfe, 0x1, 0x54, 0xe2, 0xb7, 0x48, 0x1d, 0x72, 0x27, 0xd8, 0x8d, 0x3b, 0x6e, 0x91, 0xc4, 0xe0, 0xb5, 0x4a, 0x1f, 0xa9, 0xfc, 0x3, 0x56, 0x4b, 0x1e, 0xe1, 0xb4, 0x2, 0x57, 0xa8, 0xfd, 0xd9, 0x8c, 0x73, 0x26, 0x90, 0xc5, 0x3a, 0x6f, 0xe4, 0xb1, 0x4e, 0x1b, 0xad, 0xf8, 0x7, 0x52, 0x76, 0x23, 0xdc, 0x89, 0x3f, 0x6a, 0x95, 0xc0, 0xdd, 0x88, 0x77, 0x22, 0x94, 0xc1, 0x3e, 0x6b, 0x4f, 0x1a, 0xe5, 0xb0, 0x6, 0x53, 0xac, 0xf9, 0x96, 0xc3, 0x3c, 0x69, 0xdf, 0x8a, 0x75, 0x20, 0x4, 0x51, 0xae, 0xfb, 0x4d, 0x18, 0xe7, 0xb2, 0xaf, 0xfa, 0x5, 0x50, 0xe6, 0xb3, 0x4c, 0x19, 0x3d, 0x68, 0x97, 0xc2, 0x74, 0x21, 0xde, 0x8b, 0xd5, 0x80, 0x7f, 0x2a, 0x9c, 0xc9, 0x36, 0x63, 0x47, 0x12, 0xed, 0xb8, 0xe, 0x5b, 0xa4, 0xf1, 0xec, 0xb9, 0x46, 0x13, 0xa5, 0xf0, 0xf, 0x5a, 0x7e, 0x2b, 0xd4, 0x81, 0x37, 0x62, 0x9d, 0xc8, 0xa7, 0xf2, 0xd, 0x58, 0xee, 0xbb, 0x44, 0x11, 0x35, 0x60, 0x9f, 0xca, 0x7c, 0x29, 0xd6, 0x83, 0x9e, 0xcb, 0x34, 0x61, 0xd7, 0x82, 0x7d, 0x28, 0xc, 0x59, 0xa6, 0xf3, 0x45, 0x10, 0xef, 0xba, 0x31, 0x64, 0x9b, 0xce, 0x78, 0x2d, 0xd2, 0x87, 0xa3, 0xf6, 0x9, 0x5c, 0xea, 0xbf, 0x40, 0x15, 0x8, 0x5d, 0xa2, 0xf7, 0x41, 0x14, 0xeb, 0xbe, 0x9a, 0xcf, 0x30, 0x65, 0xd3, 0x86, 0x79, 0x2c, 0x43, 0x16, 0xe9, 0xbc, 0xa, 0x5f, 0xa0, 0xf5, 0xd1, 0x84, 0x7b, 0x2e, 0x98, 0xcd, 0x32, 0x67, 0x7a, 0x2f, 0xd0, 0x85, 0x33, 0x66, 0x99, 0xcc, 0xe8, 0xbd, 0x42, 0x17, 0xa1, 0xf4, 0xb, 0x5e},
+ {0x0, 0x56, 0xac, 0xfa, 0x45, 0x13, 0xe9, 0xbf, 0x8a, 0xdc, 0x26, 0x70, 0xcf, 0x99, 0x63, 0x35, 0x9, 0x5f, 0xa5, 0xf3, 0x4c, 0x1a, 0xe0, 0xb6, 0x83, 0xd5, 0x2f, 0x79, 0xc6, 0x90, 0x6a, 0x3c, 0x12, 0x44, 0xbe, 0xe8, 0x57, 0x1, 0xfb, 0xad, 0x98, 0xce, 0x34, 0x62, 0xdd, 0x8b, 0x71, 0x27, 0x1b, 0x4d, 0xb7, 0xe1, 0x5e, 0x8, 0xf2, 0xa4, 0x91, 0xc7, 0x3d, 0x6b, 0xd4, 0x82, 0x78, 0x2e, 0x24, 0x72, 0x88, 0xde, 0x61, 0x37, 0xcd, 0x9b, 0xae, 0xf8, 0x2, 0x54, 0xeb, 0xbd, 0x47, 0x11, 0x2d, 0x7b, 0x81, 0xd7, 0x68, 0x3e, 0xc4, 0x92, 0xa7, 0xf1, 0xb, 0x5d, 0xe2, 0xb4, 0x4e, 0x18, 0x36, 0x60, 0x9a, 0xcc, 0x73, 0x25, 0xdf, 0x89, 0xbc, 0xea, 0x10, 0x46, 0xf9, 0xaf, 0x55, 0x3, 0x3f, 0x69, 0x93, 0xc5, 0x7a, 0x2c, 0xd6, 0x80, 0xb5, 0xe3, 0x19, 0x4f, 0xf0, 0xa6, 0x5c, 0xa, 0x48, 0x1e, 0xe4, 0xb2, 0xd, 0x5b, 0xa1, 0xf7, 0xc2, 0x94, 0x6e, 0x38, 0x87, 0xd1, 0x2b, 0x7d, 0x41, 0x17, 0xed, 0xbb, 0x4, 0x52, 0xa8, 0xfe, 0xcb, 0x9d, 0x67, 0x31, 0x8e, 0xd8, 0x22, 0x74, 0x5a, 0xc, 0xf6, 0xa0, 0x1f, 0x49, 0xb3, 0xe5, 0xd0, 0x86, 0x7c, 0x2a, 0x95, 0xc3, 0x39, 0x6f, 0x53, 0x5, 0xff, 0xa9, 0x16, 0x40, 0xba, 0xec, 0xd9, 0x8f, 0x75, 0x23, 0x9c, 0xca, 0x30, 0x66, 0x6c, 0x3a, 0xc0, 0x96, 0x29, 0x7f, 0x85, 0xd3, 0xe6, 0xb0, 0x4a, 0x1c, 0xa3, 0xf5, 0xf, 0x59, 0x65, 0x33, 0xc9, 0x9f, 0x20, 0x76, 0x8c, 0xda, 0xef, 0xb9, 0x43, 0x15, 0xaa, 0xfc, 0x6, 0x50, 0x7e, 0x28, 0xd2, 0x84, 0x3b, 0x6d, 0x97, 0xc1, 0xf4, 0xa2, 0x58, 0xe, 0xb1, 0xe7, 0x1d, 0x4b, 0x77, 0x21, 0xdb, 0x8d, 0x32, 0x64, 0x9e, 0xc8, 0xfd, 0xab, 0x51, 0x7, 0xb8, 0xee, 0x14, 0x42},
+ {0x0, 0x57, 0xae, 0xf9, 0x41, 0x16, 0xef, 0xb8, 0x82, 0xd5, 0x2c, 0x7b, 0xc3, 0x94, 0x6d, 0x3a, 0x19, 0x4e, 0xb7, 0xe0, 0x58, 0xf, 0xf6, 0xa1, 0x9b, 0xcc, 0x35, 0x62, 0xda, 0x8d, 0x74, 0x23, 0x32, 0x65, 0x9c, 0xcb, 0x73, 0x24, 0xdd, 0x8a, 0xb0, 0xe7, 0x1e, 0x49, 0xf1, 0xa6, 0x5f, 0x8, 0x2b, 0x7c, 0x85, 0xd2, 0x6a, 0x3d, 0xc4, 0x93, 0xa9, 0xfe, 0x7, 0x50, 0xe8, 0xbf, 0x46, 0x11, 0x64, 0x33, 0xca, 0x9d, 0x25, 0x72, 0x8b, 0xdc, 0xe6, 0xb1, 0x48, 0x1f, 0xa7, 0xf0, 0x9, 0x5e, 0x7d, 0x2a, 0xd3, 0x84, 0x3c, 0x6b, 0x92, 0xc5, 0xff, 0xa8, 0x51, 0x6, 0xbe, 0xe9, 0x10, 0x47, 0x56, 0x1, 0xf8, 0xaf, 0x17, 0x40, 0xb9, 0xee, 0xd4, 0x83, 0x7a, 0x2d, 0x95, 0xc2, 0x3b, 0x6c, 0x4f, 0x18, 0xe1, 0xb6, 0xe, 0x59, 0xa0, 0xf7, 0xcd, 0x9a, 0x63, 0x34, 0x8c, 0xdb, 0x22, 0x75, 0xc8, 0x9f, 0x66, 0x31, 0x89, 0xde, 0x27, 0x70, 0x4a, 0x1d, 0xe4, 0xb3, 0xb, 0x5c, 0xa5, 0xf2, 0xd1, 0x86, 0x7f, 0x28, 0x90, 0xc7, 0x3e, 0x69, 0x53, 0x4, 0xfd, 0xaa, 0x12, 0x45, 0xbc, 0xeb, 0xfa, 0xad, 0x54, 0x3, 0xbb, 0xec, 0x15, 0x42, 0x78, 0x2f, 0xd6, 0x81, 0x39, 0x6e, 0x97, 0xc0, 0xe3, 0xb4, 0x4d, 0x1a, 0xa2, 0xf5, 0xc, 0x5b, 0x61, 0x36, 0xcf, 0x98, 0x20, 0x77, 0x8e, 0xd9, 0xac, 0xfb, 0x2, 0x55, 0xed, 0xba, 0x43, 0x14, 0x2e, 0x79, 0x80, 0xd7, 0x6f, 0x38, 0xc1, 0x96, 0xb5, 0xe2, 0x1b, 0x4c, 0xf4, 0xa3, 0x5a, 0xd, 0x37, 0x60, 0x99, 0xce, 0x76, 0x21, 0xd8, 0x8f, 0x9e, 0xc9, 0x30, 0x67, 0xdf, 0x88, 0x71, 0x26, 0x1c, 0x4b, 0xb2, 0xe5, 0x5d, 0xa, 0xf3, 0xa4, 0x87, 0xd0, 0x29, 0x7e, 0xc6, 0x91, 0x68, 0x3f, 0x5, 0x52, 0xab, 0xfc, 0x44, 0x13, 0xea, 0xbd},
+ {0x0, 0x58, 0xb0, 0xe8, 0x7d, 0x25, 0xcd, 0x95, 0xfa, 0xa2, 0x4a, 0x12, 0x87, 0xdf, 0x37, 0x6f, 0xe9, 0xb1, 0x59, 0x1, 0x94, 0xcc, 0x24, 0x7c, 0x13, 0x4b, 0xa3, 0xfb, 0x6e, 0x36, 0xde, 0x86, 0xcf, 0x97, 0x7f, 0x27, 0xb2, 0xea, 0x2, 0x5a, 0x35, 0x6d, 0x85, 0xdd, 0x48, 0x10, 0xf8, 0xa0, 0x26, 0x7e, 0x96, 0xce, 0x5b, 0x3, 0xeb, 0xb3, 0xdc, 0x84, 0x6c, 0x34, 0xa1, 0xf9, 0x11, 0x49, 0x83, 0xdb, 0x33, 0x6b, 0xfe, 0xa6, 0x4e, 0x16, 0x79, 0x21, 0xc9, 0x91, 0x4, 0x5c, 0xb4, 0xec, 0x6a, 0x32, 0xda, 0x82, 0x17, 0x4f, 0xa7, 0xff, 0x90, 0xc8, 0x20, 0x78, 0xed, 0xb5, 0x5d, 0x5, 0x4c, 0x14, 0xfc, 0xa4, 0x31, 0x69, 0x81, 0xd9, 0xb6, 0xee, 0x6, 0x5e, 0xcb, 0x93, 0x7b, 0x23, 0xa5, 0xfd, 0x15, 0x4d, 0xd8, 0x80, 0x68, 0x30, 0x5f, 0x7, 0xef, 0xb7, 0x22, 0x7a, 0x92, 0xca, 0x1b, 0x43, 0xab, 0xf3, 0x66, 0x3e, 0xd6, 0x8e, 0xe1, 0xb9, 0x51, 0x9, 0x9c, 0xc4, 0x2c, 0x74, 0xf2, 0xaa, 0x42, 0x1a, 0x8f, 0xd7, 0x3f, 0x67, 0x8, 0x50, 0xb8, 0xe0, 0x75, 0x2d, 0xc5, 0x9d, 0xd4, 0x8c, 0x64, 0x3c, 0xa9, 0xf1, 0x19, 0x41, 0x2e, 0x76, 0x9e, 0xc6, 0x53, 0xb, 0xe3, 0xbb, 0x3d, 0x65, 0x8d, 0xd5, 0x40, 0x18, 0xf0, 0xa8, 0xc7, 0x9f, 0x77, 0x2f, 0xba, 0xe2, 0xa, 0x52, 0x98, 0xc0, 0x28, 0x70, 0xe5, 0xbd, 0x55, 0xd, 0x62, 0x3a, 0xd2, 0x8a, 0x1f, 0x47, 0xaf, 0xf7, 0x71, 0x29, 0xc1, 0x99, 0xc, 0x54, 0xbc, 0xe4, 0x8b, 0xd3, 0x3b, 0x63, 0xf6, 0xae, 0x46, 0x1e, 0x57, 0xf, 0xe7, 0xbf, 0x2a, 0x72, 0x9a, 0xc2, 0xad, 0xf5, 0x1d, 0x45, 0xd0, 0x88, 0x60, 0x38, 0xbe, 0xe6, 0xe, 0x56, 0xc3, 0x9b, 0x73, 0x2b, 0x44, 0x1c, 0xf4, 0xac, 0x39, 0x61, 0x89, 0xd1},
+ {0x0, 0x59, 0xb2, 0xeb, 0x79, 0x20, 0xcb, 0x92, 0xf2, 0xab, 0x40, 0x19, 0x8b, 0xd2, 0x39, 0x60, 0xf9, 0xa0, 0x4b, 0x12, 0x80, 0xd9, 0x32, 0x6b, 0xb, 0x52, 0xb9, 0xe0, 0x72, 0x2b, 0xc0, 0x99, 0xef, 0xb6, 0x5d, 0x4, 0x96, 0xcf, 0x24, 0x7d, 0x1d, 0x44, 0xaf, 0xf6, 0x64, 0x3d, 0xd6, 0x8f, 0x16, 0x4f, 0xa4, 0xfd, 0x6f, 0x36, 0xdd, 0x84, 0xe4, 0xbd, 0x56, 0xf, 0x9d, 0xc4, 0x2f, 0x76, 0xc3, 0x9a, 0x71, 0x28, 0xba, 0xe3, 0x8, 0x51, 0x31, 0x68, 0x83, 0xda, 0x48, 0x11, 0xfa, 0xa3, 0x3a, 0x63, 0x88, 0xd1, 0x43, 0x1a, 0xf1, 0xa8, 0xc8, 0x91, 0x7a, 0x23, 0xb1, 0xe8, 0x3, 0x5a, 0x2c, 0x75, 0x9e, 0xc7, 0x55, 0xc, 0xe7, 0xbe, 0xde, 0x87, 0x6c, 0x35, 0xa7, 0xfe, 0x15, 0x4c, 0xd5, 0x8c, 0x67, 0x3e, 0xac, 0xf5, 0x1e, 0x47, 0x27, 0x7e, 0x95, 0xcc, 0x5e, 0x7, 0xec, 0xb5, 0x9b, 0xc2, 0x29, 0x70, 0xe2, 0xbb, 0x50, 0x9, 0x69, 0x30, 0xdb, 0x82, 0x10, 0x49, 0xa2, 0xfb, 0x62, 0x3b, 0xd0, 0x89, 0x1b, 0x42, 0xa9, 0xf0, 0x90, 0xc9, 0x22, 0x7b, 0xe9, 0xb0, 0x5b, 0x2, 0x74, 0x2d, 0xc6, 0x9f, 0xd, 0x54, 0xbf, 0xe6, 0x86, 0xdf, 0x34, 0x6d, 0xff, 0xa6, 0x4d, 0x14, 0x8d, 0xd4, 0x3f, 0x66, 0xf4, 0xad, 0x46, 0x1f, 0x7f, 0x26, 0xcd, 0x94, 0x6, 0x5f, 0xb4, 0xed, 0x58, 0x1, 0xea, 0xb3, 0x21, 0x78, 0x93, 0xca, 0xaa, 0xf3, 0x18, 0x41, 0xd3, 0x8a, 0x61, 0x38, 0xa1, 0xf8, 0x13, 0x4a, 0xd8, 0x81, 0x6a, 0x33, 0x53, 0xa, 0xe1, 0xb8, 0x2a, 0x73, 0x98, 0xc1, 0xb7, 0xee, 0x5, 0x5c, 0xce, 0x97, 0x7c, 0x25, 0x45, 0x1c, 0xf7, 0xae, 0x3c, 0x65, 0x8e, 0xd7, 0x4e, 0x17, 0xfc, 0xa5, 0x37, 0x6e, 0x85, 0xdc, 0xbc, 0xe5, 0xe, 0x57, 0xc5, 0x9c, 0x77, 0x2e},
+ {0x0, 0x5a, 0xb4, 0xee, 0x75, 0x2f, 0xc1, 0x9b, 0xea, 0xb0, 0x5e, 0x4, 0x9f, 0xc5, 0x2b, 0x71, 0xc9, 0x93, 0x7d, 0x27, 0xbc, 0xe6, 0x8, 0x52, 0x23, 0x79, 0x97, 0xcd, 0x56, 0xc, 0xe2, 0xb8, 0x8f, 0xd5, 0x3b, 0x61, 0xfa, 0xa0, 0x4e, 0x14, 0x65, 0x3f, 0xd1, 0x8b, 0x10, 0x4a, 0xa4, 0xfe, 0x46, 0x1c, 0xf2, 0xa8, 0x33, 0x69, 0x87, 0xdd, 0xac, 0xf6, 0x18, 0x42, 0xd9, 0x83, 0x6d, 0x37, 0x3, 0x59, 0xb7, 0xed, 0x76, 0x2c, 0xc2, 0x98, 0xe9, 0xb3, 0x5d, 0x7, 0x9c, 0xc6, 0x28, 0x72, 0xca, 0x90, 0x7e, 0x24, 0xbf, 0xe5, 0xb, 0x51, 0x20, 0x7a, 0x94, 0xce, 0x55, 0xf, 0xe1, 0xbb, 0x8c, 0xd6, 0x38, 0x62, 0xf9, 0xa3, 0x4d, 0x17, 0x66, 0x3c, 0xd2, 0x88, 0x13, 0x49, 0xa7, 0xfd, 0x45, 0x1f, 0xf1, 0xab, 0x30, 0x6a, 0x84, 0xde, 0xaf, 0xf5, 0x1b, 0x41, 0xda, 0x80, 0x6e, 0x34, 0x6, 0x5c, 0xb2, 0xe8, 0x73, 0x29, 0xc7, 0x9d, 0xec, 0xb6, 0x58, 0x2, 0x99, 0xc3, 0x2d, 0x77, 0xcf, 0x95, 0x7b, 0x21, 0xba, 0xe0, 0xe, 0x54, 0x25, 0x7f, 0x91, 0xcb, 0x50, 0xa, 0xe4, 0xbe, 0x89, 0xd3, 0x3d, 0x67, 0xfc, 0xa6, 0x48, 0x12, 0x63, 0x39, 0xd7, 0x8d, 0x16, 0x4c, 0xa2, 0xf8, 0x40, 0x1a, 0xf4, 0xae, 0x35, 0x6f, 0x81, 0xdb, 0xaa, 0xf0, 0x1e, 0x44, 0xdf, 0x85, 0x6b, 0x31, 0x5, 0x5f, 0xb1, 0xeb, 0x70, 0x2a, 0xc4, 0x9e, 0xef, 0xb5, 0x5b, 0x1, 0x9a, 0xc0, 0x2e, 0x74, 0xcc, 0x96, 0x78, 0x22, 0xb9, 0xe3, 0xd, 0x57, 0x26, 0x7c, 0x92, 0xc8, 0x53, 0x9, 0xe7, 0xbd, 0x8a, 0xd0, 0x3e, 0x64, 0xff, 0xa5, 0x4b, 0x11, 0x60, 0x3a, 0xd4, 0x8e, 0x15, 0x4f, 0xa1, 0xfb, 0x43, 0x19, 0xf7, 0xad, 0x36, 0x6c, 0x82, 0xd8, 0xa9, 0xf3, 0x1d, 0x47, 0xdc, 0x86, 0x68, 0x32},
+ {0x0, 0x5b, 0xb6, 0xed, 0x71, 0x2a, 0xc7, 0x9c, 0xe2, 0xb9, 0x54, 0xf, 0x93, 0xc8, 0x25, 0x7e, 0xd9, 0x82, 0x6f, 0x34, 0xa8, 0xf3, 0x1e, 0x45, 0x3b, 0x60, 0x8d, 0xd6, 0x4a, 0x11, 0xfc, 0xa7, 0xaf, 0xf4, 0x19, 0x42, 0xde, 0x85, 0x68, 0x33, 0x4d, 0x16, 0xfb, 0xa0, 0x3c, 0x67, 0x8a, 0xd1, 0x76, 0x2d, 0xc0, 0x9b, 0x7, 0x5c, 0xb1, 0xea, 0x94, 0xcf, 0x22, 0x79, 0xe5, 0xbe, 0x53, 0x8, 0x43, 0x18, 0xf5, 0xae, 0x32, 0x69, 0x84, 0xdf, 0xa1, 0xfa, 0x17, 0x4c, 0xd0, 0x8b, 0x66, 0x3d, 0x9a, 0xc1, 0x2c, 0x77, 0xeb, 0xb0, 0x5d, 0x6, 0x78, 0x23, 0xce, 0x95, 0x9, 0x52, 0xbf, 0xe4, 0xec, 0xb7, 0x5a, 0x1, 0x9d, 0xc6, 0x2b, 0x70, 0xe, 0x55, 0xb8, 0xe3, 0x7f, 0x24, 0xc9, 0x92, 0x35, 0x6e, 0x83, 0xd8, 0x44, 0x1f, 0xf2, 0xa9, 0xd7, 0x8c, 0x61, 0x3a, 0xa6, 0xfd, 0x10, 0x4b, 0x86, 0xdd, 0x30, 0x6b, 0xf7, 0xac, 0x41, 0x1a, 0x64, 0x3f, 0xd2, 0x89, 0x15, 0x4e, 0xa3, 0xf8, 0x5f, 0x4, 0xe9, 0xb2, 0x2e, 0x75, 0x98, 0xc3, 0xbd, 0xe6, 0xb, 0x50, 0xcc, 0x97, 0x7a, 0x21, 0x29, 0x72, 0x9f, 0xc4, 0x58, 0x3, 0xee, 0xb5, 0xcb, 0x90, 0x7d, 0x26, 0xba, 0xe1, 0xc, 0x57, 0xf0, 0xab, 0x46, 0x1d, 0x81, 0xda, 0x37, 0x6c, 0x12, 0x49, 0xa4, 0xff, 0x63, 0x38, 0xd5, 0x8e, 0xc5, 0x9e, 0x73, 0x28, 0xb4, 0xef, 0x2, 0x59, 0x27, 0x7c, 0x91, 0xca, 0x56, 0xd, 0xe0, 0xbb, 0x1c, 0x47, 0xaa, 0xf1, 0x6d, 0x36, 0xdb, 0x80, 0xfe, 0xa5, 0x48, 0x13, 0x8f, 0xd4, 0x39, 0x62, 0x6a, 0x31, 0xdc, 0x87, 0x1b, 0x40, 0xad, 0xf6, 0x88, 0xd3, 0x3e, 0x65, 0xf9, 0xa2, 0x4f, 0x14, 0xb3, 0xe8, 0x5, 0x5e, 0xc2, 0x99, 0x74, 0x2f, 0x51, 0xa, 0xe7, 0xbc, 0x20, 0x7b, 0x96, 0xcd},
+ {0x0, 0x5c, 0xb8, 0xe4, 0x6d, 0x31, 0xd5, 0x89, 0xda, 0x86, 0x62, 0x3e, 0xb7, 0xeb, 0xf, 0x53, 0xa9, 0xf5, 0x11, 0x4d, 0xc4, 0x98, 0x7c, 0x20, 0x73, 0x2f, 0xcb, 0x97, 0x1e, 0x42, 0xa6, 0xfa, 0x4f, 0x13, 0xf7, 0xab, 0x22, 0x7e, 0x9a, 0xc6, 0x95, 0xc9, 0x2d, 0x71, 0xf8, 0xa4, 0x40, 0x1c, 0xe6, 0xba, 0x5e, 0x2, 0x8b, 0xd7, 0x33, 0x6f, 0x3c, 0x60, 0x84, 0xd8, 0x51, 0xd, 0xe9, 0xb5, 0x9e, 0xc2, 0x26, 0x7a, 0xf3, 0xaf, 0x4b, 0x17, 0x44, 0x18, 0xfc, 0xa0, 0x29, 0x75, 0x91, 0xcd, 0x37, 0x6b, 0x8f, 0xd3, 0x5a, 0x6, 0xe2, 0xbe, 0xed, 0xb1, 0x55, 0x9, 0x80, 0xdc, 0x38, 0x64, 0xd1, 0x8d, 0x69, 0x35, 0xbc, 0xe0, 0x4, 0x58, 0xb, 0x57, 0xb3, 0xef, 0x66, 0x3a, 0xde, 0x82, 0x78, 0x24, 0xc0, 0x9c, 0x15, 0x49, 0xad, 0xf1, 0xa2, 0xfe, 0x1a, 0x46, 0xcf, 0x93, 0x77, 0x2b, 0x21, 0x7d, 0x99, 0xc5, 0x4c, 0x10, 0xf4, 0xa8, 0xfb, 0xa7, 0x43, 0x1f, 0x96, 0xca, 0x2e, 0x72, 0x88, 0xd4, 0x30, 0x6c, 0xe5, 0xb9, 0x5d, 0x1, 0x52, 0xe, 0xea, 0xb6, 0x3f, 0x63, 0x87, 0xdb, 0x6e, 0x32, 0xd6, 0x8a, 0x3, 0x5f, 0xbb, 0xe7, 0xb4, 0xe8, 0xc, 0x50, 0xd9, 0x85, 0x61, 0x3d, 0xc7, 0x9b, 0x7f, 0x23, 0xaa, 0xf6, 0x12, 0x4e, 0x1d, 0x41, 0xa5, 0xf9, 0x70, 0x2c, 0xc8, 0x94, 0xbf, 0xe3, 0x7, 0x5b, 0xd2, 0x8e, 0x6a, 0x36, 0x65, 0x39, 0xdd, 0x81, 0x8, 0x54, 0xb0, 0xec, 0x16, 0x4a, 0xae, 0xf2, 0x7b, 0x27, 0xc3, 0x9f, 0xcc, 0x90, 0x74, 0x28, 0xa1, 0xfd, 0x19, 0x45, 0xf0, 0xac, 0x48, 0x14, 0x9d, 0xc1, 0x25, 0x79, 0x2a, 0x76, 0x92, 0xce, 0x47, 0x1b, 0xff, 0xa3, 0x59, 0x5, 0xe1, 0xbd, 0x34, 0x68, 0x8c, 0xd0, 0x83, 0xdf, 0x3b, 0x67, 0xee, 0xb2, 0x56, 0xa},
+ {0x0, 0x5d, 0xba, 0xe7, 0x69, 0x34, 0xd3, 0x8e, 0xd2, 0x8f, 0x68, 0x35, 0xbb, 0xe6, 0x1, 0x5c, 0xb9, 0xe4, 0x3, 0x5e, 0xd0, 0x8d, 0x6a, 0x37, 0x6b, 0x36, 0xd1, 0x8c, 0x2, 0x5f, 0xb8, 0xe5, 0x6f, 0x32, 0xd5, 0x88, 0x6, 0x5b, 0xbc, 0xe1, 0xbd, 0xe0, 0x7, 0x5a, 0xd4, 0x89, 0x6e, 0x33, 0xd6, 0x8b, 0x6c, 0x31, 0xbf, 0xe2, 0x5, 0x58, 0x4, 0x59, 0xbe, 0xe3, 0x6d, 0x30, 0xd7, 0x8a, 0xde, 0x83, 0x64, 0x39, 0xb7, 0xea, 0xd, 0x50, 0xc, 0x51, 0xb6, 0xeb, 0x65, 0x38, 0xdf, 0x82, 0x67, 0x3a, 0xdd, 0x80, 0xe, 0x53, 0xb4, 0xe9, 0xb5, 0xe8, 0xf, 0x52, 0xdc, 0x81, 0x66, 0x3b, 0xb1, 0xec, 0xb, 0x56, 0xd8, 0x85, 0x62, 0x3f, 0x63, 0x3e, 0xd9, 0x84, 0xa, 0x57, 0xb0, 0xed, 0x8, 0x55, 0xb2, 0xef, 0x61, 0x3c, 0xdb, 0x86, 0xda, 0x87, 0x60, 0x3d, 0xb3, 0xee, 0x9, 0x54, 0xa1, 0xfc, 0x1b, 0x46, 0xc8, 0x95, 0x72, 0x2f, 0x73, 0x2e, 0xc9, 0x94, 0x1a, 0x47, 0xa0, 0xfd, 0x18, 0x45, 0xa2, 0xff, 0x71, 0x2c, 0xcb, 0x96, 0xca, 0x97, 0x70, 0x2d, 0xa3, 0xfe, 0x19, 0x44, 0xce, 0x93, 0x74, 0x29, 0xa7, 0xfa, 0x1d, 0x40, 0x1c, 0x41, 0xa6, 0xfb, 0x75, 0x28, 0xcf, 0x92, 0x77, 0x2a, 0xcd, 0x90, 0x1e, 0x43, 0xa4, 0xf9, 0xa5, 0xf8, 0x1f, 0x42, 0xcc, 0x91, 0x76, 0x2b, 0x7f, 0x22, 0xc5, 0x98, 0x16, 0x4b, 0xac, 0xf1, 0xad, 0xf0, 0x17, 0x4a, 0xc4, 0x99, 0x7e, 0x23, 0xc6, 0x9b, 0x7c, 0x21, 0xaf, 0xf2, 0x15, 0x48, 0x14, 0x49, 0xae, 0xf3, 0x7d, 0x20, 0xc7, 0x9a, 0x10, 0x4d, 0xaa, 0xf7, 0x79, 0x24, 0xc3, 0x9e, 0xc2, 0x9f, 0x78, 0x25, 0xab, 0xf6, 0x11, 0x4c, 0xa9, 0xf4, 0x13, 0x4e, 0xc0, 0x9d, 0x7a, 0x27, 0x7b, 0x26, 0xc1, 0x9c, 0x12, 0x4f, 0xa8, 0xf5},
+ {0x0, 0x5e, 0xbc, 0xe2, 0x65, 0x3b, 0xd9, 0x87, 0xca, 0x94, 0x76, 0x28, 0xaf, 0xf1, 0x13, 0x4d, 0x89, 0xd7, 0x35, 0x6b, 0xec, 0xb2, 0x50, 0xe, 0x43, 0x1d, 0xff, 0xa1, 0x26, 0x78, 0x9a, 0xc4, 0xf, 0x51, 0xb3, 0xed, 0x6a, 0x34, 0xd6, 0x88, 0xc5, 0x9b, 0x79, 0x27, 0xa0, 0xfe, 0x1c, 0x42, 0x86, 0xd8, 0x3a, 0x64, 0xe3, 0xbd, 0x5f, 0x1, 0x4c, 0x12, 0xf0, 0xae, 0x29, 0x77, 0x95, 0xcb, 0x1e, 0x40, 0xa2, 0xfc, 0x7b, 0x25, 0xc7, 0x99, 0xd4, 0x8a, 0x68, 0x36, 0xb1, 0xef, 0xd, 0x53, 0x97, 0xc9, 0x2b, 0x75, 0xf2, 0xac, 0x4e, 0x10, 0x5d, 0x3, 0xe1, 0xbf, 0x38, 0x66, 0x84, 0xda, 0x11, 0x4f, 0xad, 0xf3, 0x74, 0x2a, 0xc8, 0x96, 0xdb, 0x85, 0x67, 0x39, 0xbe, 0xe0, 0x2, 0x5c, 0x98, 0xc6, 0x24, 0x7a, 0xfd, 0xa3, 0x41, 0x1f, 0x52, 0xc, 0xee, 0xb0, 0x37, 0x69, 0x8b, 0xd5, 0x3c, 0x62, 0x80, 0xde, 0x59, 0x7, 0xe5, 0xbb, 0xf6, 0xa8, 0x4a, 0x14, 0x93, 0xcd, 0x2f, 0x71, 0xb5, 0xeb, 0x9, 0x57, 0xd0, 0x8e, 0x6c, 0x32, 0x7f, 0x21, 0xc3, 0x9d, 0x1a, 0x44, 0xa6, 0xf8, 0x33, 0x6d, 0x8f, 0xd1, 0x56, 0x8, 0xea, 0xb4, 0xf9, 0xa7, 0x45, 0x1b, 0x9c, 0xc2, 0x20, 0x7e, 0xba, 0xe4, 0x6, 0x58, 0xdf, 0x81, 0x63, 0x3d, 0x70, 0x2e, 0xcc, 0x92, 0x15, 0x4b, 0xa9, 0xf7, 0x22, 0x7c, 0x9e, 0xc0, 0x47, 0x19, 0xfb, 0xa5, 0xe8, 0xb6, 0x54, 0xa, 0x8d, 0xd3, 0x31, 0x6f, 0xab, 0xf5, 0x17, 0x49, 0xce, 0x90, 0x72, 0x2c, 0x61, 0x3f, 0xdd, 0x83, 0x4, 0x5a, 0xb8, 0xe6, 0x2d, 0x73, 0x91, 0xcf, 0x48, 0x16, 0xf4, 0xaa, 0xe7, 0xb9, 0x5b, 0x5, 0x82, 0xdc, 0x3e, 0x60, 0xa4, 0xfa, 0x18, 0x46, 0xc1, 0x9f, 0x7d, 0x23, 0x6e, 0x30, 0xd2, 0x8c, 0xb, 0x55, 0xb7, 0xe9},
+ {0x0, 0x5f, 0xbe, 0xe1, 0x61, 0x3e, 0xdf, 0x80, 0xc2, 0x9d, 0x7c, 0x23, 0xa3, 0xfc, 0x1d, 0x42, 0x99, 0xc6, 0x27, 0x78, 0xf8, 0xa7, 0x46, 0x19, 0x5b, 0x4, 0xe5, 0xba, 0x3a, 0x65, 0x84, 0xdb, 0x2f, 0x70, 0x91, 0xce, 0x4e, 0x11, 0xf0, 0xaf, 0xed, 0xb2, 0x53, 0xc, 0x8c, 0xd3, 0x32, 0x6d, 0xb6, 0xe9, 0x8, 0x57, 0xd7, 0x88, 0x69, 0x36, 0x74, 0x2b, 0xca, 0x95, 0x15, 0x4a, 0xab, 0xf4, 0x5e, 0x1, 0xe0, 0xbf, 0x3f, 0x60, 0x81, 0xde, 0x9c, 0xc3, 0x22, 0x7d, 0xfd, 0xa2, 0x43, 0x1c, 0xc7, 0x98, 0x79, 0x26, 0xa6, 0xf9, 0x18, 0x47, 0x5, 0x5a, 0xbb, 0xe4, 0x64, 0x3b, 0xda, 0x85, 0x71, 0x2e, 0xcf, 0x90, 0x10, 0x4f, 0xae, 0xf1, 0xb3, 0xec, 0xd, 0x52, 0xd2, 0x8d, 0x6c, 0x33, 0xe8, 0xb7, 0x56, 0x9, 0x89, 0xd6, 0x37, 0x68, 0x2a, 0x75, 0x94, 0xcb, 0x4b, 0x14, 0xf5, 0xaa, 0xbc, 0xe3, 0x2, 0x5d, 0xdd, 0x82, 0x63, 0x3c, 0x7e, 0x21, 0xc0, 0x9f, 0x1f, 0x40, 0xa1, 0xfe, 0x25, 0x7a, 0x9b, 0xc4, 0x44, 0x1b, 0xfa, 0xa5, 0xe7, 0xb8, 0x59, 0x6, 0x86, 0xd9, 0x38, 0x67, 0x93, 0xcc, 0x2d, 0x72, 0xf2, 0xad, 0x4c, 0x13, 0x51, 0xe, 0xef, 0xb0, 0x30, 0x6f, 0x8e, 0xd1, 0xa, 0x55, 0xb4, 0xeb, 0x6b, 0x34, 0xd5, 0x8a, 0xc8, 0x97, 0x76, 0x29, 0xa9, 0xf6, 0x17, 0x48, 0xe2, 0xbd, 0x5c, 0x3, 0x83, 0xdc, 0x3d, 0x62, 0x20, 0x7f, 0x9e, 0xc1, 0x41, 0x1e, 0xff, 0xa0, 0x7b, 0x24, 0xc5, 0x9a, 0x1a, 0x45, 0xa4, 0xfb, 0xb9, 0xe6, 0x7, 0x58, 0xd8, 0x87, 0x66, 0x39, 0xcd, 0x92, 0x73, 0x2c, 0xac, 0xf3, 0x12, 0x4d, 0xf, 0x50, 0xb1, 0xee, 0x6e, 0x31, 0xd0, 0x8f, 0x54, 0xb, 0xea, 0xb5, 0x35, 0x6a, 0x8b, 0xd4, 0x96, 0xc9, 0x28, 0x77, 0xf7, 0xa8, 0x49, 0x16},
+ {0x0, 0x60, 0xc0, 0xa0, 0x9d, 0xfd, 0x5d, 0x3d, 0x27, 0x47, 0xe7, 0x87, 0xba, 0xda, 0x7a, 0x1a, 0x4e, 0x2e, 0x8e, 0xee, 0xd3, 0xb3, 0x13, 0x73, 0x69, 0x9, 0xa9, 0xc9, 0xf4, 0x94, 0x34, 0x54, 0x9c, 0xfc, 0x5c, 0x3c, 0x1, 0x61, 0xc1, 0xa1, 0xbb, 0xdb, 0x7b, 0x1b, 0x26, 0x46, 0xe6, 0x86, 0xd2, 0xb2, 0x12, 0x72, 0x4f, 0x2f, 0x8f, 0xef, 0xf5, 0x95, 0x35, 0x55, 0x68, 0x8, 0xa8, 0xc8, 0x25, 0x45, 0xe5, 0x85, 0xb8, 0xd8, 0x78, 0x18, 0x2, 0x62, 0xc2, 0xa2, 0x9f, 0xff, 0x5f, 0x3f, 0x6b, 0xb, 0xab, 0xcb, 0xf6, 0x96, 0x36, 0x56, 0x4c, 0x2c, 0x8c, 0xec, 0xd1, 0xb1, 0x11, 0x71, 0xb9, 0xd9, 0x79, 0x19, 0x24, 0x44, 0xe4, 0x84, 0x9e, 0xfe, 0x5e, 0x3e, 0x3, 0x63, 0xc3, 0xa3, 0xf7, 0x97, 0x37, 0x57, 0x6a, 0xa, 0xaa, 0xca, 0xd0, 0xb0, 0x10, 0x70, 0x4d, 0x2d, 0x8d, 0xed, 0x4a, 0x2a, 0x8a, 0xea, 0xd7, 0xb7, 0x17, 0x77, 0x6d, 0xd, 0xad, 0xcd, 0xf0, 0x90, 0x30, 0x50, 0x4, 0x64, 0xc4, 0xa4, 0x99, 0xf9, 0x59, 0x39, 0x23, 0x43, 0xe3, 0x83, 0xbe, 0xde, 0x7e, 0x1e, 0xd6, 0xb6, 0x16, 0x76, 0x4b, 0x2b, 0x8b, 0xeb, 0xf1, 0x91, 0x31, 0x51, 0x6c, 0xc, 0xac, 0xcc, 0x98, 0xf8, 0x58, 0x38, 0x5, 0x65, 0xc5, 0xa5, 0xbf, 0xdf, 0x7f, 0x1f, 0x22, 0x42, 0xe2, 0x82, 0x6f, 0xf, 0xaf, 0xcf, 0xf2, 0x92, 0x32, 0x52, 0x48, 0x28, 0x88, 0xe8, 0xd5, 0xb5, 0x15, 0x75, 0x21, 0x41, 0xe1, 0x81, 0xbc, 0xdc, 0x7c, 0x1c, 0x6, 0x66, 0xc6, 0xa6, 0x9b, 0xfb, 0x5b, 0x3b, 0xf3, 0x93, 0x33, 0x53, 0x6e, 0xe, 0xae, 0xce, 0xd4, 0xb4, 0x14, 0x74, 0x49, 0x29, 0x89, 0xe9, 0xbd, 0xdd, 0x7d, 0x1d, 0x20, 0x40, 0xe0, 0x80, 0x9a, 0xfa, 0x5a, 0x3a, 0x7, 0x67, 0xc7, 0xa7},
+ {0x0, 0x61, 0xc2, 0xa3, 0x99, 0xf8, 0x5b, 0x3a, 0x2f, 0x4e, 0xed, 0x8c, 0xb6, 0xd7, 0x74, 0x15, 0x5e, 0x3f, 0x9c, 0xfd, 0xc7, 0xa6, 0x5, 0x64, 0x71, 0x10, 0xb3, 0xd2, 0xe8, 0x89, 0x2a, 0x4b, 0xbc, 0xdd, 0x7e, 0x1f, 0x25, 0x44, 0xe7, 0x86, 0x93, 0xf2, 0x51, 0x30, 0xa, 0x6b, 0xc8, 0xa9, 0xe2, 0x83, 0x20, 0x41, 0x7b, 0x1a, 0xb9, 0xd8, 0xcd, 0xac, 0xf, 0x6e, 0x54, 0x35, 0x96, 0xf7, 0x65, 0x4, 0xa7, 0xc6, 0xfc, 0x9d, 0x3e, 0x5f, 0x4a, 0x2b, 0x88, 0xe9, 0xd3, 0xb2, 0x11, 0x70, 0x3b, 0x5a, 0xf9, 0x98, 0xa2, 0xc3, 0x60, 0x1, 0x14, 0x75, 0xd6, 0xb7, 0x8d, 0xec, 0x4f, 0x2e, 0xd9, 0xb8, 0x1b, 0x7a, 0x40, 0x21, 0x82, 0xe3, 0xf6, 0x97, 0x34, 0x55, 0x6f, 0xe, 0xad, 0xcc, 0x87, 0xe6, 0x45, 0x24, 0x1e, 0x7f, 0xdc, 0xbd, 0xa8, 0xc9, 0x6a, 0xb, 0x31, 0x50, 0xf3, 0x92, 0xca, 0xab, 0x8, 0x69, 0x53, 0x32, 0x91, 0xf0, 0xe5, 0x84, 0x27, 0x46, 0x7c, 0x1d, 0xbe, 0xdf, 0x94, 0xf5, 0x56, 0x37, 0xd, 0x6c, 0xcf, 0xae, 0xbb, 0xda, 0x79, 0x18, 0x22, 0x43, 0xe0, 0x81, 0x76, 0x17, 0xb4, 0xd5, 0xef, 0x8e, 0x2d, 0x4c, 0x59, 0x38, 0x9b, 0xfa, 0xc0, 0xa1, 0x2, 0x63, 0x28, 0x49, 0xea, 0x8b, 0xb1, 0xd0, 0x73, 0x12, 0x7, 0x66, 0xc5, 0xa4, 0x9e, 0xff, 0x5c, 0x3d, 0xaf, 0xce, 0x6d, 0xc, 0x36, 0x57, 0xf4, 0x95, 0x80, 0xe1, 0x42, 0x23, 0x19, 0x78, 0xdb, 0xba, 0xf1, 0x90, 0x33, 0x52, 0x68, 0x9, 0xaa, 0xcb, 0xde, 0xbf, 0x1c, 0x7d, 0x47, 0x26, 0x85, 0xe4, 0x13, 0x72, 0xd1, 0xb0, 0x8a, 0xeb, 0x48, 0x29, 0x3c, 0x5d, 0xfe, 0x9f, 0xa5, 0xc4, 0x67, 0x6, 0x4d, 0x2c, 0x8f, 0xee, 0xd4, 0xb5, 0x16, 0x77, 0x62, 0x3, 0xa0, 0xc1, 0xfb, 0x9a, 0x39, 0x58},
+ {0x0, 0x62, 0xc4, 0xa6, 0x95, 0xf7, 0x51, 0x33, 0x37, 0x55, 0xf3, 0x91, 0xa2, 0xc0, 0x66, 0x4, 0x6e, 0xc, 0xaa, 0xc8, 0xfb, 0x99, 0x3f, 0x5d, 0x59, 0x3b, 0x9d, 0xff, 0xcc, 0xae, 0x8, 0x6a, 0xdc, 0xbe, 0x18, 0x7a, 0x49, 0x2b, 0x8d, 0xef, 0xeb, 0x89, 0x2f, 0x4d, 0x7e, 0x1c, 0xba, 0xd8, 0xb2, 0xd0, 0x76, 0x14, 0x27, 0x45, 0xe3, 0x81, 0x85, 0xe7, 0x41, 0x23, 0x10, 0x72, 0xd4, 0xb6, 0xa5, 0xc7, 0x61, 0x3, 0x30, 0x52, 0xf4, 0x96, 0x92, 0xf0, 0x56, 0x34, 0x7, 0x65, 0xc3, 0xa1, 0xcb, 0xa9, 0xf, 0x6d, 0x5e, 0x3c, 0x9a, 0xf8, 0xfc, 0x9e, 0x38, 0x5a, 0x69, 0xb, 0xad, 0xcf, 0x79, 0x1b, 0xbd, 0xdf, 0xec, 0x8e, 0x28, 0x4a, 0x4e, 0x2c, 0x8a, 0xe8, 0xdb, 0xb9, 0x1f, 0x7d, 0x17, 0x75, 0xd3, 0xb1, 0x82, 0xe0, 0x46, 0x24, 0x20, 0x42, 0xe4, 0x86, 0xb5, 0xd7, 0x71, 0x13, 0x57, 0x35, 0x93, 0xf1, 0xc2, 0xa0, 0x6, 0x64, 0x60, 0x2, 0xa4, 0xc6, 0xf5, 0x97, 0x31, 0x53, 0x39, 0x5b, 0xfd, 0x9f, 0xac, 0xce, 0x68, 0xa, 0xe, 0x6c, 0xca, 0xa8, 0x9b, 0xf9, 0x5f, 0x3d, 0x8b, 0xe9, 0x4f, 0x2d, 0x1e, 0x7c, 0xda, 0xb8, 0xbc, 0xde, 0x78, 0x1a, 0x29, 0x4b, 0xed, 0x8f, 0xe5, 0x87, 0x21, 0x43, 0x70, 0x12, 0xb4, 0xd6, 0xd2, 0xb0, 0x16, 0x74, 0x47, 0x25, 0x83, 0xe1, 0xf2, 0x90, 0x36, 0x54, 0x67, 0x5, 0xa3, 0xc1, 0xc5, 0xa7, 0x1, 0x63, 0x50, 0x32, 0x94, 0xf6, 0x9c, 0xfe, 0x58, 0x3a, 0x9, 0x6b, 0xcd, 0xaf, 0xab, 0xc9, 0x6f, 0xd, 0x3e, 0x5c, 0xfa, 0x98, 0x2e, 0x4c, 0xea, 0x88, 0xbb, 0xd9, 0x7f, 0x1d, 0x19, 0x7b, 0xdd, 0xbf, 0x8c, 0xee, 0x48, 0x2a, 0x40, 0x22, 0x84, 0xe6, 0xd5, 0xb7, 0x11, 0x73, 0x77, 0x15, 0xb3, 0xd1, 0xe2, 0x80, 0x26, 0x44},
+ {0x0, 0x63, 0xc6, 0xa5, 0x91, 0xf2, 0x57, 0x34, 0x3f, 0x5c, 0xf9, 0x9a, 0xae, 0xcd, 0x68, 0xb, 0x7e, 0x1d, 0xb8, 0xdb, 0xef, 0x8c, 0x29, 0x4a, 0x41, 0x22, 0x87, 0xe4, 0xd0, 0xb3, 0x16, 0x75, 0xfc, 0x9f, 0x3a, 0x59, 0x6d, 0xe, 0xab, 0xc8, 0xc3, 0xa0, 0x5, 0x66, 0x52, 0x31, 0x94, 0xf7, 0x82, 0xe1, 0x44, 0x27, 0x13, 0x70, 0xd5, 0xb6, 0xbd, 0xde, 0x7b, 0x18, 0x2c, 0x4f, 0xea, 0x89, 0xe5, 0x86, 0x23, 0x40, 0x74, 0x17, 0xb2, 0xd1, 0xda, 0xb9, 0x1c, 0x7f, 0x4b, 0x28, 0x8d, 0xee, 0x9b, 0xf8, 0x5d, 0x3e, 0xa, 0x69, 0xcc, 0xaf, 0xa4, 0xc7, 0x62, 0x1, 0x35, 0x56, 0xf3, 0x90, 0x19, 0x7a, 0xdf, 0xbc, 0x88, 0xeb, 0x4e, 0x2d, 0x26, 0x45, 0xe0, 0x83, 0xb7, 0xd4, 0x71, 0x12, 0x67, 0x4, 0xa1, 0xc2, 0xf6, 0x95, 0x30, 0x53, 0x58, 0x3b, 0x9e, 0xfd, 0xc9, 0xaa, 0xf, 0x6c, 0xd7, 0xb4, 0x11, 0x72, 0x46, 0x25, 0x80, 0xe3, 0xe8, 0x8b, 0x2e, 0x4d, 0x79, 0x1a, 0xbf, 0xdc, 0xa9, 0xca, 0x6f, 0xc, 0x38, 0x5b, 0xfe, 0x9d, 0x96, 0xf5, 0x50, 0x33, 0x7, 0x64, 0xc1, 0xa2, 0x2b, 0x48, 0xed, 0x8e, 0xba, 0xd9, 0x7c, 0x1f, 0x14, 0x77, 0xd2, 0xb1, 0x85, 0xe6, 0x43, 0x20, 0x55, 0x36, 0x93, 0xf0, 0xc4, 0xa7, 0x2, 0x61, 0x6a, 0x9, 0xac, 0xcf, 0xfb, 0x98, 0x3d, 0x5e, 0x32, 0x51, 0xf4, 0x97, 0xa3, 0xc0, 0x65, 0x6, 0xd, 0x6e, 0xcb, 0xa8, 0x9c, 0xff, 0x5a, 0x39, 0x4c, 0x2f, 0x8a, 0xe9, 0xdd, 0xbe, 0x1b, 0x78, 0x73, 0x10, 0xb5, 0xd6, 0xe2, 0x81, 0x24, 0x47, 0xce, 0xad, 0x8, 0x6b, 0x5f, 0x3c, 0x99, 0xfa, 0xf1, 0x92, 0x37, 0x54, 0x60, 0x3, 0xa6, 0xc5, 0xb0, 0xd3, 0x76, 0x15, 0x21, 0x42, 0xe7, 0x84, 0x8f, 0xec, 0x49, 0x2a, 0x1e, 0x7d, 0xd8, 0xbb},
+ {0x0, 0x64, 0xc8, 0xac, 0x8d, 0xe9, 0x45, 0x21, 0x7, 0x63, 0xcf, 0xab, 0x8a, 0xee, 0x42, 0x26, 0xe, 0x6a, 0xc6, 0xa2, 0x83, 0xe7, 0x4b, 0x2f, 0x9, 0x6d, 0xc1, 0xa5, 0x84, 0xe0, 0x4c, 0x28, 0x1c, 0x78, 0xd4, 0xb0, 0x91, 0xf5, 0x59, 0x3d, 0x1b, 0x7f, 0xd3, 0xb7, 0x96, 0xf2, 0x5e, 0x3a, 0x12, 0x76, 0xda, 0xbe, 0x9f, 0xfb, 0x57, 0x33, 0x15, 0x71, 0xdd, 0xb9, 0x98, 0xfc, 0x50, 0x34, 0x38, 0x5c, 0xf0, 0x94, 0xb5, 0xd1, 0x7d, 0x19, 0x3f, 0x5b, 0xf7, 0x93, 0xb2, 0xd6, 0x7a, 0x1e, 0x36, 0x52, 0xfe, 0x9a, 0xbb, 0xdf, 0x73, 0x17, 0x31, 0x55, 0xf9, 0x9d, 0xbc, 0xd8, 0x74, 0x10, 0x24, 0x40, 0xec, 0x88, 0xa9, 0xcd, 0x61, 0x5, 0x23, 0x47, 0xeb, 0x8f, 0xae, 0xca, 0x66, 0x2, 0x2a, 0x4e, 0xe2, 0x86, 0xa7, 0xc3, 0x6f, 0xb, 0x2d, 0x49, 0xe5, 0x81, 0xa0, 0xc4, 0x68, 0xc, 0x70, 0x14, 0xb8, 0xdc, 0xfd, 0x99, 0x35, 0x51, 0x77, 0x13, 0xbf, 0xdb, 0xfa, 0x9e, 0x32, 0x56, 0x7e, 0x1a, 0xb6, 0xd2, 0xf3, 0x97, 0x3b, 0x5f, 0x79, 0x1d, 0xb1, 0xd5, 0xf4, 0x90, 0x3c, 0x58, 0x6c, 0x8, 0xa4, 0xc0, 0xe1, 0x85, 0x29, 0x4d, 0x6b, 0xf, 0xa3, 0xc7, 0xe6, 0x82, 0x2e, 0x4a, 0x62, 0x6, 0xaa, 0xce, 0xef, 0x8b, 0x27, 0x43, 0x65, 0x1, 0xad, 0xc9, 0xe8, 0x8c, 0x20, 0x44, 0x48, 0x2c, 0x80, 0xe4, 0xc5, 0xa1, 0xd, 0x69, 0x4f, 0x2b, 0x87, 0xe3, 0xc2, 0xa6, 0xa, 0x6e, 0x46, 0x22, 0x8e, 0xea, 0xcb, 0xaf, 0x3, 0x67, 0x41, 0x25, 0x89, 0xed, 0xcc, 0xa8, 0x4, 0x60, 0x54, 0x30, 0x9c, 0xf8, 0xd9, 0xbd, 0x11, 0x75, 0x53, 0x37, 0x9b, 0xff, 0xde, 0xba, 0x16, 0x72, 0x5a, 0x3e, 0x92, 0xf6, 0xd7, 0xb3, 0x1f, 0x7b, 0x5d, 0x39, 0x95, 0xf1, 0xd0, 0xb4, 0x18, 0x7c},
+ {0x0, 0x65, 0xca, 0xaf, 0x89, 0xec, 0x43, 0x26, 0xf, 0x6a, 0xc5, 0xa0, 0x86, 0xe3, 0x4c, 0x29, 0x1e, 0x7b, 0xd4, 0xb1, 0x97, 0xf2, 0x5d, 0x38, 0x11, 0x74, 0xdb, 0xbe, 0x98, 0xfd, 0x52, 0x37, 0x3c, 0x59, 0xf6, 0x93, 0xb5, 0xd0, 0x7f, 0x1a, 0x33, 0x56, 0xf9, 0x9c, 0xba, 0xdf, 0x70, 0x15, 0x22, 0x47, 0xe8, 0x8d, 0xab, 0xce, 0x61, 0x4, 0x2d, 0x48, 0xe7, 0x82, 0xa4, 0xc1, 0x6e, 0xb, 0x78, 0x1d, 0xb2, 0xd7, 0xf1, 0x94, 0x3b, 0x5e, 0x77, 0x12, 0xbd, 0xd8, 0xfe, 0x9b, 0x34, 0x51, 0x66, 0x3, 0xac, 0xc9, 0xef, 0x8a, 0x25, 0x40, 0x69, 0xc, 0xa3, 0xc6, 0xe0, 0x85, 0x2a, 0x4f, 0x44, 0x21, 0x8e, 0xeb, 0xcd, 0xa8, 0x7, 0x62, 0x4b, 0x2e, 0x81, 0xe4, 0xc2, 0xa7, 0x8, 0x6d, 0x5a, 0x3f, 0x90, 0xf5, 0xd3, 0xb6, 0x19, 0x7c, 0x55, 0x30, 0x9f, 0xfa, 0xdc, 0xb9, 0x16, 0x73, 0xf0, 0x95, 0x3a, 0x5f, 0x79, 0x1c, 0xb3, 0xd6, 0xff, 0x9a, 0x35, 0x50, 0x76, 0x13, 0xbc, 0xd9, 0xee, 0x8b, 0x24, 0x41, 0x67, 0x2, 0xad, 0xc8, 0xe1, 0x84, 0x2b, 0x4e, 0x68, 0xd, 0xa2, 0xc7, 0xcc, 0xa9, 0x6, 0x63, 0x45, 0x20, 0x8f, 0xea, 0xc3, 0xa6, 0x9, 0x6c, 0x4a, 0x2f, 0x80, 0xe5, 0xd2, 0xb7, 0x18, 0x7d, 0x5b, 0x3e, 0x91, 0xf4, 0xdd, 0xb8, 0x17, 0x72, 0x54, 0x31, 0x9e, 0xfb, 0x88, 0xed, 0x42, 0x27, 0x1, 0x64, 0xcb, 0xae, 0x87, 0xe2, 0x4d, 0x28, 0xe, 0x6b, 0xc4, 0xa1, 0x96, 0xf3, 0x5c, 0x39, 0x1f, 0x7a, 0xd5, 0xb0, 0x99, 0xfc, 0x53, 0x36, 0x10, 0x75, 0xda, 0xbf, 0xb4, 0xd1, 0x7e, 0x1b, 0x3d, 0x58, 0xf7, 0x92, 0xbb, 0xde, 0x71, 0x14, 0x32, 0x57, 0xf8, 0x9d, 0xaa, 0xcf, 0x60, 0x5, 0x23, 0x46, 0xe9, 0x8c, 0xa5, 0xc0, 0x6f, 0xa, 0x2c, 0x49, 0xe6, 0x83},
+ {0x0, 0x66, 0xcc, 0xaa, 0x85, 0xe3, 0x49, 0x2f, 0x17, 0x71, 0xdb, 0xbd, 0x92, 0xf4, 0x5e, 0x38, 0x2e, 0x48, 0xe2, 0x84, 0xab, 0xcd, 0x67, 0x1, 0x39, 0x5f, 0xf5, 0x93, 0xbc, 0xda, 0x70, 0x16, 0x5c, 0x3a, 0x90, 0xf6, 0xd9, 0xbf, 0x15, 0x73, 0x4b, 0x2d, 0x87, 0xe1, 0xce, 0xa8, 0x2, 0x64, 0x72, 0x14, 0xbe, 0xd8, 0xf7, 0x91, 0x3b, 0x5d, 0x65, 0x3, 0xa9, 0xcf, 0xe0, 0x86, 0x2c, 0x4a, 0xb8, 0xde, 0x74, 0x12, 0x3d, 0x5b, 0xf1, 0x97, 0xaf, 0xc9, 0x63, 0x5, 0x2a, 0x4c, 0xe6, 0x80, 0x96, 0xf0, 0x5a, 0x3c, 0x13, 0x75, 0xdf, 0xb9, 0x81, 0xe7, 0x4d, 0x2b, 0x4, 0x62, 0xc8, 0xae, 0xe4, 0x82, 0x28, 0x4e, 0x61, 0x7, 0xad, 0xcb, 0xf3, 0x95, 0x3f, 0x59, 0x76, 0x10, 0xba, 0xdc, 0xca, 0xac, 0x6, 0x60, 0x4f, 0x29, 0x83, 0xe5, 0xdd, 0xbb, 0x11, 0x77, 0x58, 0x3e, 0x94, 0xf2, 0x6d, 0xb, 0xa1, 0xc7, 0xe8, 0x8e, 0x24, 0x42, 0x7a, 0x1c, 0xb6, 0xd0, 0xff, 0x99, 0x33, 0x55, 0x43, 0x25, 0x8f, 0xe9, 0xc6, 0xa0, 0xa, 0x6c, 0x54, 0x32, 0x98, 0xfe, 0xd1, 0xb7, 0x1d, 0x7b, 0x31, 0x57, 0xfd, 0x9b, 0xb4, 0xd2, 0x78, 0x1e, 0x26, 0x40, 0xea, 0x8c, 0xa3, 0xc5, 0x6f, 0x9, 0x1f, 0x79, 0xd3, 0xb5, 0x9a, 0xfc, 0x56, 0x30, 0x8, 0x6e, 0xc4, 0xa2, 0x8d, 0xeb, 0x41, 0x27, 0xd5, 0xb3, 0x19, 0x7f, 0x50, 0x36, 0x9c, 0xfa, 0xc2, 0xa4, 0xe, 0x68, 0x47, 0x21, 0x8b, 0xed, 0xfb, 0x9d, 0x37, 0x51, 0x7e, 0x18, 0xb2, 0xd4, 0xec, 0x8a, 0x20, 0x46, 0x69, 0xf, 0xa5, 0xc3, 0x89, 0xef, 0x45, 0x23, 0xc, 0x6a, 0xc0, 0xa6, 0x9e, 0xf8, 0x52, 0x34, 0x1b, 0x7d, 0xd7, 0xb1, 0xa7, 0xc1, 0x6b, 0xd, 0x22, 0x44, 0xee, 0x88, 0xb0, 0xd6, 0x7c, 0x1a, 0x35, 0x53, 0xf9, 0x9f},
+ {0x0, 0x67, 0xce, 0xa9, 0x81, 0xe6, 0x4f, 0x28, 0x1f, 0x78, 0xd1, 0xb6, 0x9e, 0xf9, 0x50, 0x37, 0x3e, 0x59, 0xf0, 0x97, 0xbf, 0xd8, 0x71, 0x16, 0x21, 0x46, 0xef, 0x88, 0xa0, 0xc7, 0x6e, 0x9, 0x7c, 0x1b, 0xb2, 0xd5, 0xfd, 0x9a, 0x33, 0x54, 0x63, 0x4, 0xad, 0xca, 0xe2, 0x85, 0x2c, 0x4b, 0x42, 0x25, 0x8c, 0xeb, 0xc3, 0xa4, 0xd, 0x6a, 0x5d, 0x3a, 0x93, 0xf4, 0xdc, 0xbb, 0x12, 0x75, 0xf8, 0x9f, 0x36, 0x51, 0x79, 0x1e, 0xb7, 0xd0, 0xe7, 0x80, 0x29, 0x4e, 0x66, 0x1, 0xa8, 0xcf, 0xc6, 0xa1, 0x8, 0x6f, 0x47, 0x20, 0x89, 0xee, 0xd9, 0xbe, 0x17, 0x70, 0x58, 0x3f, 0x96, 0xf1, 0x84, 0xe3, 0x4a, 0x2d, 0x5, 0x62, 0xcb, 0xac, 0x9b, 0xfc, 0x55, 0x32, 0x1a, 0x7d, 0xd4, 0xb3, 0xba, 0xdd, 0x74, 0x13, 0x3b, 0x5c, 0xf5, 0x92, 0xa5, 0xc2, 0x6b, 0xc, 0x24, 0x43, 0xea, 0x8d, 0xed, 0x8a, 0x23, 0x44, 0x6c, 0xb, 0xa2, 0xc5, 0xf2, 0x95, 0x3c, 0x5b, 0x73, 0x14, 0xbd, 0xda, 0xd3, 0xb4, 0x1d, 0x7a, 0x52, 0x35, 0x9c, 0xfb, 0xcc, 0xab, 0x2, 0x65, 0x4d, 0x2a, 0x83, 0xe4, 0x91, 0xf6, 0x5f, 0x38, 0x10, 0x77, 0xde, 0xb9, 0x8e, 0xe9, 0x40, 0x27, 0xf, 0x68, 0xc1, 0xa6, 0xaf, 0xc8, 0x61, 0x6, 0x2e, 0x49, 0xe0, 0x87, 0xb0, 0xd7, 0x7e, 0x19, 0x31, 0x56, 0xff, 0x98, 0x15, 0x72, 0xdb, 0xbc, 0x94, 0xf3, 0x5a, 0x3d, 0xa, 0x6d, 0xc4, 0xa3, 0x8b, 0xec, 0x45, 0x22, 0x2b, 0x4c, 0xe5, 0x82, 0xaa, 0xcd, 0x64, 0x3, 0x34, 0x53, 0xfa, 0x9d, 0xb5, 0xd2, 0x7b, 0x1c, 0x69, 0xe, 0xa7, 0xc0, 0xe8, 0x8f, 0x26, 0x41, 0x76, 0x11, 0xb8, 0xdf, 0xf7, 0x90, 0x39, 0x5e, 0x57, 0x30, 0x99, 0xfe, 0xd6, 0xb1, 0x18, 0x7f, 0x48, 0x2f, 0x86, 0xe1, 0xc9, 0xae, 0x7, 0x60},
+ {0x0, 0x68, 0xd0, 0xb8, 0xbd, 0xd5, 0x6d, 0x5, 0x67, 0xf, 0xb7, 0xdf, 0xda, 0xb2, 0xa, 0x62, 0xce, 0xa6, 0x1e, 0x76, 0x73, 0x1b, 0xa3, 0xcb, 0xa9, 0xc1, 0x79, 0x11, 0x14, 0x7c, 0xc4, 0xac, 0x81, 0xe9, 0x51, 0x39, 0x3c, 0x54, 0xec, 0x84, 0xe6, 0x8e, 0x36, 0x5e, 0x5b, 0x33, 0x8b, 0xe3, 0x4f, 0x27, 0x9f, 0xf7, 0xf2, 0x9a, 0x22, 0x4a, 0x28, 0x40, 0xf8, 0x90, 0x95, 0xfd, 0x45, 0x2d, 0x1f, 0x77, 0xcf, 0xa7, 0xa2, 0xca, 0x72, 0x1a, 0x78, 0x10, 0xa8, 0xc0, 0xc5, 0xad, 0x15, 0x7d, 0xd1, 0xb9, 0x1, 0x69, 0x6c, 0x4, 0xbc, 0xd4, 0xb6, 0xde, 0x66, 0xe, 0xb, 0x63, 0xdb, 0xb3, 0x9e, 0xf6, 0x4e, 0x26, 0x23, 0x4b, 0xf3, 0x9b, 0xf9, 0x91, 0x29, 0x41, 0x44, 0x2c, 0x94, 0xfc, 0x50, 0x38, 0x80, 0xe8, 0xed, 0x85, 0x3d, 0x55, 0x37, 0x5f, 0xe7, 0x8f, 0x8a, 0xe2, 0x5a, 0x32, 0x3e, 0x56, 0xee, 0x86, 0x83, 0xeb, 0x53, 0x3b, 0x59, 0x31, 0x89, 0xe1, 0xe4, 0x8c, 0x34, 0x5c, 0xf0, 0x98, 0x20, 0x48, 0x4d, 0x25, 0x9d, 0xf5, 0x97, 0xff, 0x47, 0x2f, 0x2a, 0x42, 0xfa, 0x92, 0xbf, 0xd7, 0x6f, 0x7, 0x2, 0x6a, 0xd2, 0xba, 0xd8, 0xb0, 0x8, 0x60, 0x65, 0xd, 0xb5, 0xdd, 0x71, 0x19, 0xa1, 0xc9, 0xcc, 0xa4, 0x1c, 0x74, 0x16, 0x7e, 0xc6, 0xae, 0xab, 0xc3, 0x7b, 0x13, 0x21, 0x49, 0xf1, 0x99, 0x9c, 0xf4, 0x4c, 0x24, 0x46, 0x2e, 0x96, 0xfe, 0xfb, 0x93, 0x2b, 0x43, 0xef, 0x87, 0x3f, 0x57, 0x52, 0x3a, 0x82, 0xea, 0x88, 0xe0, 0x58, 0x30, 0x35, 0x5d, 0xe5, 0x8d, 0xa0, 0xc8, 0x70, 0x18, 0x1d, 0x75, 0xcd, 0xa5, 0xc7, 0xaf, 0x17, 0x7f, 0x7a, 0x12, 0xaa, 0xc2, 0x6e, 0x6, 0xbe, 0xd6, 0xd3, 0xbb, 0x3, 0x6b, 0x9, 0x61, 0xd9, 0xb1, 0xb4, 0xdc, 0x64, 0xc},
+ {0x0, 0x69, 0xd2, 0xbb, 0xb9, 0xd0, 0x6b, 0x2, 0x6f, 0x6, 0xbd, 0xd4, 0xd6, 0xbf, 0x4, 0x6d, 0xde, 0xb7, 0xc, 0x65, 0x67, 0xe, 0xb5, 0xdc, 0xb1, 0xd8, 0x63, 0xa, 0x8, 0x61, 0xda, 0xb3, 0xa1, 0xc8, 0x73, 0x1a, 0x18, 0x71, 0xca, 0xa3, 0xce, 0xa7, 0x1c, 0x75, 0x77, 0x1e, 0xa5, 0xcc, 0x7f, 0x16, 0xad, 0xc4, 0xc6, 0xaf, 0x14, 0x7d, 0x10, 0x79, 0xc2, 0xab, 0xa9, 0xc0, 0x7b, 0x12, 0x5f, 0x36, 0x8d, 0xe4, 0xe6, 0x8f, 0x34, 0x5d, 0x30, 0x59, 0xe2, 0x8b, 0x89, 0xe0, 0x5b, 0x32, 0x81, 0xe8, 0x53, 0x3a, 0x38, 0x51, 0xea, 0x83, 0xee, 0x87, 0x3c, 0x55, 0x57, 0x3e, 0x85, 0xec, 0xfe, 0x97, 0x2c, 0x45, 0x47, 0x2e, 0x95, 0xfc, 0x91, 0xf8, 0x43, 0x2a, 0x28, 0x41, 0xfa, 0x93, 0x20, 0x49, 0xf2, 0x9b, 0x99, 0xf0, 0x4b, 0x22, 0x4f, 0x26, 0x9d, 0xf4, 0xf6, 0x9f, 0x24, 0x4d, 0xbe, 0xd7, 0x6c, 0x5, 0x7, 0x6e, 0xd5, 0xbc, 0xd1, 0xb8, 0x3, 0x6a, 0x68, 0x1, 0xba, 0xd3, 0x60, 0x9, 0xb2, 0xdb, 0xd9, 0xb0, 0xb, 0x62, 0xf, 0x66, 0xdd, 0xb4, 0xb6, 0xdf, 0x64, 0xd, 0x1f, 0x76, 0xcd, 0xa4, 0xa6, 0xcf, 0x74, 0x1d, 0x70, 0x19, 0xa2, 0xcb, 0xc9, 0xa0, 0x1b, 0x72, 0xc1, 0xa8, 0x13, 0x7a, 0x78, 0x11, 0xaa, 0xc3, 0xae, 0xc7, 0x7c, 0x15, 0x17, 0x7e, 0xc5, 0xac, 0xe1, 0x88, 0x33, 0x5a, 0x58, 0x31, 0x8a, 0xe3, 0x8e, 0xe7, 0x5c, 0x35, 0x37, 0x5e, 0xe5, 0x8c, 0x3f, 0x56, 0xed, 0x84, 0x86, 0xef, 0x54, 0x3d, 0x50, 0x39, 0x82, 0xeb, 0xe9, 0x80, 0x3b, 0x52, 0x40, 0x29, 0x92, 0xfb, 0xf9, 0x90, 0x2b, 0x42, 0x2f, 0x46, 0xfd, 0x94, 0x96, 0xff, 0x44, 0x2d, 0x9e, 0xf7, 0x4c, 0x25, 0x27, 0x4e, 0xf5, 0x9c, 0xf1, 0x98, 0x23, 0x4a, 0x48, 0x21, 0x9a, 0xf3},
+ {0x0, 0x6a, 0xd4, 0xbe, 0xb5, 0xdf, 0x61, 0xb, 0x77, 0x1d, 0xa3, 0xc9, 0xc2, 0xa8, 0x16, 0x7c, 0xee, 0x84, 0x3a, 0x50, 0x5b, 0x31, 0x8f, 0xe5, 0x99, 0xf3, 0x4d, 0x27, 0x2c, 0x46, 0xf8, 0x92, 0xc1, 0xab, 0x15, 0x7f, 0x74, 0x1e, 0xa0, 0xca, 0xb6, 0xdc, 0x62, 0x8, 0x3, 0x69, 0xd7, 0xbd, 0x2f, 0x45, 0xfb, 0x91, 0x9a, 0xf0, 0x4e, 0x24, 0x58, 0x32, 0x8c, 0xe6, 0xed, 0x87, 0x39, 0x53, 0x9f, 0xf5, 0x4b, 0x21, 0x2a, 0x40, 0xfe, 0x94, 0xe8, 0x82, 0x3c, 0x56, 0x5d, 0x37, 0x89, 0xe3, 0x71, 0x1b, 0xa5, 0xcf, 0xc4, 0xae, 0x10, 0x7a, 0x6, 0x6c, 0xd2, 0xb8, 0xb3, 0xd9, 0x67, 0xd, 0x5e, 0x34, 0x8a, 0xe0, 0xeb, 0x81, 0x3f, 0x55, 0x29, 0x43, 0xfd, 0x97, 0x9c, 0xf6, 0x48, 0x22, 0xb0, 0xda, 0x64, 0xe, 0x5, 0x6f, 0xd1, 0xbb, 0xc7, 0xad, 0x13, 0x79, 0x72, 0x18, 0xa6, 0xcc, 0x23, 0x49, 0xf7, 0x9d, 0x96, 0xfc, 0x42, 0x28, 0x54, 0x3e, 0x80, 0xea, 0xe1, 0x8b, 0x35, 0x5f, 0xcd, 0xa7, 0x19, 0x73, 0x78, 0x12, 0xac, 0xc6, 0xba, 0xd0, 0x6e, 0x4, 0xf, 0x65, 0xdb, 0xb1, 0xe2, 0x88, 0x36, 0x5c, 0x57, 0x3d, 0x83, 0xe9, 0x95, 0xff, 0x41, 0x2b, 0x20, 0x4a, 0xf4, 0x9e, 0xc, 0x66, 0xd8, 0xb2, 0xb9, 0xd3, 0x6d, 0x7, 0x7b, 0x11, 0xaf, 0xc5, 0xce, 0xa4, 0x1a, 0x70, 0xbc, 0xd6, 0x68, 0x2, 0x9, 0x63, 0xdd, 0xb7, 0xcb, 0xa1, 0x1f, 0x75, 0x7e, 0x14, 0xaa, 0xc0, 0x52, 0x38, 0x86, 0xec, 0xe7, 0x8d, 0x33, 0x59, 0x25, 0x4f, 0xf1, 0x9b, 0x90, 0xfa, 0x44, 0x2e, 0x7d, 0x17, 0xa9, 0xc3, 0xc8, 0xa2, 0x1c, 0x76, 0xa, 0x60, 0xde, 0xb4, 0xbf, 0xd5, 0x6b, 0x1, 0x93, 0xf9, 0x47, 0x2d, 0x26, 0x4c, 0xf2, 0x98, 0xe4, 0x8e, 0x30, 0x5a, 0x51, 0x3b, 0x85, 0xef},
+ {0x0, 0x6b, 0xd6, 0xbd, 0xb1, 0xda, 0x67, 0xc, 0x7f, 0x14, 0xa9, 0xc2, 0xce, 0xa5, 0x18, 0x73, 0xfe, 0x95, 0x28, 0x43, 0x4f, 0x24, 0x99, 0xf2, 0x81, 0xea, 0x57, 0x3c, 0x30, 0x5b, 0xe6, 0x8d, 0xe1, 0x8a, 0x37, 0x5c, 0x50, 0x3b, 0x86, 0xed, 0x9e, 0xf5, 0x48, 0x23, 0x2f, 0x44, 0xf9, 0x92, 0x1f, 0x74, 0xc9, 0xa2, 0xae, 0xc5, 0x78, 0x13, 0x60, 0xb, 0xb6, 0xdd, 0xd1, 0xba, 0x7, 0x6c, 0xdf, 0xb4, 0x9, 0x62, 0x6e, 0x5, 0xb8, 0xd3, 0xa0, 0xcb, 0x76, 0x1d, 0x11, 0x7a, 0xc7, 0xac, 0x21, 0x4a, 0xf7, 0x9c, 0x90, 0xfb, 0x46, 0x2d, 0x5e, 0x35, 0x88, 0xe3, 0xef, 0x84, 0x39, 0x52, 0x3e, 0x55, 0xe8, 0x83, 0x8f, 0xe4, 0x59, 0x32, 0x41, 0x2a, 0x97, 0xfc, 0xf0, 0x9b, 0x26, 0x4d, 0xc0, 0xab, 0x16, 0x7d, 0x71, 0x1a, 0xa7, 0xcc, 0xbf, 0xd4, 0x69, 0x2, 0xe, 0x65, 0xd8, 0xb3, 0xa3, 0xc8, 0x75, 0x1e, 0x12, 0x79, 0xc4, 0xaf, 0xdc, 0xb7, 0xa, 0x61, 0x6d, 0x6, 0xbb, 0xd0, 0x5d, 0x36, 0x8b, 0xe0, 0xec, 0x87, 0x3a, 0x51, 0x22, 0x49, 0xf4, 0x9f, 0x93, 0xf8, 0x45, 0x2e, 0x42, 0x29, 0x94, 0xff, 0xf3, 0x98, 0x25, 0x4e, 0x3d, 0x56, 0xeb, 0x80, 0x8c, 0xe7, 0x5a, 0x31, 0xbc, 0xd7, 0x6a, 0x1, 0xd, 0x66, 0xdb, 0xb0, 0xc3, 0xa8, 0x15, 0x7e, 0x72, 0x19, 0xa4, 0xcf, 0x7c, 0x17, 0xaa, 0xc1, 0xcd, 0xa6, 0x1b, 0x70, 0x3, 0x68, 0xd5, 0xbe, 0xb2, 0xd9, 0x64, 0xf, 0x82, 0xe9, 0x54, 0x3f, 0x33, 0x58, 0xe5, 0x8e, 0xfd, 0x96, 0x2b, 0x40, 0x4c, 0x27, 0x9a, 0xf1, 0x9d, 0xf6, 0x4b, 0x20, 0x2c, 0x47, 0xfa, 0x91, 0xe2, 0x89, 0x34, 0x5f, 0x53, 0x38, 0x85, 0xee, 0x63, 0x8, 0xb5, 0xde, 0xd2, 0xb9, 0x4, 0x6f, 0x1c, 0x77, 0xca, 0xa1, 0xad, 0xc6, 0x7b, 0x10},
+ {0x0, 0x6c, 0xd8, 0xb4, 0xad, 0xc1, 0x75, 0x19, 0x47, 0x2b, 0x9f, 0xf3, 0xea, 0x86, 0x32, 0x5e, 0x8e, 0xe2, 0x56, 0x3a, 0x23, 0x4f, 0xfb, 0x97, 0xc9, 0xa5, 0x11, 0x7d, 0x64, 0x8, 0xbc, 0xd0, 0x1, 0x6d, 0xd9, 0xb5, 0xac, 0xc0, 0x74, 0x18, 0x46, 0x2a, 0x9e, 0xf2, 0xeb, 0x87, 0x33, 0x5f, 0x8f, 0xe3, 0x57, 0x3b, 0x22, 0x4e, 0xfa, 0x96, 0xc8, 0xa4, 0x10, 0x7c, 0x65, 0x9, 0xbd, 0xd1, 0x2, 0x6e, 0xda, 0xb6, 0xaf, 0xc3, 0x77, 0x1b, 0x45, 0x29, 0x9d, 0xf1, 0xe8, 0x84, 0x30, 0x5c, 0x8c, 0xe0, 0x54, 0x38, 0x21, 0x4d, 0xf9, 0x95, 0xcb, 0xa7, 0x13, 0x7f, 0x66, 0xa, 0xbe, 0xd2, 0x3, 0x6f, 0xdb, 0xb7, 0xae, 0xc2, 0x76, 0x1a, 0x44, 0x28, 0x9c, 0xf0, 0xe9, 0x85, 0x31, 0x5d, 0x8d, 0xe1, 0x55, 0x39, 0x20, 0x4c, 0xf8, 0x94, 0xca, 0xa6, 0x12, 0x7e, 0x67, 0xb, 0xbf, 0xd3, 0x4, 0x68, 0xdc, 0xb0, 0xa9, 0xc5, 0x71, 0x1d, 0x43, 0x2f, 0x9b, 0xf7, 0xee, 0x82, 0x36, 0x5a, 0x8a, 0xe6, 0x52, 0x3e, 0x27, 0x4b, 0xff, 0x93, 0xcd, 0xa1, 0x15, 0x79, 0x60, 0xc, 0xb8, 0xd4, 0x5, 0x69, 0xdd, 0xb1, 0xa8, 0xc4, 0x70, 0x1c, 0x42, 0x2e, 0x9a, 0xf6, 0xef, 0x83, 0x37, 0x5b, 0x8b, 0xe7, 0x53, 0x3f, 0x26, 0x4a, 0xfe, 0x92, 0xcc, 0xa0, 0x14, 0x78, 0x61, 0xd, 0xb9, 0xd5, 0x6, 0x6a, 0xde, 0xb2, 0xab, 0xc7, 0x73, 0x1f, 0x41, 0x2d, 0x99, 0xf5, 0xec, 0x80, 0x34, 0x58, 0x88, 0xe4, 0x50, 0x3c, 0x25, 0x49, 0xfd, 0x91, 0xcf, 0xa3, 0x17, 0x7b, 0x62, 0xe, 0xba, 0xd6, 0x7, 0x6b, 0xdf, 0xb3, 0xaa, 0xc6, 0x72, 0x1e, 0x40, 0x2c, 0x98, 0xf4, 0xed, 0x81, 0x35, 0x59, 0x89, 0xe5, 0x51, 0x3d, 0x24, 0x48, 0xfc, 0x90, 0xce, 0xa2, 0x16, 0x7a, 0x63, 0xf, 0xbb, 0xd7},
+ {0x0, 0x6d, 0xda, 0xb7, 0xa9, 0xc4, 0x73, 0x1e, 0x4f, 0x22, 0x95, 0xf8, 0xe6, 0x8b, 0x3c, 0x51, 0x9e, 0xf3, 0x44, 0x29, 0x37, 0x5a, 0xed, 0x80, 0xd1, 0xbc, 0xb, 0x66, 0x78, 0x15, 0xa2, 0xcf, 0x21, 0x4c, 0xfb, 0x96, 0x88, 0xe5, 0x52, 0x3f, 0x6e, 0x3, 0xb4, 0xd9, 0xc7, 0xaa, 0x1d, 0x70, 0xbf, 0xd2, 0x65, 0x8, 0x16, 0x7b, 0xcc, 0xa1, 0xf0, 0x9d, 0x2a, 0x47, 0x59, 0x34, 0x83, 0xee, 0x42, 0x2f, 0x98, 0xf5, 0xeb, 0x86, 0x31, 0x5c, 0xd, 0x60, 0xd7, 0xba, 0xa4, 0xc9, 0x7e, 0x13, 0xdc, 0xb1, 0x6, 0x6b, 0x75, 0x18, 0xaf, 0xc2, 0x93, 0xfe, 0x49, 0x24, 0x3a, 0x57, 0xe0, 0x8d, 0x63, 0xe, 0xb9, 0xd4, 0xca, 0xa7, 0x10, 0x7d, 0x2c, 0x41, 0xf6, 0x9b, 0x85, 0xe8, 0x5f, 0x32, 0xfd, 0x90, 0x27, 0x4a, 0x54, 0x39, 0x8e, 0xe3, 0xb2, 0xdf, 0x68, 0x5, 0x1b, 0x76, 0xc1, 0xac, 0x84, 0xe9, 0x5e, 0x33, 0x2d, 0x40, 0xf7, 0x9a, 0xcb, 0xa6, 0x11, 0x7c, 0x62, 0xf, 0xb8, 0xd5, 0x1a, 0x77, 0xc0, 0xad, 0xb3, 0xde, 0x69, 0x4, 0x55, 0x38, 0x8f, 0xe2, 0xfc, 0x91, 0x26, 0x4b, 0xa5, 0xc8, 0x7f, 0x12, 0xc, 0x61, 0xd6, 0xbb, 0xea, 0x87, 0x30, 0x5d, 0x43, 0x2e, 0x99, 0xf4, 0x3b, 0x56, 0xe1, 0x8c, 0x92, 0xff, 0x48, 0x25, 0x74, 0x19, 0xae, 0xc3, 0xdd, 0xb0, 0x7, 0x6a, 0xc6, 0xab, 0x1c, 0x71, 0x6f, 0x2, 0xb5, 0xd8, 0x89, 0xe4, 0x53, 0x3e, 0x20, 0x4d, 0xfa, 0x97, 0x58, 0x35, 0x82, 0xef, 0xf1, 0x9c, 0x2b, 0x46, 0x17, 0x7a, 0xcd, 0xa0, 0xbe, 0xd3, 0x64, 0x9, 0xe7, 0x8a, 0x3d, 0x50, 0x4e, 0x23, 0x94, 0xf9, 0xa8, 0xc5, 0x72, 0x1f, 0x1, 0x6c, 0xdb, 0xb6, 0x79, 0x14, 0xa3, 0xce, 0xd0, 0xbd, 0xa, 0x67, 0x36, 0x5b, 0xec, 0x81, 0x9f, 0xf2, 0x45, 0x28},
+ {0x0, 0x6e, 0xdc, 0xb2, 0xa5, 0xcb, 0x79, 0x17, 0x57, 0x39, 0x8b, 0xe5, 0xf2, 0x9c, 0x2e, 0x40, 0xae, 0xc0, 0x72, 0x1c, 0xb, 0x65, 0xd7, 0xb9, 0xf9, 0x97, 0x25, 0x4b, 0x5c, 0x32, 0x80, 0xee, 0x41, 0x2f, 0x9d, 0xf3, 0xe4, 0x8a, 0x38, 0x56, 0x16, 0x78, 0xca, 0xa4, 0xb3, 0xdd, 0x6f, 0x1, 0xef, 0x81, 0x33, 0x5d, 0x4a, 0x24, 0x96, 0xf8, 0xb8, 0xd6, 0x64, 0xa, 0x1d, 0x73, 0xc1, 0xaf, 0x82, 0xec, 0x5e, 0x30, 0x27, 0x49, 0xfb, 0x95, 0xd5, 0xbb, 0x9, 0x67, 0x70, 0x1e, 0xac, 0xc2, 0x2c, 0x42, 0xf0, 0x9e, 0x89, 0xe7, 0x55, 0x3b, 0x7b, 0x15, 0xa7, 0xc9, 0xde, 0xb0, 0x2, 0x6c, 0xc3, 0xad, 0x1f, 0x71, 0x66, 0x8, 0xba, 0xd4, 0x94, 0xfa, 0x48, 0x26, 0x31, 0x5f, 0xed, 0x83, 0x6d, 0x3, 0xb1, 0xdf, 0xc8, 0xa6, 0x14, 0x7a, 0x3a, 0x54, 0xe6, 0x88, 0x9f, 0xf1, 0x43, 0x2d, 0x19, 0x77, 0xc5, 0xab, 0xbc, 0xd2, 0x60, 0xe, 0x4e, 0x20, 0x92, 0xfc, 0xeb, 0x85, 0x37, 0x59, 0xb7, 0xd9, 0x6b, 0x5, 0x12, 0x7c, 0xce, 0xa0, 0xe0, 0x8e, 0x3c, 0x52, 0x45, 0x2b, 0x99, 0xf7, 0x58, 0x36, 0x84, 0xea, 0xfd, 0x93, 0x21, 0x4f, 0xf, 0x61, 0xd3, 0xbd, 0xaa, 0xc4, 0x76, 0x18, 0xf6, 0x98, 0x2a, 0x44, 0x53, 0x3d, 0x8f, 0xe1, 0xa1, 0xcf, 0x7d, 0x13, 0x4, 0x6a, 0xd8, 0xb6, 0x9b, 0xf5, 0x47, 0x29, 0x3e, 0x50, 0xe2, 0x8c, 0xcc, 0xa2, 0x10, 0x7e, 0x69, 0x7, 0xb5, 0xdb, 0x35, 0x5b, 0xe9, 0x87, 0x90, 0xfe, 0x4c, 0x22, 0x62, 0xc, 0xbe, 0xd0, 0xc7, 0xa9, 0x1b, 0x75, 0xda, 0xb4, 0x6, 0x68, 0x7f, 0x11, 0xa3, 0xcd, 0x8d, 0xe3, 0x51, 0x3f, 0x28, 0x46, 0xf4, 0x9a, 0x74, 0x1a, 0xa8, 0xc6, 0xd1, 0xbf, 0xd, 0x63, 0x23, 0x4d, 0xff, 0x91, 0x86, 0xe8, 0x5a, 0x34},
+ {0x0, 0x6f, 0xde, 0xb1, 0xa1, 0xce, 0x7f, 0x10, 0x5f, 0x30, 0x81, 0xee, 0xfe, 0x91, 0x20, 0x4f, 0xbe, 0xd1, 0x60, 0xf, 0x1f, 0x70, 0xc1, 0xae, 0xe1, 0x8e, 0x3f, 0x50, 0x40, 0x2f, 0x9e, 0xf1, 0x61, 0xe, 0xbf, 0xd0, 0xc0, 0xaf, 0x1e, 0x71, 0x3e, 0x51, 0xe0, 0x8f, 0x9f, 0xf0, 0x41, 0x2e, 0xdf, 0xb0, 0x1, 0x6e, 0x7e, 0x11, 0xa0, 0xcf, 0x80, 0xef, 0x5e, 0x31, 0x21, 0x4e, 0xff, 0x90, 0xc2, 0xad, 0x1c, 0x73, 0x63, 0xc, 0xbd, 0xd2, 0x9d, 0xf2, 0x43, 0x2c, 0x3c, 0x53, 0xe2, 0x8d, 0x7c, 0x13, 0xa2, 0xcd, 0xdd, 0xb2, 0x3, 0x6c, 0x23, 0x4c, 0xfd, 0x92, 0x82, 0xed, 0x5c, 0x33, 0xa3, 0xcc, 0x7d, 0x12, 0x2, 0x6d, 0xdc, 0xb3, 0xfc, 0x93, 0x22, 0x4d, 0x5d, 0x32, 0x83, 0xec, 0x1d, 0x72, 0xc3, 0xac, 0xbc, 0xd3, 0x62, 0xd, 0x42, 0x2d, 0x9c, 0xf3, 0xe3, 0x8c, 0x3d, 0x52, 0x99, 0xf6, 0x47, 0x28, 0x38, 0x57, 0xe6, 0x89, 0xc6, 0xa9, 0x18, 0x77, 0x67, 0x8, 0xb9, 0xd6, 0x27, 0x48, 0xf9, 0x96, 0x86, 0xe9, 0x58, 0x37, 0x78, 0x17, 0xa6, 0xc9, 0xd9, 0xb6, 0x7, 0x68, 0xf8, 0x97, 0x26, 0x49, 0x59, 0x36, 0x87, 0xe8, 0xa7, 0xc8, 0x79, 0x16, 0x6, 0x69, 0xd8, 0xb7, 0x46, 0x29, 0x98, 0xf7, 0xe7, 0x88, 0x39, 0x56, 0x19, 0x76, 0xc7, 0xa8, 0xb8, 0xd7, 0x66, 0x9, 0x5b, 0x34, 0x85, 0xea, 0xfa, 0x95, 0x24, 0x4b, 0x4, 0x6b, 0xda, 0xb5, 0xa5, 0xca, 0x7b, 0x14, 0xe5, 0x8a, 0x3b, 0x54, 0x44, 0x2b, 0x9a, 0xf5, 0xba, 0xd5, 0x64, 0xb, 0x1b, 0x74, 0xc5, 0xaa, 0x3a, 0x55, 0xe4, 0x8b, 0x9b, 0xf4, 0x45, 0x2a, 0x65, 0xa, 0xbb, 0xd4, 0xc4, 0xab, 0x1a, 0x75, 0x84, 0xeb, 0x5a, 0x35, 0x25, 0x4a, 0xfb, 0x94, 0xdb, 0xb4, 0x5, 0x6a, 0x7a, 0x15, 0xa4, 0xcb},
+ {0x0, 0x70, 0xe0, 0x90, 0xdd, 0xad, 0x3d, 0x4d, 0xa7, 0xd7, 0x47, 0x37, 0x7a, 0xa, 0x9a, 0xea, 0x53, 0x23, 0xb3, 0xc3, 0x8e, 0xfe, 0x6e, 0x1e, 0xf4, 0x84, 0x14, 0x64, 0x29, 0x59, 0xc9, 0xb9, 0xa6, 0xd6, 0x46, 0x36, 0x7b, 0xb, 0x9b, 0xeb, 0x1, 0x71, 0xe1, 0x91, 0xdc, 0xac, 0x3c, 0x4c, 0xf5, 0x85, 0x15, 0x65, 0x28, 0x58, 0xc8, 0xb8, 0x52, 0x22, 0xb2, 0xc2, 0x8f, 0xff, 0x6f, 0x1f, 0x51, 0x21, 0xb1, 0xc1, 0x8c, 0xfc, 0x6c, 0x1c, 0xf6, 0x86, 0x16, 0x66, 0x2b, 0x5b, 0xcb, 0xbb, 0x2, 0x72, 0xe2, 0x92, 0xdf, 0xaf, 0x3f, 0x4f, 0xa5, 0xd5, 0x45, 0x35, 0x78, 0x8, 0x98, 0xe8, 0xf7, 0x87, 0x17, 0x67, 0x2a, 0x5a, 0xca, 0xba, 0x50, 0x20, 0xb0, 0xc0, 0x8d, 0xfd, 0x6d, 0x1d, 0xa4, 0xd4, 0x44, 0x34, 0x79, 0x9, 0x99, 0xe9, 0x3, 0x73, 0xe3, 0x93, 0xde, 0xae, 0x3e, 0x4e, 0xa2, 0xd2, 0x42, 0x32, 0x7f, 0xf, 0x9f, 0xef, 0x5, 0x75, 0xe5, 0x95, 0xd8, 0xa8, 0x38, 0x48, 0xf1, 0x81, 0x11, 0x61, 0x2c, 0x5c, 0xcc, 0xbc, 0x56, 0x26, 0xb6, 0xc6, 0x8b, 0xfb, 0x6b, 0x1b, 0x4, 0x74, 0xe4, 0x94, 0xd9, 0xa9, 0x39, 0x49, 0xa3, 0xd3, 0x43, 0x33, 0x7e, 0xe, 0x9e, 0xee, 0x57, 0x27, 0xb7, 0xc7, 0x8a, 0xfa, 0x6a, 0x1a, 0xf0, 0x80, 0x10, 0x60, 0x2d, 0x5d, 0xcd, 0xbd, 0xf3, 0x83, 0x13, 0x63, 0x2e, 0x5e, 0xce, 0xbe, 0x54, 0x24, 0xb4, 0xc4, 0x89, 0xf9, 0x69, 0x19, 0xa0, 0xd0, 0x40, 0x30, 0x7d, 0xd, 0x9d, 0xed, 0x7, 0x77, 0xe7, 0x97, 0xda, 0xaa, 0x3a, 0x4a, 0x55, 0x25, 0xb5, 0xc5, 0x88, 0xf8, 0x68, 0x18, 0xf2, 0x82, 0x12, 0x62, 0x2f, 0x5f, 0xcf, 0xbf, 0x6, 0x76, 0xe6, 0x96, 0xdb, 0xab, 0x3b, 0x4b, 0xa1, 0xd1, 0x41, 0x31, 0x7c, 0xc, 0x9c, 0xec},
+ {0x0, 0x71, 0xe2, 0x93, 0xd9, 0xa8, 0x3b, 0x4a, 0xaf, 0xde, 0x4d, 0x3c, 0x76, 0x7, 0x94, 0xe5, 0x43, 0x32, 0xa1, 0xd0, 0x9a, 0xeb, 0x78, 0x9, 0xec, 0x9d, 0xe, 0x7f, 0x35, 0x44, 0xd7, 0xa6, 0x86, 0xf7, 0x64, 0x15, 0x5f, 0x2e, 0xbd, 0xcc, 0x29, 0x58, 0xcb, 0xba, 0xf0, 0x81, 0x12, 0x63, 0xc5, 0xb4, 0x27, 0x56, 0x1c, 0x6d, 0xfe, 0x8f, 0x6a, 0x1b, 0x88, 0xf9, 0xb3, 0xc2, 0x51, 0x20, 0x11, 0x60, 0xf3, 0x82, 0xc8, 0xb9, 0x2a, 0x5b, 0xbe, 0xcf, 0x5c, 0x2d, 0x67, 0x16, 0x85, 0xf4, 0x52, 0x23, 0xb0, 0xc1, 0x8b, 0xfa, 0x69, 0x18, 0xfd, 0x8c, 0x1f, 0x6e, 0x24, 0x55, 0xc6, 0xb7, 0x97, 0xe6, 0x75, 0x4, 0x4e, 0x3f, 0xac, 0xdd, 0x38, 0x49, 0xda, 0xab, 0xe1, 0x90, 0x3, 0x72, 0xd4, 0xa5, 0x36, 0x47, 0xd, 0x7c, 0xef, 0x9e, 0x7b, 0xa, 0x99, 0xe8, 0xa2, 0xd3, 0x40, 0x31, 0x22, 0x53, 0xc0, 0xb1, 0xfb, 0x8a, 0x19, 0x68, 0x8d, 0xfc, 0x6f, 0x1e, 0x54, 0x25, 0xb6, 0xc7, 0x61, 0x10, 0x83, 0xf2, 0xb8, 0xc9, 0x5a, 0x2b, 0xce, 0xbf, 0x2c, 0x5d, 0x17, 0x66, 0xf5, 0x84, 0xa4, 0xd5, 0x46, 0x37, 0x7d, 0xc, 0x9f, 0xee, 0xb, 0x7a, 0xe9, 0x98, 0xd2, 0xa3, 0x30, 0x41, 0xe7, 0x96, 0x5, 0x74, 0x3e, 0x4f, 0xdc, 0xad, 0x48, 0x39, 0xaa, 0xdb, 0x91, 0xe0, 0x73, 0x2, 0x33, 0x42, 0xd1, 0xa0, 0xea, 0x9b, 0x8, 0x79, 0x9c, 0xed, 0x7e, 0xf, 0x45, 0x34, 0xa7, 0xd6, 0x70, 0x1, 0x92, 0xe3, 0xa9, 0xd8, 0x4b, 0x3a, 0xdf, 0xae, 0x3d, 0x4c, 0x6, 0x77, 0xe4, 0x95, 0xb5, 0xc4, 0x57, 0x26, 0x6c, 0x1d, 0x8e, 0xff, 0x1a, 0x6b, 0xf8, 0x89, 0xc3, 0xb2, 0x21, 0x50, 0xf6, 0x87, 0x14, 0x65, 0x2f, 0x5e, 0xcd, 0xbc, 0x59, 0x28, 0xbb, 0xca, 0x80, 0xf1, 0x62, 0x13},
+ {0x0, 0x72, 0xe4, 0x96, 0xd5, 0xa7, 0x31, 0x43, 0xb7, 0xc5, 0x53, 0x21, 0x62, 0x10, 0x86, 0xf4, 0x73, 0x1, 0x97, 0xe5, 0xa6, 0xd4, 0x42, 0x30, 0xc4, 0xb6, 0x20, 0x52, 0x11, 0x63, 0xf5, 0x87, 0xe6, 0x94, 0x2, 0x70, 0x33, 0x41, 0xd7, 0xa5, 0x51, 0x23, 0xb5, 0xc7, 0x84, 0xf6, 0x60, 0x12, 0x95, 0xe7, 0x71, 0x3, 0x40, 0x32, 0xa4, 0xd6, 0x22, 0x50, 0xc6, 0xb4, 0xf7, 0x85, 0x13, 0x61, 0xd1, 0xa3, 0x35, 0x47, 0x4, 0x76, 0xe0, 0x92, 0x66, 0x14, 0x82, 0xf0, 0xb3, 0xc1, 0x57, 0x25, 0xa2, 0xd0, 0x46, 0x34, 0x77, 0x5, 0x93, 0xe1, 0x15, 0x67, 0xf1, 0x83, 0xc0, 0xb2, 0x24, 0x56, 0x37, 0x45, 0xd3, 0xa1, 0xe2, 0x90, 0x6, 0x74, 0x80, 0xf2, 0x64, 0x16, 0x55, 0x27, 0xb1, 0xc3, 0x44, 0x36, 0xa0, 0xd2, 0x91, 0xe3, 0x75, 0x7, 0xf3, 0x81, 0x17, 0x65, 0x26, 0x54, 0xc2, 0xb0, 0xbf, 0xcd, 0x5b, 0x29, 0x6a, 0x18, 0x8e, 0xfc, 0x8, 0x7a, 0xec, 0x9e, 0xdd, 0xaf, 0x39, 0x4b, 0xcc, 0xbe, 0x28, 0x5a, 0x19, 0x6b, 0xfd, 0x8f, 0x7b, 0x9, 0x9f, 0xed, 0xae, 0xdc, 0x4a, 0x38, 0x59, 0x2b, 0xbd, 0xcf, 0x8c, 0xfe, 0x68, 0x1a, 0xee, 0x9c, 0xa, 0x78, 0x3b, 0x49, 0xdf, 0xad, 0x2a, 0x58, 0xce, 0xbc, 0xff, 0x8d, 0x1b, 0x69, 0x9d, 0xef, 0x79, 0xb, 0x48, 0x3a, 0xac, 0xde, 0x6e, 0x1c, 0x8a, 0xf8, 0xbb, 0xc9, 0x5f, 0x2d, 0xd9, 0xab, 0x3d, 0x4f, 0xc, 0x7e, 0xe8, 0x9a, 0x1d, 0x6f, 0xf9, 0x8b, 0xc8, 0xba, 0x2c, 0x5e, 0xaa, 0xd8, 0x4e, 0x3c, 0x7f, 0xd, 0x9b, 0xe9, 0x88, 0xfa, 0x6c, 0x1e, 0x5d, 0x2f, 0xb9, 0xcb, 0x3f, 0x4d, 0xdb, 0xa9, 0xea, 0x98, 0xe, 0x7c, 0xfb, 0x89, 0x1f, 0x6d, 0x2e, 0x5c, 0xca, 0xb8, 0x4c, 0x3e, 0xa8, 0xda, 0x99, 0xeb, 0x7d, 0xf},
+ {0x0, 0x73, 0xe6, 0x95, 0xd1, 0xa2, 0x37, 0x44, 0xbf, 0xcc, 0x59, 0x2a, 0x6e, 0x1d, 0x88, 0xfb, 0x63, 0x10, 0x85, 0xf6, 0xb2, 0xc1, 0x54, 0x27, 0xdc, 0xaf, 0x3a, 0x49, 0xd, 0x7e, 0xeb, 0x98, 0xc6, 0xb5, 0x20, 0x53, 0x17, 0x64, 0xf1, 0x82, 0x79, 0xa, 0x9f, 0xec, 0xa8, 0xdb, 0x4e, 0x3d, 0xa5, 0xd6, 0x43, 0x30, 0x74, 0x7, 0x92, 0xe1, 0x1a, 0x69, 0xfc, 0x8f, 0xcb, 0xb8, 0x2d, 0x5e, 0x91, 0xe2, 0x77, 0x4, 0x40, 0x33, 0xa6, 0xd5, 0x2e, 0x5d, 0xc8, 0xbb, 0xff, 0x8c, 0x19, 0x6a, 0xf2, 0x81, 0x14, 0x67, 0x23, 0x50, 0xc5, 0xb6, 0x4d, 0x3e, 0xab, 0xd8, 0x9c, 0xef, 0x7a, 0x9, 0x57, 0x24, 0xb1, 0xc2, 0x86, 0xf5, 0x60, 0x13, 0xe8, 0x9b, 0xe, 0x7d, 0x39, 0x4a, 0xdf, 0xac, 0x34, 0x47, 0xd2, 0xa1, 0xe5, 0x96, 0x3, 0x70, 0x8b, 0xf8, 0x6d, 0x1e, 0x5a, 0x29, 0xbc, 0xcf, 0x3f, 0x4c, 0xd9, 0xaa, 0xee, 0x9d, 0x8, 0x7b, 0x80, 0xf3, 0x66, 0x15, 0x51, 0x22, 0xb7, 0xc4, 0x5c, 0x2f, 0xba, 0xc9, 0x8d, 0xfe, 0x6b, 0x18, 0xe3, 0x90, 0x5, 0x76, 0x32, 0x41, 0xd4, 0xa7, 0xf9, 0x8a, 0x1f, 0x6c, 0x28, 0x5b, 0xce, 0xbd, 0x46, 0x35, 0xa0, 0xd3, 0x97, 0xe4, 0x71, 0x2, 0x9a, 0xe9, 0x7c, 0xf, 0x4b, 0x38, 0xad, 0xde, 0x25, 0x56, 0xc3, 0xb0, 0xf4, 0x87, 0x12, 0x61, 0xae, 0xdd, 0x48, 0x3b, 0x7f, 0xc, 0x99, 0xea, 0x11, 0x62, 0xf7, 0x84, 0xc0, 0xb3, 0x26, 0x55, 0xcd, 0xbe, 0x2b, 0x58, 0x1c, 0x6f, 0xfa, 0x89, 0x72, 0x1, 0x94, 0xe7, 0xa3, 0xd0, 0x45, 0x36, 0x68, 0x1b, 0x8e, 0xfd, 0xb9, 0xca, 0x5f, 0x2c, 0xd7, 0xa4, 0x31, 0x42, 0x6, 0x75, 0xe0, 0x93, 0xb, 0x78, 0xed, 0x9e, 0xda, 0xa9, 0x3c, 0x4f, 0xb4, 0xc7, 0x52, 0x21, 0x65, 0x16, 0x83, 0xf0},
+ {0x0, 0x74, 0xe8, 0x9c, 0xcd, 0xb9, 0x25, 0x51, 0x87, 0xf3, 0x6f, 0x1b, 0x4a, 0x3e, 0xa2, 0xd6, 0x13, 0x67, 0xfb, 0x8f, 0xde, 0xaa, 0x36, 0x42, 0x94, 0xe0, 0x7c, 0x8, 0x59, 0x2d, 0xb1, 0xc5, 0x26, 0x52, 0xce, 0xba, 0xeb, 0x9f, 0x3, 0x77, 0xa1, 0xd5, 0x49, 0x3d, 0x6c, 0x18, 0x84, 0xf0, 0x35, 0x41, 0xdd, 0xa9, 0xf8, 0x8c, 0x10, 0x64, 0xb2, 0xc6, 0x5a, 0x2e, 0x7f, 0xb, 0x97, 0xe3, 0x4c, 0x38, 0xa4, 0xd0, 0x81, 0xf5, 0x69, 0x1d, 0xcb, 0xbf, 0x23, 0x57, 0x6, 0x72, 0xee, 0x9a, 0x5f, 0x2b, 0xb7, 0xc3, 0x92, 0xe6, 0x7a, 0xe, 0xd8, 0xac, 0x30, 0x44, 0x15, 0x61, 0xfd, 0x89, 0x6a, 0x1e, 0x82, 0xf6, 0xa7, 0xd3, 0x4f, 0x3b, 0xed, 0x99, 0x5, 0x71, 0x20, 0x54, 0xc8, 0xbc, 0x79, 0xd, 0x91, 0xe5, 0xb4, 0xc0, 0x5c, 0x28, 0xfe, 0x8a, 0x16, 0x62, 0x33, 0x47, 0xdb, 0xaf, 0x98, 0xec, 0x70, 0x4, 0x55, 0x21, 0xbd, 0xc9, 0x1f, 0x6b, 0xf7, 0x83, 0xd2, 0xa6, 0x3a, 0x4e, 0x8b, 0xff, 0x63, 0x17, 0x46, 0x32, 0xae, 0xda, 0xc, 0x78, 0xe4, 0x90, 0xc1, 0xb5, 0x29, 0x5d, 0xbe, 0xca, 0x56, 0x22, 0x73, 0x7, 0x9b, 0xef, 0x39, 0x4d, 0xd1, 0xa5, 0xf4, 0x80, 0x1c, 0x68, 0xad, 0xd9, 0x45, 0x31, 0x60, 0x14, 0x88, 0xfc, 0x2a, 0x5e, 0xc2, 0xb6, 0xe7, 0x93, 0xf, 0x7b, 0xd4, 0xa0, 0x3c, 0x48, 0x19, 0x6d, 0xf1, 0x85, 0x53, 0x27, 0xbb, 0xcf, 0x9e, 0xea, 0x76, 0x2, 0xc7, 0xb3, 0x2f, 0x5b, 0xa, 0x7e, 0xe2, 0x96, 0x40, 0x34, 0xa8, 0xdc, 0x8d, 0xf9, 0x65, 0x11, 0xf2, 0x86, 0x1a, 0x6e, 0x3f, 0x4b, 0xd7, 0xa3, 0x75, 0x1, 0x9d, 0xe9, 0xb8, 0xcc, 0x50, 0x24, 0xe1, 0x95, 0x9, 0x7d, 0x2c, 0x58, 0xc4, 0xb0, 0x66, 0x12, 0x8e, 0xfa, 0xab, 0xdf, 0x43, 0x37},
+ {0x0, 0x75, 0xea, 0x9f, 0xc9, 0xbc, 0x23, 0x56, 0x8f, 0xfa, 0x65, 0x10, 0x46, 0x33, 0xac, 0xd9, 0x3, 0x76, 0xe9, 0x9c, 0xca, 0xbf, 0x20, 0x55, 0x8c, 0xf9, 0x66, 0x13, 0x45, 0x30, 0xaf, 0xda, 0x6, 0x73, 0xec, 0x99, 0xcf, 0xba, 0x25, 0x50, 0x89, 0xfc, 0x63, 0x16, 0x40, 0x35, 0xaa, 0xdf, 0x5, 0x70, 0xef, 0x9a, 0xcc, 0xb9, 0x26, 0x53, 0x8a, 0xff, 0x60, 0x15, 0x43, 0x36, 0xa9, 0xdc, 0xc, 0x79, 0xe6, 0x93, 0xc5, 0xb0, 0x2f, 0x5a, 0x83, 0xf6, 0x69, 0x1c, 0x4a, 0x3f, 0xa0, 0xd5, 0xf, 0x7a, 0xe5, 0x90, 0xc6, 0xb3, 0x2c, 0x59, 0x80, 0xf5, 0x6a, 0x1f, 0x49, 0x3c, 0xa3, 0xd6, 0xa, 0x7f, 0xe0, 0x95, 0xc3, 0xb6, 0x29, 0x5c, 0x85, 0xf0, 0x6f, 0x1a, 0x4c, 0x39, 0xa6, 0xd3, 0x9, 0x7c, 0xe3, 0x96, 0xc0, 0xb5, 0x2a, 0x5f, 0x86, 0xf3, 0x6c, 0x19, 0x4f, 0x3a, 0xa5, 0xd0, 0x18, 0x6d, 0xf2, 0x87, 0xd1, 0xa4, 0x3b, 0x4e, 0x97, 0xe2, 0x7d, 0x8, 0x5e, 0x2b, 0xb4, 0xc1, 0x1b, 0x6e, 0xf1, 0x84, 0xd2, 0xa7, 0x38, 0x4d, 0x94, 0xe1, 0x7e, 0xb, 0x5d, 0x28, 0xb7, 0xc2, 0x1e, 0x6b, 0xf4, 0x81, 0xd7, 0xa2, 0x3d, 0x48, 0x91, 0xe4, 0x7b, 0xe, 0x58, 0x2d, 0xb2, 0xc7, 0x1d, 0x68, 0xf7, 0x82, 0xd4, 0xa1, 0x3e, 0x4b, 0x92, 0xe7, 0x78, 0xd, 0x5b, 0x2e, 0xb1, 0xc4, 0x14, 0x61, 0xfe, 0x8b, 0xdd, 0xa8, 0x37, 0x42, 0x9b, 0xee, 0x71, 0x4, 0x52, 0x27, 0xb8, 0xcd, 0x17, 0x62, 0xfd, 0x88, 0xde, 0xab, 0x34, 0x41, 0x98, 0xed, 0x72, 0x7, 0x51, 0x24, 0xbb, 0xce, 0x12, 0x67, 0xf8, 0x8d, 0xdb, 0xae, 0x31, 0x44, 0x9d, 0xe8, 0x77, 0x2, 0x54, 0x21, 0xbe, 0xcb, 0x11, 0x64, 0xfb, 0x8e, 0xd8, 0xad, 0x32, 0x47, 0x9e, 0xeb, 0x74, 0x1, 0x57, 0x22, 0xbd, 0xc8},
+ {0x0, 0x76, 0xec, 0x9a, 0xc5, 0xb3, 0x29, 0x5f, 0x97, 0xe1, 0x7b, 0xd, 0x52, 0x24, 0xbe, 0xc8, 0x33, 0x45, 0xdf, 0xa9, 0xf6, 0x80, 0x1a, 0x6c, 0xa4, 0xd2, 0x48, 0x3e, 0x61, 0x17, 0x8d, 0xfb, 0x66, 0x10, 0x8a, 0xfc, 0xa3, 0xd5, 0x4f, 0x39, 0xf1, 0x87, 0x1d, 0x6b, 0x34, 0x42, 0xd8, 0xae, 0x55, 0x23, 0xb9, 0xcf, 0x90, 0xe6, 0x7c, 0xa, 0xc2, 0xb4, 0x2e, 0x58, 0x7, 0x71, 0xeb, 0x9d, 0xcc, 0xba, 0x20, 0x56, 0x9, 0x7f, 0xe5, 0x93, 0x5b, 0x2d, 0xb7, 0xc1, 0x9e, 0xe8, 0x72, 0x4, 0xff, 0x89, 0x13, 0x65, 0x3a, 0x4c, 0xd6, 0xa0, 0x68, 0x1e, 0x84, 0xf2, 0xad, 0xdb, 0x41, 0x37, 0xaa, 0xdc, 0x46, 0x30, 0x6f, 0x19, 0x83, 0xf5, 0x3d, 0x4b, 0xd1, 0xa7, 0xf8, 0x8e, 0x14, 0x62, 0x99, 0xef, 0x75, 0x3, 0x5c, 0x2a, 0xb0, 0xc6, 0xe, 0x78, 0xe2, 0x94, 0xcb, 0xbd, 0x27, 0x51, 0x85, 0xf3, 0x69, 0x1f, 0x40, 0x36, 0xac, 0xda, 0x12, 0x64, 0xfe, 0x88, 0xd7, 0xa1, 0x3b, 0x4d, 0xb6, 0xc0, 0x5a, 0x2c, 0x73, 0x5, 0x9f, 0xe9, 0x21, 0x57, 0xcd, 0xbb, 0xe4, 0x92, 0x8, 0x7e, 0xe3, 0x95, 0xf, 0x79, 0x26, 0x50, 0xca, 0xbc, 0x74, 0x2, 0x98, 0xee, 0xb1, 0xc7, 0x5d, 0x2b, 0xd0, 0xa6, 0x3c, 0x4a, 0x15, 0x63, 0xf9, 0x8f, 0x47, 0x31, 0xab, 0xdd, 0x82, 0xf4, 0x6e, 0x18, 0x49, 0x3f, 0xa5, 0xd3, 0x8c, 0xfa, 0x60, 0x16, 0xde, 0xa8, 0x32, 0x44, 0x1b, 0x6d, 0xf7, 0x81, 0x7a, 0xc, 0x96, 0xe0, 0xbf, 0xc9, 0x53, 0x25, 0xed, 0x9b, 0x1, 0x77, 0x28, 0x5e, 0xc4, 0xb2, 0x2f, 0x59, 0xc3, 0xb5, 0xea, 0x9c, 0x6, 0x70, 0xb8, 0xce, 0x54, 0x22, 0x7d, 0xb, 0x91, 0xe7, 0x1c, 0x6a, 0xf0, 0x86, 0xd9, 0xaf, 0x35, 0x43, 0x8b, 0xfd, 0x67, 0x11, 0x4e, 0x38, 0xa2, 0xd4},
+ {0x0, 0x77, 0xee, 0x99, 0xc1, 0xb6, 0x2f, 0x58, 0x9f, 0xe8, 0x71, 0x6, 0x5e, 0x29, 0xb0, 0xc7, 0x23, 0x54, 0xcd, 0xba, 0xe2, 0x95, 0xc, 0x7b, 0xbc, 0xcb, 0x52, 0x25, 0x7d, 0xa, 0x93, 0xe4, 0x46, 0x31, 0xa8, 0xdf, 0x87, 0xf0, 0x69, 0x1e, 0xd9, 0xae, 0x37, 0x40, 0x18, 0x6f, 0xf6, 0x81, 0x65, 0x12, 0x8b, 0xfc, 0xa4, 0xd3, 0x4a, 0x3d, 0xfa, 0x8d, 0x14, 0x63, 0x3b, 0x4c, 0xd5, 0xa2, 0x8c, 0xfb, 0x62, 0x15, 0x4d, 0x3a, 0xa3, 0xd4, 0x13, 0x64, 0xfd, 0x8a, 0xd2, 0xa5, 0x3c, 0x4b, 0xaf, 0xd8, 0x41, 0x36, 0x6e, 0x19, 0x80, 0xf7, 0x30, 0x47, 0xde, 0xa9, 0xf1, 0x86, 0x1f, 0x68, 0xca, 0xbd, 0x24, 0x53, 0xb, 0x7c, 0xe5, 0x92, 0x55, 0x22, 0xbb, 0xcc, 0x94, 0xe3, 0x7a, 0xd, 0xe9, 0x9e, 0x7, 0x70, 0x28, 0x5f, 0xc6, 0xb1, 0x76, 0x1, 0x98, 0xef, 0xb7, 0xc0, 0x59, 0x2e, 0x5, 0x72, 0xeb, 0x9c, 0xc4, 0xb3, 0x2a, 0x5d, 0x9a, 0xed, 0x74, 0x3, 0x5b, 0x2c, 0xb5, 0xc2, 0x26, 0x51, 0xc8, 0xbf, 0xe7, 0x90, 0x9, 0x7e, 0xb9, 0xce, 0x57, 0x20, 0x78, 0xf, 0x96, 0xe1, 0x43, 0x34, 0xad, 0xda, 0x82, 0xf5, 0x6c, 0x1b, 0xdc, 0xab, 0x32, 0x45, 0x1d, 0x6a, 0xf3, 0x84, 0x60, 0x17, 0x8e, 0xf9, 0xa1, 0xd6, 0x4f, 0x38, 0xff, 0x88, 0x11, 0x66, 0x3e, 0x49, 0xd0, 0xa7, 0x89, 0xfe, 0x67, 0x10, 0x48, 0x3f, 0xa6, 0xd1, 0x16, 0x61, 0xf8, 0x8f, 0xd7, 0xa0, 0x39, 0x4e, 0xaa, 0xdd, 0x44, 0x33, 0x6b, 0x1c, 0x85, 0xf2, 0x35, 0x42, 0xdb, 0xac, 0xf4, 0x83, 0x1a, 0x6d, 0xcf, 0xb8, 0x21, 0x56, 0xe, 0x79, 0xe0, 0x97, 0x50, 0x27, 0xbe, 0xc9, 0x91, 0xe6, 0x7f, 0x8, 0xec, 0x9b, 0x2, 0x75, 0x2d, 0x5a, 0xc3, 0xb4, 0x73, 0x4, 0x9d, 0xea, 0xb2, 0xc5, 0x5c, 0x2b},
+ {0x0, 0x78, 0xf0, 0x88, 0xfd, 0x85, 0xd, 0x75, 0xe7, 0x9f, 0x17, 0x6f, 0x1a, 0x62, 0xea, 0x92, 0xd3, 0xab, 0x23, 0x5b, 0x2e, 0x56, 0xde, 0xa6, 0x34, 0x4c, 0xc4, 0xbc, 0xc9, 0xb1, 0x39, 0x41, 0xbb, 0xc3, 0x4b, 0x33, 0x46, 0x3e, 0xb6, 0xce, 0x5c, 0x24, 0xac, 0xd4, 0xa1, 0xd9, 0x51, 0x29, 0x68, 0x10, 0x98, 0xe0, 0x95, 0xed, 0x65, 0x1d, 0x8f, 0xf7, 0x7f, 0x7, 0x72, 0xa, 0x82, 0xfa, 0x6b, 0x13, 0x9b, 0xe3, 0x96, 0xee, 0x66, 0x1e, 0x8c, 0xf4, 0x7c, 0x4, 0x71, 0x9, 0x81, 0xf9, 0xb8, 0xc0, 0x48, 0x30, 0x45, 0x3d, 0xb5, 0xcd, 0x5f, 0x27, 0xaf, 0xd7, 0xa2, 0xda, 0x52, 0x2a, 0xd0, 0xa8, 0x20, 0x58, 0x2d, 0x55, 0xdd, 0xa5, 0x37, 0x4f, 0xc7, 0xbf, 0xca, 0xb2, 0x3a, 0x42, 0x3, 0x7b, 0xf3, 0x8b, 0xfe, 0x86, 0xe, 0x76, 0xe4, 0x9c, 0x14, 0x6c, 0x19, 0x61, 0xe9, 0x91, 0xd6, 0xae, 0x26, 0x5e, 0x2b, 0x53, 0xdb, 0xa3, 0x31, 0x49, 0xc1, 0xb9, 0xcc, 0xb4, 0x3c, 0x44, 0x5, 0x7d, 0xf5, 0x8d, 0xf8, 0x80, 0x8, 0x70, 0xe2, 0x9a, 0x12, 0x6a, 0x1f, 0x67, 0xef, 0x97, 0x6d, 0x15, 0x9d, 0xe5, 0x90, 0xe8, 0x60, 0x18, 0x8a, 0xf2, 0x7a, 0x2, 0x77, 0xf, 0x87, 0xff, 0xbe, 0xc6, 0x4e, 0x36, 0x43, 0x3b, 0xb3, 0xcb, 0x59, 0x21, 0xa9, 0xd1, 0xa4, 0xdc, 0x54, 0x2c, 0xbd, 0xc5, 0x4d, 0x35, 0x40, 0x38, 0xb0, 0xc8, 0x5a, 0x22, 0xaa, 0xd2, 0xa7, 0xdf, 0x57, 0x2f, 0x6e, 0x16, 0x9e, 0xe6, 0x93, 0xeb, 0x63, 0x1b, 0x89, 0xf1, 0x79, 0x1, 0x74, 0xc, 0x84, 0xfc, 0x6, 0x7e, 0xf6, 0x8e, 0xfb, 0x83, 0xb, 0x73, 0xe1, 0x99, 0x11, 0x69, 0x1c, 0x64, 0xec, 0x94, 0xd5, 0xad, 0x25, 0x5d, 0x28, 0x50, 0xd8, 0xa0, 0x32, 0x4a, 0xc2, 0xba, 0xcf, 0xb7, 0x3f, 0x47},
+ {0x0, 0x79, 0xf2, 0x8b, 0xf9, 0x80, 0xb, 0x72, 0xef, 0x96, 0x1d, 0x64, 0x16, 0x6f, 0xe4, 0x9d, 0xc3, 0xba, 0x31, 0x48, 0x3a, 0x43, 0xc8, 0xb1, 0x2c, 0x55, 0xde, 0xa7, 0xd5, 0xac, 0x27, 0x5e, 0x9b, 0xe2, 0x69, 0x10, 0x62, 0x1b, 0x90, 0xe9, 0x74, 0xd, 0x86, 0xff, 0x8d, 0xf4, 0x7f, 0x6, 0x58, 0x21, 0xaa, 0xd3, 0xa1, 0xd8, 0x53, 0x2a, 0xb7, 0xce, 0x45, 0x3c, 0x4e, 0x37, 0xbc, 0xc5, 0x2b, 0x52, 0xd9, 0xa0, 0xd2, 0xab, 0x20, 0x59, 0xc4, 0xbd, 0x36, 0x4f, 0x3d, 0x44, 0xcf, 0xb6, 0xe8, 0x91, 0x1a, 0x63, 0x11, 0x68, 0xe3, 0x9a, 0x7, 0x7e, 0xf5, 0x8c, 0xfe, 0x87, 0xc, 0x75, 0xb0, 0xc9, 0x42, 0x3b, 0x49, 0x30, 0xbb, 0xc2, 0x5f, 0x26, 0xad, 0xd4, 0xa6, 0xdf, 0x54, 0x2d, 0x73, 0xa, 0x81, 0xf8, 0x8a, 0xf3, 0x78, 0x1, 0x9c, 0xe5, 0x6e, 0x17, 0x65, 0x1c, 0x97, 0xee, 0x56, 0x2f, 0xa4, 0xdd, 0xaf, 0xd6, 0x5d, 0x24, 0xb9, 0xc0, 0x4b, 0x32, 0x40, 0x39, 0xb2, 0xcb, 0x95, 0xec, 0x67, 0x1e, 0x6c, 0x15, 0x9e, 0xe7, 0x7a, 0x3, 0x88, 0xf1, 0x83, 0xfa, 0x71, 0x8, 0xcd, 0xb4, 0x3f, 0x46, 0x34, 0x4d, 0xc6, 0xbf, 0x22, 0x5b, 0xd0, 0xa9, 0xdb, 0xa2, 0x29, 0x50, 0xe, 0x77, 0xfc, 0x85, 0xf7, 0x8e, 0x5, 0x7c, 0xe1, 0x98, 0x13, 0x6a, 0x18, 0x61, 0xea, 0x93, 0x7d, 0x4, 0x8f, 0xf6, 0x84, 0xfd, 0x76, 0xf, 0x92, 0xeb, 0x60, 0x19, 0x6b, 0x12, 0x99, 0xe0, 0xbe, 0xc7, 0x4c, 0x35, 0x47, 0x3e, 0xb5, 0xcc, 0x51, 0x28, 0xa3, 0xda, 0xa8, 0xd1, 0x5a, 0x23, 0xe6, 0x9f, 0x14, 0x6d, 0x1f, 0x66, 0xed, 0x94, 0x9, 0x70, 0xfb, 0x82, 0xf0, 0x89, 0x2, 0x7b, 0x25, 0x5c, 0xd7, 0xae, 0xdc, 0xa5, 0x2e, 0x57, 0xca, 0xb3, 0x38, 0x41, 0x33, 0x4a, 0xc1, 0xb8},
+ {0x0, 0x7a, 0xf4, 0x8e, 0xf5, 0x8f, 0x1, 0x7b, 0xf7, 0x8d, 0x3, 0x79, 0x2, 0x78, 0xf6, 0x8c, 0xf3, 0x89, 0x7, 0x7d, 0x6, 0x7c, 0xf2, 0x88, 0x4, 0x7e, 0xf0, 0x8a, 0xf1, 0x8b, 0x5, 0x7f, 0xfb, 0x81, 0xf, 0x75, 0xe, 0x74, 0xfa, 0x80, 0xc, 0x76, 0xf8, 0x82, 0xf9, 0x83, 0xd, 0x77, 0x8, 0x72, 0xfc, 0x86, 0xfd, 0x87, 0x9, 0x73, 0xff, 0x85, 0xb, 0x71, 0xa, 0x70, 0xfe, 0x84, 0xeb, 0x91, 0x1f, 0x65, 0x1e, 0x64, 0xea, 0x90, 0x1c, 0x66, 0xe8, 0x92, 0xe9, 0x93, 0x1d, 0x67, 0x18, 0x62, 0xec, 0x96, 0xed, 0x97, 0x19, 0x63, 0xef, 0x95, 0x1b, 0x61, 0x1a, 0x60, 0xee, 0x94, 0x10, 0x6a, 0xe4, 0x9e, 0xe5, 0x9f, 0x11, 0x6b, 0xe7, 0x9d, 0x13, 0x69, 0x12, 0x68, 0xe6, 0x9c, 0xe3, 0x99, 0x17, 0x6d, 0x16, 0x6c, 0xe2, 0x98, 0x14, 0x6e, 0xe0, 0x9a, 0xe1, 0x9b, 0x15, 0x6f, 0xcb, 0xb1, 0x3f, 0x45, 0x3e, 0x44, 0xca, 0xb0, 0x3c, 0x46, 0xc8, 0xb2, 0xc9, 0xb3, 0x3d, 0x47, 0x38, 0x42, 0xcc, 0xb6, 0xcd, 0xb7, 0x39, 0x43, 0xcf, 0xb5, 0x3b, 0x41, 0x3a, 0x40, 0xce, 0xb4, 0x30, 0x4a, 0xc4, 0xbe, 0xc5, 0xbf, 0x31, 0x4b, 0xc7, 0xbd, 0x33, 0x49, 0x32, 0x48, 0xc6, 0xbc, 0xc3, 0xb9, 0x37, 0x4d, 0x36, 0x4c, 0xc2, 0xb8, 0x34, 0x4e, 0xc0, 0xba, 0xc1, 0xbb, 0x35, 0x4f, 0x20, 0x5a, 0xd4, 0xae, 0xd5, 0xaf, 0x21, 0x5b, 0xd7, 0xad, 0x23, 0x59, 0x22, 0x58, 0xd6, 0xac, 0xd3, 0xa9, 0x27, 0x5d, 0x26, 0x5c, 0xd2, 0xa8, 0x24, 0x5e, 0xd0, 0xaa, 0xd1, 0xab, 0x25, 0x5f, 0xdb, 0xa1, 0x2f, 0x55, 0x2e, 0x54, 0xda, 0xa0, 0x2c, 0x56, 0xd8, 0xa2, 0xd9, 0xa3, 0x2d, 0x57, 0x28, 0x52, 0xdc, 0xa6, 0xdd, 0xa7, 0x29, 0x53, 0xdf, 0xa5, 0x2b, 0x51, 0x2a, 0x50, 0xde, 0xa4},
+ {0x0, 0x7b, 0xf6, 0x8d, 0xf1, 0x8a, 0x7, 0x7c, 0xff, 0x84, 0x9, 0x72, 0xe, 0x75, 0xf8, 0x83, 0xe3, 0x98, 0x15, 0x6e, 0x12, 0x69, 0xe4, 0x9f, 0x1c, 0x67, 0xea, 0x91, 0xed, 0x96, 0x1b, 0x60, 0xdb, 0xa0, 0x2d, 0x56, 0x2a, 0x51, 0xdc, 0xa7, 0x24, 0x5f, 0xd2, 0xa9, 0xd5, 0xae, 0x23, 0x58, 0x38, 0x43, 0xce, 0xb5, 0xc9, 0xb2, 0x3f, 0x44, 0xc7, 0xbc, 0x31, 0x4a, 0x36, 0x4d, 0xc0, 0xbb, 0xab, 0xd0, 0x5d, 0x26, 0x5a, 0x21, 0xac, 0xd7, 0x54, 0x2f, 0xa2, 0xd9, 0xa5, 0xde, 0x53, 0x28, 0x48, 0x33, 0xbe, 0xc5, 0xb9, 0xc2, 0x4f, 0x34, 0xb7, 0xcc, 0x41, 0x3a, 0x46, 0x3d, 0xb0, 0xcb, 0x70, 0xb, 0x86, 0xfd, 0x81, 0xfa, 0x77, 0xc, 0x8f, 0xf4, 0x79, 0x2, 0x7e, 0x5, 0x88, 0xf3, 0x93, 0xe8, 0x65, 0x1e, 0x62, 0x19, 0x94, 0xef, 0x6c, 0x17, 0x9a, 0xe1, 0x9d, 0xe6, 0x6b, 0x10, 0x4b, 0x30, 0xbd, 0xc6, 0xba, 0xc1, 0x4c, 0x37, 0xb4, 0xcf, 0x42, 0x39, 0x45, 0x3e, 0xb3, 0xc8, 0xa8, 0xd3, 0x5e, 0x25, 0x59, 0x22, 0xaf, 0xd4, 0x57, 0x2c, 0xa1, 0xda, 0xa6, 0xdd, 0x50, 0x2b, 0x90, 0xeb, 0x66, 0x1d, 0x61, 0x1a, 0x97, 0xec, 0x6f, 0x14, 0x99, 0xe2, 0x9e, 0xe5, 0x68, 0x13, 0x73, 0x8, 0x85, 0xfe, 0x82, 0xf9, 0x74, 0xf, 0x8c, 0xf7, 0x7a, 0x1, 0x7d, 0x6, 0x8b, 0xf0, 0xe0, 0x9b, 0x16, 0x6d, 0x11, 0x6a, 0xe7, 0x9c, 0x1f, 0x64, 0xe9, 0x92, 0xee, 0x95, 0x18, 0x63, 0x3, 0x78, 0xf5, 0x8e, 0xf2, 0x89, 0x4, 0x7f, 0xfc, 0x87, 0xa, 0x71, 0xd, 0x76, 0xfb, 0x80, 0x3b, 0x40, 0xcd, 0xb6, 0xca, 0xb1, 0x3c, 0x47, 0xc4, 0xbf, 0x32, 0x49, 0x35, 0x4e, 0xc3, 0xb8, 0xd8, 0xa3, 0x2e, 0x55, 0x29, 0x52, 0xdf, 0xa4, 0x27, 0x5c, 0xd1, 0xaa, 0xd6, 0xad, 0x20, 0x5b},
+ {0x0, 0x7c, 0xf8, 0x84, 0xed, 0x91, 0x15, 0x69, 0xc7, 0xbb, 0x3f, 0x43, 0x2a, 0x56, 0xd2, 0xae, 0x93, 0xef, 0x6b, 0x17, 0x7e, 0x2, 0x86, 0xfa, 0x54, 0x28, 0xac, 0xd0, 0xb9, 0xc5, 0x41, 0x3d, 0x3b, 0x47, 0xc3, 0xbf, 0xd6, 0xaa, 0x2e, 0x52, 0xfc, 0x80, 0x4, 0x78, 0x11, 0x6d, 0xe9, 0x95, 0xa8, 0xd4, 0x50, 0x2c, 0x45, 0x39, 0xbd, 0xc1, 0x6f, 0x13, 0x97, 0xeb, 0x82, 0xfe, 0x7a, 0x6, 0x76, 0xa, 0x8e, 0xf2, 0x9b, 0xe7, 0x63, 0x1f, 0xb1, 0xcd, 0x49, 0x35, 0x5c, 0x20, 0xa4, 0xd8, 0xe5, 0x99, 0x1d, 0x61, 0x8, 0x74, 0xf0, 0x8c, 0x22, 0x5e, 0xda, 0xa6, 0xcf, 0xb3, 0x37, 0x4b, 0x4d, 0x31, 0xb5, 0xc9, 0xa0, 0xdc, 0x58, 0x24, 0x8a, 0xf6, 0x72, 0xe, 0x67, 0x1b, 0x9f, 0xe3, 0xde, 0xa2, 0x26, 0x5a, 0x33, 0x4f, 0xcb, 0xb7, 0x19, 0x65, 0xe1, 0x9d, 0xf4, 0x88, 0xc, 0x70, 0xec, 0x90, 0x14, 0x68, 0x1, 0x7d, 0xf9, 0x85, 0x2b, 0x57, 0xd3, 0xaf, 0xc6, 0xba, 0x3e, 0x42, 0x7f, 0x3, 0x87, 0xfb, 0x92, 0xee, 0x6a, 0x16, 0xb8, 0xc4, 0x40, 0x3c, 0x55, 0x29, 0xad, 0xd1, 0xd7, 0xab, 0x2f, 0x53, 0x3a, 0x46, 0xc2, 0xbe, 0x10, 0x6c, 0xe8, 0x94, 0xfd, 0x81, 0x5, 0x79, 0x44, 0x38, 0xbc, 0xc0, 0xa9, 0xd5, 0x51, 0x2d, 0x83, 0xff, 0x7b, 0x7, 0x6e, 0x12, 0x96, 0xea, 0x9a, 0xe6, 0x62, 0x1e, 0x77, 0xb, 0x8f, 0xf3, 0x5d, 0x21, 0xa5, 0xd9, 0xb0, 0xcc, 0x48, 0x34, 0x9, 0x75, 0xf1, 0x8d, 0xe4, 0x98, 0x1c, 0x60, 0xce, 0xb2, 0x36, 0x4a, 0x23, 0x5f, 0xdb, 0xa7, 0xa1, 0xdd, 0x59, 0x25, 0x4c, 0x30, 0xb4, 0xc8, 0x66, 0x1a, 0x9e, 0xe2, 0x8b, 0xf7, 0x73, 0xf, 0x32, 0x4e, 0xca, 0xb6, 0xdf, 0xa3, 0x27, 0x5b, 0xf5, 0x89, 0xd, 0x71, 0x18, 0x64, 0xe0, 0x9c},
+ {0x0, 0x7d, 0xfa, 0x87, 0xe9, 0x94, 0x13, 0x6e, 0xcf, 0xb2, 0x35, 0x48, 0x26, 0x5b, 0xdc, 0xa1, 0x83, 0xfe, 0x79, 0x4, 0x6a, 0x17, 0x90, 0xed, 0x4c, 0x31, 0xb6, 0xcb, 0xa5, 0xd8, 0x5f, 0x22, 0x1b, 0x66, 0xe1, 0x9c, 0xf2, 0x8f, 0x8, 0x75, 0xd4, 0xa9, 0x2e, 0x53, 0x3d, 0x40, 0xc7, 0xba, 0x98, 0xe5, 0x62, 0x1f, 0x71, 0xc, 0x8b, 0xf6, 0x57, 0x2a, 0xad, 0xd0, 0xbe, 0xc3, 0x44, 0x39, 0x36, 0x4b, 0xcc, 0xb1, 0xdf, 0xa2, 0x25, 0x58, 0xf9, 0x84, 0x3, 0x7e, 0x10, 0x6d, 0xea, 0x97, 0xb5, 0xc8, 0x4f, 0x32, 0x5c, 0x21, 0xa6, 0xdb, 0x7a, 0x7, 0x80, 0xfd, 0x93, 0xee, 0x69, 0x14, 0x2d, 0x50, 0xd7, 0xaa, 0xc4, 0xb9, 0x3e, 0x43, 0xe2, 0x9f, 0x18, 0x65, 0xb, 0x76, 0xf1, 0x8c, 0xae, 0xd3, 0x54, 0x29, 0x47, 0x3a, 0xbd, 0xc0, 0x61, 0x1c, 0x9b, 0xe6, 0x88, 0xf5, 0x72, 0xf, 0x6c, 0x11, 0x96, 0xeb, 0x85, 0xf8, 0x7f, 0x2, 0xa3, 0xde, 0x59, 0x24, 0x4a, 0x37, 0xb0, 0xcd, 0xef, 0x92, 0x15, 0x68, 0x6, 0x7b, 0xfc, 0x81, 0x20, 0x5d, 0xda, 0xa7, 0xc9, 0xb4, 0x33, 0x4e, 0x77, 0xa, 0x8d, 0xf0, 0x9e, 0xe3, 0x64, 0x19, 0xb8, 0xc5, 0x42, 0x3f, 0x51, 0x2c, 0xab, 0xd6, 0xf4, 0x89, 0xe, 0x73, 0x1d, 0x60, 0xe7, 0x9a, 0x3b, 0x46, 0xc1, 0xbc, 0xd2, 0xaf, 0x28, 0x55, 0x5a, 0x27, 0xa0, 0xdd, 0xb3, 0xce, 0x49, 0x34, 0x95, 0xe8, 0x6f, 0x12, 0x7c, 0x1, 0x86, 0xfb, 0xd9, 0xa4, 0x23, 0x5e, 0x30, 0x4d, 0xca, 0xb7, 0x16, 0x6b, 0xec, 0x91, 0xff, 0x82, 0x5, 0x78, 0x41, 0x3c, 0xbb, 0xc6, 0xa8, 0xd5, 0x52, 0x2f, 0x8e, 0xf3, 0x74, 0x9, 0x67, 0x1a, 0x9d, 0xe0, 0xc2, 0xbf, 0x38, 0x45, 0x2b, 0x56, 0xd1, 0xac, 0xd, 0x70, 0xf7, 0x8a, 0xe4, 0x99, 0x1e, 0x63},
+ {0x0, 0x7e, 0xfc, 0x82, 0xe5, 0x9b, 0x19, 0x67, 0xd7, 0xa9, 0x2b, 0x55, 0x32, 0x4c, 0xce, 0xb0, 0xb3, 0xcd, 0x4f, 0x31, 0x56, 0x28, 0xaa, 0xd4, 0x64, 0x1a, 0x98, 0xe6, 0x81, 0xff, 0x7d, 0x3, 0x7b, 0x5, 0x87, 0xf9, 0x9e, 0xe0, 0x62, 0x1c, 0xac, 0xd2, 0x50, 0x2e, 0x49, 0x37, 0xb5, 0xcb, 0xc8, 0xb6, 0x34, 0x4a, 0x2d, 0x53, 0xd1, 0xaf, 0x1f, 0x61, 0xe3, 0x9d, 0xfa, 0x84, 0x6, 0x78, 0xf6, 0x88, 0xa, 0x74, 0x13, 0x6d, 0xef, 0x91, 0x21, 0x5f, 0xdd, 0xa3, 0xc4, 0xba, 0x38, 0x46, 0x45, 0x3b, 0xb9, 0xc7, 0xa0, 0xde, 0x5c, 0x22, 0x92, 0xec, 0x6e, 0x10, 0x77, 0x9, 0x8b, 0xf5, 0x8d, 0xf3, 0x71, 0xf, 0x68, 0x16, 0x94, 0xea, 0x5a, 0x24, 0xa6, 0xd8, 0xbf, 0xc1, 0x43, 0x3d, 0x3e, 0x40, 0xc2, 0xbc, 0xdb, 0xa5, 0x27, 0x59, 0xe9, 0x97, 0x15, 0x6b, 0xc, 0x72, 0xf0, 0x8e, 0xf1, 0x8f, 0xd, 0x73, 0x14, 0x6a, 0xe8, 0x96, 0x26, 0x58, 0xda, 0xa4, 0xc3, 0xbd, 0x3f, 0x41, 0x42, 0x3c, 0xbe, 0xc0, 0xa7, 0xd9, 0x5b, 0x25, 0x95, 0xeb, 0x69, 0x17, 0x70, 0xe, 0x8c, 0xf2, 0x8a, 0xf4, 0x76, 0x8, 0x6f, 0x11, 0x93, 0xed, 0x5d, 0x23, 0xa1, 0xdf, 0xb8, 0xc6, 0x44, 0x3a, 0x39, 0x47, 0xc5, 0xbb, 0xdc, 0xa2, 0x20, 0x5e, 0xee, 0x90, 0x12, 0x6c, 0xb, 0x75, 0xf7, 0x89, 0x7, 0x79, 0xfb, 0x85, 0xe2, 0x9c, 0x1e, 0x60, 0xd0, 0xae, 0x2c, 0x52, 0x35, 0x4b, 0xc9, 0xb7, 0xb4, 0xca, 0x48, 0x36, 0x51, 0x2f, 0xad, 0xd3, 0x63, 0x1d, 0x9f, 0xe1, 0x86, 0xf8, 0x7a, 0x4, 0x7c, 0x2, 0x80, 0xfe, 0x99, 0xe7, 0x65, 0x1b, 0xab, 0xd5, 0x57, 0x29, 0x4e, 0x30, 0xb2, 0xcc, 0xcf, 0xb1, 0x33, 0x4d, 0x2a, 0x54, 0xd6, 0xa8, 0x18, 0x66, 0xe4, 0x9a, 0xfd, 0x83, 0x1, 0x7f},
+ {0x0, 0x7f, 0xfe, 0x81, 0xe1, 0x9e, 0x1f, 0x60, 0xdf, 0xa0, 0x21, 0x5e, 0x3e, 0x41, 0xc0, 0xbf, 0xa3, 0xdc, 0x5d, 0x22, 0x42, 0x3d, 0xbc, 0xc3, 0x7c, 0x3, 0x82, 0xfd, 0x9d, 0xe2, 0x63, 0x1c, 0x5b, 0x24, 0xa5, 0xda, 0xba, 0xc5, 0x44, 0x3b, 0x84, 0xfb, 0x7a, 0x5, 0x65, 0x1a, 0x9b, 0xe4, 0xf8, 0x87, 0x6, 0x79, 0x19, 0x66, 0xe7, 0x98, 0x27, 0x58, 0xd9, 0xa6, 0xc6, 0xb9, 0x38, 0x47, 0xb6, 0xc9, 0x48, 0x37, 0x57, 0x28, 0xa9, 0xd6, 0x69, 0x16, 0x97, 0xe8, 0x88, 0xf7, 0x76, 0x9, 0x15, 0x6a, 0xeb, 0x94, 0xf4, 0x8b, 0xa, 0x75, 0xca, 0xb5, 0x34, 0x4b, 0x2b, 0x54, 0xd5, 0xaa, 0xed, 0x92, 0x13, 0x6c, 0xc, 0x73, 0xf2, 0x8d, 0x32, 0x4d, 0xcc, 0xb3, 0xd3, 0xac, 0x2d, 0x52, 0x4e, 0x31, 0xb0, 0xcf, 0xaf, 0xd0, 0x51, 0x2e, 0x91, 0xee, 0x6f, 0x10, 0x70, 0xf, 0x8e, 0xf1, 0x71, 0xe, 0x8f, 0xf0, 0x90, 0xef, 0x6e, 0x11, 0xae, 0xd1, 0x50, 0x2f, 0x4f, 0x30, 0xb1, 0xce, 0xd2, 0xad, 0x2c, 0x53, 0x33, 0x4c, 0xcd, 0xb2, 0xd, 0x72, 0xf3, 0x8c, 0xec, 0x93, 0x12, 0x6d, 0x2a, 0x55, 0xd4, 0xab, 0xcb, 0xb4, 0x35, 0x4a, 0xf5, 0x8a, 0xb, 0x74, 0x14, 0x6b, 0xea, 0x95, 0x89, 0xf6, 0x77, 0x8, 0x68, 0x17, 0x96, 0xe9, 0x56, 0x29, 0xa8, 0xd7, 0xb7, 0xc8, 0x49, 0x36, 0xc7, 0xb8, 0x39, 0x46, 0x26, 0x59, 0xd8, 0xa7, 0x18, 0x67, 0xe6, 0x99, 0xf9, 0x86, 0x7, 0x78, 0x64, 0x1b, 0x9a, 0xe5, 0x85, 0xfa, 0x7b, 0x4, 0xbb, 0xc4, 0x45, 0x3a, 0x5a, 0x25, 0xa4, 0xdb, 0x9c, 0xe3, 0x62, 0x1d, 0x7d, 0x2, 0x83, 0xfc, 0x43, 0x3c, 0xbd, 0xc2, 0xa2, 0xdd, 0x5c, 0x23, 0x3f, 0x40, 0xc1, 0xbe, 0xde, 0xa1, 0x20, 0x5f, 0xe0, 0x9f, 0x1e, 0x61, 0x1, 0x7e, 0xff, 0x80},
+ {0x0, 0x80, 0x1d, 0x9d, 0x3a, 0xba, 0x27, 0xa7, 0x74, 0xf4, 0x69, 0xe9, 0x4e, 0xce, 0x53, 0xd3, 0xe8, 0x68, 0xf5, 0x75, 0xd2, 0x52, 0xcf, 0x4f, 0x9c, 0x1c, 0x81, 0x1, 0xa6, 0x26, 0xbb, 0x3b, 0xcd, 0x4d, 0xd0, 0x50, 0xf7, 0x77, 0xea, 0x6a, 0xb9, 0x39, 0xa4, 0x24, 0x83, 0x3, 0x9e, 0x1e, 0x25, 0xa5, 0x38, 0xb8, 0x1f, 0x9f, 0x2, 0x82, 0x51, 0xd1, 0x4c, 0xcc, 0x6b, 0xeb, 0x76, 0xf6, 0x87, 0x7, 0x9a, 0x1a, 0xbd, 0x3d, 0xa0, 0x20, 0xf3, 0x73, 0xee, 0x6e, 0xc9, 0x49, 0xd4, 0x54, 0x6f, 0xef, 0x72, 0xf2, 0x55, 0xd5, 0x48, 0xc8, 0x1b, 0x9b, 0x6, 0x86, 0x21, 0xa1, 0x3c, 0xbc, 0x4a, 0xca, 0x57, 0xd7, 0x70, 0xf0, 0x6d, 0xed, 0x3e, 0xbe, 0x23, 0xa3, 0x4, 0x84, 0x19, 0x99, 0xa2, 0x22, 0xbf, 0x3f, 0x98, 0x18, 0x85, 0x5, 0xd6, 0x56, 0xcb, 0x4b, 0xec, 0x6c, 0xf1, 0x71, 0x13, 0x93, 0xe, 0x8e, 0x29, 0xa9, 0x34, 0xb4, 0x67, 0xe7, 0x7a, 0xfa, 0x5d, 0xdd, 0x40, 0xc0, 0xfb, 0x7b, 0xe6, 0x66, 0xc1, 0x41, 0xdc, 0x5c, 0x8f, 0xf, 0x92, 0x12, 0xb5, 0x35, 0xa8, 0x28, 0xde, 0x5e, 0xc3, 0x43, 0xe4, 0x64, 0xf9, 0x79, 0xaa, 0x2a, 0xb7, 0x37, 0x90, 0x10, 0x8d, 0xd, 0x36, 0xb6, 0x2b, 0xab, 0xc, 0x8c, 0x11, 0x91, 0x42, 0xc2, 0x5f, 0xdf, 0x78, 0xf8, 0x65, 0xe5, 0x94, 0x14, 0x89, 0x9, 0xae, 0x2e, 0xb3, 0x33, 0xe0, 0x60, 0xfd, 0x7d, 0xda, 0x5a, 0xc7, 0x47, 0x7c, 0xfc, 0x61, 0xe1, 0x46, 0xc6, 0x5b, 0xdb, 0x8, 0x88, 0x15, 0x95, 0x32, 0xb2, 0x2f, 0xaf, 0x59, 0xd9, 0x44, 0xc4, 0x63, 0xe3, 0x7e, 0xfe, 0x2d, 0xad, 0x30, 0xb0, 0x17, 0x97, 0xa, 0x8a, 0xb1, 0x31, 0xac, 0x2c, 0x8b, 0xb, 0x96, 0x16, 0xc5, 0x45, 0xd8, 0x58, 0xff, 0x7f, 0xe2, 0x62},
+ {0x0, 0x81, 0x1f, 0x9e, 0x3e, 0xbf, 0x21, 0xa0, 0x7c, 0xfd, 0x63, 0xe2, 0x42, 0xc3, 0x5d, 0xdc, 0xf8, 0x79, 0xe7, 0x66, 0xc6, 0x47, 0xd9, 0x58, 0x84, 0x5, 0x9b, 0x1a, 0xba, 0x3b, 0xa5, 0x24, 0xed, 0x6c, 0xf2, 0x73, 0xd3, 0x52, 0xcc, 0x4d, 0x91, 0x10, 0x8e, 0xf, 0xaf, 0x2e, 0xb0, 0x31, 0x15, 0x94, 0xa, 0x8b, 0x2b, 0xaa, 0x34, 0xb5, 0x69, 0xe8, 0x76, 0xf7, 0x57, 0xd6, 0x48, 0xc9, 0xc7, 0x46, 0xd8, 0x59, 0xf9, 0x78, 0xe6, 0x67, 0xbb, 0x3a, 0xa4, 0x25, 0x85, 0x4, 0x9a, 0x1b, 0x3f, 0xbe, 0x20, 0xa1, 0x1, 0x80, 0x1e, 0x9f, 0x43, 0xc2, 0x5c, 0xdd, 0x7d, 0xfc, 0x62, 0xe3, 0x2a, 0xab, 0x35, 0xb4, 0x14, 0x95, 0xb, 0x8a, 0x56, 0xd7, 0x49, 0xc8, 0x68, 0xe9, 0x77, 0xf6, 0xd2, 0x53, 0xcd, 0x4c, 0xec, 0x6d, 0xf3, 0x72, 0xae, 0x2f, 0xb1, 0x30, 0x90, 0x11, 0x8f, 0xe, 0x93, 0x12, 0x8c, 0xd, 0xad, 0x2c, 0xb2, 0x33, 0xef, 0x6e, 0xf0, 0x71, 0xd1, 0x50, 0xce, 0x4f, 0x6b, 0xea, 0x74, 0xf5, 0x55, 0xd4, 0x4a, 0xcb, 0x17, 0x96, 0x8, 0x89, 0x29, 0xa8, 0x36, 0xb7, 0x7e, 0xff, 0x61, 0xe0, 0x40, 0xc1, 0x5f, 0xde, 0x2, 0x83, 0x1d, 0x9c, 0x3c, 0xbd, 0x23, 0xa2, 0x86, 0x7, 0x99, 0x18, 0xb8, 0x39, 0xa7, 0x26, 0xfa, 0x7b, 0xe5, 0x64, 0xc4, 0x45, 0xdb, 0x5a, 0x54, 0xd5, 0x4b, 0xca, 0x6a, 0xeb, 0x75, 0xf4, 0x28, 0xa9, 0x37, 0xb6, 0x16, 0x97, 0x9, 0x88, 0xac, 0x2d, 0xb3, 0x32, 0x92, 0x13, 0x8d, 0xc, 0xd0, 0x51, 0xcf, 0x4e, 0xee, 0x6f, 0xf1, 0x70, 0xb9, 0x38, 0xa6, 0x27, 0x87, 0x6, 0x98, 0x19, 0xc5, 0x44, 0xda, 0x5b, 0xfb, 0x7a, 0xe4, 0x65, 0x41, 0xc0, 0x5e, 0xdf, 0x7f, 0xfe, 0x60, 0xe1, 0x3d, 0xbc, 0x22, 0xa3, 0x3, 0x82, 0x1c, 0x9d},
+ {0x0, 0x82, 0x19, 0x9b, 0x32, 0xb0, 0x2b, 0xa9, 0x64, 0xe6, 0x7d, 0xff, 0x56, 0xd4, 0x4f, 0xcd, 0xc8, 0x4a, 0xd1, 0x53, 0xfa, 0x78, 0xe3, 0x61, 0xac, 0x2e, 0xb5, 0x37, 0x9e, 0x1c, 0x87, 0x5, 0x8d, 0xf, 0x94, 0x16, 0xbf, 0x3d, 0xa6, 0x24, 0xe9, 0x6b, 0xf0, 0x72, 0xdb, 0x59, 0xc2, 0x40, 0x45, 0xc7, 0x5c, 0xde, 0x77, 0xf5, 0x6e, 0xec, 0x21, 0xa3, 0x38, 0xba, 0x13, 0x91, 0xa, 0x88, 0x7, 0x85, 0x1e, 0x9c, 0x35, 0xb7, 0x2c, 0xae, 0x63, 0xe1, 0x7a, 0xf8, 0x51, 0xd3, 0x48, 0xca, 0xcf, 0x4d, 0xd6, 0x54, 0xfd, 0x7f, 0xe4, 0x66, 0xab, 0x29, 0xb2, 0x30, 0x99, 0x1b, 0x80, 0x2, 0x8a, 0x8, 0x93, 0x11, 0xb8, 0x3a, 0xa1, 0x23, 0xee, 0x6c, 0xf7, 0x75, 0xdc, 0x5e, 0xc5, 0x47, 0x42, 0xc0, 0x5b, 0xd9, 0x70, 0xf2, 0x69, 0xeb, 0x26, 0xa4, 0x3f, 0xbd, 0x14, 0x96, 0xd, 0x8f, 0xe, 0x8c, 0x17, 0x95, 0x3c, 0xbe, 0x25, 0xa7, 0x6a, 0xe8, 0x73, 0xf1, 0x58, 0xda, 0x41, 0xc3, 0xc6, 0x44, 0xdf, 0x5d, 0xf4, 0x76, 0xed, 0x6f, 0xa2, 0x20, 0xbb, 0x39, 0x90, 0x12, 0x89, 0xb, 0x83, 0x1, 0x9a, 0x18, 0xb1, 0x33, 0xa8, 0x2a, 0xe7, 0x65, 0xfe, 0x7c, 0xd5, 0x57, 0xcc, 0x4e, 0x4b, 0xc9, 0x52, 0xd0, 0x79, 0xfb, 0x60, 0xe2, 0x2f, 0xad, 0x36, 0xb4, 0x1d, 0x9f, 0x4, 0x86, 0x9, 0x8b, 0x10, 0x92, 0x3b, 0xb9, 0x22, 0xa0, 0x6d, 0xef, 0x74, 0xf6, 0x5f, 0xdd, 0x46, 0xc4, 0xc1, 0x43, 0xd8, 0x5a, 0xf3, 0x71, 0xea, 0x68, 0xa5, 0x27, 0xbc, 0x3e, 0x97, 0x15, 0x8e, 0xc, 0x84, 0x6, 0x9d, 0x1f, 0xb6, 0x34, 0xaf, 0x2d, 0xe0, 0x62, 0xf9, 0x7b, 0xd2, 0x50, 0xcb, 0x49, 0x4c, 0xce, 0x55, 0xd7, 0x7e, 0xfc, 0x67, 0xe5, 0x28, 0xaa, 0x31, 0xb3, 0x1a, 0x98, 0x3, 0x81},
+ {0x0, 0x83, 0x1b, 0x98, 0x36, 0xb5, 0x2d, 0xae, 0x6c, 0xef, 0x77, 0xf4, 0x5a, 0xd9, 0x41, 0xc2, 0xd8, 0x5b, 0xc3, 0x40, 0xee, 0x6d, 0xf5, 0x76, 0xb4, 0x37, 0xaf, 0x2c, 0x82, 0x1, 0x99, 0x1a, 0xad, 0x2e, 0xb6, 0x35, 0x9b, 0x18, 0x80, 0x3, 0xc1, 0x42, 0xda, 0x59, 0xf7, 0x74, 0xec, 0x6f, 0x75, 0xf6, 0x6e, 0xed, 0x43, 0xc0, 0x58, 0xdb, 0x19, 0x9a, 0x2, 0x81, 0x2f, 0xac, 0x34, 0xb7, 0x47, 0xc4, 0x5c, 0xdf, 0x71, 0xf2, 0x6a, 0xe9, 0x2b, 0xa8, 0x30, 0xb3, 0x1d, 0x9e, 0x6, 0x85, 0x9f, 0x1c, 0x84, 0x7, 0xa9, 0x2a, 0xb2, 0x31, 0xf3, 0x70, 0xe8, 0x6b, 0xc5, 0x46, 0xde, 0x5d, 0xea, 0x69, 0xf1, 0x72, 0xdc, 0x5f, 0xc7, 0x44, 0x86, 0x5, 0x9d, 0x1e, 0xb0, 0x33, 0xab, 0x28, 0x32, 0xb1, 0x29, 0xaa, 0x4, 0x87, 0x1f, 0x9c, 0x5e, 0xdd, 0x45, 0xc6, 0x68, 0xeb, 0x73, 0xf0, 0x8e, 0xd, 0x95, 0x16, 0xb8, 0x3b, 0xa3, 0x20, 0xe2, 0x61, 0xf9, 0x7a, 0xd4, 0x57, 0xcf, 0x4c, 0x56, 0xd5, 0x4d, 0xce, 0x60, 0xe3, 0x7b, 0xf8, 0x3a, 0xb9, 0x21, 0xa2, 0xc, 0x8f, 0x17, 0x94, 0x23, 0xa0, 0x38, 0xbb, 0x15, 0x96, 0xe, 0x8d, 0x4f, 0xcc, 0x54, 0xd7, 0x79, 0xfa, 0x62, 0xe1, 0xfb, 0x78, 0xe0, 0x63, 0xcd, 0x4e, 0xd6, 0x55, 0x97, 0x14, 0x8c, 0xf, 0xa1, 0x22, 0xba, 0x39, 0xc9, 0x4a, 0xd2, 0x51, 0xff, 0x7c, 0xe4, 0x67, 0xa5, 0x26, 0xbe, 0x3d, 0x93, 0x10, 0x88, 0xb, 0x11, 0x92, 0xa, 0x89, 0x27, 0xa4, 0x3c, 0xbf, 0x7d, 0xfe, 0x66, 0xe5, 0x4b, 0xc8, 0x50, 0xd3, 0x64, 0xe7, 0x7f, 0xfc, 0x52, 0xd1, 0x49, 0xca, 0x8, 0x8b, 0x13, 0x90, 0x3e, 0xbd, 0x25, 0xa6, 0xbc, 0x3f, 0xa7, 0x24, 0x8a, 0x9, 0x91, 0x12, 0xd0, 0x53, 0xcb, 0x48, 0xe6, 0x65, 0xfd, 0x7e},
+ {0x0, 0x84, 0x15, 0x91, 0x2a, 0xae, 0x3f, 0xbb, 0x54, 0xd0, 0x41, 0xc5, 0x7e, 0xfa, 0x6b, 0xef, 0xa8, 0x2c, 0xbd, 0x39, 0x82, 0x6, 0x97, 0x13, 0xfc, 0x78, 0xe9, 0x6d, 0xd6, 0x52, 0xc3, 0x47, 0x4d, 0xc9, 0x58, 0xdc, 0x67, 0xe3, 0x72, 0xf6, 0x19, 0x9d, 0xc, 0x88, 0x33, 0xb7, 0x26, 0xa2, 0xe5, 0x61, 0xf0, 0x74, 0xcf, 0x4b, 0xda, 0x5e, 0xb1, 0x35, 0xa4, 0x20, 0x9b, 0x1f, 0x8e, 0xa, 0x9a, 0x1e, 0x8f, 0xb, 0xb0, 0x34, 0xa5, 0x21, 0xce, 0x4a, 0xdb, 0x5f, 0xe4, 0x60, 0xf1, 0x75, 0x32, 0xb6, 0x27, 0xa3, 0x18, 0x9c, 0xd, 0x89, 0x66, 0xe2, 0x73, 0xf7, 0x4c, 0xc8, 0x59, 0xdd, 0xd7, 0x53, 0xc2, 0x46, 0xfd, 0x79, 0xe8, 0x6c, 0x83, 0x7, 0x96, 0x12, 0xa9, 0x2d, 0xbc, 0x38, 0x7f, 0xfb, 0x6a, 0xee, 0x55, 0xd1, 0x40, 0xc4, 0x2b, 0xaf, 0x3e, 0xba, 0x1, 0x85, 0x14, 0x90, 0x29, 0xad, 0x3c, 0xb8, 0x3, 0x87, 0x16, 0x92, 0x7d, 0xf9, 0x68, 0xec, 0x57, 0xd3, 0x42, 0xc6, 0x81, 0x5, 0x94, 0x10, 0xab, 0x2f, 0xbe, 0x3a, 0xd5, 0x51, 0xc0, 0x44, 0xff, 0x7b, 0xea, 0x6e, 0x64, 0xe0, 0x71, 0xf5, 0x4e, 0xca, 0x5b, 0xdf, 0x30, 0xb4, 0x25, 0xa1, 0x1a, 0x9e, 0xf, 0x8b, 0xcc, 0x48, 0xd9, 0x5d, 0xe6, 0x62, 0xf3, 0x77, 0x98, 0x1c, 0x8d, 0x9, 0xb2, 0x36, 0xa7, 0x23, 0xb3, 0x37, 0xa6, 0x22, 0x99, 0x1d, 0x8c, 0x8, 0xe7, 0x63, 0xf2, 0x76, 0xcd, 0x49, 0xd8, 0x5c, 0x1b, 0x9f, 0xe, 0x8a, 0x31, 0xb5, 0x24, 0xa0, 0x4f, 0xcb, 0x5a, 0xde, 0x65, 0xe1, 0x70, 0xf4, 0xfe, 0x7a, 0xeb, 0x6f, 0xd4, 0x50, 0xc1, 0x45, 0xaa, 0x2e, 0xbf, 0x3b, 0x80, 0x4, 0x95, 0x11, 0x56, 0xd2, 0x43, 0xc7, 0x7c, 0xf8, 0x69, 0xed, 0x2, 0x86, 0x17, 0x93, 0x28, 0xac, 0x3d, 0xb9},
+ {0x0, 0x85, 0x17, 0x92, 0x2e, 0xab, 0x39, 0xbc, 0x5c, 0xd9, 0x4b, 0xce, 0x72, 0xf7, 0x65, 0xe0, 0xb8, 0x3d, 0xaf, 0x2a, 0x96, 0x13, 0x81, 0x4, 0xe4, 0x61, 0xf3, 0x76, 0xca, 0x4f, 0xdd, 0x58, 0x6d, 0xe8, 0x7a, 0xff, 0x43, 0xc6, 0x54, 0xd1, 0x31, 0xb4, 0x26, 0xa3, 0x1f, 0x9a, 0x8, 0x8d, 0xd5, 0x50, 0xc2, 0x47, 0xfb, 0x7e, 0xec, 0x69, 0x89, 0xc, 0x9e, 0x1b, 0xa7, 0x22, 0xb0, 0x35, 0xda, 0x5f, 0xcd, 0x48, 0xf4, 0x71, 0xe3, 0x66, 0x86, 0x3, 0x91, 0x14, 0xa8, 0x2d, 0xbf, 0x3a, 0x62, 0xe7, 0x75, 0xf0, 0x4c, 0xc9, 0x5b, 0xde, 0x3e, 0xbb, 0x29, 0xac, 0x10, 0x95, 0x7, 0x82, 0xb7, 0x32, 0xa0, 0x25, 0x99, 0x1c, 0x8e, 0xb, 0xeb, 0x6e, 0xfc, 0x79, 0xc5, 0x40, 0xd2, 0x57, 0xf, 0x8a, 0x18, 0x9d, 0x21, 0xa4, 0x36, 0xb3, 0x53, 0xd6, 0x44, 0xc1, 0x7d, 0xf8, 0x6a, 0xef, 0xa9, 0x2c, 0xbe, 0x3b, 0x87, 0x2, 0x90, 0x15, 0xf5, 0x70, 0xe2, 0x67, 0xdb, 0x5e, 0xcc, 0x49, 0x11, 0x94, 0x6, 0x83, 0x3f, 0xba, 0x28, 0xad, 0x4d, 0xc8, 0x5a, 0xdf, 0x63, 0xe6, 0x74, 0xf1, 0xc4, 0x41, 0xd3, 0x56, 0xea, 0x6f, 0xfd, 0x78, 0x98, 0x1d, 0x8f, 0xa, 0xb6, 0x33, 0xa1, 0x24, 0x7c, 0xf9, 0x6b, 0xee, 0x52, 0xd7, 0x45, 0xc0, 0x20, 0xa5, 0x37, 0xb2, 0xe, 0x8b, 0x19, 0x9c, 0x73, 0xf6, 0x64, 0xe1, 0x5d, 0xd8, 0x4a, 0xcf, 0x2f, 0xaa, 0x38, 0xbd, 0x1, 0x84, 0x16, 0x93, 0xcb, 0x4e, 0xdc, 0x59, 0xe5, 0x60, 0xf2, 0x77, 0x97, 0x12, 0x80, 0x5, 0xb9, 0x3c, 0xae, 0x2b, 0x1e, 0x9b, 0x9, 0x8c, 0x30, 0xb5, 0x27, 0xa2, 0x42, 0xc7, 0x55, 0xd0, 0x6c, 0xe9, 0x7b, 0xfe, 0xa6, 0x23, 0xb1, 0x34, 0x88, 0xd, 0x9f, 0x1a, 0xfa, 0x7f, 0xed, 0x68, 0xd4, 0x51, 0xc3, 0x46},
+ {0x0, 0x86, 0x11, 0x97, 0x22, 0xa4, 0x33, 0xb5, 0x44, 0xc2, 0x55, 0xd3, 0x66, 0xe0, 0x77, 0xf1, 0x88, 0xe, 0x99, 0x1f, 0xaa, 0x2c, 0xbb, 0x3d, 0xcc, 0x4a, 0xdd, 0x5b, 0xee, 0x68, 0xff, 0x79, 0xd, 0x8b, 0x1c, 0x9a, 0x2f, 0xa9, 0x3e, 0xb8, 0x49, 0xcf, 0x58, 0xde, 0x6b, 0xed, 0x7a, 0xfc, 0x85, 0x3, 0x94, 0x12, 0xa7, 0x21, 0xb6, 0x30, 0xc1, 0x47, 0xd0, 0x56, 0xe3, 0x65, 0xf2, 0x74, 0x1a, 0x9c, 0xb, 0x8d, 0x38, 0xbe, 0x29, 0xaf, 0x5e, 0xd8, 0x4f, 0xc9, 0x7c, 0xfa, 0x6d, 0xeb, 0x92, 0x14, 0x83, 0x5, 0xb0, 0x36, 0xa1, 0x27, 0xd6, 0x50, 0xc7, 0x41, 0xf4, 0x72, 0xe5, 0x63, 0x17, 0x91, 0x6, 0x80, 0x35, 0xb3, 0x24, 0xa2, 0x53, 0xd5, 0x42, 0xc4, 0x71, 0xf7, 0x60, 0xe6, 0x9f, 0x19, 0x8e, 0x8, 0xbd, 0x3b, 0xac, 0x2a, 0xdb, 0x5d, 0xca, 0x4c, 0xf9, 0x7f, 0xe8, 0x6e, 0x34, 0xb2, 0x25, 0xa3, 0x16, 0x90, 0x7, 0x81, 0x70, 0xf6, 0x61, 0xe7, 0x52, 0xd4, 0x43, 0xc5, 0xbc, 0x3a, 0xad, 0x2b, 0x9e, 0x18, 0x8f, 0x9, 0xf8, 0x7e, 0xe9, 0x6f, 0xda, 0x5c, 0xcb, 0x4d, 0x39, 0xbf, 0x28, 0xae, 0x1b, 0x9d, 0xa, 0x8c, 0x7d, 0xfb, 0x6c, 0xea, 0x5f, 0xd9, 0x4e, 0xc8, 0xb1, 0x37, 0xa0, 0x26, 0x93, 0x15, 0x82, 0x4, 0xf5, 0x73, 0xe4, 0x62, 0xd7, 0x51, 0xc6, 0x40, 0x2e, 0xa8, 0x3f, 0xb9, 0xc, 0x8a, 0x1d, 0x9b, 0x6a, 0xec, 0x7b, 0xfd, 0x48, 0xce, 0x59, 0xdf, 0xa6, 0x20, 0xb7, 0x31, 0x84, 0x2, 0x95, 0x13, 0xe2, 0x64, 0xf3, 0x75, 0xc0, 0x46, 0xd1, 0x57, 0x23, 0xa5, 0x32, 0xb4, 0x1, 0x87, 0x10, 0x96, 0x67, 0xe1, 0x76, 0xf0, 0x45, 0xc3, 0x54, 0xd2, 0xab, 0x2d, 0xba, 0x3c, 0x89, 0xf, 0x98, 0x1e, 0xef, 0x69, 0xfe, 0x78, 0xcd, 0x4b, 0xdc, 0x5a},
+ {0x0, 0x87, 0x13, 0x94, 0x26, 0xa1, 0x35, 0xb2, 0x4c, 0xcb, 0x5f, 0xd8, 0x6a, 0xed, 0x79, 0xfe, 0x98, 0x1f, 0x8b, 0xc, 0xbe, 0x39, 0xad, 0x2a, 0xd4, 0x53, 0xc7, 0x40, 0xf2, 0x75, 0xe1, 0x66, 0x2d, 0xaa, 0x3e, 0xb9, 0xb, 0x8c, 0x18, 0x9f, 0x61, 0xe6, 0x72, 0xf5, 0x47, 0xc0, 0x54, 0xd3, 0xb5, 0x32, 0xa6, 0x21, 0x93, 0x14, 0x80, 0x7, 0xf9, 0x7e, 0xea, 0x6d, 0xdf, 0x58, 0xcc, 0x4b, 0x5a, 0xdd, 0x49, 0xce, 0x7c, 0xfb, 0x6f, 0xe8, 0x16, 0x91, 0x5, 0x82, 0x30, 0xb7, 0x23, 0xa4, 0xc2, 0x45, 0xd1, 0x56, 0xe4, 0x63, 0xf7, 0x70, 0x8e, 0x9, 0x9d, 0x1a, 0xa8, 0x2f, 0xbb, 0x3c, 0x77, 0xf0, 0x64, 0xe3, 0x51, 0xd6, 0x42, 0xc5, 0x3b, 0xbc, 0x28, 0xaf, 0x1d, 0x9a, 0xe, 0x89, 0xef, 0x68, 0xfc, 0x7b, 0xc9, 0x4e, 0xda, 0x5d, 0xa3, 0x24, 0xb0, 0x37, 0x85, 0x2, 0x96, 0x11, 0xb4, 0x33, 0xa7, 0x20, 0x92, 0x15, 0x81, 0x6, 0xf8, 0x7f, 0xeb, 0x6c, 0xde, 0x59, 0xcd, 0x4a, 0x2c, 0xab, 0x3f, 0xb8, 0xa, 0x8d, 0x19, 0x9e, 0x60, 0xe7, 0x73, 0xf4, 0x46, 0xc1, 0x55, 0xd2, 0x99, 0x1e, 0x8a, 0xd, 0xbf, 0x38, 0xac, 0x2b, 0xd5, 0x52, 0xc6, 0x41, 0xf3, 0x74, 0xe0, 0x67, 0x1, 0x86, 0x12, 0x95, 0x27, 0xa0, 0x34, 0xb3, 0x4d, 0xca, 0x5e, 0xd9, 0x6b, 0xec, 0x78, 0xff, 0xee, 0x69, 0xfd, 0x7a, 0xc8, 0x4f, 0xdb, 0x5c, 0xa2, 0x25, 0xb1, 0x36, 0x84, 0x3, 0x97, 0x10, 0x76, 0xf1, 0x65, 0xe2, 0x50, 0xd7, 0x43, 0xc4, 0x3a, 0xbd, 0x29, 0xae, 0x1c, 0x9b, 0xf, 0x88, 0xc3, 0x44, 0xd0, 0x57, 0xe5, 0x62, 0xf6, 0x71, 0x8f, 0x8, 0x9c, 0x1b, 0xa9, 0x2e, 0xba, 0x3d, 0x5b, 0xdc, 0x48, 0xcf, 0x7d, 0xfa, 0x6e, 0xe9, 0x17, 0x90, 0x4, 0x83, 0x31, 0xb6, 0x22, 0xa5},
+ {0x0, 0x88, 0xd, 0x85, 0x1a, 0x92, 0x17, 0x9f, 0x34, 0xbc, 0x39, 0xb1, 0x2e, 0xa6, 0x23, 0xab, 0x68, 0xe0, 0x65, 0xed, 0x72, 0xfa, 0x7f, 0xf7, 0x5c, 0xd4, 0x51, 0xd9, 0x46, 0xce, 0x4b, 0xc3, 0xd0, 0x58, 0xdd, 0x55, 0xca, 0x42, 0xc7, 0x4f, 0xe4, 0x6c, 0xe9, 0x61, 0xfe, 0x76, 0xf3, 0x7b, 0xb8, 0x30, 0xb5, 0x3d, 0xa2, 0x2a, 0xaf, 0x27, 0x8c, 0x4, 0x81, 0x9, 0x96, 0x1e, 0x9b, 0x13, 0xbd, 0x35, 0xb0, 0x38, 0xa7, 0x2f, 0xaa, 0x22, 0x89, 0x1, 0x84, 0xc, 0x93, 0x1b, 0x9e, 0x16, 0xd5, 0x5d, 0xd8, 0x50, 0xcf, 0x47, 0xc2, 0x4a, 0xe1, 0x69, 0xec, 0x64, 0xfb, 0x73, 0xf6, 0x7e, 0x6d, 0xe5, 0x60, 0xe8, 0x77, 0xff, 0x7a, 0xf2, 0x59, 0xd1, 0x54, 0xdc, 0x43, 0xcb, 0x4e, 0xc6, 0x5, 0x8d, 0x8, 0x80, 0x1f, 0x97, 0x12, 0x9a, 0x31, 0xb9, 0x3c, 0xb4, 0x2b, 0xa3, 0x26, 0xae, 0x67, 0xef, 0x6a, 0xe2, 0x7d, 0xf5, 0x70, 0xf8, 0x53, 0xdb, 0x5e, 0xd6, 0x49, 0xc1, 0x44, 0xcc, 0xf, 0x87, 0x2, 0x8a, 0x15, 0x9d, 0x18, 0x90, 0x3b, 0xb3, 0x36, 0xbe, 0x21, 0xa9, 0x2c, 0xa4, 0xb7, 0x3f, 0xba, 0x32, 0xad, 0x25, 0xa0, 0x28, 0x83, 0xb, 0x8e, 0x6, 0x99, 0x11, 0x94, 0x1c, 0xdf, 0x57, 0xd2, 0x5a, 0xc5, 0x4d, 0xc8, 0x40, 0xeb, 0x63, 0xe6, 0x6e, 0xf1, 0x79, 0xfc, 0x74, 0xda, 0x52, 0xd7, 0x5f, 0xc0, 0x48, 0xcd, 0x45, 0xee, 0x66, 0xe3, 0x6b, 0xf4, 0x7c, 0xf9, 0x71, 0xb2, 0x3a, 0xbf, 0x37, 0xa8, 0x20, 0xa5, 0x2d, 0x86, 0xe, 0x8b, 0x3, 0x9c, 0x14, 0x91, 0x19, 0xa, 0x82, 0x7, 0x8f, 0x10, 0x98, 0x1d, 0x95, 0x3e, 0xb6, 0x33, 0xbb, 0x24, 0xac, 0x29, 0xa1, 0x62, 0xea, 0x6f, 0xe7, 0x78, 0xf0, 0x75, 0xfd, 0x56, 0xde, 0x5b, 0xd3, 0x4c, 0xc4, 0x41, 0xc9},
+ {0x0, 0x89, 0xf, 0x86, 0x1e, 0x97, 0x11, 0x98, 0x3c, 0xb5, 0x33, 0xba, 0x22, 0xab, 0x2d, 0xa4, 0x78, 0xf1, 0x77, 0xfe, 0x66, 0xef, 0x69, 0xe0, 0x44, 0xcd, 0x4b, 0xc2, 0x5a, 0xd3, 0x55, 0xdc, 0xf0, 0x79, 0xff, 0x76, 0xee, 0x67, 0xe1, 0x68, 0xcc, 0x45, 0xc3, 0x4a, 0xd2, 0x5b, 0xdd, 0x54, 0x88, 0x1, 0x87, 0xe, 0x96, 0x1f, 0x99, 0x10, 0xb4, 0x3d, 0xbb, 0x32, 0xaa, 0x23, 0xa5, 0x2c, 0xfd, 0x74, 0xf2, 0x7b, 0xe3, 0x6a, 0xec, 0x65, 0xc1, 0x48, 0xce, 0x47, 0xdf, 0x56, 0xd0, 0x59, 0x85, 0xc, 0x8a, 0x3, 0x9b, 0x12, 0x94, 0x1d, 0xb9, 0x30, 0xb6, 0x3f, 0xa7, 0x2e, 0xa8, 0x21, 0xd, 0x84, 0x2, 0x8b, 0x13, 0x9a, 0x1c, 0x95, 0x31, 0xb8, 0x3e, 0xb7, 0x2f, 0xa6, 0x20, 0xa9, 0x75, 0xfc, 0x7a, 0xf3, 0x6b, 0xe2, 0x64, 0xed, 0x49, 0xc0, 0x46, 0xcf, 0x57, 0xde, 0x58, 0xd1, 0xe7, 0x6e, 0xe8, 0x61, 0xf9, 0x70, 0xf6, 0x7f, 0xdb, 0x52, 0xd4, 0x5d, 0xc5, 0x4c, 0xca, 0x43, 0x9f, 0x16, 0x90, 0x19, 0x81, 0x8, 0x8e, 0x7, 0xa3, 0x2a, 0xac, 0x25, 0xbd, 0x34, 0xb2, 0x3b, 0x17, 0x9e, 0x18, 0x91, 0x9, 0x80, 0x6, 0x8f, 0x2b, 0xa2, 0x24, 0xad, 0x35, 0xbc, 0x3a, 0xb3, 0x6f, 0xe6, 0x60, 0xe9, 0x71, 0xf8, 0x7e, 0xf7, 0x53, 0xda, 0x5c, 0xd5, 0x4d, 0xc4, 0x42, 0xcb, 0x1a, 0x93, 0x15, 0x9c, 0x4, 0x8d, 0xb, 0x82, 0x26, 0xaf, 0x29, 0xa0, 0x38, 0xb1, 0x37, 0xbe, 0x62, 0xeb, 0x6d, 0xe4, 0x7c, 0xf5, 0x73, 0xfa, 0x5e, 0xd7, 0x51, 0xd8, 0x40, 0xc9, 0x4f, 0xc6, 0xea, 0x63, 0xe5, 0x6c, 0xf4, 0x7d, 0xfb, 0x72, 0xd6, 0x5f, 0xd9, 0x50, 0xc8, 0x41, 0xc7, 0x4e, 0x92, 0x1b, 0x9d, 0x14, 0x8c, 0x5, 0x83, 0xa, 0xae, 0x27, 0xa1, 0x28, 0xb0, 0x39, 0xbf, 0x36},
+ {0x0, 0x8a, 0x9, 0x83, 0x12, 0x98, 0x1b, 0x91, 0x24, 0xae, 0x2d, 0xa7, 0x36, 0xbc, 0x3f, 0xb5, 0x48, 0xc2, 0x41, 0xcb, 0x5a, 0xd0, 0x53, 0xd9, 0x6c, 0xe6, 0x65, 0xef, 0x7e, 0xf4, 0x77, 0xfd, 0x90, 0x1a, 0x99, 0x13, 0x82, 0x8, 0x8b, 0x1, 0xb4, 0x3e, 0xbd, 0x37, 0xa6, 0x2c, 0xaf, 0x25, 0xd8, 0x52, 0xd1, 0x5b, 0xca, 0x40, 0xc3, 0x49, 0xfc, 0x76, 0xf5, 0x7f, 0xee, 0x64, 0xe7, 0x6d, 0x3d, 0xb7, 0x34, 0xbe, 0x2f, 0xa5, 0x26, 0xac, 0x19, 0x93, 0x10, 0x9a, 0xb, 0x81, 0x2, 0x88, 0x75, 0xff, 0x7c, 0xf6, 0x67, 0xed, 0x6e, 0xe4, 0x51, 0xdb, 0x58, 0xd2, 0x43, 0xc9, 0x4a, 0xc0, 0xad, 0x27, 0xa4, 0x2e, 0xbf, 0x35, 0xb6, 0x3c, 0x89, 0x3, 0x80, 0xa, 0x9b, 0x11, 0x92, 0x18, 0xe5, 0x6f, 0xec, 0x66, 0xf7, 0x7d, 0xfe, 0x74, 0xc1, 0x4b, 0xc8, 0x42, 0xd3, 0x59, 0xda, 0x50, 0x7a, 0xf0, 0x73, 0xf9, 0x68, 0xe2, 0x61, 0xeb, 0x5e, 0xd4, 0x57, 0xdd, 0x4c, 0xc6, 0x45, 0xcf, 0x32, 0xb8, 0x3b, 0xb1, 0x20, 0xaa, 0x29, 0xa3, 0x16, 0x9c, 0x1f, 0x95, 0x4, 0x8e, 0xd, 0x87, 0xea, 0x60, 0xe3, 0x69, 0xf8, 0x72, 0xf1, 0x7b, 0xce, 0x44, 0xc7, 0x4d, 0xdc, 0x56, 0xd5, 0x5f, 0xa2, 0x28, 0xab, 0x21, 0xb0, 0x3a, 0xb9, 0x33, 0x86, 0xc, 0x8f, 0x5, 0x94, 0x1e, 0x9d, 0x17, 0x47, 0xcd, 0x4e, 0xc4, 0x55, 0xdf, 0x5c, 0xd6, 0x63, 0xe9, 0x6a, 0xe0, 0x71, 0xfb, 0x78, 0xf2, 0xf, 0x85, 0x6, 0x8c, 0x1d, 0x97, 0x14, 0x9e, 0x2b, 0xa1, 0x22, 0xa8, 0x39, 0xb3, 0x30, 0xba, 0xd7, 0x5d, 0xde, 0x54, 0xc5, 0x4f, 0xcc, 0x46, 0xf3, 0x79, 0xfa, 0x70, 0xe1, 0x6b, 0xe8, 0x62, 0x9f, 0x15, 0x96, 0x1c, 0x8d, 0x7, 0x84, 0xe, 0xbb, 0x31, 0xb2, 0x38, 0xa9, 0x23, 0xa0, 0x2a},
+ {0x0, 0x8b, 0xb, 0x80, 0x16, 0x9d, 0x1d, 0x96, 0x2c, 0xa7, 0x27, 0xac, 0x3a, 0xb1, 0x31, 0xba, 0x58, 0xd3, 0x53, 0xd8, 0x4e, 0xc5, 0x45, 0xce, 0x74, 0xff, 0x7f, 0xf4, 0x62, 0xe9, 0x69, 0xe2, 0xb0, 0x3b, 0xbb, 0x30, 0xa6, 0x2d, 0xad, 0x26, 0x9c, 0x17, 0x97, 0x1c, 0x8a, 0x1, 0x81, 0xa, 0xe8, 0x63, 0xe3, 0x68, 0xfe, 0x75, 0xf5, 0x7e, 0xc4, 0x4f, 0xcf, 0x44, 0xd2, 0x59, 0xd9, 0x52, 0x7d, 0xf6, 0x76, 0xfd, 0x6b, 0xe0, 0x60, 0xeb, 0x51, 0xda, 0x5a, 0xd1, 0x47, 0xcc, 0x4c, 0xc7, 0x25, 0xae, 0x2e, 0xa5, 0x33, 0xb8, 0x38, 0xb3, 0x9, 0x82, 0x2, 0x89, 0x1f, 0x94, 0x14, 0x9f, 0xcd, 0x46, 0xc6, 0x4d, 0xdb, 0x50, 0xd0, 0x5b, 0xe1, 0x6a, 0xea, 0x61, 0xf7, 0x7c, 0xfc, 0x77, 0x95, 0x1e, 0x9e, 0x15, 0x83, 0x8, 0x88, 0x3, 0xb9, 0x32, 0xb2, 0x39, 0xaf, 0x24, 0xa4, 0x2f, 0xfa, 0x71, 0xf1, 0x7a, 0xec, 0x67, 0xe7, 0x6c, 0xd6, 0x5d, 0xdd, 0x56, 0xc0, 0x4b, 0xcb, 0x40, 0xa2, 0x29, 0xa9, 0x22, 0xb4, 0x3f, 0xbf, 0x34, 0x8e, 0x5, 0x85, 0xe, 0x98, 0x13, 0x93, 0x18, 0x4a, 0xc1, 0x41, 0xca, 0x5c, 0xd7, 0x57, 0xdc, 0x66, 0xed, 0x6d, 0xe6, 0x70, 0xfb, 0x7b, 0xf0, 0x12, 0x99, 0x19, 0x92, 0x4, 0x8f, 0xf, 0x84, 0x3e, 0xb5, 0x35, 0xbe, 0x28, 0xa3, 0x23, 0xa8, 0x87, 0xc, 0x8c, 0x7, 0x91, 0x1a, 0x9a, 0x11, 0xab, 0x20, 0xa0, 0x2b, 0xbd, 0x36, 0xb6, 0x3d, 0xdf, 0x54, 0xd4, 0x5f, 0xc9, 0x42, 0xc2, 0x49, 0xf3, 0x78, 0xf8, 0x73, 0xe5, 0x6e, 0xee, 0x65, 0x37, 0xbc, 0x3c, 0xb7, 0x21, 0xaa, 0x2a, 0xa1, 0x1b, 0x90, 0x10, 0x9b, 0xd, 0x86, 0x6, 0x8d, 0x6f, 0xe4, 0x64, 0xef, 0x79, 0xf2, 0x72, 0xf9, 0x43, 0xc8, 0x48, 0xc3, 0x55, 0xde, 0x5e, 0xd5},
+ {0x0, 0x8c, 0x5, 0x89, 0xa, 0x86, 0xf, 0x83, 0x14, 0x98, 0x11, 0x9d, 0x1e, 0x92, 0x1b, 0x97, 0x28, 0xa4, 0x2d, 0xa1, 0x22, 0xae, 0x27, 0xab, 0x3c, 0xb0, 0x39, 0xb5, 0x36, 0xba, 0x33, 0xbf, 0x50, 0xdc, 0x55, 0xd9, 0x5a, 0xd6, 0x5f, 0xd3, 0x44, 0xc8, 0x41, 0xcd, 0x4e, 0xc2, 0x4b, 0xc7, 0x78, 0xf4, 0x7d, 0xf1, 0x72, 0xfe, 0x77, 0xfb, 0x6c, 0xe0, 0x69, 0xe5, 0x66, 0xea, 0x63, 0xef, 0xa0, 0x2c, 0xa5, 0x29, 0xaa, 0x26, 0xaf, 0x23, 0xb4, 0x38, 0xb1, 0x3d, 0xbe, 0x32, 0xbb, 0x37, 0x88, 0x4, 0x8d, 0x1, 0x82, 0xe, 0x87, 0xb, 0x9c, 0x10, 0x99, 0x15, 0x96, 0x1a, 0x93, 0x1f, 0xf0, 0x7c, 0xf5, 0x79, 0xfa, 0x76, 0xff, 0x73, 0xe4, 0x68, 0xe1, 0x6d, 0xee, 0x62, 0xeb, 0x67, 0xd8, 0x54, 0xdd, 0x51, 0xd2, 0x5e, 0xd7, 0x5b, 0xcc, 0x40, 0xc9, 0x45, 0xc6, 0x4a, 0xc3, 0x4f, 0x5d, 0xd1, 0x58, 0xd4, 0x57, 0xdb, 0x52, 0xde, 0x49, 0xc5, 0x4c, 0xc0, 0x43, 0xcf, 0x46, 0xca, 0x75, 0xf9, 0x70, 0xfc, 0x7f, 0xf3, 0x7a, 0xf6, 0x61, 0xed, 0x64, 0xe8, 0x6b, 0xe7, 0x6e, 0xe2, 0xd, 0x81, 0x8, 0x84, 0x7, 0x8b, 0x2, 0x8e, 0x19, 0x95, 0x1c, 0x90, 0x13, 0x9f, 0x16, 0x9a, 0x25, 0xa9, 0x20, 0xac, 0x2f, 0xa3, 0x2a, 0xa6, 0x31, 0xbd, 0x34, 0xb8, 0x3b, 0xb7, 0x3e, 0xb2, 0xfd, 0x71, 0xf8, 0x74, 0xf7, 0x7b, 0xf2, 0x7e, 0xe9, 0x65, 0xec, 0x60, 0xe3, 0x6f, 0xe6, 0x6a, 0xd5, 0x59, 0xd0, 0x5c, 0xdf, 0x53, 0xda, 0x56, 0xc1, 0x4d, 0xc4, 0x48, 0xcb, 0x47, 0xce, 0x42, 0xad, 0x21, 0xa8, 0x24, 0xa7, 0x2b, 0xa2, 0x2e, 0xb9, 0x35, 0xbc, 0x30, 0xb3, 0x3f, 0xb6, 0x3a, 0x85, 0x9, 0x80, 0xc, 0x8f, 0x3, 0x8a, 0x6, 0x91, 0x1d, 0x94, 0x18, 0x9b, 0x17, 0x9e, 0x12},
+ {0x0, 0x8d, 0x7, 0x8a, 0xe, 0x83, 0x9, 0x84, 0x1c, 0x91, 0x1b, 0x96, 0x12, 0x9f, 0x15, 0x98, 0x38, 0xb5, 0x3f, 0xb2, 0x36, 0xbb, 0x31, 0xbc, 0x24, 0xa9, 0x23, 0xae, 0x2a, 0xa7, 0x2d, 0xa0, 0x70, 0xfd, 0x77, 0xfa, 0x7e, 0xf3, 0x79, 0xf4, 0x6c, 0xe1, 0x6b, 0xe6, 0x62, 0xef, 0x65, 0xe8, 0x48, 0xc5, 0x4f, 0xc2, 0x46, 0xcb, 0x41, 0xcc, 0x54, 0xd9, 0x53, 0xde, 0x5a, 0xd7, 0x5d, 0xd0, 0xe0, 0x6d, 0xe7, 0x6a, 0xee, 0x63, 0xe9, 0x64, 0xfc, 0x71, 0xfb, 0x76, 0xf2, 0x7f, 0xf5, 0x78, 0xd8, 0x55, 0xdf, 0x52, 0xd6, 0x5b, 0xd1, 0x5c, 0xc4, 0x49, 0xc3, 0x4e, 0xca, 0x47, 0xcd, 0x40, 0x90, 0x1d, 0x97, 0x1a, 0x9e, 0x13, 0x99, 0x14, 0x8c, 0x1, 0x8b, 0x6, 0x82, 0xf, 0x85, 0x8, 0xa8, 0x25, 0xaf, 0x22, 0xa6, 0x2b, 0xa1, 0x2c, 0xb4, 0x39, 0xb3, 0x3e, 0xba, 0x37, 0xbd, 0x30, 0xdd, 0x50, 0xda, 0x57, 0xd3, 0x5e, 0xd4, 0x59, 0xc1, 0x4c, 0xc6, 0x4b, 0xcf, 0x42, 0xc8, 0x45, 0xe5, 0x68, 0xe2, 0x6f, 0xeb, 0x66, 0xec, 0x61, 0xf9, 0x74, 0xfe, 0x73, 0xf7, 0x7a, 0xf0, 0x7d, 0xad, 0x20, 0xaa, 0x27, 0xa3, 0x2e, 0xa4, 0x29, 0xb1, 0x3c, 0xb6, 0x3b, 0xbf, 0x32, 0xb8, 0x35, 0x95, 0x18, 0x92, 0x1f, 0x9b, 0x16, 0x9c, 0x11, 0x89, 0x4, 0x8e, 0x3, 0x87, 0xa, 0x80, 0xd, 0x3d, 0xb0, 0x3a, 0xb7, 0x33, 0xbe, 0x34, 0xb9, 0x21, 0xac, 0x26, 0xab, 0x2f, 0xa2, 0x28, 0xa5, 0x5, 0x88, 0x2, 0x8f, 0xb, 0x86, 0xc, 0x81, 0x19, 0x94, 0x1e, 0x93, 0x17, 0x9a, 0x10, 0x9d, 0x4d, 0xc0, 0x4a, 0xc7, 0x43, 0xce, 0x44, 0xc9, 0x51, 0xdc, 0x56, 0xdb, 0x5f, 0xd2, 0x58, 0xd5, 0x75, 0xf8, 0x72, 0xff, 0x7b, 0xf6, 0x7c, 0xf1, 0x69, 0xe4, 0x6e, 0xe3, 0x67, 0xea, 0x60, 0xed},
+ {0x0, 0x8e, 0x1, 0x8f, 0x2, 0x8c, 0x3, 0x8d, 0x4, 0x8a, 0x5, 0x8b, 0x6, 0x88, 0x7, 0x89, 0x8, 0x86, 0x9, 0x87, 0xa, 0x84, 0xb, 0x85, 0xc, 0x82, 0xd, 0x83, 0xe, 0x80, 0xf, 0x81, 0x10, 0x9e, 0x11, 0x9f, 0x12, 0x9c, 0x13, 0x9d, 0x14, 0x9a, 0x15, 0x9b, 0x16, 0x98, 0x17, 0x99, 0x18, 0x96, 0x19, 0x97, 0x1a, 0x94, 0x1b, 0x95, 0x1c, 0x92, 0x1d, 0x93, 0x1e, 0x90, 0x1f, 0x91, 0x20, 0xae, 0x21, 0xaf, 0x22, 0xac, 0x23, 0xad, 0x24, 0xaa, 0x25, 0xab, 0x26, 0xa8, 0x27, 0xa9, 0x28, 0xa6, 0x29, 0xa7, 0x2a, 0xa4, 0x2b, 0xa5, 0x2c, 0xa2, 0x2d, 0xa3, 0x2e, 0xa0, 0x2f, 0xa1, 0x30, 0xbe, 0x31, 0xbf, 0x32, 0xbc, 0x33, 0xbd, 0x34, 0xba, 0x35, 0xbb, 0x36, 0xb8, 0x37, 0xb9, 0x38, 0xb6, 0x39, 0xb7, 0x3a, 0xb4, 0x3b, 0xb5, 0x3c, 0xb2, 0x3d, 0xb3, 0x3e, 0xb0, 0x3f, 0xb1, 0x40, 0xce, 0x41, 0xcf, 0x42, 0xcc, 0x43, 0xcd, 0x44, 0xca, 0x45, 0xcb, 0x46, 0xc8, 0x47, 0xc9, 0x48, 0xc6, 0x49, 0xc7, 0x4a, 0xc4, 0x4b, 0xc5, 0x4c, 0xc2, 0x4d, 0xc3, 0x4e, 0xc0, 0x4f, 0xc1, 0x50, 0xde, 0x51, 0xdf, 0x52, 0xdc, 0x53, 0xdd, 0x54, 0xda, 0x55, 0xdb, 0x56, 0xd8, 0x57, 0xd9, 0x58, 0xd6, 0x59, 0xd7, 0x5a, 0xd4, 0x5b, 0xd5, 0x5c, 0xd2, 0x5d, 0xd3, 0x5e, 0xd0, 0x5f, 0xd1, 0x60, 0xee, 0x61, 0xef, 0x62, 0xec, 0x63, 0xed, 0x64, 0xea, 0x65, 0xeb, 0x66, 0xe8, 0x67, 0xe9, 0x68, 0xe6, 0x69, 0xe7, 0x6a, 0xe4, 0x6b, 0xe5, 0x6c, 0xe2, 0x6d, 0xe3, 0x6e, 0xe0, 0x6f, 0xe1, 0x70, 0xfe, 0x71, 0xff, 0x72, 0xfc, 0x73, 0xfd, 0x74, 0xfa, 0x75, 0xfb, 0x76, 0xf8, 0x77, 0xf9, 0x78, 0xf6, 0x79, 0xf7, 0x7a, 0xf4, 0x7b, 0xf5, 0x7c, 0xf2, 0x7d, 0xf3, 0x7e, 0xf0, 0x7f, 0xf1},
+ {0x0, 0x8f, 0x3, 0x8c, 0x6, 0x89, 0x5, 0x8a, 0xc, 0x83, 0xf, 0x80, 0xa, 0x85, 0x9, 0x86, 0x18, 0x97, 0x1b, 0x94, 0x1e, 0x91, 0x1d, 0x92, 0x14, 0x9b, 0x17, 0x98, 0x12, 0x9d, 0x11, 0x9e, 0x30, 0xbf, 0x33, 0xbc, 0x36, 0xb9, 0x35, 0xba, 0x3c, 0xb3, 0x3f, 0xb0, 0x3a, 0xb5, 0x39, 0xb6, 0x28, 0xa7, 0x2b, 0xa4, 0x2e, 0xa1, 0x2d, 0xa2, 0x24, 0xab, 0x27, 0xa8, 0x22, 0xad, 0x21, 0xae, 0x60, 0xef, 0x63, 0xec, 0x66, 0xe9, 0x65, 0xea, 0x6c, 0xe3, 0x6f, 0xe0, 0x6a, 0xe5, 0x69, 0xe6, 0x78, 0xf7, 0x7b, 0xf4, 0x7e, 0xf1, 0x7d, 0xf2, 0x74, 0xfb, 0x77, 0xf8, 0x72, 0xfd, 0x71, 0xfe, 0x50, 0xdf, 0x53, 0xdc, 0x56, 0xd9, 0x55, 0xda, 0x5c, 0xd3, 0x5f, 0xd0, 0x5a, 0xd5, 0x59, 0xd6, 0x48, 0xc7, 0x4b, 0xc4, 0x4e, 0xc1, 0x4d, 0xc2, 0x44, 0xcb, 0x47, 0xc8, 0x42, 0xcd, 0x41, 0xce, 0xc0, 0x4f, 0xc3, 0x4c, 0xc6, 0x49, 0xc5, 0x4a, 0xcc, 0x43, 0xcf, 0x40, 0xca, 0x45, 0xc9, 0x46, 0xd8, 0x57, 0xdb, 0x54, 0xde, 0x51, 0xdd, 0x52, 0xd4, 0x5b, 0xd7, 0x58, 0xd2, 0x5d, 0xd1, 0x5e, 0xf0, 0x7f, 0xf3, 0x7c, 0xf6, 0x79, 0xf5, 0x7a, 0xfc, 0x73, 0xff, 0x70, 0xfa, 0x75, 0xf9, 0x76, 0xe8, 0x67, 0xeb, 0x64, 0xee, 0x61, 0xed, 0x62, 0xe4, 0x6b, 0xe7, 0x68, 0xe2, 0x6d, 0xe1, 0x6e, 0xa0, 0x2f, 0xa3, 0x2c, 0xa6, 0x29, 0xa5, 0x2a, 0xac, 0x23, 0xaf, 0x20, 0xaa, 0x25, 0xa9, 0x26, 0xb8, 0x37, 0xbb, 0x34, 0xbe, 0x31, 0xbd, 0x32, 0xb4, 0x3b, 0xb7, 0x38, 0xb2, 0x3d, 0xb1, 0x3e, 0x90, 0x1f, 0x93, 0x1c, 0x96, 0x19, 0x95, 0x1a, 0x9c, 0x13, 0x9f, 0x10, 0x9a, 0x15, 0x99, 0x16, 0x88, 0x7, 0x8b, 0x4, 0x8e, 0x1, 0x8d, 0x2, 0x84, 0xb, 0x87, 0x8, 0x82, 0xd, 0x81, 0xe},
+ {0x0, 0x90, 0x3d, 0xad, 0x7a, 0xea, 0x47, 0xd7, 0xf4, 0x64, 0xc9, 0x59, 0x8e, 0x1e, 0xb3, 0x23, 0xf5, 0x65, 0xc8, 0x58, 0x8f, 0x1f, 0xb2, 0x22, 0x1, 0x91, 0x3c, 0xac, 0x7b, 0xeb, 0x46, 0xd6, 0xf7, 0x67, 0xca, 0x5a, 0x8d, 0x1d, 0xb0, 0x20, 0x3, 0x93, 0x3e, 0xae, 0x79, 0xe9, 0x44, 0xd4, 0x2, 0x92, 0x3f, 0xaf, 0x78, 0xe8, 0x45, 0xd5, 0xf6, 0x66, 0xcb, 0x5b, 0x8c, 0x1c, 0xb1, 0x21, 0xf3, 0x63, 0xce, 0x5e, 0x89, 0x19, 0xb4, 0x24, 0x7, 0x97, 0x3a, 0xaa, 0x7d, 0xed, 0x40, 0xd0, 0x6, 0x96, 0x3b, 0xab, 0x7c, 0xec, 0x41, 0xd1, 0xf2, 0x62, 0xcf, 0x5f, 0x88, 0x18, 0xb5, 0x25, 0x4, 0x94, 0x39, 0xa9, 0x7e, 0xee, 0x43, 0xd3, 0xf0, 0x60, 0xcd, 0x5d, 0x8a, 0x1a, 0xb7, 0x27, 0xf1, 0x61, 0xcc, 0x5c, 0x8b, 0x1b, 0xb6, 0x26, 0x5, 0x95, 0x38, 0xa8, 0x7f, 0xef, 0x42, 0xd2, 0xfb, 0x6b, 0xc6, 0x56, 0x81, 0x11, 0xbc, 0x2c, 0xf, 0x9f, 0x32, 0xa2, 0x75, 0xe5, 0x48, 0xd8, 0xe, 0x9e, 0x33, 0xa3, 0x74, 0xe4, 0x49, 0xd9, 0xfa, 0x6a, 0xc7, 0x57, 0x80, 0x10, 0xbd, 0x2d, 0xc, 0x9c, 0x31, 0xa1, 0x76, 0xe6, 0x4b, 0xdb, 0xf8, 0x68, 0xc5, 0x55, 0x82, 0x12, 0xbf, 0x2f, 0xf9, 0x69, 0xc4, 0x54, 0x83, 0x13, 0xbe, 0x2e, 0xd, 0x9d, 0x30, 0xa0, 0x77, 0xe7, 0x4a, 0xda, 0x8, 0x98, 0x35, 0xa5, 0x72, 0xe2, 0x4f, 0xdf, 0xfc, 0x6c, 0xc1, 0x51, 0x86, 0x16, 0xbb, 0x2b, 0xfd, 0x6d, 0xc0, 0x50, 0x87, 0x17, 0xba, 0x2a, 0x9, 0x99, 0x34, 0xa4, 0x73, 0xe3, 0x4e, 0xde, 0xff, 0x6f, 0xc2, 0x52, 0x85, 0x15, 0xb8, 0x28, 0xb, 0x9b, 0x36, 0xa6, 0x71, 0xe1, 0x4c, 0xdc, 0xa, 0x9a, 0x37, 0xa7, 0x70, 0xe0, 0x4d, 0xdd, 0xfe, 0x6e, 0xc3, 0x53, 0x84, 0x14, 0xb9, 0x29},
+ {0x0, 0x91, 0x3f, 0xae, 0x7e, 0xef, 0x41, 0xd0, 0xfc, 0x6d, 0xc3, 0x52, 0x82, 0x13, 0xbd, 0x2c, 0xe5, 0x74, 0xda, 0x4b, 0x9b, 0xa, 0xa4, 0x35, 0x19, 0x88, 0x26, 0xb7, 0x67, 0xf6, 0x58, 0xc9, 0xd7, 0x46, 0xe8, 0x79, 0xa9, 0x38, 0x96, 0x7, 0x2b, 0xba, 0x14, 0x85, 0x55, 0xc4, 0x6a, 0xfb, 0x32, 0xa3, 0xd, 0x9c, 0x4c, 0xdd, 0x73, 0xe2, 0xce, 0x5f, 0xf1, 0x60, 0xb0, 0x21, 0x8f, 0x1e, 0xb3, 0x22, 0x8c, 0x1d, 0xcd, 0x5c, 0xf2, 0x63, 0x4f, 0xde, 0x70, 0xe1, 0x31, 0xa0, 0xe, 0x9f, 0x56, 0xc7, 0x69, 0xf8, 0x28, 0xb9, 0x17, 0x86, 0xaa, 0x3b, 0x95, 0x4, 0xd4, 0x45, 0xeb, 0x7a, 0x64, 0xf5, 0x5b, 0xca, 0x1a, 0x8b, 0x25, 0xb4, 0x98, 0x9, 0xa7, 0x36, 0xe6, 0x77, 0xd9, 0x48, 0x81, 0x10, 0xbe, 0x2f, 0xff, 0x6e, 0xc0, 0x51, 0x7d, 0xec, 0x42, 0xd3, 0x3, 0x92, 0x3c, 0xad, 0x7b, 0xea, 0x44, 0xd5, 0x5, 0x94, 0x3a, 0xab, 0x87, 0x16, 0xb8, 0x29, 0xf9, 0x68, 0xc6, 0x57, 0x9e, 0xf, 0xa1, 0x30, 0xe0, 0x71, 0xdf, 0x4e, 0x62, 0xf3, 0x5d, 0xcc, 0x1c, 0x8d, 0x23, 0xb2, 0xac, 0x3d, 0x93, 0x2, 0xd2, 0x43, 0xed, 0x7c, 0x50, 0xc1, 0x6f, 0xfe, 0x2e, 0xbf, 0x11, 0x80, 0x49, 0xd8, 0x76, 0xe7, 0x37, 0xa6, 0x8, 0x99, 0xb5, 0x24, 0x8a, 0x1b, 0xcb, 0x5a, 0xf4, 0x65, 0xc8, 0x59, 0xf7, 0x66, 0xb6, 0x27, 0x89, 0x18, 0x34, 0xa5, 0xb, 0x9a, 0x4a, 0xdb, 0x75, 0xe4, 0x2d, 0xbc, 0x12, 0x83, 0x53, 0xc2, 0x6c, 0xfd, 0xd1, 0x40, 0xee, 0x7f, 0xaf, 0x3e, 0x90, 0x1, 0x1f, 0x8e, 0x20, 0xb1, 0x61, 0xf0, 0x5e, 0xcf, 0xe3, 0x72, 0xdc, 0x4d, 0x9d, 0xc, 0xa2, 0x33, 0xfa, 0x6b, 0xc5, 0x54, 0x84, 0x15, 0xbb, 0x2a, 0x6, 0x97, 0x39, 0xa8, 0x78, 0xe9, 0x47, 0xd6},
+ {0x0, 0x92, 0x39, 0xab, 0x72, 0xe0, 0x4b, 0xd9, 0xe4, 0x76, 0xdd, 0x4f, 0x96, 0x4, 0xaf, 0x3d, 0xd5, 0x47, 0xec, 0x7e, 0xa7, 0x35, 0x9e, 0xc, 0x31, 0xa3, 0x8, 0x9a, 0x43, 0xd1, 0x7a, 0xe8, 0xb7, 0x25, 0x8e, 0x1c, 0xc5, 0x57, 0xfc, 0x6e, 0x53, 0xc1, 0x6a, 0xf8, 0x21, 0xb3, 0x18, 0x8a, 0x62, 0xf0, 0x5b, 0xc9, 0x10, 0x82, 0x29, 0xbb, 0x86, 0x14, 0xbf, 0x2d, 0xf4, 0x66, 0xcd, 0x5f, 0x73, 0xe1, 0x4a, 0xd8, 0x1, 0x93, 0x38, 0xaa, 0x97, 0x5, 0xae, 0x3c, 0xe5, 0x77, 0xdc, 0x4e, 0xa6, 0x34, 0x9f, 0xd, 0xd4, 0x46, 0xed, 0x7f, 0x42, 0xd0, 0x7b, 0xe9, 0x30, 0xa2, 0x9, 0x9b, 0xc4, 0x56, 0xfd, 0x6f, 0xb6, 0x24, 0x8f, 0x1d, 0x20, 0xb2, 0x19, 0x8b, 0x52, 0xc0, 0x6b, 0xf9, 0x11, 0x83, 0x28, 0xba, 0x63, 0xf1, 0x5a, 0xc8, 0xf5, 0x67, 0xcc, 0x5e, 0x87, 0x15, 0xbe, 0x2c, 0xe6, 0x74, 0xdf, 0x4d, 0x94, 0x6, 0xad, 0x3f, 0x2, 0x90, 0x3b, 0xa9, 0x70, 0xe2, 0x49, 0xdb, 0x33, 0xa1, 0xa, 0x98, 0x41, 0xd3, 0x78, 0xea, 0xd7, 0x45, 0xee, 0x7c, 0xa5, 0x37, 0x9c, 0xe, 0x51, 0xc3, 0x68, 0xfa, 0x23, 0xb1, 0x1a, 0x88, 0xb5, 0x27, 0x8c, 0x1e, 0xc7, 0x55, 0xfe, 0x6c, 0x84, 0x16, 0xbd, 0x2f, 0xf6, 0x64, 0xcf, 0x5d, 0x60, 0xf2, 0x59, 0xcb, 0x12, 0x80, 0x2b, 0xb9, 0x95, 0x7, 0xac, 0x3e, 0xe7, 0x75, 0xde, 0x4c, 0x71, 0xe3, 0x48, 0xda, 0x3, 0x91, 0x3a, 0xa8, 0x40, 0xd2, 0x79, 0xeb, 0x32, 0xa0, 0xb, 0x99, 0xa4, 0x36, 0x9d, 0xf, 0xd6, 0x44, 0xef, 0x7d, 0x22, 0xb0, 0x1b, 0x89, 0x50, 0xc2, 0x69, 0xfb, 0xc6, 0x54, 0xff, 0x6d, 0xb4, 0x26, 0x8d, 0x1f, 0xf7, 0x65, 0xce, 0x5c, 0x85, 0x17, 0xbc, 0x2e, 0x13, 0x81, 0x2a, 0xb8, 0x61, 0xf3, 0x58, 0xca},
+ {0x0, 0x93, 0x3b, 0xa8, 0x76, 0xe5, 0x4d, 0xde, 0xec, 0x7f, 0xd7, 0x44, 0x9a, 0x9, 0xa1, 0x32, 0xc5, 0x56, 0xfe, 0x6d, 0xb3, 0x20, 0x88, 0x1b, 0x29, 0xba, 0x12, 0x81, 0x5f, 0xcc, 0x64, 0xf7, 0x97, 0x4, 0xac, 0x3f, 0xe1, 0x72, 0xda, 0x49, 0x7b, 0xe8, 0x40, 0xd3, 0xd, 0x9e, 0x36, 0xa5, 0x52, 0xc1, 0x69, 0xfa, 0x24, 0xb7, 0x1f, 0x8c, 0xbe, 0x2d, 0x85, 0x16, 0xc8, 0x5b, 0xf3, 0x60, 0x33, 0xa0, 0x8, 0x9b, 0x45, 0xd6, 0x7e, 0xed, 0xdf, 0x4c, 0xe4, 0x77, 0xa9, 0x3a, 0x92, 0x1, 0xf6, 0x65, 0xcd, 0x5e, 0x80, 0x13, 0xbb, 0x28, 0x1a, 0x89, 0x21, 0xb2, 0x6c, 0xff, 0x57, 0xc4, 0xa4, 0x37, 0x9f, 0xc, 0xd2, 0x41, 0xe9, 0x7a, 0x48, 0xdb, 0x73, 0xe0, 0x3e, 0xad, 0x5, 0x96, 0x61, 0xf2, 0x5a, 0xc9, 0x17, 0x84, 0x2c, 0xbf, 0x8d, 0x1e, 0xb6, 0x25, 0xfb, 0x68, 0xc0, 0x53, 0x66, 0xf5, 0x5d, 0xce, 0x10, 0x83, 0x2b, 0xb8, 0x8a, 0x19, 0xb1, 0x22, 0xfc, 0x6f, 0xc7, 0x54, 0xa3, 0x30, 0x98, 0xb, 0xd5, 0x46, 0xee, 0x7d, 0x4f, 0xdc, 0x74, 0xe7, 0x39, 0xaa, 0x2, 0x91, 0xf1, 0x62, 0xca, 0x59, 0x87, 0x14, 0xbc, 0x2f, 0x1d, 0x8e, 0x26, 0xb5, 0x6b, 0xf8, 0x50, 0xc3, 0x34, 0xa7, 0xf, 0x9c, 0x42, 0xd1, 0x79, 0xea, 0xd8, 0x4b, 0xe3, 0x70, 0xae, 0x3d, 0x95, 0x6, 0x55, 0xc6, 0x6e, 0xfd, 0x23, 0xb0, 0x18, 0x8b, 0xb9, 0x2a, 0x82, 0x11, 0xcf, 0x5c, 0xf4, 0x67, 0x90, 0x3, 0xab, 0x38, 0xe6, 0x75, 0xdd, 0x4e, 0x7c, 0xef, 0x47, 0xd4, 0xa, 0x99, 0x31, 0xa2, 0xc2, 0x51, 0xf9, 0x6a, 0xb4, 0x27, 0x8f, 0x1c, 0x2e, 0xbd, 0x15, 0x86, 0x58, 0xcb, 0x63, 0xf0, 0x7, 0x94, 0x3c, 0xaf, 0x71, 0xe2, 0x4a, 0xd9, 0xeb, 0x78, 0xd0, 0x43, 0x9d, 0xe, 0xa6, 0x35},
+ {0x0, 0x94, 0x35, 0xa1, 0x6a, 0xfe, 0x5f, 0xcb, 0xd4, 0x40, 0xe1, 0x75, 0xbe, 0x2a, 0x8b, 0x1f, 0xb5, 0x21, 0x80, 0x14, 0xdf, 0x4b, 0xea, 0x7e, 0x61, 0xf5, 0x54, 0xc0, 0xb, 0x9f, 0x3e, 0xaa, 0x77, 0xe3, 0x42, 0xd6, 0x1d, 0x89, 0x28, 0xbc, 0xa3, 0x37, 0x96, 0x2, 0xc9, 0x5d, 0xfc, 0x68, 0xc2, 0x56, 0xf7, 0x63, 0xa8, 0x3c, 0x9d, 0x9, 0x16, 0x82, 0x23, 0xb7, 0x7c, 0xe8, 0x49, 0xdd, 0xee, 0x7a, 0xdb, 0x4f, 0x84, 0x10, 0xb1, 0x25, 0x3a, 0xae, 0xf, 0x9b, 0x50, 0xc4, 0x65, 0xf1, 0x5b, 0xcf, 0x6e, 0xfa, 0x31, 0xa5, 0x4, 0x90, 0x8f, 0x1b, 0xba, 0x2e, 0xe5, 0x71, 0xd0, 0x44, 0x99, 0xd, 0xac, 0x38, 0xf3, 0x67, 0xc6, 0x52, 0x4d, 0xd9, 0x78, 0xec, 0x27, 0xb3, 0x12, 0x86, 0x2c, 0xb8, 0x19, 0x8d, 0x46, 0xd2, 0x73, 0xe7, 0xf8, 0x6c, 0xcd, 0x59, 0x92, 0x6, 0xa7, 0x33, 0xc1, 0x55, 0xf4, 0x60, 0xab, 0x3f, 0x9e, 0xa, 0x15, 0x81, 0x20, 0xb4, 0x7f, 0xeb, 0x4a, 0xde, 0x74, 0xe0, 0x41, 0xd5, 0x1e, 0x8a, 0x2b, 0xbf, 0xa0, 0x34, 0x95, 0x1, 0xca, 0x5e, 0xff, 0x6b, 0xb6, 0x22, 0x83, 0x17, 0xdc, 0x48, 0xe9, 0x7d, 0x62, 0xf6, 0x57, 0xc3, 0x8, 0x9c, 0x3d, 0xa9, 0x3, 0x97, 0x36, 0xa2, 0x69, 0xfd, 0x5c, 0xc8, 0xd7, 0x43, 0xe2, 0x76, 0xbd, 0x29, 0x88, 0x1c, 0x2f, 0xbb, 0x1a, 0x8e, 0x45, 0xd1, 0x70, 0xe4, 0xfb, 0x6f, 0xce, 0x5a, 0x91, 0x5, 0xa4, 0x30, 0x9a, 0xe, 0xaf, 0x3b, 0xf0, 0x64, 0xc5, 0x51, 0x4e, 0xda, 0x7b, 0xef, 0x24, 0xb0, 0x11, 0x85, 0x58, 0xcc, 0x6d, 0xf9, 0x32, 0xa6, 0x7, 0x93, 0x8c, 0x18, 0xb9, 0x2d, 0xe6, 0x72, 0xd3, 0x47, 0xed, 0x79, 0xd8, 0x4c, 0x87, 0x13, 0xb2, 0x26, 0x39, 0xad, 0xc, 0x98, 0x53, 0xc7, 0x66, 0xf2},
+ {0x0, 0x95, 0x37, 0xa2, 0x6e, 0xfb, 0x59, 0xcc, 0xdc, 0x49, 0xeb, 0x7e, 0xb2, 0x27, 0x85, 0x10, 0xa5, 0x30, 0x92, 0x7, 0xcb, 0x5e, 0xfc, 0x69, 0x79, 0xec, 0x4e, 0xdb, 0x17, 0x82, 0x20, 0xb5, 0x57, 0xc2, 0x60, 0xf5, 0x39, 0xac, 0xe, 0x9b, 0x8b, 0x1e, 0xbc, 0x29, 0xe5, 0x70, 0xd2, 0x47, 0xf2, 0x67, 0xc5, 0x50, 0x9c, 0x9, 0xab, 0x3e, 0x2e, 0xbb, 0x19, 0x8c, 0x40, 0xd5, 0x77, 0xe2, 0xae, 0x3b, 0x99, 0xc, 0xc0, 0x55, 0xf7, 0x62, 0x72, 0xe7, 0x45, 0xd0, 0x1c, 0x89, 0x2b, 0xbe, 0xb, 0x9e, 0x3c, 0xa9, 0x65, 0xf0, 0x52, 0xc7, 0xd7, 0x42, 0xe0, 0x75, 0xb9, 0x2c, 0x8e, 0x1b, 0xf9, 0x6c, 0xce, 0x5b, 0x97, 0x2, 0xa0, 0x35, 0x25, 0xb0, 0x12, 0x87, 0x4b, 0xde, 0x7c, 0xe9, 0x5c, 0xc9, 0x6b, 0xfe, 0x32, 0xa7, 0x5, 0x90, 0x80, 0x15, 0xb7, 0x22, 0xee, 0x7b, 0xd9, 0x4c, 0x41, 0xd4, 0x76, 0xe3, 0x2f, 0xba, 0x18, 0x8d, 0x9d, 0x8, 0xaa, 0x3f, 0xf3, 0x66, 0xc4, 0x51, 0xe4, 0x71, 0xd3, 0x46, 0x8a, 0x1f, 0xbd, 0x28, 0x38, 0xad, 0xf, 0x9a, 0x56, 0xc3, 0x61, 0xf4, 0x16, 0x83, 0x21, 0xb4, 0x78, 0xed, 0x4f, 0xda, 0xca, 0x5f, 0xfd, 0x68, 0xa4, 0x31, 0x93, 0x6, 0xb3, 0x26, 0x84, 0x11, 0xdd, 0x48, 0xea, 0x7f, 0x6f, 0xfa, 0x58, 0xcd, 0x1, 0x94, 0x36, 0xa3, 0xef, 0x7a, 0xd8, 0x4d, 0x81, 0x14, 0xb6, 0x23, 0x33, 0xa6, 0x4, 0x91, 0x5d, 0xc8, 0x6a, 0xff, 0x4a, 0xdf, 0x7d, 0xe8, 0x24, 0xb1, 0x13, 0x86, 0x96, 0x3, 0xa1, 0x34, 0xf8, 0x6d, 0xcf, 0x5a, 0xb8, 0x2d, 0x8f, 0x1a, 0xd6, 0x43, 0xe1, 0x74, 0x64, 0xf1, 0x53, 0xc6, 0xa, 0x9f, 0x3d, 0xa8, 0x1d, 0x88, 0x2a, 0xbf, 0x73, 0xe6, 0x44, 0xd1, 0xc1, 0x54, 0xf6, 0x63, 0xaf, 0x3a, 0x98, 0xd},
+ {0x0, 0x96, 0x31, 0xa7, 0x62, 0xf4, 0x53, 0xc5, 0xc4, 0x52, 0xf5, 0x63, 0xa6, 0x30, 0x97, 0x1, 0x95, 0x3, 0xa4, 0x32, 0xf7, 0x61, 0xc6, 0x50, 0x51, 0xc7, 0x60, 0xf6, 0x33, 0xa5, 0x2, 0x94, 0x37, 0xa1, 0x6, 0x90, 0x55, 0xc3, 0x64, 0xf2, 0xf3, 0x65, 0xc2, 0x54, 0x91, 0x7, 0xa0, 0x36, 0xa2, 0x34, 0x93, 0x5, 0xc0, 0x56, 0xf1, 0x67, 0x66, 0xf0, 0x57, 0xc1, 0x4, 0x92, 0x35, 0xa3, 0x6e, 0xf8, 0x5f, 0xc9, 0xc, 0x9a, 0x3d, 0xab, 0xaa, 0x3c, 0x9b, 0xd, 0xc8, 0x5e, 0xf9, 0x6f, 0xfb, 0x6d, 0xca, 0x5c, 0x99, 0xf, 0xa8, 0x3e, 0x3f, 0xa9, 0xe, 0x98, 0x5d, 0xcb, 0x6c, 0xfa, 0x59, 0xcf, 0x68, 0xfe, 0x3b, 0xad, 0xa, 0x9c, 0x9d, 0xb, 0xac, 0x3a, 0xff, 0x69, 0xce, 0x58, 0xcc, 0x5a, 0xfd, 0x6b, 0xae, 0x38, 0x9f, 0x9, 0x8, 0x9e, 0x39, 0xaf, 0x6a, 0xfc, 0x5b, 0xcd, 0xdc, 0x4a, 0xed, 0x7b, 0xbe, 0x28, 0x8f, 0x19, 0x18, 0x8e, 0x29, 0xbf, 0x7a, 0xec, 0x4b, 0xdd, 0x49, 0xdf, 0x78, 0xee, 0x2b, 0xbd, 0x1a, 0x8c, 0x8d, 0x1b, 0xbc, 0x2a, 0xef, 0x79, 0xde, 0x48, 0xeb, 0x7d, 0xda, 0x4c, 0x89, 0x1f, 0xb8, 0x2e, 0x2f, 0xb9, 0x1e, 0x88, 0x4d, 0xdb, 0x7c, 0xea, 0x7e, 0xe8, 0x4f, 0xd9, 0x1c, 0x8a, 0x2d, 0xbb, 0xba, 0x2c, 0x8b, 0x1d, 0xd8, 0x4e, 0xe9, 0x7f, 0xb2, 0x24, 0x83, 0x15, 0xd0, 0x46, 0xe1, 0x77, 0x76, 0xe0, 0x47, 0xd1, 0x14, 0x82, 0x25, 0xb3, 0x27, 0xb1, 0x16, 0x80, 0x45, 0xd3, 0x74, 0xe2, 0xe3, 0x75, 0xd2, 0x44, 0x81, 0x17, 0xb0, 0x26, 0x85, 0x13, 0xb4, 0x22, 0xe7, 0x71, 0xd6, 0x40, 0x41, 0xd7, 0x70, 0xe6, 0x23, 0xb5, 0x12, 0x84, 0x10, 0x86, 0x21, 0xb7, 0x72, 0xe4, 0x43, 0xd5, 0xd4, 0x42, 0xe5, 0x73, 0xb6, 0x20, 0x87, 0x11},
+ {0x0, 0x97, 0x33, 0xa4, 0x66, 0xf1, 0x55, 0xc2, 0xcc, 0x5b, 0xff, 0x68, 0xaa, 0x3d, 0x99, 0xe, 0x85, 0x12, 0xb6, 0x21, 0xe3, 0x74, 0xd0, 0x47, 0x49, 0xde, 0x7a, 0xed, 0x2f, 0xb8, 0x1c, 0x8b, 0x17, 0x80, 0x24, 0xb3, 0x71, 0xe6, 0x42, 0xd5, 0xdb, 0x4c, 0xe8, 0x7f, 0xbd, 0x2a, 0x8e, 0x19, 0x92, 0x5, 0xa1, 0x36, 0xf4, 0x63, 0xc7, 0x50, 0x5e, 0xc9, 0x6d, 0xfa, 0x38, 0xaf, 0xb, 0x9c, 0x2e, 0xb9, 0x1d, 0x8a, 0x48, 0xdf, 0x7b, 0xec, 0xe2, 0x75, 0xd1, 0x46, 0x84, 0x13, 0xb7, 0x20, 0xab, 0x3c, 0x98, 0xf, 0xcd, 0x5a, 0xfe, 0x69, 0x67, 0xf0, 0x54, 0xc3, 0x1, 0x96, 0x32, 0xa5, 0x39, 0xae, 0xa, 0x9d, 0x5f, 0xc8, 0x6c, 0xfb, 0xf5, 0x62, 0xc6, 0x51, 0x93, 0x4, 0xa0, 0x37, 0xbc, 0x2b, 0x8f, 0x18, 0xda, 0x4d, 0xe9, 0x7e, 0x70, 0xe7, 0x43, 0xd4, 0x16, 0x81, 0x25, 0xb2, 0x5c, 0xcb, 0x6f, 0xf8, 0x3a, 0xad, 0x9, 0x9e, 0x90, 0x7, 0xa3, 0x34, 0xf6, 0x61, 0xc5, 0x52, 0xd9, 0x4e, 0xea, 0x7d, 0xbf, 0x28, 0x8c, 0x1b, 0x15, 0x82, 0x26, 0xb1, 0x73, 0xe4, 0x40, 0xd7, 0x4b, 0xdc, 0x78, 0xef, 0x2d, 0xba, 0x1e, 0x89, 0x87, 0x10, 0xb4, 0x23, 0xe1, 0x76, 0xd2, 0x45, 0xce, 0x59, 0xfd, 0x6a, 0xa8, 0x3f, 0x9b, 0xc, 0x2, 0x95, 0x31, 0xa6, 0x64, 0xf3, 0x57, 0xc0, 0x72, 0xe5, 0x41, 0xd6, 0x14, 0x83, 0x27, 0xb0, 0xbe, 0x29, 0x8d, 0x1a, 0xd8, 0x4f, 0xeb, 0x7c, 0xf7, 0x60, 0xc4, 0x53, 0x91, 0x6, 0xa2, 0x35, 0x3b, 0xac, 0x8, 0x9f, 0x5d, 0xca, 0x6e, 0xf9, 0x65, 0xf2, 0x56, 0xc1, 0x3, 0x94, 0x30, 0xa7, 0xa9, 0x3e, 0x9a, 0xd, 0xcf, 0x58, 0xfc, 0x6b, 0xe0, 0x77, 0xd3, 0x44, 0x86, 0x11, 0xb5, 0x22, 0x2c, 0xbb, 0x1f, 0x88, 0x4a, 0xdd, 0x79, 0xee},
+ {0x0, 0x98, 0x2d, 0xb5, 0x5a, 0xc2, 0x77, 0xef, 0xb4, 0x2c, 0x99, 0x1, 0xee, 0x76, 0xc3, 0x5b, 0x75, 0xed, 0x58, 0xc0, 0x2f, 0xb7, 0x2, 0x9a, 0xc1, 0x59, 0xec, 0x74, 0x9b, 0x3, 0xb6, 0x2e, 0xea, 0x72, 0xc7, 0x5f, 0xb0, 0x28, 0x9d, 0x5, 0x5e, 0xc6, 0x73, 0xeb, 0x4, 0x9c, 0x29, 0xb1, 0x9f, 0x7, 0xb2, 0x2a, 0xc5, 0x5d, 0xe8, 0x70, 0x2b, 0xb3, 0x6, 0x9e, 0x71, 0xe9, 0x5c, 0xc4, 0xc9, 0x51, 0xe4, 0x7c, 0x93, 0xb, 0xbe, 0x26, 0x7d, 0xe5, 0x50, 0xc8, 0x27, 0xbf, 0xa, 0x92, 0xbc, 0x24, 0x91, 0x9, 0xe6, 0x7e, 0xcb, 0x53, 0x8, 0x90, 0x25, 0xbd, 0x52, 0xca, 0x7f, 0xe7, 0x23, 0xbb, 0xe, 0x96, 0x79, 0xe1, 0x54, 0xcc, 0x97, 0xf, 0xba, 0x22, 0xcd, 0x55, 0xe0, 0x78, 0x56, 0xce, 0x7b, 0xe3, 0xc, 0x94, 0x21, 0xb9, 0xe2, 0x7a, 0xcf, 0x57, 0xb8, 0x20, 0x95, 0xd, 0x8f, 0x17, 0xa2, 0x3a, 0xd5, 0x4d, 0xf8, 0x60, 0x3b, 0xa3, 0x16, 0x8e, 0x61, 0xf9, 0x4c, 0xd4, 0xfa, 0x62, 0xd7, 0x4f, 0xa0, 0x38, 0x8d, 0x15, 0x4e, 0xd6, 0x63, 0xfb, 0x14, 0x8c, 0x39, 0xa1, 0x65, 0xfd, 0x48, 0xd0, 0x3f, 0xa7, 0x12, 0x8a, 0xd1, 0x49, 0xfc, 0x64, 0x8b, 0x13, 0xa6, 0x3e, 0x10, 0x88, 0x3d, 0xa5, 0x4a, 0xd2, 0x67, 0xff, 0xa4, 0x3c, 0x89, 0x11, 0xfe, 0x66, 0xd3, 0x4b, 0x46, 0xde, 0x6b, 0xf3, 0x1c, 0x84, 0x31, 0xa9, 0xf2, 0x6a, 0xdf, 0x47, 0xa8, 0x30, 0x85, 0x1d, 0x33, 0xab, 0x1e, 0x86, 0x69, 0xf1, 0x44, 0xdc, 0x87, 0x1f, 0xaa, 0x32, 0xdd, 0x45, 0xf0, 0x68, 0xac, 0x34, 0x81, 0x19, 0xf6, 0x6e, 0xdb, 0x43, 0x18, 0x80, 0x35, 0xad, 0x42, 0xda, 0x6f, 0xf7, 0xd9, 0x41, 0xf4, 0x6c, 0x83, 0x1b, 0xae, 0x36, 0x6d, 0xf5, 0x40, 0xd8, 0x37, 0xaf, 0x1a, 0x82},
+ {0x0, 0x99, 0x2f, 0xb6, 0x5e, 0xc7, 0x71, 0xe8, 0xbc, 0x25, 0x93, 0xa, 0xe2, 0x7b, 0xcd, 0x54, 0x65, 0xfc, 0x4a, 0xd3, 0x3b, 0xa2, 0x14, 0x8d, 0xd9, 0x40, 0xf6, 0x6f, 0x87, 0x1e, 0xa8, 0x31, 0xca, 0x53, 0xe5, 0x7c, 0x94, 0xd, 0xbb, 0x22, 0x76, 0xef, 0x59, 0xc0, 0x28, 0xb1, 0x7, 0x9e, 0xaf, 0x36, 0x80, 0x19, 0xf1, 0x68, 0xde, 0x47, 0x13, 0x8a, 0x3c, 0xa5, 0x4d, 0xd4, 0x62, 0xfb, 0x89, 0x10, 0xa6, 0x3f, 0xd7, 0x4e, 0xf8, 0x61, 0x35, 0xac, 0x1a, 0x83, 0x6b, 0xf2, 0x44, 0xdd, 0xec, 0x75, 0xc3, 0x5a, 0xb2, 0x2b, 0x9d, 0x4, 0x50, 0xc9, 0x7f, 0xe6, 0xe, 0x97, 0x21, 0xb8, 0x43, 0xda, 0x6c, 0xf5, 0x1d, 0x84, 0x32, 0xab, 0xff, 0x66, 0xd0, 0x49, 0xa1, 0x38, 0x8e, 0x17, 0x26, 0xbf, 0x9, 0x90, 0x78, 0xe1, 0x57, 0xce, 0x9a, 0x3, 0xb5, 0x2c, 0xc4, 0x5d, 0xeb, 0x72, 0xf, 0x96, 0x20, 0xb9, 0x51, 0xc8, 0x7e, 0xe7, 0xb3, 0x2a, 0x9c, 0x5, 0xed, 0x74, 0xc2, 0x5b, 0x6a, 0xf3, 0x45, 0xdc, 0x34, 0xad, 0x1b, 0x82, 0xd6, 0x4f, 0xf9, 0x60, 0x88, 0x11, 0xa7, 0x3e, 0xc5, 0x5c, 0xea, 0x73, 0x9b, 0x2, 0xb4, 0x2d, 0x79, 0xe0, 0x56, 0xcf, 0x27, 0xbe, 0x8, 0x91, 0xa0, 0x39, 0x8f, 0x16, 0xfe, 0x67, 0xd1, 0x48, 0x1c, 0x85, 0x33, 0xaa, 0x42, 0xdb, 0x6d, 0xf4, 0x86, 0x1f, 0xa9, 0x30, 0xd8, 0x41, 0xf7, 0x6e, 0x3a, 0xa3, 0x15, 0x8c, 0x64, 0xfd, 0x4b, 0xd2, 0xe3, 0x7a, 0xcc, 0x55, 0xbd, 0x24, 0x92, 0xb, 0x5f, 0xc6, 0x70, 0xe9, 0x1, 0x98, 0x2e, 0xb7, 0x4c, 0xd5, 0x63, 0xfa, 0x12, 0x8b, 0x3d, 0xa4, 0xf0, 0x69, 0xdf, 0x46, 0xae, 0x37, 0x81, 0x18, 0x29, 0xb0, 0x6, 0x9f, 0x77, 0xee, 0x58, 0xc1, 0x95, 0xc, 0xba, 0x23, 0xcb, 0x52, 0xe4, 0x7d},
+ {0x0, 0x9a, 0x29, 0xb3, 0x52, 0xc8, 0x7b, 0xe1, 0xa4, 0x3e, 0x8d, 0x17, 0xf6, 0x6c, 0xdf, 0x45, 0x55, 0xcf, 0x7c, 0xe6, 0x7, 0x9d, 0x2e, 0xb4, 0xf1, 0x6b, 0xd8, 0x42, 0xa3, 0x39, 0x8a, 0x10, 0xaa, 0x30, 0x83, 0x19, 0xf8, 0x62, 0xd1, 0x4b, 0xe, 0x94, 0x27, 0xbd, 0x5c, 0xc6, 0x75, 0xef, 0xff, 0x65, 0xd6, 0x4c, 0xad, 0x37, 0x84, 0x1e, 0x5b, 0xc1, 0x72, 0xe8, 0x9, 0x93, 0x20, 0xba, 0x49, 0xd3, 0x60, 0xfa, 0x1b, 0x81, 0x32, 0xa8, 0xed, 0x77, 0xc4, 0x5e, 0xbf, 0x25, 0x96, 0xc, 0x1c, 0x86, 0x35, 0xaf, 0x4e, 0xd4, 0x67, 0xfd, 0xb8, 0x22, 0x91, 0xb, 0xea, 0x70, 0xc3, 0x59, 0xe3, 0x79, 0xca, 0x50, 0xb1, 0x2b, 0x98, 0x2, 0x47, 0xdd, 0x6e, 0xf4, 0x15, 0x8f, 0x3c, 0xa6, 0xb6, 0x2c, 0x9f, 0x5, 0xe4, 0x7e, 0xcd, 0x57, 0x12, 0x88, 0x3b, 0xa1, 0x40, 0xda, 0x69, 0xf3, 0x92, 0x8, 0xbb, 0x21, 0xc0, 0x5a, 0xe9, 0x73, 0x36, 0xac, 0x1f, 0x85, 0x64, 0xfe, 0x4d, 0xd7, 0xc7, 0x5d, 0xee, 0x74, 0x95, 0xf, 0xbc, 0x26, 0x63, 0xf9, 0x4a, 0xd0, 0x31, 0xab, 0x18, 0x82, 0x38, 0xa2, 0x11, 0x8b, 0x6a, 0xf0, 0x43, 0xd9, 0x9c, 0x6, 0xb5, 0x2f, 0xce, 0x54, 0xe7, 0x7d, 0x6d, 0xf7, 0x44, 0xde, 0x3f, 0xa5, 0x16, 0x8c, 0xc9, 0x53, 0xe0, 0x7a, 0x9b, 0x1, 0xb2, 0x28, 0xdb, 0x41, 0xf2, 0x68, 0x89, 0x13, 0xa0, 0x3a, 0x7f, 0xe5, 0x56, 0xcc, 0x2d, 0xb7, 0x4, 0x9e, 0x8e, 0x14, 0xa7, 0x3d, 0xdc, 0x46, 0xf5, 0x6f, 0x2a, 0xb0, 0x3, 0x99, 0x78, 0xe2, 0x51, 0xcb, 0x71, 0xeb, 0x58, 0xc2, 0x23, 0xb9, 0xa, 0x90, 0xd5, 0x4f, 0xfc, 0x66, 0x87, 0x1d, 0xae, 0x34, 0x24, 0xbe, 0xd, 0x97, 0x76, 0xec, 0x5f, 0xc5, 0x80, 0x1a, 0xa9, 0x33, 0xd2, 0x48, 0xfb, 0x61},
+ {0x0, 0x9b, 0x2b, 0xb0, 0x56, 0xcd, 0x7d, 0xe6, 0xac, 0x37, 0x87, 0x1c, 0xfa, 0x61, 0xd1, 0x4a, 0x45, 0xde, 0x6e, 0xf5, 0x13, 0x88, 0x38, 0xa3, 0xe9, 0x72, 0xc2, 0x59, 0xbf, 0x24, 0x94, 0xf, 0x8a, 0x11, 0xa1, 0x3a, 0xdc, 0x47, 0xf7, 0x6c, 0x26, 0xbd, 0xd, 0x96, 0x70, 0xeb, 0x5b, 0xc0, 0xcf, 0x54, 0xe4, 0x7f, 0x99, 0x2, 0xb2, 0x29, 0x63, 0xf8, 0x48, 0xd3, 0x35, 0xae, 0x1e, 0x85, 0x9, 0x92, 0x22, 0xb9, 0x5f, 0xc4, 0x74, 0xef, 0xa5, 0x3e, 0x8e, 0x15, 0xf3, 0x68, 0xd8, 0x43, 0x4c, 0xd7, 0x67, 0xfc, 0x1a, 0x81, 0x31, 0xaa, 0xe0, 0x7b, 0xcb, 0x50, 0xb6, 0x2d, 0x9d, 0x6, 0x83, 0x18, 0xa8, 0x33, 0xd5, 0x4e, 0xfe, 0x65, 0x2f, 0xb4, 0x4, 0x9f, 0x79, 0xe2, 0x52, 0xc9, 0xc6, 0x5d, 0xed, 0x76, 0x90, 0xb, 0xbb, 0x20, 0x6a, 0xf1, 0x41, 0xda, 0x3c, 0xa7, 0x17, 0x8c, 0x12, 0x89, 0x39, 0xa2, 0x44, 0xdf, 0x6f, 0xf4, 0xbe, 0x25, 0x95, 0xe, 0xe8, 0x73, 0xc3, 0x58, 0x57, 0xcc, 0x7c, 0xe7, 0x1, 0x9a, 0x2a, 0xb1, 0xfb, 0x60, 0xd0, 0x4b, 0xad, 0x36, 0x86, 0x1d, 0x98, 0x3, 0xb3, 0x28, 0xce, 0x55, 0xe5, 0x7e, 0x34, 0xaf, 0x1f, 0x84, 0x62, 0xf9, 0x49, 0xd2, 0xdd, 0x46, 0xf6, 0x6d, 0x8b, 0x10, 0xa0, 0x3b, 0x71, 0xea, 0x5a, 0xc1, 0x27, 0xbc, 0xc, 0x97, 0x1b, 0x80, 0x30, 0xab, 0x4d, 0xd6, 0x66, 0xfd, 0xb7, 0x2c, 0x9c, 0x7, 0xe1, 0x7a, 0xca, 0x51, 0x5e, 0xc5, 0x75, 0xee, 0x8, 0x93, 0x23, 0xb8, 0xf2, 0x69, 0xd9, 0x42, 0xa4, 0x3f, 0x8f, 0x14, 0x91, 0xa, 0xba, 0x21, 0xc7, 0x5c, 0xec, 0x77, 0x3d, 0xa6, 0x16, 0x8d, 0x6b, 0xf0, 0x40, 0xdb, 0xd4, 0x4f, 0xff, 0x64, 0x82, 0x19, 0xa9, 0x32, 0x78, 0xe3, 0x53, 0xc8, 0x2e, 0xb5, 0x5, 0x9e},
+ {0x0, 0x9c, 0x25, 0xb9, 0x4a, 0xd6, 0x6f, 0xf3, 0x94, 0x8, 0xb1, 0x2d, 0xde, 0x42, 0xfb, 0x67, 0x35, 0xa9, 0x10, 0x8c, 0x7f, 0xe3, 0x5a, 0xc6, 0xa1, 0x3d, 0x84, 0x18, 0xeb, 0x77, 0xce, 0x52, 0x6a, 0xf6, 0x4f, 0xd3, 0x20, 0xbc, 0x5, 0x99, 0xfe, 0x62, 0xdb, 0x47, 0xb4, 0x28, 0x91, 0xd, 0x5f, 0xc3, 0x7a, 0xe6, 0x15, 0x89, 0x30, 0xac, 0xcb, 0x57, 0xee, 0x72, 0x81, 0x1d, 0xa4, 0x38, 0xd4, 0x48, 0xf1, 0x6d, 0x9e, 0x2, 0xbb, 0x27, 0x40, 0xdc, 0x65, 0xf9, 0xa, 0x96, 0x2f, 0xb3, 0xe1, 0x7d, 0xc4, 0x58, 0xab, 0x37, 0x8e, 0x12, 0x75, 0xe9, 0x50, 0xcc, 0x3f, 0xa3, 0x1a, 0x86, 0xbe, 0x22, 0x9b, 0x7, 0xf4, 0x68, 0xd1, 0x4d, 0x2a, 0xb6, 0xf, 0x93, 0x60, 0xfc, 0x45, 0xd9, 0x8b, 0x17, 0xae, 0x32, 0xc1, 0x5d, 0xe4, 0x78, 0x1f, 0x83, 0x3a, 0xa6, 0x55, 0xc9, 0x70, 0xec, 0xb5, 0x29, 0x90, 0xc, 0xff, 0x63, 0xda, 0x46, 0x21, 0xbd, 0x4, 0x98, 0x6b, 0xf7, 0x4e, 0xd2, 0x80, 0x1c, 0xa5, 0x39, 0xca, 0x56, 0xef, 0x73, 0x14, 0x88, 0x31, 0xad, 0x5e, 0xc2, 0x7b, 0xe7, 0xdf, 0x43, 0xfa, 0x66, 0x95, 0x9, 0xb0, 0x2c, 0x4b, 0xd7, 0x6e, 0xf2, 0x1, 0x9d, 0x24, 0xb8, 0xea, 0x76, 0xcf, 0x53, 0xa0, 0x3c, 0x85, 0x19, 0x7e, 0xe2, 0x5b, 0xc7, 0x34, 0xa8, 0x11, 0x8d, 0x61, 0xfd, 0x44, 0xd8, 0x2b, 0xb7, 0xe, 0x92, 0xf5, 0x69, 0xd0, 0x4c, 0xbf, 0x23, 0x9a, 0x6, 0x54, 0xc8, 0x71, 0xed, 0x1e, 0x82, 0x3b, 0xa7, 0xc0, 0x5c, 0xe5, 0x79, 0x8a, 0x16, 0xaf, 0x33, 0xb, 0x97, 0x2e, 0xb2, 0x41, 0xdd, 0x64, 0xf8, 0x9f, 0x3, 0xba, 0x26, 0xd5, 0x49, 0xf0, 0x6c, 0x3e, 0xa2, 0x1b, 0x87, 0x74, 0xe8, 0x51, 0xcd, 0xaa, 0x36, 0x8f, 0x13, 0xe0, 0x7c, 0xc5, 0x59},
+ {0x0, 0x9d, 0x27, 0xba, 0x4e, 0xd3, 0x69, 0xf4, 0x9c, 0x1, 0xbb, 0x26, 0xd2, 0x4f, 0xf5, 0x68, 0x25, 0xb8, 0x2, 0x9f, 0x6b, 0xf6, 0x4c, 0xd1, 0xb9, 0x24, 0x9e, 0x3, 0xf7, 0x6a, 0xd0, 0x4d, 0x4a, 0xd7, 0x6d, 0xf0, 0x4, 0x99, 0x23, 0xbe, 0xd6, 0x4b, 0xf1, 0x6c, 0x98, 0x5, 0xbf, 0x22, 0x6f, 0xf2, 0x48, 0xd5, 0x21, 0xbc, 0x6, 0x9b, 0xf3, 0x6e, 0xd4, 0x49, 0xbd, 0x20, 0x9a, 0x7, 0x94, 0x9, 0xb3, 0x2e, 0xda, 0x47, 0xfd, 0x60, 0x8, 0x95, 0x2f, 0xb2, 0x46, 0xdb, 0x61, 0xfc, 0xb1, 0x2c, 0x96, 0xb, 0xff, 0x62, 0xd8, 0x45, 0x2d, 0xb0, 0xa, 0x97, 0x63, 0xfe, 0x44, 0xd9, 0xde, 0x43, 0xf9, 0x64, 0x90, 0xd, 0xb7, 0x2a, 0x42, 0xdf, 0x65, 0xf8, 0xc, 0x91, 0x2b, 0xb6, 0xfb, 0x66, 0xdc, 0x41, 0xb5, 0x28, 0x92, 0xf, 0x67, 0xfa, 0x40, 0xdd, 0x29, 0xb4, 0xe, 0x93, 0x35, 0xa8, 0x12, 0x8f, 0x7b, 0xe6, 0x5c, 0xc1, 0xa9, 0x34, 0x8e, 0x13, 0xe7, 0x7a, 0xc0, 0x5d, 0x10, 0x8d, 0x37, 0xaa, 0x5e, 0xc3, 0x79, 0xe4, 0x8c, 0x11, 0xab, 0x36, 0xc2, 0x5f, 0xe5, 0x78, 0x7f, 0xe2, 0x58, 0xc5, 0x31, 0xac, 0x16, 0x8b, 0xe3, 0x7e, 0xc4, 0x59, 0xad, 0x30, 0x8a, 0x17, 0x5a, 0xc7, 0x7d, 0xe0, 0x14, 0x89, 0x33, 0xae, 0xc6, 0x5b, 0xe1, 0x7c, 0x88, 0x15, 0xaf, 0x32, 0xa1, 0x3c, 0x86, 0x1b, 0xef, 0x72, 0xc8, 0x55, 0x3d, 0xa0, 0x1a, 0x87, 0x73, 0xee, 0x54, 0xc9, 0x84, 0x19, 0xa3, 0x3e, 0xca, 0x57, 0xed, 0x70, 0x18, 0x85, 0x3f, 0xa2, 0x56, 0xcb, 0x71, 0xec, 0xeb, 0x76, 0xcc, 0x51, 0xa5, 0x38, 0x82, 0x1f, 0x77, 0xea, 0x50, 0xcd, 0x39, 0xa4, 0x1e, 0x83, 0xce, 0x53, 0xe9, 0x74, 0x80, 0x1d, 0xa7, 0x3a, 0x52, 0xcf, 0x75, 0xe8, 0x1c, 0x81, 0x3b, 0xa6},
+ {0x0, 0x9e, 0x21, 0xbf, 0x42, 0xdc, 0x63, 0xfd, 0x84, 0x1a, 0xa5, 0x3b, 0xc6, 0x58, 0xe7, 0x79, 0x15, 0x8b, 0x34, 0xaa, 0x57, 0xc9, 0x76, 0xe8, 0x91, 0xf, 0xb0, 0x2e, 0xd3, 0x4d, 0xf2, 0x6c, 0x2a, 0xb4, 0xb, 0x95, 0x68, 0xf6, 0x49, 0xd7, 0xae, 0x30, 0x8f, 0x11, 0xec, 0x72, 0xcd, 0x53, 0x3f, 0xa1, 0x1e, 0x80, 0x7d, 0xe3, 0x5c, 0xc2, 0xbb, 0x25, 0x9a, 0x4, 0xf9, 0x67, 0xd8, 0x46, 0x54, 0xca, 0x75, 0xeb, 0x16, 0x88, 0x37, 0xa9, 0xd0, 0x4e, 0xf1, 0x6f, 0x92, 0xc, 0xb3, 0x2d, 0x41, 0xdf, 0x60, 0xfe, 0x3, 0x9d, 0x22, 0xbc, 0xc5, 0x5b, 0xe4, 0x7a, 0x87, 0x19, 0xa6, 0x38, 0x7e, 0xe0, 0x5f, 0xc1, 0x3c, 0xa2, 0x1d, 0x83, 0xfa, 0x64, 0xdb, 0x45, 0xb8, 0x26, 0x99, 0x7, 0x6b, 0xf5, 0x4a, 0xd4, 0x29, 0xb7, 0x8, 0x96, 0xef, 0x71, 0xce, 0x50, 0xad, 0x33, 0x8c, 0x12, 0xa8, 0x36, 0x89, 0x17, 0xea, 0x74, 0xcb, 0x55, 0x2c, 0xb2, 0xd, 0x93, 0x6e, 0xf0, 0x4f, 0xd1, 0xbd, 0x23, 0x9c, 0x2, 0xff, 0x61, 0xde, 0x40, 0x39, 0xa7, 0x18, 0x86, 0x7b, 0xe5, 0x5a, 0xc4, 0x82, 0x1c, 0xa3, 0x3d, 0xc0, 0x5e, 0xe1, 0x7f, 0x6, 0x98, 0x27, 0xb9, 0x44, 0xda, 0x65, 0xfb, 0x97, 0x9, 0xb6, 0x28, 0xd5, 0x4b, 0xf4, 0x6a, 0x13, 0x8d, 0x32, 0xac, 0x51, 0xcf, 0x70, 0xee, 0xfc, 0x62, 0xdd, 0x43, 0xbe, 0x20, 0x9f, 0x1, 0x78, 0xe6, 0x59, 0xc7, 0x3a, 0xa4, 0x1b, 0x85, 0xe9, 0x77, 0xc8, 0x56, 0xab, 0x35, 0x8a, 0x14, 0x6d, 0xf3, 0x4c, 0xd2, 0x2f, 0xb1, 0xe, 0x90, 0xd6, 0x48, 0xf7, 0x69, 0x94, 0xa, 0xb5, 0x2b, 0x52, 0xcc, 0x73, 0xed, 0x10, 0x8e, 0x31, 0xaf, 0xc3, 0x5d, 0xe2, 0x7c, 0x81, 0x1f, 0xa0, 0x3e, 0x47, 0xd9, 0x66, 0xf8, 0x5, 0x9b, 0x24, 0xba},
+ {0x0, 0x9f, 0x23, 0xbc, 0x46, 0xd9, 0x65, 0xfa, 0x8c, 0x13, 0xaf, 0x30, 0xca, 0x55, 0xe9, 0x76, 0x5, 0x9a, 0x26, 0xb9, 0x43, 0xdc, 0x60, 0xff, 0x89, 0x16, 0xaa, 0x35, 0xcf, 0x50, 0xec, 0x73, 0xa, 0x95, 0x29, 0xb6, 0x4c, 0xd3, 0x6f, 0xf0, 0x86, 0x19, 0xa5, 0x3a, 0xc0, 0x5f, 0xe3, 0x7c, 0xf, 0x90, 0x2c, 0xb3, 0x49, 0xd6, 0x6a, 0xf5, 0x83, 0x1c, 0xa0, 0x3f, 0xc5, 0x5a, 0xe6, 0x79, 0x14, 0x8b, 0x37, 0xa8, 0x52, 0xcd, 0x71, 0xee, 0x98, 0x7, 0xbb, 0x24, 0xde, 0x41, 0xfd, 0x62, 0x11, 0x8e, 0x32, 0xad, 0x57, 0xc8, 0x74, 0xeb, 0x9d, 0x2, 0xbe, 0x21, 0xdb, 0x44, 0xf8, 0x67, 0x1e, 0x81, 0x3d, 0xa2, 0x58, 0xc7, 0x7b, 0xe4, 0x92, 0xd, 0xb1, 0x2e, 0xd4, 0x4b, 0xf7, 0x68, 0x1b, 0x84, 0x38, 0xa7, 0x5d, 0xc2, 0x7e, 0xe1, 0x97, 0x8, 0xb4, 0x2b, 0xd1, 0x4e, 0xf2, 0x6d, 0x28, 0xb7, 0xb, 0x94, 0x6e, 0xf1, 0x4d, 0xd2, 0xa4, 0x3b, 0x87, 0x18, 0xe2, 0x7d, 0xc1, 0x5e, 0x2d, 0xb2, 0xe, 0x91, 0x6b, 0xf4, 0x48, 0xd7, 0xa1, 0x3e, 0x82, 0x1d, 0xe7, 0x78, 0xc4, 0x5b, 0x22, 0xbd, 0x1, 0x9e, 0x64, 0xfb, 0x47, 0xd8, 0xae, 0x31, 0x8d, 0x12, 0xe8, 0x77, 0xcb, 0x54, 0x27, 0xb8, 0x4, 0x9b, 0x61, 0xfe, 0x42, 0xdd, 0xab, 0x34, 0x88, 0x17, 0xed, 0x72, 0xce, 0x51, 0x3c, 0xa3, 0x1f, 0x80, 0x7a, 0xe5, 0x59, 0xc6, 0xb0, 0x2f, 0x93, 0xc, 0xf6, 0x69, 0xd5, 0x4a, 0x39, 0xa6, 0x1a, 0x85, 0x7f, 0xe0, 0x5c, 0xc3, 0xb5, 0x2a, 0x96, 0x9, 0xf3, 0x6c, 0xd0, 0x4f, 0x36, 0xa9, 0x15, 0x8a, 0x70, 0xef, 0x53, 0xcc, 0xba, 0x25, 0x99, 0x6, 0xfc, 0x63, 0xdf, 0x40, 0x33, 0xac, 0x10, 0x8f, 0x75, 0xea, 0x56, 0xc9, 0xbf, 0x20, 0x9c, 0x3, 0xf9, 0x66, 0xda, 0x45},
+ {0x0, 0xa0, 0x5d, 0xfd, 0xba, 0x1a, 0xe7, 0x47, 0x69, 0xc9, 0x34, 0x94, 0xd3, 0x73, 0x8e, 0x2e, 0xd2, 0x72, 0x8f, 0x2f, 0x68, 0xc8, 0x35, 0x95, 0xbb, 0x1b, 0xe6, 0x46, 0x1, 0xa1, 0x5c, 0xfc, 0xb9, 0x19, 0xe4, 0x44, 0x3, 0xa3, 0x5e, 0xfe, 0xd0, 0x70, 0x8d, 0x2d, 0x6a, 0xca, 0x37, 0x97, 0x6b, 0xcb, 0x36, 0x96, 0xd1, 0x71, 0x8c, 0x2c, 0x2, 0xa2, 0x5f, 0xff, 0xb8, 0x18, 0xe5, 0x45, 0x6f, 0xcf, 0x32, 0x92, 0xd5, 0x75, 0x88, 0x28, 0x6, 0xa6, 0x5b, 0xfb, 0xbc, 0x1c, 0xe1, 0x41, 0xbd, 0x1d, 0xe0, 0x40, 0x7, 0xa7, 0x5a, 0xfa, 0xd4, 0x74, 0x89, 0x29, 0x6e, 0xce, 0x33, 0x93, 0xd6, 0x76, 0x8b, 0x2b, 0x6c, 0xcc, 0x31, 0x91, 0xbf, 0x1f, 0xe2, 0x42, 0x5, 0xa5, 0x58, 0xf8, 0x4, 0xa4, 0x59, 0xf9, 0xbe, 0x1e, 0xe3, 0x43, 0x6d, 0xcd, 0x30, 0x90, 0xd7, 0x77, 0x8a, 0x2a, 0xde, 0x7e, 0x83, 0x23, 0x64, 0xc4, 0x39, 0x99, 0xb7, 0x17, 0xea, 0x4a, 0xd, 0xad, 0x50, 0xf0, 0xc, 0xac, 0x51, 0xf1, 0xb6, 0x16, 0xeb, 0x4b, 0x65, 0xc5, 0x38, 0x98, 0xdf, 0x7f, 0x82, 0x22, 0x67, 0xc7, 0x3a, 0x9a, 0xdd, 0x7d, 0x80, 0x20, 0xe, 0xae, 0x53, 0xf3, 0xb4, 0x14, 0xe9, 0x49, 0xb5, 0x15, 0xe8, 0x48, 0xf, 0xaf, 0x52, 0xf2, 0xdc, 0x7c, 0x81, 0x21, 0x66, 0xc6, 0x3b, 0x9b, 0xb1, 0x11, 0xec, 0x4c, 0xb, 0xab, 0x56, 0xf6, 0xd8, 0x78, 0x85, 0x25, 0x62, 0xc2, 0x3f, 0x9f, 0x63, 0xc3, 0x3e, 0x9e, 0xd9, 0x79, 0x84, 0x24, 0xa, 0xaa, 0x57, 0xf7, 0xb0, 0x10, 0xed, 0x4d, 0x8, 0xa8, 0x55, 0xf5, 0xb2, 0x12, 0xef, 0x4f, 0x61, 0xc1, 0x3c, 0x9c, 0xdb, 0x7b, 0x86, 0x26, 0xda, 0x7a, 0x87, 0x27, 0x60, 0xc0, 0x3d, 0x9d, 0xb3, 0x13, 0xee, 0x4e, 0x9, 0xa9, 0x54, 0xf4},
+ {0x0, 0xa1, 0x5f, 0xfe, 0xbe, 0x1f, 0xe1, 0x40, 0x61, 0xc0, 0x3e, 0x9f, 0xdf, 0x7e, 0x80, 0x21, 0xc2, 0x63, 0x9d, 0x3c, 0x7c, 0xdd, 0x23, 0x82, 0xa3, 0x2, 0xfc, 0x5d, 0x1d, 0xbc, 0x42, 0xe3, 0x99, 0x38, 0xc6, 0x67, 0x27, 0x86, 0x78, 0xd9, 0xf8, 0x59, 0xa7, 0x6, 0x46, 0xe7, 0x19, 0xb8, 0x5b, 0xfa, 0x4, 0xa5, 0xe5, 0x44, 0xba, 0x1b, 0x3a, 0x9b, 0x65, 0xc4, 0x84, 0x25, 0xdb, 0x7a, 0x2f, 0x8e, 0x70, 0xd1, 0x91, 0x30, 0xce, 0x6f, 0x4e, 0xef, 0x11, 0xb0, 0xf0, 0x51, 0xaf, 0xe, 0xed, 0x4c, 0xb2, 0x13, 0x53, 0xf2, 0xc, 0xad, 0x8c, 0x2d, 0xd3, 0x72, 0x32, 0x93, 0x6d, 0xcc, 0xb6, 0x17, 0xe9, 0x48, 0x8, 0xa9, 0x57, 0xf6, 0xd7, 0x76, 0x88, 0x29, 0x69, 0xc8, 0x36, 0x97, 0x74, 0xd5, 0x2b, 0x8a, 0xca, 0x6b, 0x95, 0x34, 0x15, 0xb4, 0x4a, 0xeb, 0xab, 0xa, 0xf4, 0x55, 0x5e, 0xff, 0x1, 0xa0, 0xe0, 0x41, 0xbf, 0x1e, 0x3f, 0x9e, 0x60, 0xc1, 0x81, 0x20, 0xde, 0x7f, 0x9c, 0x3d, 0xc3, 0x62, 0x22, 0x83, 0x7d, 0xdc, 0xfd, 0x5c, 0xa2, 0x3, 0x43, 0xe2, 0x1c, 0xbd, 0xc7, 0x66, 0x98, 0x39, 0x79, 0xd8, 0x26, 0x87, 0xa6, 0x7, 0xf9, 0x58, 0x18, 0xb9, 0x47, 0xe6, 0x5, 0xa4, 0x5a, 0xfb, 0xbb, 0x1a, 0xe4, 0x45, 0x64, 0xc5, 0x3b, 0x9a, 0xda, 0x7b, 0x85, 0x24, 0x71, 0xd0, 0x2e, 0x8f, 0xcf, 0x6e, 0x90, 0x31, 0x10, 0xb1, 0x4f, 0xee, 0xae, 0xf, 0xf1, 0x50, 0xb3, 0x12, 0xec, 0x4d, 0xd, 0xac, 0x52, 0xf3, 0xd2, 0x73, 0x8d, 0x2c, 0x6c, 0xcd, 0x33, 0x92, 0xe8, 0x49, 0xb7, 0x16, 0x56, 0xf7, 0x9, 0xa8, 0x89, 0x28, 0xd6, 0x77, 0x37, 0x96, 0x68, 0xc9, 0x2a, 0x8b, 0x75, 0xd4, 0x94, 0x35, 0xcb, 0x6a, 0x4b, 0xea, 0x14, 0xb5, 0xf5, 0x54, 0xaa, 0xb},
+ {0x0, 0xa2, 0x59, 0xfb, 0xb2, 0x10, 0xeb, 0x49, 0x79, 0xdb, 0x20, 0x82, 0xcb, 0x69, 0x92, 0x30, 0xf2, 0x50, 0xab, 0x9, 0x40, 0xe2, 0x19, 0xbb, 0x8b, 0x29, 0xd2, 0x70, 0x39, 0x9b, 0x60, 0xc2, 0xf9, 0x5b, 0xa0, 0x2, 0x4b, 0xe9, 0x12, 0xb0, 0x80, 0x22, 0xd9, 0x7b, 0x32, 0x90, 0x6b, 0xc9, 0xb, 0xa9, 0x52, 0xf0, 0xb9, 0x1b, 0xe0, 0x42, 0x72, 0xd0, 0x2b, 0x89, 0xc0, 0x62, 0x99, 0x3b, 0xef, 0x4d, 0xb6, 0x14, 0x5d, 0xff, 0x4, 0xa6, 0x96, 0x34, 0xcf, 0x6d, 0x24, 0x86, 0x7d, 0xdf, 0x1d, 0xbf, 0x44, 0xe6, 0xaf, 0xd, 0xf6, 0x54, 0x64, 0xc6, 0x3d, 0x9f, 0xd6, 0x74, 0x8f, 0x2d, 0x16, 0xb4, 0x4f, 0xed, 0xa4, 0x6, 0xfd, 0x5f, 0x6f, 0xcd, 0x36, 0x94, 0xdd, 0x7f, 0x84, 0x26, 0xe4, 0x46, 0xbd, 0x1f, 0x56, 0xf4, 0xf, 0xad, 0x9d, 0x3f, 0xc4, 0x66, 0x2f, 0x8d, 0x76, 0xd4, 0xc3, 0x61, 0x9a, 0x38, 0x71, 0xd3, 0x28, 0x8a, 0xba, 0x18, 0xe3, 0x41, 0x8, 0xaa, 0x51, 0xf3, 0x31, 0x93, 0x68, 0xca, 0x83, 0x21, 0xda, 0x78, 0x48, 0xea, 0x11, 0xb3, 0xfa, 0x58, 0xa3, 0x1, 0x3a, 0x98, 0x63, 0xc1, 0x88, 0x2a, 0xd1, 0x73, 0x43, 0xe1, 0x1a, 0xb8, 0xf1, 0x53, 0xa8, 0xa, 0xc8, 0x6a, 0x91, 0x33, 0x7a, 0xd8, 0x23, 0x81, 0xb1, 0x13, 0xe8, 0x4a, 0x3, 0xa1, 0x5a, 0xf8, 0x2c, 0x8e, 0x75, 0xd7, 0x9e, 0x3c, 0xc7, 0x65, 0x55, 0xf7, 0xc, 0xae, 0xe7, 0x45, 0xbe, 0x1c, 0xde, 0x7c, 0x87, 0x25, 0x6c, 0xce, 0x35, 0x97, 0xa7, 0x5, 0xfe, 0x5c, 0x15, 0xb7, 0x4c, 0xee, 0xd5, 0x77, 0x8c, 0x2e, 0x67, 0xc5, 0x3e, 0x9c, 0xac, 0xe, 0xf5, 0x57, 0x1e, 0xbc, 0x47, 0xe5, 0x27, 0x85, 0x7e, 0xdc, 0x95, 0x37, 0xcc, 0x6e, 0x5e, 0xfc, 0x7, 0xa5, 0xec, 0x4e, 0xb5, 0x17},
+ {0x0, 0xa3, 0x5b, 0xf8, 0xb6, 0x15, 0xed, 0x4e, 0x71, 0xd2, 0x2a, 0x89, 0xc7, 0x64, 0x9c, 0x3f, 0xe2, 0x41, 0xb9, 0x1a, 0x54, 0xf7, 0xf, 0xac, 0x93, 0x30, 0xc8, 0x6b, 0x25, 0x86, 0x7e, 0xdd, 0xd9, 0x7a, 0x82, 0x21, 0x6f, 0xcc, 0x34, 0x97, 0xa8, 0xb, 0xf3, 0x50, 0x1e, 0xbd, 0x45, 0xe6, 0x3b, 0x98, 0x60, 0xc3, 0x8d, 0x2e, 0xd6, 0x75, 0x4a, 0xe9, 0x11, 0xb2, 0xfc, 0x5f, 0xa7, 0x4, 0xaf, 0xc, 0xf4, 0x57, 0x19, 0xba, 0x42, 0xe1, 0xde, 0x7d, 0x85, 0x26, 0x68, 0xcb, 0x33, 0x90, 0x4d, 0xee, 0x16, 0xb5, 0xfb, 0x58, 0xa0, 0x3, 0x3c, 0x9f, 0x67, 0xc4, 0x8a, 0x29, 0xd1, 0x72, 0x76, 0xd5, 0x2d, 0x8e, 0xc0, 0x63, 0x9b, 0x38, 0x7, 0xa4, 0x5c, 0xff, 0xb1, 0x12, 0xea, 0x49, 0x94, 0x37, 0xcf, 0x6c, 0x22, 0x81, 0x79, 0xda, 0xe5, 0x46, 0xbe, 0x1d, 0x53, 0xf0, 0x8, 0xab, 0x43, 0xe0, 0x18, 0xbb, 0xf5, 0x56, 0xae, 0xd, 0x32, 0x91, 0x69, 0xca, 0x84, 0x27, 0xdf, 0x7c, 0xa1, 0x2, 0xfa, 0x59, 0x17, 0xb4, 0x4c, 0xef, 0xd0, 0x73, 0x8b, 0x28, 0x66, 0xc5, 0x3d, 0x9e, 0x9a, 0x39, 0xc1, 0x62, 0x2c, 0x8f, 0x77, 0xd4, 0xeb, 0x48, 0xb0, 0x13, 0x5d, 0xfe, 0x6, 0xa5, 0x78, 0xdb, 0x23, 0x80, 0xce, 0x6d, 0x95, 0x36, 0x9, 0xaa, 0x52, 0xf1, 0xbf, 0x1c, 0xe4, 0x47, 0xec, 0x4f, 0xb7, 0x14, 0x5a, 0xf9, 0x1, 0xa2, 0x9d, 0x3e, 0xc6, 0x65, 0x2b, 0x88, 0x70, 0xd3, 0xe, 0xad, 0x55, 0xf6, 0xb8, 0x1b, 0xe3, 0x40, 0x7f, 0xdc, 0x24, 0x87, 0xc9, 0x6a, 0x92, 0x31, 0x35, 0x96, 0x6e, 0xcd, 0x83, 0x20, 0xd8, 0x7b, 0x44, 0xe7, 0x1f, 0xbc, 0xf2, 0x51, 0xa9, 0xa, 0xd7, 0x74, 0x8c, 0x2f, 0x61, 0xc2, 0x3a, 0x99, 0xa6, 0x5, 0xfd, 0x5e, 0x10, 0xb3, 0x4b, 0xe8},
+ {0x0, 0xa4, 0x55, 0xf1, 0xaa, 0xe, 0xff, 0x5b, 0x49, 0xed, 0x1c, 0xb8, 0xe3, 0x47, 0xb6, 0x12, 0x92, 0x36, 0xc7, 0x63, 0x38, 0x9c, 0x6d, 0xc9, 0xdb, 0x7f, 0x8e, 0x2a, 0x71, 0xd5, 0x24, 0x80, 0x39, 0x9d, 0x6c, 0xc8, 0x93, 0x37, 0xc6, 0x62, 0x70, 0xd4, 0x25, 0x81, 0xda, 0x7e, 0x8f, 0x2b, 0xab, 0xf, 0xfe, 0x5a, 0x1, 0xa5, 0x54, 0xf0, 0xe2, 0x46, 0xb7, 0x13, 0x48, 0xec, 0x1d, 0xb9, 0x72, 0xd6, 0x27, 0x83, 0xd8, 0x7c, 0x8d, 0x29, 0x3b, 0x9f, 0x6e, 0xca, 0x91, 0x35, 0xc4, 0x60, 0xe0, 0x44, 0xb5, 0x11, 0x4a, 0xee, 0x1f, 0xbb, 0xa9, 0xd, 0xfc, 0x58, 0x3, 0xa7, 0x56, 0xf2, 0x4b, 0xef, 0x1e, 0xba, 0xe1, 0x45, 0xb4, 0x10, 0x2, 0xa6, 0x57, 0xf3, 0xa8, 0xc, 0xfd, 0x59, 0xd9, 0x7d, 0x8c, 0x28, 0x73, 0xd7, 0x26, 0x82, 0x90, 0x34, 0xc5, 0x61, 0x3a, 0x9e, 0x6f, 0xcb, 0xe4, 0x40, 0xb1, 0x15, 0x4e, 0xea, 0x1b, 0xbf, 0xad, 0x9, 0xf8, 0x5c, 0x7, 0xa3, 0x52, 0xf6, 0x76, 0xd2, 0x23, 0x87, 0xdc, 0x78, 0x89, 0x2d, 0x3f, 0x9b, 0x6a, 0xce, 0x95, 0x31, 0xc0, 0x64, 0xdd, 0x79, 0x88, 0x2c, 0x77, 0xd3, 0x22, 0x86, 0x94, 0x30, 0xc1, 0x65, 0x3e, 0x9a, 0x6b, 0xcf, 0x4f, 0xeb, 0x1a, 0xbe, 0xe5, 0x41, 0xb0, 0x14, 0x6, 0xa2, 0x53, 0xf7, 0xac, 0x8, 0xf9, 0x5d, 0x96, 0x32, 0xc3, 0x67, 0x3c, 0x98, 0x69, 0xcd, 0xdf, 0x7b, 0x8a, 0x2e, 0x75, 0xd1, 0x20, 0x84, 0x4, 0xa0, 0x51, 0xf5, 0xae, 0xa, 0xfb, 0x5f, 0x4d, 0xe9, 0x18, 0xbc, 0xe7, 0x43, 0xb2, 0x16, 0xaf, 0xb, 0xfa, 0x5e, 0x5, 0xa1, 0x50, 0xf4, 0xe6, 0x42, 0xb3, 0x17, 0x4c, 0xe8, 0x19, 0xbd, 0x3d, 0x99, 0x68, 0xcc, 0x97, 0x33, 0xc2, 0x66, 0x74, 0xd0, 0x21, 0x85, 0xde, 0x7a, 0x8b, 0x2f},
+ {0x0, 0xa5, 0x57, 0xf2, 0xae, 0xb, 0xf9, 0x5c, 0x41, 0xe4, 0x16, 0xb3, 0xef, 0x4a, 0xb8, 0x1d, 0x82, 0x27, 0xd5, 0x70, 0x2c, 0x89, 0x7b, 0xde, 0xc3, 0x66, 0x94, 0x31, 0x6d, 0xc8, 0x3a, 0x9f, 0x19, 0xbc, 0x4e, 0xeb, 0xb7, 0x12, 0xe0, 0x45, 0x58, 0xfd, 0xf, 0xaa, 0xf6, 0x53, 0xa1, 0x4, 0x9b, 0x3e, 0xcc, 0x69, 0x35, 0x90, 0x62, 0xc7, 0xda, 0x7f, 0x8d, 0x28, 0x74, 0xd1, 0x23, 0x86, 0x32, 0x97, 0x65, 0xc0, 0x9c, 0x39, 0xcb, 0x6e, 0x73, 0xd6, 0x24, 0x81, 0xdd, 0x78, 0x8a, 0x2f, 0xb0, 0x15, 0xe7, 0x42, 0x1e, 0xbb, 0x49, 0xec, 0xf1, 0x54, 0xa6, 0x3, 0x5f, 0xfa, 0x8, 0xad, 0x2b, 0x8e, 0x7c, 0xd9, 0x85, 0x20, 0xd2, 0x77, 0x6a, 0xcf, 0x3d, 0x98, 0xc4, 0x61, 0x93, 0x36, 0xa9, 0xc, 0xfe, 0x5b, 0x7, 0xa2, 0x50, 0xf5, 0xe8, 0x4d, 0xbf, 0x1a, 0x46, 0xe3, 0x11, 0xb4, 0x64, 0xc1, 0x33, 0x96, 0xca, 0x6f, 0x9d, 0x38, 0x25, 0x80, 0x72, 0xd7, 0x8b, 0x2e, 0xdc, 0x79, 0xe6, 0x43, 0xb1, 0x14, 0x48, 0xed, 0x1f, 0xba, 0xa7, 0x2, 0xf0, 0x55, 0x9, 0xac, 0x5e, 0xfb, 0x7d, 0xd8, 0x2a, 0x8f, 0xd3, 0x76, 0x84, 0x21, 0x3c, 0x99, 0x6b, 0xce, 0x92, 0x37, 0xc5, 0x60, 0xff, 0x5a, 0xa8, 0xd, 0x51, 0xf4, 0x6, 0xa3, 0xbe, 0x1b, 0xe9, 0x4c, 0x10, 0xb5, 0x47, 0xe2, 0x56, 0xf3, 0x1, 0xa4, 0xf8, 0x5d, 0xaf, 0xa, 0x17, 0xb2, 0x40, 0xe5, 0xb9, 0x1c, 0xee, 0x4b, 0xd4, 0x71, 0x83, 0x26, 0x7a, 0xdf, 0x2d, 0x88, 0x95, 0x30, 0xc2, 0x67, 0x3b, 0x9e, 0x6c, 0xc9, 0x4f, 0xea, 0x18, 0xbd, 0xe1, 0x44, 0xb6, 0x13, 0xe, 0xab, 0x59, 0xfc, 0xa0, 0x5, 0xf7, 0x52, 0xcd, 0x68, 0x9a, 0x3f, 0x63, 0xc6, 0x34, 0x91, 0x8c, 0x29, 0xdb, 0x7e, 0x22, 0x87, 0x75, 0xd0},
+ {0x0, 0xa6, 0x51, 0xf7, 0xa2, 0x4, 0xf3, 0x55, 0x59, 0xff, 0x8, 0xae, 0xfb, 0x5d, 0xaa, 0xc, 0xb2, 0x14, 0xe3, 0x45, 0x10, 0xb6, 0x41, 0xe7, 0xeb, 0x4d, 0xba, 0x1c, 0x49, 0xef, 0x18, 0xbe, 0x79, 0xdf, 0x28, 0x8e, 0xdb, 0x7d, 0x8a, 0x2c, 0x20, 0x86, 0x71, 0xd7, 0x82, 0x24, 0xd3, 0x75, 0xcb, 0x6d, 0x9a, 0x3c, 0x69, 0xcf, 0x38, 0x9e, 0x92, 0x34, 0xc3, 0x65, 0x30, 0x96, 0x61, 0xc7, 0xf2, 0x54, 0xa3, 0x5, 0x50, 0xf6, 0x1, 0xa7, 0xab, 0xd, 0xfa, 0x5c, 0x9, 0xaf, 0x58, 0xfe, 0x40, 0xe6, 0x11, 0xb7, 0xe2, 0x44, 0xb3, 0x15, 0x19, 0xbf, 0x48, 0xee, 0xbb, 0x1d, 0xea, 0x4c, 0x8b, 0x2d, 0xda, 0x7c, 0x29, 0x8f, 0x78, 0xde, 0xd2, 0x74, 0x83, 0x25, 0x70, 0xd6, 0x21, 0x87, 0x39, 0x9f, 0x68, 0xce, 0x9b, 0x3d, 0xca, 0x6c, 0x60, 0xc6, 0x31, 0x97, 0xc2, 0x64, 0x93, 0x35, 0xf9, 0x5f, 0xa8, 0xe, 0x5b, 0xfd, 0xa, 0xac, 0xa0, 0x6, 0xf1, 0x57, 0x2, 0xa4, 0x53, 0xf5, 0x4b, 0xed, 0x1a, 0xbc, 0xe9, 0x4f, 0xb8, 0x1e, 0x12, 0xb4, 0x43, 0xe5, 0xb0, 0x16, 0xe1, 0x47, 0x80, 0x26, 0xd1, 0x77, 0x22, 0x84, 0x73, 0xd5, 0xd9, 0x7f, 0x88, 0x2e, 0x7b, 0xdd, 0x2a, 0x8c, 0x32, 0x94, 0x63, 0xc5, 0x90, 0x36, 0xc1, 0x67, 0x6b, 0xcd, 0x3a, 0x9c, 0xc9, 0x6f, 0x98, 0x3e, 0xb, 0xad, 0x5a, 0xfc, 0xa9, 0xf, 0xf8, 0x5e, 0x52, 0xf4, 0x3, 0xa5, 0xf0, 0x56, 0xa1, 0x7, 0xb9, 0x1f, 0xe8, 0x4e, 0x1b, 0xbd, 0x4a, 0xec, 0xe0, 0x46, 0xb1, 0x17, 0x42, 0xe4, 0x13, 0xb5, 0x72, 0xd4, 0x23, 0x85, 0xd0, 0x76, 0x81, 0x27, 0x2b, 0x8d, 0x7a, 0xdc, 0x89, 0x2f, 0xd8, 0x7e, 0xc0, 0x66, 0x91, 0x37, 0x62, 0xc4, 0x33, 0x95, 0x99, 0x3f, 0xc8, 0x6e, 0x3b, 0x9d, 0x6a, 0xcc},
+ {0x0, 0xa7, 0x53, 0xf4, 0xa6, 0x1, 0xf5, 0x52, 0x51, 0xf6, 0x2, 0xa5, 0xf7, 0x50, 0xa4, 0x3, 0xa2, 0x5, 0xf1, 0x56, 0x4, 0xa3, 0x57, 0xf0, 0xf3, 0x54, 0xa0, 0x7, 0x55, 0xf2, 0x6, 0xa1, 0x59, 0xfe, 0xa, 0xad, 0xff, 0x58, 0xac, 0xb, 0x8, 0xaf, 0x5b, 0xfc, 0xae, 0x9, 0xfd, 0x5a, 0xfb, 0x5c, 0xa8, 0xf, 0x5d, 0xfa, 0xe, 0xa9, 0xaa, 0xd, 0xf9, 0x5e, 0xc, 0xab, 0x5f, 0xf8, 0xb2, 0x15, 0xe1, 0x46, 0x14, 0xb3, 0x47, 0xe0, 0xe3, 0x44, 0xb0, 0x17, 0x45, 0xe2, 0x16, 0xb1, 0x10, 0xb7, 0x43, 0xe4, 0xb6, 0x11, 0xe5, 0x42, 0x41, 0xe6, 0x12, 0xb5, 0xe7, 0x40, 0xb4, 0x13, 0xeb, 0x4c, 0xb8, 0x1f, 0x4d, 0xea, 0x1e, 0xb9, 0xba, 0x1d, 0xe9, 0x4e, 0x1c, 0xbb, 0x4f, 0xe8, 0x49, 0xee, 0x1a, 0xbd, 0xef, 0x48, 0xbc, 0x1b, 0x18, 0xbf, 0x4b, 0xec, 0xbe, 0x19, 0xed, 0x4a, 0x79, 0xde, 0x2a, 0x8d, 0xdf, 0x78, 0x8c, 0x2b, 0x28, 0x8f, 0x7b, 0xdc, 0x8e, 0x29, 0xdd, 0x7a, 0xdb, 0x7c, 0x88, 0x2f, 0x7d, 0xda, 0x2e, 0x89, 0x8a, 0x2d, 0xd9, 0x7e, 0x2c, 0x8b, 0x7f, 0xd8, 0x20, 0x87, 0x73, 0xd4, 0x86, 0x21, 0xd5, 0x72, 0x71, 0xd6, 0x22, 0x85, 0xd7, 0x70, 0x84, 0x23, 0x82, 0x25, 0xd1, 0x76, 0x24, 0x83, 0x77, 0xd0, 0xd3, 0x74, 0x80, 0x27, 0x75, 0xd2, 0x26, 0x81, 0xcb, 0x6c, 0x98, 0x3f, 0x6d, 0xca, 0x3e, 0x99, 0x9a, 0x3d, 0xc9, 0x6e, 0x3c, 0x9b, 0x6f, 0xc8, 0x69, 0xce, 0x3a, 0x9d, 0xcf, 0x68, 0x9c, 0x3b, 0x38, 0x9f, 0x6b, 0xcc, 0x9e, 0x39, 0xcd, 0x6a, 0x92, 0x35, 0xc1, 0x66, 0x34, 0x93, 0x67, 0xc0, 0xc3, 0x64, 0x90, 0x37, 0x65, 0xc2, 0x36, 0x91, 0x30, 0x97, 0x63, 0xc4, 0x96, 0x31, 0xc5, 0x62, 0x61, 0xc6, 0x32, 0x95, 0xc7, 0x60, 0x94, 0x33},
+ {0x0, 0xa8, 0x4d, 0xe5, 0x9a, 0x32, 0xd7, 0x7f, 0x29, 0x81, 0x64, 0xcc, 0xb3, 0x1b, 0xfe, 0x56, 0x52, 0xfa, 0x1f, 0xb7, 0xc8, 0x60, 0x85, 0x2d, 0x7b, 0xd3, 0x36, 0x9e, 0xe1, 0x49, 0xac, 0x4, 0xa4, 0xc, 0xe9, 0x41, 0x3e, 0x96, 0x73, 0xdb, 0x8d, 0x25, 0xc0, 0x68, 0x17, 0xbf, 0x5a, 0xf2, 0xf6, 0x5e, 0xbb, 0x13, 0x6c, 0xc4, 0x21, 0x89, 0xdf, 0x77, 0x92, 0x3a, 0x45, 0xed, 0x8, 0xa0, 0x55, 0xfd, 0x18, 0xb0, 0xcf, 0x67, 0x82, 0x2a, 0x7c, 0xd4, 0x31, 0x99, 0xe6, 0x4e, 0xab, 0x3, 0x7, 0xaf, 0x4a, 0xe2, 0x9d, 0x35, 0xd0, 0x78, 0x2e, 0x86, 0x63, 0xcb, 0xb4, 0x1c, 0xf9, 0x51, 0xf1, 0x59, 0xbc, 0x14, 0x6b, 0xc3, 0x26, 0x8e, 0xd8, 0x70, 0x95, 0x3d, 0x42, 0xea, 0xf, 0xa7, 0xa3, 0xb, 0xee, 0x46, 0x39, 0x91, 0x74, 0xdc, 0x8a, 0x22, 0xc7, 0x6f, 0x10, 0xb8, 0x5d, 0xf5, 0xaa, 0x2, 0xe7, 0x4f, 0x30, 0x98, 0x7d, 0xd5, 0x83, 0x2b, 0xce, 0x66, 0x19, 0xb1, 0x54, 0xfc, 0xf8, 0x50, 0xb5, 0x1d, 0x62, 0xca, 0x2f, 0x87, 0xd1, 0x79, 0x9c, 0x34, 0x4b, 0xe3, 0x6, 0xae, 0xe, 0xa6, 0x43, 0xeb, 0x94, 0x3c, 0xd9, 0x71, 0x27, 0x8f, 0x6a, 0xc2, 0xbd, 0x15, 0xf0, 0x58, 0x5c, 0xf4, 0x11, 0xb9, 0xc6, 0x6e, 0x8b, 0x23, 0x75, 0xdd, 0x38, 0x90, 0xef, 0x47, 0xa2, 0xa, 0xff, 0x57, 0xb2, 0x1a, 0x65, 0xcd, 0x28, 0x80, 0xd6, 0x7e, 0x9b, 0x33, 0x4c, 0xe4, 0x1, 0xa9, 0xad, 0x5, 0xe0, 0x48, 0x37, 0x9f, 0x7a, 0xd2, 0x84, 0x2c, 0xc9, 0x61, 0x1e, 0xb6, 0x53, 0xfb, 0x5b, 0xf3, 0x16, 0xbe, 0xc1, 0x69, 0x8c, 0x24, 0x72, 0xda, 0x3f, 0x97, 0xe8, 0x40, 0xa5, 0xd, 0x9, 0xa1, 0x44, 0xec, 0x93, 0x3b, 0xde, 0x76, 0x20, 0x88, 0x6d, 0xc5, 0xba, 0x12, 0xf7, 0x5f},
+ {0x0, 0xa9, 0x4f, 0xe6, 0x9e, 0x37, 0xd1, 0x78, 0x21, 0x88, 0x6e, 0xc7, 0xbf, 0x16, 0xf0, 0x59, 0x42, 0xeb, 0xd, 0xa4, 0xdc, 0x75, 0x93, 0x3a, 0x63, 0xca, 0x2c, 0x85, 0xfd, 0x54, 0xb2, 0x1b, 0x84, 0x2d, 0xcb, 0x62, 0x1a, 0xb3, 0x55, 0xfc, 0xa5, 0xc, 0xea, 0x43, 0x3b, 0x92, 0x74, 0xdd, 0xc6, 0x6f, 0x89, 0x20, 0x58, 0xf1, 0x17, 0xbe, 0xe7, 0x4e, 0xa8, 0x1, 0x79, 0xd0, 0x36, 0x9f, 0x15, 0xbc, 0x5a, 0xf3, 0x8b, 0x22, 0xc4, 0x6d, 0x34, 0x9d, 0x7b, 0xd2, 0xaa, 0x3, 0xe5, 0x4c, 0x57, 0xfe, 0x18, 0xb1, 0xc9, 0x60, 0x86, 0x2f, 0x76, 0xdf, 0x39, 0x90, 0xe8, 0x41, 0xa7, 0xe, 0x91, 0x38, 0xde, 0x77, 0xf, 0xa6, 0x40, 0xe9, 0xb0, 0x19, 0xff, 0x56, 0x2e, 0x87, 0x61, 0xc8, 0xd3, 0x7a, 0x9c, 0x35, 0x4d, 0xe4, 0x2, 0xab, 0xf2, 0x5b, 0xbd, 0x14, 0x6c, 0xc5, 0x23, 0x8a, 0x2a, 0x83, 0x65, 0xcc, 0xb4, 0x1d, 0xfb, 0x52, 0xb, 0xa2, 0x44, 0xed, 0x95, 0x3c, 0xda, 0x73, 0x68, 0xc1, 0x27, 0x8e, 0xf6, 0x5f, 0xb9, 0x10, 0x49, 0xe0, 0x6, 0xaf, 0xd7, 0x7e, 0x98, 0x31, 0xae, 0x7, 0xe1, 0x48, 0x30, 0x99, 0x7f, 0xd6, 0x8f, 0x26, 0xc0, 0x69, 0x11, 0xb8, 0x5e, 0xf7, 0xec, 0x45, 0xa3, 0xa, 0x72, 0xdb, 0x3d, 0x94, 0xcd, 0x64, 0x82, 0x2b, 0x53, 0xfa, 0x1c, 0xb5, 0x3f, 0x96, 0x70, 0xd9, 0xa1, 0x8, 0xee, 0x47, 0x1e, 0xb7, 0x51, 0xf8, 0x80, 0x29, 0xcf, 0x66, 0x7d, 0xd4, 0x32, 0x9b, 0xe3, 0x4a, 0xac, 0x5, 0x5c, 0xf5, 0x13, 0xba, 0xc2, 0x6b, 0x8d, 0x24, 0xbb, 0x12, 0xf4, 0x5d, 0x25, 0x8c, 0x6a, 0xc3, 0x9a, 0x33, 0xd5, 0x7c, 0x4, 0xad, 0x4b, 0xe2, 0xf9, 0x50, 0xb6, 0x1f, 0x67, 0xce, 0x28, 0x81, 0xd8, 0x71, 0x97, 0x3e, 0x46, 0xef, 0x9, 0xa0},
+ {0x0, 0xaa, 0x49, 0xe3, 0x92, 0x38, 0xdb, 0x71, 0x39, 0x93, 0x70, 0xda, 0xab, 0x1, 0xe2, 0x48, 0x72, 0xd8, 0x3b, 0x91, 0xe0, 0x4a, 0xa9, 0x3, 0x4b, 0xe1, 0x2, 0xa8, 0xd9, 0x73, 0x90, 0x3a, 0xe4, 0x4e, 0xad, 0x7, 0x76, 0xdc, 0x3f, 0x95, 0xdd, 0x77, 0x94, 0x3e, 0x4f, 0xe5, 0x6, 0xac, 0x96, 0x3c, 0xdf, 0x75, 0x4, 0xae, 0x4d, 0xe7, 0xaf, 0x5, 0xe6, 0x4c, 0x3d, 0x97, 0x74, 0xde, 0xd5, 0x7f, 0x9c, 0x36, 0x47, 0xed, 0xe, 0xa4, 0xec, 0x46, 0xa5, 0xf, 0x7e, 0xd4, 0x37, 0x9d, 0xa7, 0xd, 0xee, 0x44, 0x35, 0x9f, 0x7c, 0xd6, 0x9e, 0x34, 0xd7, 0x7d, 0xc, 0xa6, 0x45, 0xef, 0x31, 0x9b, 0x78, 0xd2, 0xa3, 0x9, 0xea, 0x40, 0x8, 0xa2, 0x41, 0xeb, 0x9a, 0x30, 0xd3, 0x79, 0x43, 0xe9, 0xa, 0xa0, 0xd1, 0x7b, 0x98, 0x32, 0x7a, 0xd0, 0x33, 0x99, 0xe8, 0x42, 0xa1, 0xb, 0xb7, 0x1d, 0xfe, 0x54, 0x25, 0x8f, 0x6c, 0xc6, 0x8e, 0x24, 0xc7, 0x6d, 0x1c, 0xb6, 0x55, 0xff, 0xc5, 0x6f, 0x8c, 0x26, 0x57, 0xfd, 0x1e, 0xb4, 0xfc, 0x56, 0xb5, 0x1f, 0x6e, 0xc4, 0x27, 0x8d, 0x53, 0xf9, 0x1a, 0xb0, 0xc1, 0x6b, 0x88, 0x22, 0x6a, 0xc0, 0x23, 0x89, 0xf8, 0x52, 0xb1, 0x1b, 0x21, 0x8b, 0x68, 0xc2, 0xb3, 0x19, 0xfa, 0x50, 0x18, 0xb2, 0x51, 0xfb, 0x8a, 0x20, 0xc3, 0x69, 0x62, 0xc8, 0x2b, 0x81, 0xf0, 0x5a, 0xb9, 0x13, 0x5b, 0xf1, 0x12, 0xb8, 0xc9, 0x63, 0x80, 0x2a, 0x10, 0xba, 0x59, 0xf3, 0x82, 0x28, 0xcb, 0x61, 0x29, 0x83, 0x60, 0xca, 0xbb, 0x11, 0xf2, 0x58, 0x86, 0x2c, 0xcf, 0x65, 0x14, 0xbe, 0x5d, 0xf7, 0xbf, 0x15, 0xf6, 0x5c, 0x2d, 0x87, 0x64, 0xce, 0xf4, 0x5e, 0xbd, 0x17, 0x66, 0xcc, 0x2f, 0x85, 0xcd, 0x67, 0x84, 0x2e, 0x5f, 0xf5, 0x16, 0xbc},
+ {0x0, 0xab, 0x4b, 0xe0, 0x96, 0x3d, 0xdd, 0x76, 0x31, 0x9a, 0x7a, 0xd1, 0xa7, 0xc, 0xec, 0x47, 0x62, 0xc9, 0x29, 0x82, 0xf4, 0x5f, 0xbf, 0x14, 0x53, 0xf8, 0x18, 0xb3, 0xc5, 0x6e, 0x8e, 0x25, 0xc4, 0x6f, 0x8f, 0x24, 0x52, 0xf9, 0x19, 0xb2, 0xf5, 0x5e, 0xbe, 0x15, 0x63, 0xc8, 0x28, 0x83, 0xa6, 0xd, 0xed, 0x46, 0x30, 0x9b, 0x7b, 0xd0, 0x97, 0x3c, 0xdc, 0x77, 0x1, 0xaa, 0x4a, 0xe1, 0x95, 0x3e, 0xde, 0x75, 0x3, 0xa8, 0x48, 0xe3, 0xa4, 0xf, 0xef, 0x44, 0x32, 0x99, 0x79, 0xd2, 0xf7, 0x5c, 0xbc, 0x17, 0x61, 0xca, 0x2a, 0x81, 0xc6, 0x6d, 0x8d, 0x26, 0x50, 0xfb, 0x1b, 0xb0, 0x51, 0xfa, 0x1a, 0xb1, 0xc7, 0x6c, 0x8c, 0x27, 0x60, 0xcb, 0x2b, 0x80, 0xf6, 0x5d, 0xbd, 0x16, 0x33, 0x98, 0x78, 0xd3, 0xa5, 0xe, 0xee, 0x45, 0x2, 0xa9, 0x49, 0xe2, 0x94, 0x3f, 0xdf, 0x74, 0x37, 0x9c, 0x7c, 0xd7, 0xa1, 0xa, 0xea, 0x41, 0x6, 0xad, 0x4d, 0xe6, 0x90, 0x3b, 0xdb, 0x70, 0x55, 0xfe, 0x1e, 0xb5, 0xc3, 0x68, 0x88, 0x23, 0x64, 0xcf, 0x2f, 0x84, 0xf2, 0x59, 0xb9, 0x12, 0xf3, 0x58, 0xb8, 0x13, 0x65, 0xce, 0x2e, 0x85, 0xc2, 0x69, 0x89, 0x22, 0x54, 0xff, 0x1f, 0xb4, 0x91, 0x3a, 0xda, 0x71, 0x7, 0xac, 0x4c, 0xe7, 0xa0, 0xb, 0xeb, 0x40, 0x36, 0x9d, 0x7d, 0xd6, 0xa2, 0x9, 0xe9, 0x42, 0x34, 0x9f, 0x7f, 0xd4, 0x93, 0x38, 0xd8, 0x73, 0x5, 0xae, 0x4e, 0xe5, 0xc0, 0x6b, 0x8b, 0x20, 0x56, 0xfd, 0x1d, 0xb6, 0xf1, 0x5a, 0xba, 0x11, 0x67, 0xcc, 0x2c, 0x87, 0x66, 0xcd, 0x2d, 0x86, 0xf0, 0x5b, 0xbb, 0x10, 0x57, 0xfc, 0x1c, 0xb7, 0xc1, 0x6a, 0x8a, 0x21, 0x4, 0xaf, 0x4f, 0xe4, 0x92, 0x39, 0xd9, 0x72, 0x35, 0x9e, 0x7e, 0xd5, 0xa3, 0x8, 0xe8, 0x43},
+ {0x0, 0xac, 0x45, 0xe9, 0x8a, 0x26, 0xcf, 0x63, 0x9, 0xa5, 0x4c, 0xe0, 0x83, 0x2f, 0xc6, 0x6a, 0x12, 0xbe, 0x57, 0xfb, 0x98, 0x34, 0xdd, 0x71, 0x1b, 0xb7, 0x5e, 0xf2, 0x91, 0x3d, 0xd4, 0x78, 0x24, 0x88, 0x61, 0xcd, 0xae, 0x2, 0xeb, 0x47, 0x2d, 0x81, 0x68, 0xc4, 0xa7, 0xb, 0xe2, 0x4e, 0x36, 0x9a, 0x73, 0xdf, 0xbc, 0x10, 0xf9, 0x55, 0x3f, 0x93, 0x7a, 0xd6, 0xb5, 0x19, 0xf0, 0x5c, 0x48, 0xe4, 0xd, 0xa1, 0xc2, 0x6e, 0x87, 0x2b, 0x41, 0xed, 0x4, 0xa8, 0xcb, 0x67, 0x8e, 0x22, 0x5a, 0xf6, 0x1f, 0xb3, 0xd0, 0x7c, 0x95, 0x39, 0x53, 0xff, 0x16, 0xba, 0xd9, 0x75, 0x9c, 0x30, 0x6c, 0xc0, 0x29, 0x85, 0xe6, 0x4a, 0xa3, 0xf, 0x65, 0xc9, 0x20, 0x8c, 0xef, 0x43, 0xaa, 0x6, 0x7e, 0xd2, 0x3b, 0x97, 0xf4, 0x58, 0xb1, 0x1d, 0x77, 0xdb, 0x32, 0x9e, 0xfd, 0x51, 0xb8, 0x14, 0x90, 0x3c, 0xd5, 0x79, 0x1a, 0xb6, 0x5f, 0xf3, 0x99, 0x35, 0xdc, 0x70, 0x13, 0xbf, 0x56, 0xfa, 0x82, 0x2e, 0xc7, 0x6b, 0x8, 0xa4, 0x4d, 0xe1, 0x8b, 0x27, 0xce, 0x62, 0x1, 0xad, 0x44, 0xe8, 0xb4, 0x18, 0xf1, 0x5d, 0x3e, 0x92, 0x7b, 0xd7, 0xbd, 0x11, 0xf8, 0x54, 0x37, 0x9b, 0x72, 0xde, 0xa6, 0xa, 0xe3, 0x4f, 0x2c, 0x80, 0x69, 0xc5, 0xaf, 0x3, 0xea, 0x46, 0x25, 0x89, 0x60, 0xcc, 0xd8, 0x74, 0x9d, 0x31, 0x52, 0xfe, 0x17, 0xbb, 0xd1, 0x7d, 0x94, 0x38, 0x5b, 0xf7, 0x1e, 0xb2, 0xca, 0x66, 0x8f, 0x23, 0x40, 0xec, 0x5, 0xa9, 0xc3, 0x6f, 0x86, 0x2a, 0x49, 0xe5, 0xc, 0xa0, 0xfc, 0x50, 0xb9, 0x15, 0x76, 0xda, 0x33, 0x9f, 0xf5, 0x59, 0xb0, 0x1c, 0x7f, 0xd3, 0x3a, 0x96, 0xee, 0x42, 0xab, 0x7, 0x64, 0xc8, 0x21, 0x8d, 0xe7, 0x4b, 0xa2, 0xe, 0x6d, 0xc1, 0x28, 0x84},
+ {0x0, 0xad, 0x47, 0xea, 0x8e, 0x23, 0xc9, 0x64, 0x1, 0xac, 0x46, 0xeb, 0x8f, 0x22, 0xc8, 0x65, 0x2, 0xaf, 0x45, 0xe8, 0x8c, 0x21, 0xcb, 0x66, 0x3, 0xae, 0x44, 0xe9, 0x8d, 0x20, 0xca, 0x67, 0x4, 0xa9, 0x43, 0xee, 0x8a, 0x27, 0xcd, 0x60, 0x5, 0xa8, 0x42, 0xef, 0x8b, 0x26, 0xcc, 0x61, 0x6, 0xab, 0x41, 0xec, 0x88, 0x25, 0xcf, 0x62, 0x7, 0xaa, 0x40, 0xed, 0x89, 0x24, 0xce, 0x63, 0x8, 0xa5, 0x4f, 0xe2, 0x86, 0x2b, 0xc1, 0x6c, 0x9, 0xa4, 0x4e, 0xe3, 0x87, 0x2a, 0xc0, 0x6d, 0xa, 0xa7, 0x4d, 0xe0, 0x84, 0x29, 0xc3, 0x6e, 0xb, 0xa6, 0x4c, 0xe1, 0x85, 0x28, 0xc2, 0x6f, 0xc, 0xa1, 0x4b, 0xe6, 0x82, 0x2f, 0xc5, 0x68, 0xd, 0xa0, 0x4a, 0xe7, 0x83, 0x2e, 0xc4, 0x69, 0xe, 0xa3, 0x49, 0xe4, 0x80, 0x2d, 0xc7, 0x6a, 0xf, 0xa2, 0x48, 0xe5, 0x81, 0x2c, 0xc6, 0x6b, 0x10, 0xbd, 0x57, 0xfa, 0x9e, 0x33, 0xd9, 0x74, 0x11, 0xbc, 0x56, 0xfb, 0x9f, 0x32, 0xd8, 0x75, 0x12, 0xbf, 0x55, 0xf8, 0x9c, 0x31, 0xdb, 0x76, 0x13, 0xbe, 0x54, 0xf9, 0x9d, 0x30, 0xda, 0x77, 0x14, 0xb9, 0x53, 0xfe, 0x9a, 0x37, 0xdd, 0x70, 0x15, 0xb8, 0x52, 0xff, 0x9b, 0x36, 0xdc, 0x71, 0x16, 0xbb, 0x51, 0xfc, 0x98, 0x35, 0xdf, 0x72, 0x17, 0xba, 0x50, 0xfd, 0x99, 0x34, 0xde, 0x73, 0x18, 0xb5, 0x5f, 0xf2, 0x96, 0x3b, 0xd1, 0x7c, 0x19, 0xb4, 0x5e, 0xf3, 0x97, 0x3a, 0xd0, 0x7d, 0x1a, 0xb7, 0x5d, 0xf0, 0x94, 0x39, 0xd3, 0x7e, 0x1b, 0xb6, 0x5c, 0xf1, 0x95, 0x38, 0xd2, 0x7f, 0x1c, 0xb1, 0x5b, 0xf6, 0x92, 0x3f, 0xd5, 0x78, 0x1d, 0xb0, 0x5a, 0xf7, 0x93, 0x3e, 0xd4, 0x79, 0x1e, 0xb3, 0x59, 0xf4, 0x90, 0x3d, 0xd7, 0x7a, 0x1f, 0xb2, 0x58, 0xf5, 0x91, 0x3c, 0xd6, 0x7b},
+ {0x0, 0xae, 0x41, 0xef, 0x82, 0x2c, 0xc3, 0x6d, 0x19, 0xb7, 0x58, 0xf6, 0x9b, 0x35, 0xda, 0x74, 0x32, 0x9c, 0x73, 0xdd, 0xb0, 0x1e, 0xf1, 0x5f, 0x2b, 0x85, 0x6a, 0xc4, 0xa9, 0x7, 0xe8, 0x46, 0x64, 0xca, 0x25, 0x8b, 0xe6, 0x48, 0xa7, 0x9, 0x7d, 0xd3, 0x3c, 0x92, 0xff, 0x51, 0xbe, 0x10, 0x56, 0xf8, 0x17, 0xb9, 0xd4, 0x7a, 0x95, 0x3b, 0x4f, 0xe1, 0xe, 0xa0, 0xcd, 0x63, 0x8c, 0x22, 0xc8, 0x66, 0x89, 0x27, 0x4a, 0xe4, 0xb, 0xa5, 0xd1, 0x7f, 0x90, 0x3e, 0x53, 0xfd, 0x12, 0xbc, 0xfa, 0x54, 0xbb, 0x15, 0x78, 0xd6, 0x39, 0x97, 0xe3, 0x4d, 0xa2, 0xc, 0x61, 0xcf, 0x20, 0x8e, 0xac, 0x2, 0xed, 0x43, 0x2e, 0x80, 0x6f, 0xc1, 0xb5, 0x1b, 0xf4, 0x5a, 0x37, 0x99, 0x76, 0xd8, 0x9e, 0x30, 0xdf, 0x71, 0x1c, 0xb2, 0x5d, 0xf3, 0x87, 0x29, 0xc6, 0x68, 0x5, 0xab, 0x44, 0xea, 0x8d, 0x23, 0xcc, 0x62, 0xf, 0xa1, 0x4e, 0xe0, 0x94, 0x3a, 0xd5, 0x7b, 0x16, 0xb8, 0x57, 0xf9, 0xbf, 0x11, 0xfe, 0x50, 0x3d, 0x93, 0x7c, 0xd2, 0xa6, 0x8, 0xe7, 0x49, 0x24, 0x8a, 0x65, 0xcb, 0xe9, 0x47, 0xa8, 0x6, 0x6b, 0xc5, 0x2a, 0x84, 0xf0, 0x5e, 0xb1, 0x1f, 0x72, 0xdc, 0x33, 0x9d, 0xdb, 0x75, 0x9a, 0x34, 0x59, 0xf7, 0x18, 0xb6, 0xc2, 0x6c, 0x83, 0x2d, 0x40, 0xee, 0x1, 0xaf, 0x45, 0xeb, 0x4, 0xaa, 0xc7, 0x69, 0x86, 0x28, 0x5c, 0xf2, 0x1d, 0xb3, 0xde, 0x70, 0x9f, 0x31, 0x77, 0xd9, 0x36, 0x98, 0xf5, 0x5b, 0xb4, 0x1a, 0x6e, 0xc0, 0x2f, 0x81, 0xec, 0x42, 0xad, 0x3, 0x21, 0x8f, 0x60, 0xce, 0xa3, 0xd, 0xe2, 0x4c, 0x38, 0x96, 0x79, 0xd7, 0xba, 0x14, 0xfb, 0x55, 0x13, 0xbd, 0x52, 0xfc, 0x91, 0x3f, 0xd0, 0x7e, 0xa, 0xa4, 0x4b, 0xe5, 0x88, 0x26, 0xc9, 0x67},
+ {0x0, 0xaf, 0x43, 0xec, 0x86, 0x29, 0xc5, 0x6a, 0x11, 0xbe, 0x52, 0xfd, 0x97, 0x38, 0xd4, 0x7b, 0x22, 0x8d, 0x61, 0xce, 0xa4, 0xb, 0xe7, 0x48, 0x33, 0x9c, 0x70, 0xdf, 0xb5, 0x1a, 0xf6, 0x59, 0x44, 0xeb, 0x7, 0xa8, 0xc2, 0x6d, 0x81, 0x2e, 0x55, 0xfa, 0x16, 0xb9, 0xd3, 0x7c, 0x90, 0x3f, 0x66, 0xc9, 0x25, 0x8a, 0xe0, 0x4f, 0xa3, 0xc, 0x77, 0xd8, 0x34, 0x9b, 0xf1, 0x5e, 0xb2, 0x1d, 0x88, 0x27, 0xcb, 0x64, 0xe, 0xa1, 0x4d, 0xe2, 0x99, 0x36, 0xda, 0x75, 0x1f, 0xb0, 0x5c, 0xf3, 0xaa, 0x5, 0xe9, 0x46, 0x2c, 0x83, 0x6f, 0xc0, 0xbb, 0x14, 0xf8, 0x57, 0x3d, 0x92, 0x7e, 0xd1, 0xcc, 0x63, 0x8f, 0x20, 0x4a, 0xe5, 0x9, 0xa6, 0xdd, 0x72, 0x9e, 0x31, 0x5b, 0xf4, 0x18, 0xb7, 0xee, 0x41, 0xad, 0x2, 0x68, 0xc7, 0x2b, 0x84, 0xff, 0x50, 0xbc, 0x13, 0x79, 0xd6, 0x3a, 0x95, 0xd, 0xa2, 0x4e, 0xe1, 0x8b, 0x24, 0xc8, 0x67, 0x1c, 0xb3, 0x5f, 0xf0, 0x9a, 0x35, 0xd9, 0x76, 0x2f, 0x80, 0x6c, 0xc3, 0xa9, 0x6, 0xea, 0x45, 0x3e, 0x91, 0x7d, 0xd2, 0xb8, 0x17, 0xfb, 0x54, 0x49, 0xe6, 0xa, 0xa5, 0xcf, 0x60, 0x8c, 0x23, 0x58, 0xf7, 0x1b, 0xb4, 0xde, 0x71, 0x9d, 0x32, 0x6b, 0xc4, 0x28, 0x87, 0xed, 0x42, 0xae, 0x1, 0x7a, 0xd5, 0x39, 0x96, 0xfc, 0x53, 0xbf, 0x10, 0x85, 0x2a, 0xc6, 0x69, 0x3, 0xac, 0x40, 0xef, 0x94, 0x3b, 0xd7, 0x78, 0x12, 0xbd, 0x51, 0xfe, 0xa7, 0x8, 0xe4, 0x4b, 0x21, 0x8e, 0x62, 0xcd, 0xb6, 0x19, 0xf5, 0x5a, 0x30, 0x9f, 0x73, 0xdc, 0xc1, 0x6e, 0x82, 0x2d, 0x47, 0xe8, 0x4, 0xab, 0xd0, 0x7f, 0x93, 0x3c, 0x56, 0xf9, 0x15, 0xba, 0xe3, 0x4c, 0xa0, 0xf, 0x65, 0xca, 0x26, 0x89, 0xf2, 0x5d, 0xb1, 0x1e, 0x74, 0xdb, 0x37, 0x98},
+ {0x0, 0xb0, 0x7d, 0xcd, 0xfa, 0x4a, 0x87, 0x37, 0xe9, 0x59, 0x94, 0x24, 0x13, 0xa3, 0x6e, 0xde, 0xcf, 0x7f, 0xb2, 0x2, 0x35, 0x85, 0x48, 0xf8, 0x26, 0x96, 0x5b, 0xeb, 0xdc, 0x6c, 0xa1, 0x11, 0x83, 0x33, 0xfe, 0x4e, 0x79, 0xc9, 0x4, 0xb4, 0x6a, 0xda, 0x17, 0xa7, 0x90, 0x20, 0xed, 0x5d, 0x4c, 0xfc, 0x31, 0x81, 0xb6, 0x6, 0xcb, 0x7b, 0xa5, 0x15, 0xd8, 0x68, 0x5f, 0xef, 0x22, 0x92, 0x1b, 0xab, 0x66, 0xd6, 0xe1, 0x51, 0x9c, 0x2c, 0xf2, 0x42, 0x8f, 0x3f, 0x8, 0xb8, 0x75, 0xc5, 0xd4, 0x64, 0xa9, 0x19, 0x2e, 0x9e, 0x53, 0xe3, 0x3d, 0x8d, 0x40, 0xf0, 0xc7, 0x77, 0xba, 0xa, 0x98, 0x28, 0xe5, 0x55, 0x62, 0xd2, 0x1f, 0xaf, 0x71, 0xc1, 0xc, 0xbc, 0x8b, 0x3b, 0xf6, 0x46, 0x57, 0xe7, 0x2a, 0x9a, 0xad, 0x1d, 0xd0, 0x60, 0xbe, 0xe, 0xc3, 0x73, 0x44, 0xf4, 0x39, 0x89, 0x36, 0x86, 0x4b, 0xfb, 0xcc, 0x7c, 0xb1, 0x1, 0xdf, 0x6f, 0xa2, 0x12, 0x25, 0x95, 0x58, 0xe8, 0xf9, 0x49, 0x84, 0x34, 0x3, 0xb3, 0x7e, 0xce, 0x10, 0xa0, 0x6d, 0xdd, 0xea, 0x5a, 0x97, 0x27, 0xb5, 0x5, 0xc8, 0x78, 0x4f, 0xff, 0x32, 0x82, 0x5c, 0xec, 0x21, 0x91, 0xa6, 0x16, 0xdb, 0x6b, 0x7a, 0xca, 0x7, 0xb7, 0x80, 0x30, 0xfd, 0x4d, 0x93, 0x23, 0xee, 0x5e, 0x69, 0xd9, 0x14, 0xa4, 0x2d, 0x9d, 0x50, 0xe0, 0xd7, 0x67, 0xaa, 0x1a, 0xc4, 0x74, 0xb9, 0x9, 0x3e, 0x8e, 0x43, 0xf3, 0xe2, 0x52, 0x9f, 0x2f, 0x18, 0xa8, 0x65, 0xd5, 0xb, 0xbb, 0x76, 0xc6, 0xf1, 0x41, 0x8c, 0x3c, 0xae, 0x1e, 0xd3, 0x63, 0x54, 0xe4, 0x29, 0x99, 0x47, 0xf7, 0x3a, 0x8a, 0xbd, 0xd, 0xc0, 0x70, 0x61, 0xd1, 0x1c, 0xac, 0x9b, 0x2b, 0xe6, 0x56, 0x88, 0x38, 0xf5, 0x45, 0x72, 0xc2, 0xf, 0xbf},
+ {0x0, 0xb1, 0x7f, 0xce, 0xfe, 0x4f, 0x81, 0x30, 0xe1, 0x50, 0x9e, 0x2f, 0x1f, 0xae, 0x60, 0xd1, 0xdf, 0x6e, 0xa0, 0x11, 0x21, 0x90, 0x5e, 0xef, 0x3e, 0x8f, 0x41, 0xf0, 0xc0, 0x71, 0xbf, 0xe, 0xa3, 0x12, 0xdc, 0x6d, 0x5d, 0xec, 0x22, 0x93, 0x42, 0xf3, 0x3d, 0x8c, 0xbc, 0xd, 0xc3, 0x72, 0x7c, 0xcd, 0x3, 0xb2, 0x82, 0x33, 0xfd, 0x4c, 0x9d, 0x2c, 0xe2, 0x53, 0x63, 0xd2, 0x1c, 0xad, 0x5b, 0xea, 0x24, 0x95, 0xa5, 0x14, 0xda, 0x6b, 0xba, 0xb, 0xc5, 0x74, 0x44, 0xf5, 0x3b, 0x8a, 0x84, 0x35, 0xfb, 0x4a, 0x7a, 0xcb, 0x5, 0xb4, 0x65, 0xd4, 0x1a, 0xab, 0x9b, 0x2a, 0xe4, 0x55, 0xf8, 0x49, 0x87, 0x36, 0x6, 0xb7, 0x79, 0xc8, 0x19, 0xa8, 0x66, 0xd7, 0xe7, 0x56, 0x98, 0x29, 0x27, 0x96, 0x58, 0xe9, 0xd9, 0x68, 0xa6, 0x17, 0xc6, 0x77, 0xb9, 0x8, 0x38, 0x89, 0x47, 0xf6, 0xb6, 0x7, 0xc9, 0x78, 0x48, 0xf9, 0x37, 0x86, 0x57, 0xe6, 0x28, 0x99, 0xa9, 0x18, 0xd6, 0x67, 0x69, 0xd8, 0x16, 0xa7, 0x97, 0x26, 0xe8, 0x59, 0x88, 0x39, 0xf7, 0x46, 0x76, 0xc7, 0x9, 0xb8, 0x15, 0xa4, 0x6a, 0xdb, 0xeb, 0x5a, 0x94, 0x25, 0xf4, 0x45, 0x8b, 0x3a, 0xa, 0xbb, 0x75, 0xc4, 0xca, 0x7b, 0xb5, 0x4, 0x34, 0x85, 0x4b, 0xfa, 0x2b, 0x9a, 0x54, 0xe5, 0xd5, 0x64, 0xaa, 0x1b, 0xed, 0x5c, 0x92, 0x23, 0x13, 0xa2, 0x6c, 0xdd, 0xc, 0xbd, 0x73, 0xc2, 0xf2, 0x43, 0x8d, 0x3c, 0x32, 0x83, 0x4d, 0xfc, 0xcc, 0x7d, 0xb3, 0x2, 0xd3, 0x62, 0xac, 0x1d, 0x2d, 0x9c, 0x52, 0xe3, 0x4e, 0xff, 0x31, 0x80, 0xb0, 0x1, 0xcf, 0x7e, 0xaf, 0x1e, 0xd0, 0x61, 0x51, 0xe0, 0x2e, 0x9f, 0x91, 0x20, 0xee, 0x5f, 0x6f, 0xde, 0x10, 0xa1, 0x70, 0xc1, 0xf, 0xbe, 0x8e, 0x3f, 0xf1, 0x40},
+ {0x0, 0xb2, 0x79, 0xcb, 0xf2, 0x40, 0x8b, 0x39, 0xf9, 0x4b, 0x80, 0x32, 0xb, 0xb9, 0x72, 0xc0, 0xef, 0x5d, 0x96, 0x24, 0x1d, 0xaf, 0x64, 0xd6, 0x16, 0xa4, 0x6f, 0xdd, 0xe4, 0x56, 0x9d, 0x2f, 0xc3, 0x71, 0xba, 0x8, 0x31, 0x83, 0x48, 0xfa, 0x3a, 0x88, 0x43, 0xf1, 0xc8, 0x7a, 0xb1, 0x3, 0x2c, 0x9e, 0x55, 0xe7, 0xde, 0x6c, 0xa7, 0x15, 0xd5, 0x67, 0xac, 0x1e, 0x27, 0x95, 0x5e, 0xec, 0x9b, 0x29, 0xe2, 0x50, 0x69, 0xdb, 0x10, 0xa2, 0x62, 0xd0, 0x1b, 0xa9, 0x90, 0x22, 0xe9, 0x5b, 0x74, 0xc6, 0xd, 0xbf, 0x86, 0x34, 0xff, 0x4d, 0x8d, 0x3f, 0xf4, 0x46, 0x7f, 0xcd, 0x6, 0xb4, 0x58, 0xea, 0x21, 0x93, 0xaa, 0x18, 0xd3, 0x61, 0xa1, 0x13, 0xd8, 0x6a, 0x53, 0xe1, 0x2a, 0x98, 0xb7, 0x5, 0xce, 0x7c, 0x45, 0xf7, 0x3c, 0x8e, 0x4e, 0xfc, 0x37, 0x85, 0xbc, 0xe, 0xc5, 0x77, 0x2b, 0x99, 0x52, 0xe0, 0xd9, 0x6b, 0xa0, 0x12, 0xd2, 0x60, 0xab, 0x19, 0x20, 0x92, 0x59, 0xeb, 0xc4, 0x76, 0xbd, 0xf, 0x36, 0x84, 0x4f, 0xfd, 0x3d, 0x8f, 0x44, 0xf6, 0xcf, 0x7d, 0xb6, 0x4, 0xe8, 0x5a, 0x91, 0x23, 0x1a, 0xa8, 0x63, 0xd1, 0x11, 0xa3, 0x68, 0xda, 0xe3, 0x51, 0x9a, 0x28, 0x7, 0xb5, 0x7e, 0xcc, 0xf5, 0x47, 0x8c, 0x3e, 0xfe, 0x4c, 0x87, 0x35, 0xc, 0xbe, 0x75, 0xc7, 0xb0, 0x2, 0xc9, 0x7b, 0x42, 0xf0, 0x3b, 0x89, 0x49, 0xfb, 0x30, 0x82, 0xbb, 0x9, 0xc2, 0x70, 0x5f, 0xed, 0x26, 0x94, 0xad, 0x1f, 0xd4, 0x66, 0xa6, 0x14, 0xdf, 0x6d, 0x54, 0xe6, 0x2d, 0x9f, 0x73, 0xc1, 0xa, 0xb8, 0x81, 0x33, 0xf8, 0x4a, 0x8a, 0x38, 0xf3, 0x41, 0x78, 0xca, 0x1, 0xb3, 0x9c, 0x2e, 0xe5, 0x57, 0x6e, 0xdc, 0x17, 0xa5, 0x65, 0xd7, 0x1c, 0xae, 0x97, 0x25, 0xee, 0x5c},
+ {0x0, 0xb3, 0x7b, 0xc8, 0xf6, 0x45, 0x8d, 0x3e, 0xf1, 0x42, 0x8a, 0x39, 0x7, 0xb4, 0x7c, 0xcf, 0xff, 0x4c, 0x84, 0x37, 0x9, 0xba, 0x72, 0xc1, 0xe, 0xbd, 0x75, 0xc6, 0xf8, 0x4b, 0x83, 0x30, 0xe3, 0x50, 0x98, 0x2b, 0x15, 0xa6, 0x6e, 0xdd, 0x12, 0xa1, 0x69, 0xda, 0xe4, 0x57, 0x9f, 0x2c, 0x1c, 0xaf, 0x67, 0xd4, 0xea, 0x59, 0x91, 0x22, 0xed, 0x5e, 0x96, 0x25, 0x1b, 0xa8, 0x60, 0xd3, 0xdb, 0x68, 0xa0, 0x13, 0x2d, 0x9e, 0x56, 0xe5, 0x2a, 0x99, 0x51, 0xe2, 0xdc, 0x6f, 0xa7, 0x14, 0x24, 0x97, 0x5f, 0xec, 0xd2, 0x61, 0xa9, 0x1a, 0xd5, 0x66, 0xae, 0x1d, 0x23, 0x90, 0x58, 0xeb, 0x38, 0x8b, 0x43, 0xf0, 0xce, 0x7d, 0xb5, 0x6, 0xc9, 0x7a, 0xb2, 0x1, 0x3f, 0x8c, 0x44, 0xf7, 0xc7, 0x74, 0xbc, 0xf, 0x31, 0x82, 0x4a, 0xf9, 0x36, 0x85, 0x4d, 0xfe, 0xc0, 0x73, 0xbb, 0x8, 0xab, 0x18, 0xd0, 0x63, 0x5d, 0xee, 0x26, 0x95, 0x5a, 0xe9, 0x21, 0x92, 0xac, 0x1f, 0xd7, 0x64, 0x54, 0xe7, 0x2f, 0x9c, 0xa2, 0x11, 0xd9, 0x6a, 0xa5, 0x16, 0xde, 0x6d, 0x53, 0xe0, 0x28, 0x9b, 0x48, 0xfb, 0x33, 0x80, 0xbe, 0xd, 0xc5, 0x76, 0xb9, 0xa, 0xc2, 0x71, 0x4f, 0xfc, 0x34, 0x87, 0xb7, 0x4, 0xcc, 0x7f, 0x41, 0xf2, 0x3a, 0x89, 0x46, 0xf5, 0x3d, 0x8e, 0xb0, 0x3, 0xcb, 0x78, 0x70, 0xc3, 0xb, 0xb8, 0x86, 0x35, 0xfd, 0x4e, 0x81, 0x32, 0xfa, 0x49, 0x77, 0xc4, 0xc, 0xbf, 0x8f, 0x3c, 0xf4, 0x47, 0x79, 0xca, 0x2, 0xb1, 0x7e, 0xcd, 0x5, 0xb6, 0x88, 0x3b, 0xf3, 0x40, 0x93, 0x20, 0xe8, 0x5b, 0x65, 0xd6, 0x1e, 0xad, 0x62, 0xd1, 0x19, 0xaa, 0x94, 0x27, 0xef, 0x5c, 0x6c, 0xdf, 0x17, 0xa4, 0x9a, 0x29, 0xe1, 0x52, 0x9d, 0x2e, 0xe6, 0x55, 0x6b, 0xd8, 0x10, 0xa3},
+ {0x0, 0xb4, 0x75, 0xc1, 0xea, 0x5e, 0x9f, 0x2b, 0xc9, 0x7d, 0xbc, 0x8, 0x23, 0x97, 0x56, 0xe2, 0x8f, 0x3b, 0xfa, 0x4e, 0x65, 0xd1, 0x10, 0xa4, 0x46, 0xf2, 0x33, 0x87, 0xac, 0x18, 0xd9, 0x6d, 0x3, 0xb7, 0x76, 0xc2, 0xe9, 0x5d, 0x9c, 0x28, 0xca, 0x7e, 0xbf, 0xb, 0x20, 0x94, 0x55, 0xe1, 0x8c, 0x38, 0xf9, 0x4d, 0x66, 0xd2, 0x13, 0xa7, 0x45, 0xf1, 0x30, 0x84, 0xaf, 0x1b, 0xda, 0x6e, 0x6, 0xb2, 0x73, 0xc7, 0xec, 0x58, 0x99, 0x2d, 0xcf, 0x7b, 0xba, 0xe, 0x25, 0x91, 0x50, 0xe4, 0x89, 0x3d, 0xfc, 0x48, 0x63, 0xd7, 0x16, 0xa2, 0x40, 0xf4, 0x35, 0x81, 0xaa, 0x1e, 0xdf, 0x6b, 0x5, 0xb1, 0x70, 0xc4, 0xef, 0x5b, 0x9a, 0x2e, 0xcc, 0x78, 0xb9, 0xd, 0x26, 0x92, 0x53, 0xe7, 0x8a, 0x3e, 0xff, 0x4b, 0x60, 0xd4, 0x15, 0xa1, 0x43, 0xf7, 0x36, 0x82, 0xa9, 0x1d, 0xdc, 0x68, 0xc, 0xb8, 0x79, 0xcd, 0xe6, 0x52, 0x93, 0x27, 0xc5, 0x71, 0xb0, 0x4, 0x2f, 0x9b, 0x5a, 0xee, 0x83, 0x37, 0xf6, 0x42, 0x69, 0xdd, 0x1c, 0xa8, 0x4a, 0xfe, 0x3f, 0x8b, 0xa0, 0x14, 0xd5, 0x61, 0xf, 0xbb, 0x7a, 0xce, 0xe5, 0x51, 0x90, 0x24, 0xc6, 0x72, 0xb3, 0x7, 0x2c, 0x98, 0x59, 0xed, 0x80, 0x34, 0xf5, 0x41, 0x6a, 0xde, 0x1f, 0xab, 0x49, 0xfd, 0x3c, 0x88, 0xa3, 0x17, 0xd6, 0x62, 0xa, 0xbe, 0x7f, 0xcb, 0xe0, 0x54, 0x95, 0x21, 0xc3, 0x77, 0xb6, 0x2, 0x29, 0x9d, 0x5c, 0xe8, 0x85, 0x31, 0xf0, 0x44, 0x6f, 0xdb, 0x1a, 0xae, 0x4c, 0xf8, 0x39, 0x8d, 0xa6, 0x12, 0xd3, 0x67, 0x9, 0xbd, 0x7c, 0xc8, 0xe3, 0x57, 0x96, 0x22, 0xc0, 0x74, 0xb5, 0x1, 0x2a, 0x9e, 0x5f, 0xeb, 0x86, 0x32, 0xf3, 0x47, 0x6c, 0xd8, 0x19, 0xad, 0x4f, 0xfb, 0x3a, 0x8e, 0xa5, 0x11, 0xd0, 0x64},
+ {0x0, 0xb5, 0x77, 0xc2, 0xee, 0x5b, 0x99, 0x2c, 0xc1, 0x74, 0xb6, 0x3, 0x2f, 0x9a, 0x58, 0xed, 0x9f, 0x2a, 0xe8, 0x5d, 0x71, 0xc4, 0x6, 0xb3, 0x5e, 0xeb, 0x29, 0x9c, 0xb0, 0x5, 0xc7, 0x72, 0x23, 0x96, 0x54, 0xe1, 0xcd, 0x78, 0xba, 0xf, 0xe2, 0x57, 0x95, 0x20, 0xc, 0xb9, 0x7b, 0xce, 0xbc, 0x9, 0xcb, 0x7e, 0x52, 0xe7, 0x25, 0x90, 0x7d, 0xc8, 0xa, 0xbf, 0x93, 0x26, 0xe4, 0x51, 0x46, 0xf3, 0x31, 0x84, 0xa8, 0x1d, 0xdf, 0x6a, 0x87, 0x32, 0xf0, 0x45, 0x69, 0xdc, 0x1e, 0xab, 0xd9, 0x6c, 0xae, 0x1b, 0x37, 0x82, 0x40, 0xf5, 0x18, 0xad, 0x6f, 0xda, 0xf6, 0x43, 0x81, 0x34, 0x65, 0xd0, 0x12, 0xa7, 0x8b, 0x3e, 0xfc, 0x49, 0xa4, 0x11, 0xd3, 0x66, 0x4a, 0xff, 0x3d, 0x88, 0xfa, 0x4f, 0x8d, 0x38, 0x14, 0xa1, 0x63, 0xd6, 0x3b, 0x8e, 0x4c, 0xf9, 0xd5, 0x60, 0xa2, 0x17, 0x8c, 0x39, 0xfb, 0x4e, 0x62, 0xd7, 0x15, 0xa0, 0x4d, 0xf8, 0x3a, 0x8f, 0xa3, 0x16, 0xd4, 0x61, 0x13, 0xa6, 0x64, 0xd1, 0xfd, 0x48, 0x8a, 0x3f, 0xd2, 0x67, 0xa5, 0x10, 0x3c, 0x89, 0x4b, 0xfe, 0xaf, 0x1a, 0xd8, 0x6d, 0x41, 0xf4, 0x36, 0x83, 0x6e, 0xdb, 0x19, 0xac, 0x80, 0x35, 0xf7, 0x42, 0x30, 0x85, 0x47, 0xf2, 0xde, 0x6b, 0xa9, 0x1c, 0xf1, 0x44, 0x86, 0x33, 0x1f, 0xaa, 0x68, 0xdd, 0xca, 0x7f, 0xbd, 0x8, 0x24, 0x91, 0x53, 0xe6, 0xb, 0xbe, 0x7c, 0xc9, 0xe5, 0x50, 0x92, 0x27, 0x55, 0xe0, 0x22, 0x97, 0xbb, 0xe, 0xcc, 0x79, 0x94, 0x21, 0xe3, 0x56, 0x7a, 0xcf, 0xd, 0xb8, 0xe9, 0x5c, 0x9e, 0x2b, 0x7, 0xb2, 0x70, 0xc5, 0x28, 0x9d, 0x5f, 0xea, 0xc6, 0x73, 0xb1, 0x4, 0x76, 0xc3, 0x1, 0xb4, 0x98, 0x2d, 0xef, 0x5a, 0xb7, 0x2, 0xc0, 0x75, 0x59, 0xec, 0x2e, 0x9b},
+ {0x0, 0xb6, 0x71, 0xc7, 0xe2, 0x54, 0x93, 0x25, 0xd9, 0x6f, 0xa8, 0x1e, 0x3b, 0x8d, 0x4a, 0xfc, 0xaf, 0x19, 0xde, 0x68, 0x4d, 0xfb, 0x3c, 0x8a, 0x76, 0xc0, 0x7, 0xb1, 0x94, 0x22, 0xe5, 0x53, 0x43, 0xf5, 0x32, 0x84, 0xa1, 0x17, 0xd0, 0x66, 0x9a, 0x2c, 0xeb, 0x5d, 0x78, 0xce, 0x9, 0xbf, 0xec, 0x5a, 0x9d, 0x2b, 0xe, 0xb8, 0x7f, 0xc9, 0x35, 0x83, 0x44, 0xf2, 0xd7, 0x61, 0xa6, 0x10, 0x86, 0x30, 0xf7, 0x41, 0x64, 0xd2, 0x15, 0xa3, 0x5f, 0xe9, 0x2e, 0x98, 0xbd, 0xb, 0xcc, 0x7a, 0x29, 0x9f, 0x58, 0xee, 0xcb, 0x7d, 0xba, 0xc, 0xf0, 0x46, 0x81, 0x37, 0x12, 0xa4, 0x63, 0xd5, 0xc5, 0x73, 0xb4, 0x2, 0x27, 0x91, 0x56, 0xe0, 0x1c, 0xaa, 0x6d, 0xdb, 0xfe, 0x48, 0x8f, 0x39, 0x6a, 0xdc, 0x1b, 0xad, 0x88, 0x3e, 0xf9, 0x4f, 0xb3, 0x5, 0xc2, 0x74, 0x51, 0xe7, 0x20, 0x96, 0x11, 0xa7, 0x60, 0xd6, 0xf3, 0x45, 0x82, 0x34, 0xc8, 0x7e, 0xb9, 0xf, 0x2a, 0x9c, 0x5b, 0xed, 0xbe, 0x8, 0xcf, 0x79, 0x5c, 0xea, 0x2d, 0x9b, 0x67, 0xd1, 0x16, 0xa0, 0x85, 0x33, 0xf4, 0x42, 0x52, 0xe4, 0x23, 0x95, 0xb0, 0x6, 0xc1, 0x77, 0x8b, 0x3d, 0xfa, 0x4c, 0x69, 0xdf, 0x18, 0xae, 0xfd, 0x4b, 0x8c, 0x3a, 0x1f, 0xa9, 0x6e, 0xd8, 0x24, 0x92, 0x55, 0xe3, 0xc6, 0x70, 0xb7, 0x1, 0x97, 0x21, 0xe6, 0x50, 0x75, 0xc3, 0x4, 0xb2, 0x4e, 0xf8, 0x3f, 0x89, 0xac, 0x1a, 0xdd, 0x6b, 0x38, 0x8e, 0x49, 0xff, 0xda, 0x6c, 0xab, 0x1d, 0xe1, 0x57, 0x90, 0x26, 0x3, 0xb5, 0x72, 0xc4, 0xd4, 0x62, 0xa5, 0x13, 0x36, 0x80, 0x47, 0xf1, 0xd, 0xbb, 0x7c, 0xca, 0xef, 0x59, 0x9e, 0x28, 0x7b, 0xcd, 0xa, 0xbc, 0x99, 0x2f, 0xe8, 0x5e, 0xa2, 0x14, 0xd3, 0x65, 0x40, 0xf6, 0x31, 0x87},
+ {0x0, 0xb7, 0x73, 0xc4, 0xe6, 0x51, 0x95, 0x22, 0xd1, 0x66, 0xa2, 0x15, 0x37, 0x80, 0x44, 0xf3, 0xbf, 0x8, 0xcc, 0x7b, 0x59, 0xee, 0x2a, 0x9d, 0x6e, 0xd9, 0x1d, 0xaa, 0x88, 0x3f, 0xfb, 0x4c, 0x63, 0xd4, 0x10, 0xa7, 0x85, 0x32, 0xf6, 0x41, 0xb2, 0x5, 0xc1, 0x76, 0x54, 0xe3, 0x27, 0x90, 0xdc, 0x6b, 0xaf, 0x18, 0x3a, 0x8d, 0x49, 0xfe, 0xd, 0xba, 0x7e, 0xc9, 0xeb, 0x5c, 0x98, 0x2f, 0xc6, 0x71, 0xb5, 0x2, 0x20, 0x97, 0x53, 0xe4, 0x17, 0xa0, 0x64, 0xd3, 0xf1, 0x46, 0x82, 0x35, 0x79, 0xce, 0xa, 0xbd, 0x9f, 0x28, 0xec, 0x5b, 0xa8, 0x1f, 0xdb, 0x6c, 0x4e, 0xf9, 0x3d, 0x8a, 0xa5, 0x12, 0xd6, 0x61, 0x43, 0xf4, 0x30, 0x87, 0x74, 0xc3, 0x7, 0xb0, 0x92, 0x25, 0xe1, 0x56, 0x1a, 0xad, 0x69, 0xde, 0xfc, 0x4b, 0x8f, 0x38, 0xcb, 0x7c, 0xb8, 0xf, 0x2d, 0x9a, 0x5e, 0xe9, 0x91, 0x26, 0xe2, 0x55, 0x77, 0xc0, 0x4, 0xb3, 0x40, 0xf7, 0x33, 0x84, 0xa6, 0x11, 0xd5, 0x62, 0x2e, 0x99, 0x5d, 0xea, 0xc8, 0x7f, 0xbb, 0xc, 0xff, 0x48, 0x8c, 0x3b, 0x19, 0xae, 0x6a, 0xdd, 0xf2, 0x45, 0x81, 0x36, 0x14, 0xa3, 0x67, 0xd0, 0x23, 0x94, 0x50, 0xe7, 0xc5, 0x72, 0xb6, 0x1, 0x4d, 0xfa, 0x3e, 0x89, 0xab, 0x1c, 0xd8, 0x6f, 0x9c, 0x2b, 0xef, 0x58, 0x7a, 0xcd, 0x9, 0xbe, 0x57, 0xe0, 0x24, 0x93, 0xb1, 0x6, 0xc2, 0x75, 0x86, 0x31, 0xf5, 0x42, 0x60, 0xd7, 0x13, 0xa4, 0xe8, 0x5f, 0x9b, 0x2c, 0xe, 0xb9, 0x7d, 0xca, 0x39, 0x8e, 0x4a, 0xfd, 0xdf, 0x68, 0xac, 0x1b, 0x34, 0x83, 0x47, 0xf0, 0xd2, 0x65, 0xa1, 0x16, 0xe5, 0x52, 0x96, 0x21, 0x3, 0xb4, 0x70, 0xc7, 0x8b, 0x3c, 0xf8, 0x4f, 0x6d, 0xda, 0x1e, 0xa9, 0x5a, 0xed, 0x29, 0x9e, 0xbc, 0xb, 0xcf, 0x78},
+ {0x0, 0xb8, 0x6d, 0xd5, 0xda, 0x62, 0xb7, 0xf, 0xa9, 0x11, 0xc4, 0x7c, 0x73, 0xcb, 0x1e, 0xa6, 0x4f, 0xf7, 0x22, 0x9a, 0x95, 0x2d, 0xf8, 0x40, 0xe6, 0x5e, 0x8b, 0x33, 0x3c, 0x84, 0x51, 0xe9, 0x9e, 0x26, 0xf3, 0x4b, 0x44, 0xfc, 0x29, 0x91, 0x37, 0x8f, 0x5a, 0xe2, 0xed, 0x55, 0x80, 0x38, 0xd1, 0x69, 0xbc, 0x4, 0xb, 0xb3, 0x66, 0xde, 0x78, 0xc0, 0x15, 0xad, 0xa2, 0x1a, 0xcf, 0x77, 0x21, 0x99, 0x4c, 0xf4, 0xfb, 0x43, 0x96, 0x2e, 0x88, 0x30, 0xe5, 0x5d, 0x52, 0xea, 0x3f, 0x87, 0x6e, 0xd6, 0x3, 0xbb, 0xb4, 0xc, 0xd9, 0x61, 0xc7, 0x7f, 0xaa, 0x12, 0x1d, 0xa5, 0x70, 0xc8, 0xbf, 0x7, 0xd2, 0x6a, 0x65, 0xdd, 0x8, 0xb0, 0x16, 0xae, 0x7b, 0xc3, 0xcc, 0x74, 0xa1, 0x19, 0xf0, 0x48, 0x9d, 0x25, 0x2a, 0x92, 0x47, 0xff, 0x59, 0xe1, 0x34, 0x8c, 0x83, 0x3b, 0xee, 0x56, 0x42, 0xfa, 0x2f, 0x97, 0x98, 0x20, 0xf5, 0x4d, 0xeb, 0x53, 0x86, 0x3e, 0x31, 0x89, 0x5c, 0xe4, 0xd, 0xb5, 0x60, 0xd8, 0xd7, 0x6f, 0xba, 0x2, 0xa4, 0x1c, 0xc9, 0x71, 0x7e, 0xc6, 0x13, 0xab, 0xdc, 0x64, 0xb1, 0x9, 0x6, 0xbe, 0x6b, 0xd3, 0x75, 0xcd, 0x18, 0xa0, 0xaf, 0x17, 0xc2, 0x7a, 0x93, 0x2b, 0xfe, 0x46, 0x49, 0xf1, 0x24, 0x9c, 0x3a, 0x82, 0x57, 0xef, 0xe0, 0x58, 0x8d, 0x35, 0x63, 0xdb, 0xe, 0xb6, 0xb9, 0x1, 0xd4, 0x6c, 0xca, 0x72, 0xa7, 0x1f, 0x10, 0xa8, 0x7d, 0xc5, 0x2c, 0x94, 0x41, 0xf9, 0xf6, 0x4e, 0x9b, 0x23, 0x85, 0x3d, 0xe8, 0x50, 0x5f, 0xe7, 0x32, 0x8a, 0xfd, 0x45, 0x90, 0x28, 0x27, 0x9f, 0x4a, 0xf2, 0x54, 0xec, 0x39, 0x81, 0x8e, 0x36, 0xe3, 0x5b, 0xb2, 0xa, 0xdf, 0x67, 0x68, 0xd0, 0x5, 0xbd, 0x1b, 0xa3, 0x76, 0xce, 0xc1, 0x79, 0xac, 0x14},
+ {0x0, 0xb9, 0x6f, 0xd6, 0xde, 0x67, 0xb1, 0x8, 0xa1, 0x18, 0xce, 0x77, 0x7f, 0xc6, 0x10, 0xa9, 0x5f, 0xe6, 0x30, 0x89, 0x81, 0x38, 0xee, 0x57, 0xfe, 0x47, 0x91, 0x28, 0x20, 0x99, 0x4f, 0xf6, 0xbe, 0x7, 0xd1, 0x68, 0x60, 0xd9, 0xf, 0xb6, 0x1f, 0xa6, 0x70, 0xc9, 0xc1, 0x78, 0xae, 0x17, 0xe1, 0x58, 0x8e, 0x37, 0x3f, 0x86, 0x50, 0xe9, 0x40, 0xf9, 0x2f, 0x96, 0x9e, 0x27, 0xf1, 0x48, 0x61, 0xd8, 0xe, 0xb7, 0xbf, 0x6, 0xd0, 0x69, 0xc0, 0x79, 0xaf, 0x16, 0x1e, 0xa7, 0x71, 0xc8, 0x3e, 0x87, 0x51, 0xe8, 0xe0, 0x59, 0x8f, 0x36, 0x9f, 0x26, 0xf0, 0x49, 0x41, 0xf8, 0x2e, 0x97, 0xdf, 0x66, 0xb0, 0x9, 0x1, 0xb8, 0x6e, 0xd7, 0x7e, 0xc7, 0x11, 0xa8, 0xa0, 0x19, 0xcf, 0x76, 0x80, 0x39, 0xef, 0x56, 0x5e, 0xe7, 0x31, 0x88, 0x21, 0x98, 0x4e, 0xf7, 0xff, 0x46, 0x90, 0x29, 0xc2, 0x7b, 0xad, 0x14, 0x1c, 0xa5, 0x73, 0xca, 0x63, 0xda, 0xc, 0xb5, 0xbd, 0x4, 0xd2, 0x6b, 0x9d, 0x24, 0xf2, 0x4b, 0x43, 0xfa, 0x2c, 0x95, 0x3c, 0x85, 0x53, 0xea, 0xe2, 0x5b, 0x8d, 0x34, 0x7c, 0xc5, 0x13, 0xaa, 0xa2, 0x1b, 0xcd, 0x74, 0xdd, 0x64, 0xb2, 0xb, 0x3, 0xba, 0x6c, 0xd5, 0x23, 0x9a, 0x4c, 0xf5, 0xfd, 0x44, 0x92, 0x2b, 0x82, 0x3b, 0xed, 0x54, 0x5c, 0xe5, 0x33, 0x8a, 0xa3, 0x1a, 0xcc, 0x75, 0x7d, 0xc4, 0x12, 0xab, 0x2, 0xbb, 0x6d, 0xd4, 0xdc, 0x65, 0xb3, 0xa, 0xfc, 0x45, 0x93, 0x2a, 0x22, 0x9b, 0x4d, 0xf4, 0x5d, 0xe4, 0x32, 0x8b, 0x83, 0x3a, 0xec, 0x55, 0x1d, 0xa4, 0x72, 0xcb, 0xc3, 0x7a, 0xac, 0x15, 0xbc, 0x5, 0xd3, 0x6a, 0x62, 0xdb, 0xd, 0xb4, 0x42, 0xfb, 0x2d, 0x94, 0x9c, 0x25, 0xf3, 0x4a, 0xe3, 0x5a, 0x8c, 0x35, 0x3d, 0x84, 0x52, 0xeb},
+ {0x0, 0xba, 0x69, 0xd3, 0xd2, 0x68, 0xbb, 0x1, 0xb9, 0x3, 0xd0, 0x6a, 0x6b, 0xd1, 0x2, 0xb8, 0x6f, 0xd5, 0x6, 0xbc, 0xbd, 0x7, 0xd4, 0x6e, 0xd6, 0x6c, 0xbf, 0x5, 0x4, 0xbe, 0x6d, 0xd7, 0xde, 0x64, 0xb7, 0xd, 0xc, 0xb6, 0x65, 0xdf, 0x67, 0xdd, 0xe, 0xb4, 0xb5, 0xf, 0xdc, 0x66, 0xb1, 0xb, 0xd8, 0x62, 0x63, 0xd9, 0xa, 0xb0, 0x8, 0xb2, 0x61, 0xdb, 0xda, 0x60, 0xb3, 0x9, 0xa1, 0x1b, 0xc8, 0x72, 0x73, 0xc9, 0x1a, 0xa0, 0x18, 0xa2, 0x71, 0xcb, 0xca, 0x70, 0xa3, 0x19, 0xce, 0x74, 0xa7, 0x1d, 0x1c, 0xa6, 0x75, 0xcf, 0x77, 0xcd, 0x1e, 0xa4, 0xa5, 0x1f, 0xcc, 0x76, 0x7f, 0xc5, 0x16, 0xac, 0xad, 0x17, 0xc4, 0x7e, 0xc6, 0x7c, 0xaf, 0x15, 0x14, 0xae, 0x7d, 0xc7, 0x10, 0xaa, 0x79, 0xc3, 0xc2, 0x78, 0xab, 0x11, 0xa9, 0x13, 0xc0, 0x7a, 0x7b, 0xc1, 0x12, 0xa8, 0x5f, 0xe5, 0x36, 0x8c, 0x8d, 0x37, 0xe4, 0x5e, 0xe6, 0x5c, 0x8f, 0x35, 0x34, 0x8e, 0x5d, 0xe7, 0x30, 0x8a, 0x59, 0xe3, 0xe2, 0x58, 0x8b, 0x31, 0x89, 0x33, 0xe0, 0x5a, 0x5b, 0xe1, 0x32, 0x88, 0x81, 0x3b, 0xe8, 0x52, 0x53, 0xe9, 0x3a, 0x80, 0x38, 0x82, 0x51, 0xeb, 0xea, 0x50, 0x83, 0x39, 0xee, 0x54, 0x87, 0x3d, 0x3c, 0x86, 0x55, 0xef, 0x57, 0xed, 0x3e, 0x84, 0x85, 0x3f, 0xec, 0x56, 0xfe, 0x44, 0x97, 0x2d, 0x2c, 0x96, 0x45, 0xff, 0x47, 0xfd, 0x2e, 0x94, 0x95, 0x2f, 0xfc, 0x46, 0x91, 0x2b, 0xf8, 0x42, 0x43, 0xf9, 0x2a, 0x90, 0x28, 0x92, 0x41, 0xfb, 0xfa, 0x40, 0x93, 0x29, 0x20, 0x9a, 0x49, 0xf3, 0xf2, 0x48, 0x9b, 0x21, 0x99, 0x23, 0xf0, 0x4a, 0x4b, 0xf1, 0x22, 0x98, 0x4f, 0xf5, 0x26, 0x9c, 0x9d, 0x27, 0xf4, 0x4e, 0xf6, 0x4c, 0x9f, 0x25, 0x24, 0x9e, 0x4d, 0xf7},
+ {0x0, 0xbb, 0x6b, 0xd0, 0xd6, 0x6d, 0xbd, 0x6, 0xb1, 0xa, 0xda, 0x61, 0x67, 0xdc, 0xc, 0xb7, 0x7f, 0xc4, 0x14, 0xaf, 0xa9, 0x12, 0xc2, 0x79, 0xce, 0x75, 0xa5, 0x1e, 0x18, 0xa3, 0x73, 0xc8, 0xfe, 0x45, 0x95, 0x2e, 0x28, 0x93, 0x43, 0xf8, 0x4f, 0xf4, 0x24, 0x9f, 0x99, 0x22, 0xf2, 0x49, 0x81, 0x3a, 0xea, 0x51, 0x57, 0xec, 0x3c, 0x87, 0x30, 0x8b, 0x5b, 0xe0, 0xe6, 0x5d, 0x8d, 0x36, 0xe1, 0x5a, 0x8a, 0x31, 0x37, 0x8c, 0x5c, 0xe7, 0x50, 0xeb, 0x3b, 0x80, 0x86, 0x3d, 0xed, 0x56, 0x9e, 0x25, 0xf5, 0x4e, 0x48, 0xf3, 0x23, 0x98, 0x2f, 0x94, 0x44, 0xff, 0xf9, 0x42, 0x92, 0x29, 0x1f, 0xa4, 0x74, 0xcf, 0xc9, 0x72, 0xa2, 0x19, 0xae, 0x15, 0xc5, 0x7e, 0x78, 0xc3, 0x13, 0xa8, 0x60, 0xdb, 0xb, 0xb0, 0xb6, 0xd, 0xdd, 0x66, 0xd1, 0x6a, 0xba, 0x1, 0x7, 0xbc, 0x6c, 0xd7, 0xdf, 0x64, 0xb4, 0xf, 0x9, 0xb2, 0x62, 0xd9, 0x6e, 0xd5, 0x5, 0xbe, 0xb8, 0x3, 0xd3, 0x68, 0xa0, 0x1b, 0xcb, 0x70, 0x76, 0xcd, 0x1d, 0xa6, 0x11, 0xaa, 0x7a, 0xc1, 0xc7, 0x7c, 0xac, 0x17, 0x21, 0x9a, 0x4a, 0xf1, 0xf7, 0x4c, 0x9c, 0x27, 0x90, 0x2b, 0xfb, 0x40, 0x46, 0xfd, 0x2d, 0x96, 0x5e, 0xe5, 0x35, 0x8e, 0x88, 0x33, 0xe3, 0x58, 0xef, 0x54, 0x84, 0x3f, 0x39, 0x82, 0x52, 0xe9, 0x3e, 0x85, 0x55, 0xee, 0xe8, 0x53, 0x83, 0x38, 0x8f, 0x34, 0xe4, 0x5f, 0x59, 0xe2, 0x32, 0x89, 0x41, 0xfa, 0x2a, 0x91, 0x97, 0x2c, 0xfc, 0x47, 0xf0, 0x4b, 0x9b, 0x20, 0x26, 0x9d, 0x4d, 0xf6, 0xc0, 0x7b, 0xab, 0x10, 0x16, 0xad, 0x7d, 0xc6, 0x71, 0xca, 0x1a, 0xa1, 0xa7, 0x1c, 0xcc, 0x77, 0xbf, 0x4, 0xd4, 0x6f, 0x69, 0xd2, 0x2, 0xb9, 0xe, 0xb5, 0x65, 0xde, 0xd8, 0x63, 0xb3, 0x8},
+ {0x0, 0xbc, 0x65, 0xd9, 0xca, 0x76, 0xaf, 0x13, 0x89, 0x35, 0xec, 0x50, 0x43, 0xff, 0x26, 0x9a, 0xf, 0xb3, 0x6a, 0xd6, 0xc5, 0x79, 0xa0, 0x1c, 0x86, 0x3a, 0xe3, 0x5f, 0x4c, 0xf0, 0x29, 0x95, 0x1e, 0xa2, 0x7b, 0xc7, 0xd4, 0x68, 0xb1, 0xd, 0x97, 0x2b, 0xf2, 0x4e, 0x5d, 0xe1, 0x38, 0x84, 0x11, 0xad, 0x74, 0xc8, 0xdb, 0x67, 0xbe, 0x2, 0x98, 0x24, 0xfd, 0x41, 0x52, 0xee, 0x37, 0x8b, 0x3c, 0x80, 0x59, 0xe5, 0xf6, 0x4a, 0x93, 0x2f, 0xb5, 0x9, 0xd0, 0x6c, 0x7f, 0xc3, 0x1a, 0xa6, 0x33, 0x8f, 0x56, 0xea, 0xf9, 0x45, 0x9c, 0x20, 0xba, 0x6, 0xdf, 0x63, 0x70, 0xcc, 0x15, 0xa9, 0x22, 0x9e, 0x47, 0xfb, 0xe8, 0x54, 0x8d, 0x31, 0xab, 0x17, 0xce, 0x72, 0x61, 0xdd, 0x4, 0xb8, 0x2d, 0x91, 0x48, 0xf4, 0xe7, 0x5b, 0x82, 0x3e, 0xa4, 0x18, 0xc1, 0x7d, 0x6e, 0xd2, 0xb, 0xb7, 0x78, 0xc4, 0x1d, 0xa1, 0xb2, 0xe, 0xd7, 0x6b, 0xf1, 0x4d, 0x94, 0x28, 0x3b, 0x87, 0x5e, 0xe2, 0x77, 0xcb, 0x12, 0xae, 0xbd, 0x1, 0xd8, 0x64, 0xfe, 0x42, 0x9b, 0x27, 0x34, 0x88, 0x51, 0xed, 0x66, 0xda, 0x3, 0xbf, 0xac, 0x10, 0xc9, 0x75, 0xef, 0x53, 0x8a, 0x36, 0x25, 0x99, 0x40, 0xfc, 0x69, 0xd5, 0xc, 0xb0, 0xa3, 0x1f, 0xc6, 0x7a, 0xe0, 0x5c, 0x85, 0x39, 0x2a, 0x96, 0x4f, 0xf3, 0x44, 0xf8, 0x21, 0x9d, 0x8e, 0x32, 0xeb, 0x57, 0xcd, 0x71, 0xa8, 0x14, 0x7, 0xbb, 0x62, 0xde, 0x4b, 0xf7, 0x2e, 0x92, 0x81, 0x3d, 0xe4, 0x58, 0xc2, 0x7e, 0xa7, 0x1b, 0x8, 0xb4, 0x6d, 0xd1, 0x5a, 0xe6, 0x3f, 0x83, 0x90, 0x2c, 0xf5, 0x49, 0xd3, 0x6f, 0xb6, 0xa, 0x19, 0xa5, 0x7c, 0xc0, 0x55, 0xe9, 0x30, 0x8c, 0x9f, 0x23, 0xfa, 0x46, 0xdc, 0x60, 0xb9, 0x5, 0x16, 0xaa, 0x73, 0xcf},
+ {0x0, 0xbd, 0x67, 0xda, 0xce, 0x73, 0xa9, 0x14, 0x81, 0x3c, 0xe6, 0x5b, 0x4f, 0xf2, 0x28, 0x95, 0x1f, 0xa2, 0x78, 0xc5, 0xd1, 0x6c, 0xb6, 0xb, 0x9e, 0x23, 0xf9, 0x44, 0x50, 0xed, 0x37, 0x8a, 0x3e, 0x83, 0x59, 0xe4, 0xf0, 0x4d, 0x97, 0x2a, 0xbf, 0x2, 0xd8, 0x65, 0x71, 0xcc, 0x16, 0xab, 0x21, 0x9c, 0x46, 0xfb, 0xef, 0x52, 0x88, 0x35, 0xa0, 0x1d, 0xc7, 0x7a, 0x6e, 0xd3, 0x9, 0xb4, 0x7c, 0xc1, 0x1b, 0xa6, 0xb2, 0xf, 0xd5, 0x68, 0xfd, 0x40, 0x9a, 0x27, 0x33, 0x8e, 0x54, 0xe9, 0x63, 0xde, 0x4, 0xb9, 0xad, 0x10, 0xca, 0x77, 0xe2, 0x5f, 0x85, 0x38, 0x2c, 0x91, 0x4b, 0xf6, 0x42, 0xff, 0x25, 0x98, 0x8c, 0x31, 0xeb, 0x56, 0xc3, 0x7e, 0xa4, 0x19, 0xd, 0xb0, 0x6a, 0xd7, 0x5d, 0xe0, 0x3a, 0x87, 0x93, 0x2e, 0xf4, 0x49, 0xdc, 0x61, 0xbb, 0x6, 0x12, 0xaf, 0x75, 0xc8, 0xf8, 0x45, 0x9f, 0x22, 0x36, 0x8b, 0x51, 0xec, 0x79, 0xc4, 0x1e, 0xa3, 0xb7, 0xa, 0xd0, 0x6d, 0xe7, 0x5a, 0x80, 0x3d, 0x29, 0x94, 0x4e, 0xf3, 0x66, 0xdb, 0x1, 0xbc, 0xa8, 0x15, 0xcf, 0x72, 0xc6, 0x7b, 0xa1, 0x1c, 0x8, 0xb5, 0x6f, 0xd2, 0x47, 0xfa, 0x20, 0x9d, 0x89, 0x34, 0xee, 0x53, 0xd9, 0x64, 0xbe, 0x3, 0x17, 0xaa, 0x70, 0xcd, 0x58, 0xe5, 0x3f, 0x82, 0x96, 0x2b, 0xf1, 0x4c, 0x84, 0x39, 0xe3, 0x5e, 0x4a, 0xf7, 0x2d, 0x90, 0x5, 0xb8, 0x62, 0xdf, 0xcb, 0x76, 0xac, 0x11, 0x9b, 0x26, 0xfc, 0x41, 0x55, 0xe8, 0x32, 0x8f, 0x1a, 0xa7, 0x7d, 0xc0, 0xd4, 0x69, 0xb3, 0xe, 0xba, 0x7, 0xdd, 0x60, 0x74, 0xc9, 0x13, 0xae, 0x3b, 0x86, 0x5c, 0xe1, 0xf5, 0x48, 0x92, 0x2f, 0xa5, 0x18, 0xc2, 0x7f, 0x6b, 0xd6, 0xc, 0xb1, 0x24, 0x99, 0x43, 0xfe, 0xea, 0x57, 0x8d, 0x30},
+ {0x0, 0xbe, 0x61, 0xdf, 0xc2, 0x7c, 0xa3, 0x1d, 0x99, 0x27, 0xf8, 0x46, 0x5b, 0xe5, 0x3a, 0x84, 0x2f, 0x91, 0x4e, 0xf0, 0xed, 0x53, 0x8c, 0x32, 0xb6, 0x8, 0xd7, 0x69, 0x74, 0xca, 0x15, 0xab, 0x5e, 0xe0, 0x3f, 0x81, 0x9c, 0x22, 0xfd, 0x43, 0xc7, 0x79, 0xa6, 0x18, 0x5, 0xbb, 0x64, 0xda, 0x71, 0xcf, 0x10, 0xae, 0xb3, 0xd, 0xd2, 0x6c, 0xe8, 0x56, 0x89, 0x37, 0x2a, 0x94, 0x4b, 0xf5, 0xbc, 0x2, 0xdd, 0x63, 0x7e, 0xc0, 0x1f, 0xa1, 0x25, 0x9b, 0x44, 0xfa, 0xe7, 0x59, 0x86, 0x38, 0x93, 0x2d, 0xf2, 0x4c, 0x51, 0xef, 0x30, 0x8e, 0xa, 0xb4, 0x6b, 0xd5, 0xc8, 0x76, 0xa9, 0x17, 0xe2, 0x5c, 0x83, 0x3d, 0x20, 0x9e, 0x41, 0xff, 0x7b, 0xc5, 0x1a, 0xa4, 0xb9, 0x7, 0xd8, 0x66, 0xcd, 0x73, 0xac, 0x12, 0xf, 0xb1, 0x6e, 0xd0, 0x54, 0xea, 0x35, 0x8b, 0x96, 0x28, 0xf7, 0x49, 0x65, 0xdb, 0x4, 0xba, 0xa7, 0x19, 0xc6, 0x78, 0xfc, 0x42, 0x9d, 0x23, 0x3e, 0x80, 0x5f, 0xe1, 0x4a, 0xf4, 0x2b, 0x95, 0x88, 0x36, 0xe9, 0x57, 0xd3, 0x6d, 0xb2, 0xc, 0x11, 0xaf, 0x70, 0xce, 0x3b, 0x85, 0x5a, 0xe4, 0xf9, 0x47, 0x98, 0x26, 0xa2, 0x1c, 0xc3, 0x7d, 0x60, 0xde, 0x1, 0xbf, 0x14, 0xaa, 0x75, 0xcb, 0xd6, 0x68, 0xb7, 0x9, 0x8d, 0x33, 0xec, 0x52, 0x4f, 0xf1, 0x2e, 0x90, 0xd9, 0x67, 0xb8, 0x6, 0x1b, 0xa5, 0x7a, 0xc4, 0x40, 0xfe, 0x21, 0x9f, 0x82, 0x3c, 0xe3, 0x5d, 0xf6, 0x48, 0x97, 0x29, 0x34, 0x8a, 0x55, 0xeb, 0x6f, 0xd1, 0xe, 0xb0, 0xad, 0x13, 0xcc, 0x72, 0x87, 0x39, 0xe6, 0x58, 0x45, 0xfb, 0x24, 0x9a, 0x1e, 0xa0, 0x7f, 0xc1, 0xdc, 0x62, 0xbd, 0x3, 0xa8, 0x16, 0xc9, 0x77, 0x6a, 0xd4, 0xb, 0xb5, 0x31, 0x8f, 0x50, 0xee, 0xf3, 0x4d, 0x92, 0x2c},
+ {0x0, 0xbf, 0x63, 0xdc, 0xc6, 0x79, 0xa5, 0x1a, 0x91, 0x2e, 0xf2, 0x4d, 0x57, 0xe8, 0x34, 0x8b, 0x3f, 0x80, 0x5c, 0xe3, 0xf9, 0x46, 0x9a, 0x25, 0xae, 0x11, 0xcd, 0x72, 0x68, 0xd7, 0xb, 0xb4, 0x7e, 0xc1, 0x1d, 0xa2, 0xb8, 0x7, 0xdb, 0x64, 0xef, 0x50, 0x8c, 0x33, 0x29, 0x96, 0x4a, 0xf5, 0x41, 0xfe, 0x22, 0x9d, 0x87, 0x38, 0xe4, 0x5b, 0xd0, 0x6f, 0xb3, 0xc, 0x16, 0xa9, 0x75, 0xca, 0xfc, 0x43, 0x9f, 0x20, 0x3a, 0x85, 0x59, 0xe6, 0x6d, 0xd2, 0xe, 0xb1, 0xab, 0x14, 0xc8, 0x77, 0xc3, 0x7c, 0xa0, 0x1f, 0x5, 0xba, 0x66, 0xd9, 0x52, 0xed, 0x31, 0x8e, 0x94, 0x2b, 0xf7, 0x48, 0x82, 0x3d, 0xe1, 0x5e, 0x44, 0xfb, 0x27, 0x98, 0x13, 0xac, 0x70, 0xcf, 0xd5, 0x6a, 0xb6, 0x9, 0xbd, 0x2, 0xde, 0x61, 0x7b, 0xc4, 0x18, 0xa7, 0x2c, 0x93, 0x4f, 0xf0, 0xea, 0x55, 0x89, 0x36, 0xe5, 0x5a, 0x86, 0x39, 0x23, 0x9c, 0x40, 0xff, 0x74, 0xcb, 0x17, 0xa8, 0xb2, 0xd, 0xd1, 0x6e, 0xda, 0x65, 0xb9, 0x6, 0x1c, 0xa3, 0x7f, 0xc0, 0x4b, 0xf4, 0x28, 0x97, 0x8d, 0x32, 0xee, 0x51, 0x9b, 0x24, 0xf8, 0x47, 0x5d, 0xe2, 0x3e, 0x81, 0xa, 0xb5, 0x69, 0xd6, 0xcc, 0x73, 0xaf, 0x10, 0xa4, 0x1b, 0xc7, 0x78, 0x62, 0xdd, 0x1, 0xbe, 0x35, 0x8a, 0x56, 0xe9, 0xf3, 0x4c, 0x90, 0x2f, 0x19, 0xa6, 0x7a, 0xc5, 0xdf, 0x60, 0xbc, 0x3, 0x88, 0x37, 0xeb, 0x54, 0x4e, 0xf1, 0x2d, 0x92, 0x26, 0x99, 0x45, 0xfa, 0xe0, 0x5f, 0x83, 0x3c, 0xb7, 0x8, 0xd4, 0x6b, 0x71, 0xce, 0x12, 0xad, 0x67, 0xd8, 0x4, 0xbb, 0xa1, 0x1e, 0xc2, 0x7d, 0xf6, 0x49, 0x95, 0x2a, 0x30, 0x8f, 0x53, 0xec, 0x58, 0xe7, 0x3b, 0x84, 0x9e, 0x21, 0xfd, 0x42, 0xc9, 0x76, 0xaa, 0x15, 0xf, 0xb0, 0x6c, 0xd3},
+ {0x0, 0xc0, 0x9d, 0x5d, 0x27, 0xe7, 0xba, 0x7a, 0x4e, 0x8e, 0xd3, 0x13, 0x69, 0xa9, 0xf4, 0x34, 0x9c, 0x5c, 0x1, 0xc1, 0xbb, 0x7b, 0x26, 0xe6, 0xd2, 0x12, 0x4f, 0x8f, 0xf5, 0x35, 0x68, 0xa8, 0x25, 0xe5, 0xb8, 0x78, 0x2, 0xc2, 0x9f, 0x5f, 0x6b, 0xab, 0xf6, 0x36, 0x4c, 0x8c, 0xd1, 0x11, 0xb9, 0x79, 0x24, 0xe4, 0x9e, 0x5e, 0x3, 0xc3, 0xf7, 0x37, 0x6a, 0xaa, 0xd0, 0x10, 0x4d, 0x8d, 0x4a, 0x8a, 0xd7, 0x17, 0x6d, 0xad, 0xf0, 0x30, 0x4, 0xc4, 0x99, 0x59, 0x23, 0xe3, 0xbe, 0x7e, 0xd6, 0x16, 0x4b, 0x8b, 0xf1, 0x31, 0x6c, 0xac, 0x98, 0x58, 0x5, 0xc5, 0xbf, 0x7f, 0x22, 0xe2, 0x6f, 0xaf, 0xf2, 0x32, 0x48, 0x88, 0xd5, 0x15, 0x21, 0xe1, 0xbc, 0x7c, 0x6, 0xc6, 0x9b, 0x5b, 0xf3, 0x33, 0x6e, 0xae, 0xd4, 0x14, 0x49, 0x89, 0xbd, 0x7d, 0x20, 0xe0, 0x9a, 0x5a, 0x7, 0xc7, 0x94, 0x54, 0x9, 0xc9, 0xb3, 0x73, 0x2e, 0xee, 0xda, 0x1a, 0x47, 0x87, 0xfd, 0x3d, 0x60, 0xa0, 0x8, 0xc8, 0x95, 0x55, 0x2f, 0xef, 0xb2, 0x72, 0x46, 0x86, 0xdb, 0x1b, 0x61, 0xa1, 0xfc, 0x3c, 0xb1, 0x71, 0x2c, 0xec, 0x96, 0x56, 0xb, 0xcb, 0xff, 0x3f, 0x62, 0xa2, 0xd8, 0x18, 0x45, 0x85, 0x2d, 0xed, 0xb0, 0x70, 0xa, 0xca, 0x97, 0x57, 0x63, 0xa3, 0xfe, 0x3e, 0x44, 0x84, 0xd9, 0x19, 0xde, 0x1e, 0x43, 0x83, 0xf9, 0x39, 0x64, 0xa4, 0x90, 0x50, 0xd, 0xcd, 0xb7, 0x77, 0x2a, 0xea, 0x42, 0x82, 0xdf, 0x1f, 0x65, 0xa5, 0xf8, 0x38, 0xc, 0xcc, 0x91, 0x51, 0x2b, 0xeb, 0xb6, 0x76, 0xfb, 0x3b, 0x66, 0xa6, 0xdc, 0x1c, 0x41, 0x81, 0xb5, 0x75, 0x28, 0xe8, 0x92, 0x52, 0xf, 0xcf, 0x67, 0xa7, 0xfa, 0x3a, 0x40, 0x80, 0xdd, 0x1d, 0x29, 0xe9, 0xb4, 0x74, 0xe, 0xce, 0x93, 0x53},
+ {0x0, 0xc1, 0x9f, 0x5e, 0x23, 0xe2, 0xbc, 0x7d, 0x46, 0x87, 0xd9, 0x18, 0x65, 0xa4, 0xfa, 0x3b, 0x8c, 0x4d, 0x13, 0xd2, 0xaf, 0x6e, 0x30, 0xf1, 0xca, 0xb, 0x55, 0x94, 0xe9, 0x28, 0x76, 0xb7, 0x5, 0xc4, 0x9a, 0x5b, 0x26, 0xe7, 0xb9, 0x78, 0x43, 0x82, 0xdc, 0x1d, 0x60, 0xa1, 0xff, 0x3e, 0x89, 0x48, 0x16, 0xd7, 0xaa, 0x6b, 0x35, 0xf4, 0xcf, 0xe, 0x50, 0x91, 0xec, 0x2d, 0x73, 0xb2, 0xa, 0xcb, 0x95, 0x54, 0x29, 0xe8, 0xb6, 0x77, 0x4c, 0x8d, 0xd3, 0x12, 0x6f, 0xae, 0xf0, 0x31, 0x86, 0x47, 0x19, 0xd8, 0xa5, 0x64, 0x3a, 0xfb, 0xc0, 0x1, 0x5f, 0x9e, 0xe3, 0x22, 0x7c, 0xbd, 0xf, 0xce, 0x90, 0x51, 0x2c, 0xed, 0xb3, 0x72, 0x49, 0x88, 0xd6, 0x17, 0x6a, 0xab, 0xf5, 0x34, 0x83, 0x42, 0x1c, 0xdd, 0xa0, 0x61, 0x3f, 0xfe, 0xc5, 0x4, 0x5a, 0x9b, 0xe6, 0x27, 0x79, 0xb8, 0x14, 0xd5, 0x8b, 0x4a, 0x37, 0xf6, 0xa8, 0x69, 0x52, 0x93, 0xcd, 0xc, 0x71, 0xb0, 0xee, 0x2f, 0x98, 0x59, 0x7, 0xc6, 0xbb, 0x7a, 0x24, 0xe5, 0xde, 0x1f, 0x41, 0x80, 0xfd, 0x3c, 0x62, 0xa3, 0x11, 0xd0, 0x8e, 0x4f, 0x32, 0xf3, 0xad, 0x6c, 0x57, 0x96, 0xc8, 0x9, 0x74, 0xb5, 0xeb, 0x2a, 0x9d, 0x5c, 0x2, 0xc3, 0xbe, 0x7f, 0x21, 0xe0, 0xdb, 0x1a, 0x44, 0x85, 0xf8, 0x39, 0x67, 0xa6, 0x1e, 0xdf, 0x81, 0x40, 0x3d, 0xfc, 0xa2, 0x63, 0x58, 0x99, 0xc7, 0x6, 0x7b, 0xba, 0xe4, 0x25, 0x92, 0x53, 0xd, 0xcc, 0xb1, 0x70, 0x2e, 0xef, 0xd4, 0x15, 0x4b, 0x8a, 0xf7, 0x36, 0x68, 0xa9, 0x1b, 0xda, 0x84, 0x45, 0x38, 0xf9, 0xa7, 0x66, 0x5d, 0x9c, 0xc2, 0x3, 0x7e, 0xbf, 0xe1, 0x20, 0x97, 0x56, 0x8, 0xc9, 0xb4, 0x75, 0x2b, 0xea, 0xd1, 0x10, 0x4e, 0x8f, 0xf2, 0x33, 0x6d, 0xac},
+ {0x0, 0xc2, 0x99, 0x5b, 0x2f, 0xed, 0xb6, 0x74, 0x5e, 0x9c, 0xc7, 0x5, 0x71, 0xb3, 0xe8, 0x2a, 0xbc, 0x7e, 0x25, 0xe7, 0x93, 0x51, 0xa, 0xc8, 0xe2, 0x20, 0x7b, 0xb9, 0xcd, 0xf, 0x54, 0x96, 0x65, 0xa7, 0xfc, 0x3e, 0x4a, 0x88, 0xd3, 0x11, 0x3b, 0xf9, 0xa2, 0x60, 0x14, 0xd6, 0x8d, 0x4f, 0xd9, 0x1b, 0x40, 0x82, 0xf6, 0x34, 0x6f, 0xad, 0x87, 0x45, 0x1e, 0xdc, 0xa8, 0x6a, 0x31, 0xf3, 0xca, 0x8, 0x53, 0x91, 0xe5, 0x27, 0x7c, 0xbe, 0x94, 0x56, 0xd, 0xcf, 0xbb, 0x79, 0x22, 0xe0, 0x76, 0xb4, 0xef, 0x2d, 0x59, 0x9b, 0xc0, 0x2, 0x28, 0xea, 0xb1, 0x73, 0x7, 0xc5, 0x9e, 0x5c, 0xaf, 0x6d, 0x36, 0xf4, 0x80, 0x42, 0x19, 0xdb, 0xf1, 0x33, 0x68, 0xaa, 0xde, 0x1c, 0x47, 0x85, 0x13, 0xd1, 0x8a, 0x48, 0x3c, 0xfe, 0xa5, 0x67, 0x4d, 0x8f, 0xd4, 0x16, 0x62, 0xa0, 0xfb, 0x39, 0x89, 0x4b, 0x10, 0xd2, 0xa6, 0x64, 0x3f, 0xfd, 0xd7, 0x15, 0x4e, 0x8c, 0xf8, 0x3a, 0x61, 0xa3, 0x35, 0xf7, 0xac, 0x6e, 0x1a, 0xd8, 0x83, 0x41, 0x6b, 0xa9, 0xf2, 0x30, 0x44, 0x86, 0xdd, 0x1f, 0xec, 0x2e, 0x75, 0xb7, 0xc3, 0x1, 0x5a, 0x98, 0xb2, 0x70, 0x2b, 0xe9, 0x9d, 0x5f, 0x4, 0xc6, 0x50, 0x92, 0xc9, 0xb, 0x7f, 0xbd, 0xe6, 0x24, 0xe, 0xcc, 0x97, 0x55, 0x21, 0xe3, 0xb8, 0x7a, 0x43, 0x81, 0xda, 0x18, 0x6c, 0xae, 0xf5, 0x37, 0x1d, 0xdf, 0x84, 0x46, 0x32, 0xf0, 0xab, 0x69, 0xff, 0x3d, 0x66, 0xa4, 0xd0, 0x12, 0x49, 0x8b, 0xa1, 0x63, 0x38, 0xfa, 0x8e, 0x4c, 0x17, 0xd5, 0x26, 0xe4, 0xbf, 0x7d, 0x9, 0xcb, 0x90, 0x52, 0x78, 0xba, 0xe1, 0x23, 0x57, 0x95, 0xce, 0xc, 0x9a, 0x58, 0x3, 0xc1, 0xb5, 0x77, 0x2c, 0xee, 0xc4, 0x6, 0x5d, 0x9f, 0xeb, 0x29, 0x72, 0xb0},
+ {0x0, 0xc3, 0x9b, 0x58, 0x2b, 0xe8, 0xb0, 0x73, 0x56, 0x95, 0xcd, 0xe, 0x7d, 0xbe, 0xe6, 0x25, 0xac, 0x6f, 0x37, 0xf4, 0x87, 0x44, 0x1c, 0xdf, 0xfa, 0x39, 0x61, 0xa2, 0xd1, 0x12, 0x4a, 0x89, 0x45, 0x86, 0xde, 0x1d, 0x6e, 0xad, 0xf5, 0x36, 0x13, 0xd0, 0x88, 0x4b, 0x38, 0xfb, 0xa3, 0x60, 0xe9, 0x2a, 0x72, 0xb1, 0xc2, 0x1, 0x59, 0x9a, 0xbf, 0x7c, 0x24, 0xe7, 0x94, 0x57, 0xf, 0xcc, 0x8a, 0x49, 0x11, 0xd2, 0xa1, 0x62, 0x3a, 0xf9, 0xdc, 0x1f, 0x47, 0x84, 0xf7, 0x34, 0x6c, 0xaf, 0x26, 0xe5, 0xbd, 0x7e, 0xd, 0xce, 0x96, 0x55, 0x70, 0xb3, 0xeb, 0x28, 0x5b, 0x98, 0xc0, 0x3, 0xcf, 0xc, 0x54, 0x97, 0xe4, 0x27, 0x7f, 0xbc, 0x99, 0x5a, 0x2, 0xc1, 0xb2, 0x71, 0x29, 0xea, 0x63, 0xa0, 0xf8, 0x3b, 0x48, 0x8b, 0xd3, 0x10, 0x35, 0xf6, 0xae, 0x6d, 0x1e, 0xdd, 0x85, 0x46, 0x9, 0xca, 0x92, 0x51, 0x22, 0xe1, 0xb9, 0x7a, 0x5f, 0x9c, 0xc4, 0x7, 0x74, 0xb7, 0xef, 0x2c, 0xa5, 0x66, 0x3e, 0xfd, 0x8e, 0x4d, 0x15, 0xd6, 0xf3, 0x30, 0x68, 0xab, 0xd8, 0x1b, 0x43, 0x80, 0x4c, 0x8f, 0xd7, 0x14, 0x67, 0xa4, 0xfc, 0x3f, 0x1a, 0xd9, 0x81, 0x42, 0x31, 0xf2, 0xaa, 0x69, 0xe0, 0x23, 0x7b, 0xb8, 0xcb, 0x8, 0x50, 0x93, 0xb6, 0x75, 0x2d, 0xee, 0x9d, 0x5e, 0x6, 0xc5, 0x83, 0x40, 0x18, 0xdb, 0xa8, 0x6b, 0x33, 0xf0, 0xd5, 0x16, 0x4e, 0x8d, 0xfe, 0x3d, 0x65, 0xa6, 0x2f, 0xec, 0xb4, 0x77, 0x4, 0xc7, 0x9f, 0x5c, 0x79, 0xba, 0xe2, 0x21, 0x52, 0x91, 0xc9, 0xa, 0xc6, 0x5, 0x5d, 0x9e, 0xed, 0x2e, 0x76, 0xb5, 0x90, 0x53, 0xb, 0xc8, 0xbb, 0x78, 0x20, 0xe3, 0x6a, 0xa9, 0xf1, 0x32, 0x41, 0x82, 0xda, 0x19, 0x3c, 0xff, 0xa7, 0x64, 0x17, 0xd4, 0x8c, 0x4f},
+ {0x0, 0xc4, 0x95, 0x51, 0x37, 0xf3, 0xa2, 0x66, 0x6e, 0xaa, 0xfb, 0x3f, 0x59, 0x9d, 0xcc, 0x8, 0xdc, 0x18, 0x49, 0x8d, 0xeb, 0x2f, 0x7e, 0xba, 0xb2, 0x76, 0x27, 0xe3, 0x85, 0x41, 0x10, 0xd4, 0xa5, 0x61, 0x30, 0xf4, 0x92, 0x56, 0x7, 0xc3, 0xcb, 0xf, 0x5e, 0x9a, 0xfc, 0x38, 0x69, 0xad, 0x79, 0xbd, 0xec, 0x28, 0x4e, 0x8a, 0xdb, 0x1f, 0x17, 0xd3, 0x82, 0x46, 0x20, 0xe4, 0xb5, 0x71, 0x57, 0x93, 0xc2, 0x6, 0x60, 0xa4, 0xf5, 0x31, 0x39, 0xfd, 0xac, 0x68, 0xe, 0xca, 0x9b, 0x5f, 0x8b, 0x4f, 0x1e, 0xda, 0xbc, 0x78, 0x29, 0xed, 0xe5, 0x21, 0x70, 0xb4, 0xd2, 0x16, 0x47, 0x83, 0xf2, 0x36, 0x67, 0xa3, 0xc5, 0x1, 0x50, 0x94, 0x9c, 0x58, 0x9, 0xcd, 0xab, 0x6f, 0x3e, 0xfa, 0x2e, 0xea, 0xbb, 0x7f, 0x19, 0xdd, 0x8c, 0x48, 0x40, 0x84, 0xd5, 0x11, 0x77, 0xb3, 0xe2, 0x26, 0xae, 0x6a, 0x3b, 0xff, 0x99, 0x5d, 0xc, 0xc8, 0xc0, 0x4, 0x55, 0x91, 0xf7, 0x33, 0x62, 0xa6, 0x72, 0xb6, 0xe7, 0x23, 0x45, 0x81, 0xd0, 0x14, 0x1c, 0xd8, 0x89, 0x4d, 0x2b, 0xef, 0xbe, 0x7a, 0xb, 0xcf, 0x9e, 0x5a, 0x3c, 0xf8, 0xa9, 0x6d, 0x65, 0xa1, 0xf0, 0x34, 0x52, 0x96, 0xc7, 0x3, 0xd7, 0x13, 0x42, 0x86, 0xe0, 0x24, 0x75, 0xb1, 0xb9, 0x7d, 0x2c, 0xe8, 0x8e, 0x4a, 0x1b, 0xdf, 0xf9, 0x3d, 0x6c, 0xa8, 0xce, 0xa, 0x5b, 0x9f, 0x97, 0x53, 0x2, 0xc6, 0xa0, 0x64, 0x35, 0xf1, 0x25, 0xe1, 0xb0, 0x74, 0x12, 0xd6, 0x87, 0x43, 0x4b, 0x8f, 0xde, 0x1a, 0x7c, 0xb8, 0xe9, 0x2d, 0x5c, 0x98, 0xc9, 0xd, 0x6b, 0xaf, 0xfe, 0x3a, 0x32, 0xf6, 0xa7, 0x63, 0x5, 0xc1, 0x90, 0x54, 0x80, 0x44, 0x15, 0xd1, 0xb7, 0x73, 0x22, 0xe6, 0xee, 0x2a, 0x7b, 0xbf, 0xd9, 0x1d, 0x4c, 0x88},
+ {0x0, 0xc5, 0x97, 0x52, 0x33, 0xf6, 0xa4, 0x61, 0x66, 0xa3, 0xf1, 0x34, 0x55, 0x90, 0xc2, 0x7, 0xcc, 0x9, 0x5b, 0x9e, 0xff, 0x3a, 0x68, 0xad, 0xaa, 0x6f, 0x3d, 0xf8, 0x99, 0x5c, 0xe, 0xcb, 0x85, 0x40, 0x12, 0xd7, 0xb6, 0x73, 0x21, 0xe4, 0xe3, 0x26, 0x74, 0xb1, 0xd0, 0x15, 0x47, 0x82, 0x49, 0x8c, 0xde, 0x1b, 0x7a, 0xbf, 0xed, 0x28, 0x2f, 0xea, 0xb8, 0x7d, 0x1c, 0xd9, 0x8b, 0x4e, 0x17, 0xd2, 0x80, 0x45, 0x24, 0xe1, 0xb3, 0x76, 0x71, 0xb4, 0xe6, 0x23, 0x42, 0x87, 0xd5, 0x10, 0xdb, 0x1e, 0x4c, 0x89, 0xe8, 0x2d, 0x7f, 0xba, 0xbd, 0x78, 0x2a, 0xef, 0x8e, 0x4b, 0x19, 0xdc, 0x92, 0x57, 0x5, 0xc0, 0xa1, 0x64, 0x36, 0xf3, 0xf4, 0x31, 0x63, 0xa6, 0xc7, 0x2, 0x50, 0x95, 0x5e, 0x9b, 0xc9, 0xc, 0x6d, 0xa8, 0xfa, 0x3f, 0x38, 0xfd, 0xaf, 0x6a, 0xb, 0xce, 0x9c, 0x59, 0x2e, 0xeb, 0xb9, 0x7c, 0x1d, 0xd8, 0x8a, 0x4f, 0x48, 0x8d, 0xdf, 0x1a, 0x7b, 0xbe, 0xec, 0x29, 0xe2, 0x27, 0x75, 0xb0, 0xd1, 0x14, 0x46, 0x83, 0x84, 0x41, 0x13, 0xd6, 0xb7, 0x72, 0x20, 0xe5, 0xab, 0x6e, 0x3c, 0xf9, 0x98, 0x5d, 0xf, 0xca, 0xcd, 0x8, 0x5a, 0x9f, 0xfe, 0x3b, 0x69, 0xac, 0x67, 0xa2, 0xf0, 0x35, 0x54, 0x91, 0xc3, 0x6, 0x1, 0xc4, 0x96, 0x53, 0x32, 0xf7, 0xa5, 0x60, 0x39, 0xfc, 0xae, 0x6b, 0xa, 0xcf, 0x9d, 0x58, 0x5f, 0x9a, 0xc8, 0xd, 0x6c, 0xa9, 0xfb, 0x3e, 0xf5, 0x30, 0x62, 0xa7, 0xc6, 0x3, 0x51, 0x94, 0x93, 0x56, 0x4, 0xc1, 0xa0, 0x65, 0x37, 0xf2, 0xbc, 0x79, 0x2b, 0xee, 0x8f, 0x4a, 0x18, 0xdd, 0xda, 0x1f, 0x4d, 0x88, 0xe9, 0x2c, 0x7e, 0xbb, 0x70, 0xb5, 0xe7, 0x22, 0x43, 0x86, 0xd4, 0x11, 0x16, 0xd3, 0x81, 0x44, 0x25, 0xe0, 0xb2, 0x77},
+ {0x0, 0xc6, 0x91, 0x57, 0x3f, 0xf9, 0xae, 0x68, 0x7e, 0xb8, 0xef, 0x29, 0x41, 0x87, 0xd0, 0x16, 0xfc, 0x3a, 0x6d, 0xab, 0xc3, 0x5, 0x52, 0x94, 0x82, 0x44, 0x13, 0xd5, 0xbd, 0x7b, 0x2c, 0xea, 0xe5, 0x23, 0x74, 0xb2, 0xda, 0x1c, 0x4b, 0x8d, 0x9b, 0x5d, 0xa, 0xcc, 0xa4, 0x62, 0x35, 0xf3, 0x19, 0xdf, 0x88, 0x4e, 0x26, 0xe0, 0xb7, 0x71, 0x67, 0xa1, 0xf6, 0x30, 0x58, 0x9e, 0xc9, 0xf, 0xd7, 0x11, 0x46, 0x80, 0xe8, 0x2e, 0x79, 0xbf, 0xa9, 0x6f, 0x38, 0xfe, 0x96, 0x50, 0x7, 0xc1, 0x2b, 0xed, 0xba, 0x7c, 0x14, 0xd2, 0x85, 0x43, 0x55, 0x93, 0xc4, 0x2, 0x6a, 0xac, 0xfb, 0x3d, 0x32, 0xf4, 0xa3, 0x65, 0xd, 0xcb, 0x9c, 0x5a, 0x4c, 0x8a, 0xdd, 0x1b, 0x73, 0xb5, 0xe2, 0x24, 0xce, 0x8, 0x5f, 0x99, 0xf1, 0x37, 0x60, 0xa6, 0xb0, 0x76, 0x21, 0xe7, 0x8f, 0x49, 0x1e, 0xd8, 0xb3, 0x75, 0x22, 0xe4, 0x8c, 0x4a, 0x1d, 0xdb, 0xcd, 0xb, 0x5c, 0x9a, 0xf2, 0x34, 0x63, 0xa5, 0x4f, 0x89, 0xde, 0x18, 0x70, 0xb6, 0xe1, 0x27, 0x31, 0xf7, 0xa0, 0x66, 0xe, 0xc8, 0x9f, 0x59, 0x56, 0x90, 0xc7, 0x1, 0x69, 0xaf, 0xf8, 0x3e, 0x28, 0xee, 0xb9, 0x7f, 0x17, 0xd1, 0x86, 0x40, 0xaa, 0x6c, 0x3b, 0xfd, 0x95, 0x53, 0x4, 0xc2, 0xd4, 0x12, 0x45, 0x83, 0xeb, 0x2d, 0x7a, 0xbc, 0x64, 0xa2, 0xf5, 0x33, 0x5b, 0x9d, 0xca, 0xc, 0x1a, 0xdc, 0x8b, 0x4d, 0x25, 0xe3, 0xb4, 0x72, 0x98, 0x5e, 0x9, 0xcf, 0xa7, 0x61, 0x36, 0xf0, 0xe6, 0x20, 0x77, 0xb1, 0xd9, 0x1f, 0x48, 0x8e, 0x81, 0x47, 0x10, 0xd6, 0xbe, 0x78, 0x2f, 0xe9, 0xff, 0x39, 0x6e, 0xa8, 0xc0, 0x6, 0x51, 0x97, 0x7d, 0xbb, 0xec, 0x2a, 0x42, 0x84, 0xd3, 0x15, 0x3, 0xc5, 0x92, 0x54, 0x3c, 0xfa, 0xad, 0x6b},
+ {0x0, 0xc7, 0x93, 0x54, 0x3b, 0xfc, 0xa8, 0x6f, 0x76, 0xb1, 0xe5, 0x22, 0x4d, 0x8a, 0xde, 0x19, 0xec, 0x2b, 0x7f, 0xb8, 0xd7, 0x10, 0x44, 0x83, 0x9a, 0x5d, 0x9, 0xce, 0xa1, 0x66, 0x32, 0xf5, 0xc5, 0x2, 0x56, 0x91, 0xfe, 0x39, 0x6d, 0xaa, 0xb3, 0x74, 0x20, 0xe7, 0x88, 0x4f, 0x1b, 0xdc, 0x29, 0xee, 0xba, 0x7d, 0x12, 0xd5, 0x81, 0x46, 0x5f, 0x98, 0xcc, 0xb, 0x64, 0xa3, 0xf7, 0x30, 0x97, 0x50, 0x4, 0xc3, 0xac, 0x6b, 0x3f, 0xf8, 0xe1, 0x26, 0x72, 0xb5, 0xda, 0x1d, 0x49, 0x8e, 0x7b, 0xbc, 0xe8, 0x2f, 0x40, 0x87, 0xd3, 0x14, 0xd, 0xca, 0x9e, 0x59, 0x36, 0xf1, 0xa5, 0x62, 0x52, 0x95, 0xc1, 0x6, 0x69, 0xae, 0xfa, 0x3d, 0x24, 0xe3, 0xb7, 0x70, 0x1f, 0xd8, 0x8c, 0x4b, 0xbe, 0x79, 0x2d, 0xea, 0x85, 0x42, 0x16, 0xd1, 0xc8, 0xf, 0x5b, 0x9c, 0xf3, 0x34, 0x60, 0xa7, 0x33, 0xf4, 0xa0, 0x67, 0x8, 0xcf, 0x9b, 0x5c, 0x45, 0x82, 0xd6, 0x11, 0x7e, 0xb9, 0xed, 0x2a, 0xdf, 0x18, 0x4c, 0x8b, 0xe4, 0x23, 0x77, 0xb0, 0xa9, 0x6e, 0x3a, 0xfd, 0x92, 0x55, 0x1, 0xc6, 0xf6, 0x31, 0x65, 0xa2, 0xcd, 0xa, 0x5e, 0x99, 0x80, 0x47, 0x13, 0xd4, 0xbb, 0x7c, 0x28, 0xef, 0x1a, 0xdd, 0x89, 0x4e, 0x21, 0xe6, 0xb2, 0x75, 0x6c, 0xab, 0xff, 0x38, 0x57, 0x90, 0xc4, 0x3, 0xa4, 0x63, 0x37, 0xf0, 0x9f, 0x58, 0xc, 0xcb, 0xd2, 0x15, 0x41, 0x86, 0xe9, 0x2e, 0x7a, 0xbd, 0x48, 0x8f, 0xdb, 0x1c, 0x73, 0xb4, 0xe0, 0x27, 0x3e, 0xf9, 0xad, 0x6a, 0x5, 0xc2, 0x96, 0x51, 0x61, 0xa6, 0xf2, 0x35, 0x5a, 0x9d, 0xc9, 0xe, 0x17, 0xd0, 0x84, 0x43, 0x2c, 0xeb, 0xbf, 0x78, 0x8d, 0x4a, 0x1e, 0xd9, 0xb6, 0x71, 0x25, 0xe2, 0xfb, 0x3c, 0x68, 0xaf, 0xc0, 0x7, 0x53, 0x94},
+ {0x0, 0xc8, 0x8d, 0x45, 0x7, 0xcf, 0x8a, 0x42, 0xe, 0xc6, 0x83, 0x4b, 0x9, 0xc1, 0x84, 0x4c, 0x1c, 0xd4, 0x91, 0x59, 0x1b, 0xd3, 0x96, 0x5e, 0x12, 0xda, 0x9f, 0x57, 0x15, 0xdd, 0x98, 0x50, 0x38, 0xf0, 0xb5, 0x7d, 0x3f, 0xf7, 0xb2, 0x7a, 0x36, 0xfe, 0xbb, 0x73, 0x31, 0xf9, 0xbc, 0x74, 0x24, 0xec, 0xa9, 0x61, 0x23, 0xeb, 0xae, 0x66, 0x2a, 0xe2, 0xa7, 0x6f, 0x2d, 0xe5, 0xa0, 0x68, 0x70, 0xb8, 0xfd, 0x35, 0x77, 0xbf, 0xfa, 0x32, 0x7e, 0xb6, 0xf3, 0x3b, 0x79, 0xb1, 0xf4, 0x3c, 0x6c, 0xa4, 0xe1, 0x29, 0x6b, 0xa3, 0xe6, 0x2e, 0x62, 0xaa, 0xef, 0x27, 0x65, 0xad, 0xe8, 0x20, 0x48, 0x80, 0xc5, 0xd, 0x4f, 0x87, 0xc2, 0xa, 0x46, 0x8e, 0xcb, 0x3, 0x41, 0x89, 0xcc, 0x4, 0x54, 0x9c, 0xd9, 0x11, 0x53, 0x9b, 0xde, 0x16, 0x5a, 0x92, 0xd7, 0x1f, 0x5d, 0x95, 0xd0, 0x18, 0xe0, 0x28, 0x6d, 0xa5, 0xe7, 0x2f, 0x6a, 0xa2, 0xee, 0x26, 0x63, 0xab, 0xe9, 0x21, 0x64, 0xac, 0xfc, 0x34, 0x71, 0xb9, 0xfb, 0x33, 0x76, 0xbe, 0xf2, 0x3a, 0x7f, 0xb7, 0xf5, 0x3d, 0x78, 0xb0, 0xd8, 0x10, 0x55, 0x9d, 0xdf, 0x17, 0x52, 0x9a, 0xd6, 0x1e, 0x5b, 0x93, 0xd1, 0x19, 0x5c, 0x94, 0xc4, 0xc, 0x49, 0x81, 0xc3, 0xb, 0x4e, 0x86, 0xca, 0x2, 0x47, 0x8f, 0xcd, 0x5, 0x40, 0x88, 0x90, 0x58, 0x1d, 0xd5, 0x97, 0x5f, 0x1a, 0xd2, 0x9e, 0x56, 0x13, 0xdb, 0x99, 0x51, 0x14, 0xdc, 0x8c, 0x44, 0x1, 0xc9, 0x8b, 0x43, 0x6, 0xce, 0x82, 0x4a, 0xf, 0xc7, 0x85, 0x4d, 0x8, 0xc0, 0xa8, 0x60, 0x25, 0xed, 0xaf, 0x67, 0x22, 0xea, 0xa6, 0x6e, 0x2b, 0xe3, 0xa1, 0x69, 0x2c, 0xe4, 0xb4, 0x7c, 0x39, 0xf1, 0xb3, 0x7b, 0x3e, 0xf6, 0xba, 0x72, 0x37, 0xff, 0xbd, 0x75, 0x30, 0xf8},
+ {0x0, 0xc9, 0x8f, 0x46, 0x3, 0xca, 0x8c, 0x45, 0x6, 0xcf, 0x89, 0x40, 0x5, 0xcc, 0x8a, 0x43, 0xc, 0xc5, 0x83, 0x4a, 0xf, 0xc6, 0x80, 0x49, 0xa, 0xc3, 0x85, 0x4c, 0x9, 0xc0, 0x86, 0x4f, 0x18, 0xd1, 0x97, 0x5e, 0x1b, 0xd2, 0x94, 0x5d, 0x1e, 0xd7, 0x91, 0x58, 0x1d, 0xd4, 0x92, 0x5b, 0x14, 0xdd, 0x9b, 0x52, 0x17, 0xde, 0x98, 0x51, 0x12, 0xdb, 0x9d, 0x54, 0x11, 0xd8, 0x9e, 0x57, 0x30, 0xf9, 0xbf, 0x76, 0x33, 0xfa, 0xbc, 0x75, 0x36, 0xff, 0xb9, 0x70, 0x35, 0xfc, 0xba, 0x73, 0x3c, 0xf5, 0xb3, 0x7a, 0x3f, 0xf6, 0xb0, 0x79, 0x3a, 0xf3, 0xb5, 0x7c, 0x39, 0xf0, 0xb6, 0x7f, 0x28, 0xe1, 0xa7, 0x6e, 0x2b, 0xe2, 0xa4, 0x6d, 0x2e, 0xe7, 0xa1, 0x68, 0x2d, 0xe4, 0xa2, 0x6b, 0x24, 0xed, 0xab, 0x62, 0x27, 0xee, 0xa8, 0x61, 0x22, 0xeb, 0xad, 0x64, 0x21, 0xe8, 0xae, 0x67, 0x60, 0xa9, 0xef, 0x26, 0x63, 0xaa, 0xec, 0x25, 0x66, 0xaf, 0xe9, 0x20, 0x65, 0xac, 0xea, 0x23, 0x6c, 0xa5, 0xe3, 0x2a, 0x6f, 0xa6, 0xe0, 0x29, 0x6a, 0xa3, 0xe5, 0x2c, 0x69, 0xa0, 0xe6, 0x2f, 0x78, 0xb1, 0xf7, 0x3e, 0x7b, 0xb2, 0xf4, 0x3d, 0x7e, 0xb7, 0xf1, 0x38, 0x7d, 0xb4, 0xf2, 0x3b, 0x74, 0xbd, 0xfb, 0x32, 0x77, 0xbe, 0xf8, 0x31, 0x72, 0xbb, 0xfd, 0x34, 0x71, 0xb8, 0xfe, 0x37, 0x50, 0x99, 0xdf, 0x16, 0x53, 0x9a, 0xdc, 0x15, 0x56, 0x9f, 0xd9, 0x10, 0x55, 0x9c, 0xda, 0x13, 0x5c, 0x95, 0xd3, 0x1a, 0x5f, 0x96, 0xd0, 0x19, 0x5a, 0x93, 0xd5, 0x1c, 0x59, 0x90, 0xd6, 0x1f, 0x48, 0x81, 0xc7, 0xe, 0x4b, 0x82, 0xc4, 0xd, 0x4e, 0x87, 0xc1, 0x8, 0x4d, 0x84, 0xc2, 0xb, 0x44, 0x8d, 0xcb, 0x2, 0x47, 0x8e, 0xc8, 0x1, 0x42, 0x8b, 0xcd, 0x4, 0x41, 0x88, 0xce, 0x7},
+ {0x0, 0xca, 0x89, 0x43, 0xf, 0xc5, 0x86, 0x4c, 0x1e, 0xd4, 0x97, 0x5d, 0x11, 0xdb, 0x98, 0x52, 0x3c, 0xf6, 0xb5, 0x7f, 0x33, 0xf9, 0xba, 0x70, 0x22, 0xe8, 0xab, 0x61, 0x2d, 0xe7, 0xa4, 0x6e, 0x78, 0xb2, 0xf1, 0x3b, 0x77, 0xbd, 0xfe, 0x34, 0x66, 0xac, 0xef, 0x25, 0x69, 0xa3, 0xe0, 0x2a, 0x44, 0x8e, 0xcd, 0x7, 0x4b, 0x81, 0xc2, 0x8, 0x5a, 0x90, 0xd3, 0x19, 0x55, 0x9f, 0xdc, 0x16, 0xf0, 0x3a, 0x79, 0xb3, 0xff, 0x35, 0x76, 0xbc, 0xee, 0x24, 0x67, 0xad, 0xe1, 0x2b, 0x68, 0xa2, 0xcc, 0x6, 0x45, 0x8f, 0xc3, 0x9, 0x4a, 0x80, 0xd2, 0x18, 0x5b, 0x91, 0xdd, 0x17, 0x54, 0x9e, 0x88, 0x42, 0x1, 0xcb, 0x87, 0x4d, 0xe, 0xc4, 0x96, 0x5c, 0x1f, 0xd5, 0x99, 0x53, 0x10, 0xda, 0xb4, 0x7e, 0x3d, 0xf7, 0xbb, 0x71, 0x32, 0xf8, 0xaa, 0x60, 0x23, 0xe9, 0xa5, 0x6f, 0x2c, 0xe6, 0xfd, 0x37, 0x74, 0xbe, 0xf2, 0x38, 0x7b, 0xb1, 0xe3, 0x29, 0x6a, 0xa0, 0xec, 0x26, 0x65, 0xaf, 0xc1, 0xb, 0x48, 0x82, 0xce, 0x4, 0x47, 0x8d, 0xdf, 0x15, 0x56, 0x9c, 0xd0, 0x1a, 0x59, 0x93, 0x85, 0x4f, 0xc, 0xc6, 0x8a, 0x40, 0x3, 0xc9, 0x9b, 0x51, 0x12, 0xd8, 0x94, 0x5e, 0x1d, 0xd7, 0xb9, 0x73, 0x30, 0xfa, 0xb6, 0x7c, 0x3f, 0xf5, 0xa7, 0x6d, 0x2e, 0xe4, 0xa8, 0x62, 0x21, 0xeb, 0xd, 0xc7, 0x84, 0x4e, 0x2, 0xc8, 0x8b, 0x41, 0x13, 0xd9, 0x9a, 0x50, 0x1c, 0xd6, 0x95, 0x5f, 0x31, 0xfb, 0xb8, 0x72, 0x3e, 0xf4, 0xb7, 0x7d, 0x2f, 0xe5, 0xa6, 0x6c, 0x20, 0xea, 0xa9, 0x63, 0x75, 0xbf, 0xfc, 0x36, 0x7a, 0xb0, 0xf3, 0x39, 0x6b, 0xa1, 0xe2, 0x28, 0x64, 0xae, 0xed, 0x27, 0x49, 0x83, 0xc0, 0xa, 0x46, 0x8c, 0xcf, 0x5, 0x57, 0x9d, 0xde, 0x14, 0x58, 0x92, 0xd1, 0x1b},
+ {0x0, 0xcb, 0x8b, 0x40, 0xb, 0xc0, 0x80, 0x4b, 0x16, 0xdd, 0x9d, 0x56, 0x1d, 0xd6, 0x96, 0x5d, 0x2c, 0xe7, 0xa7, 0x6c, 0x27, 0xec, 0xac, 0x67, 0x3a, 0xf1, 0xb1, 0x7a, 0x31, 0xfa, 0xba, 0x71, 0x58, 0x93, 0xd3, 0x18, 0x53, 0x98, 0xd8, 0x13, 0x4e, 0x85, 0xc5, 0xe, 0x45, 0x8e, 0xce, 0x5, 0x74, 0xbf, 0xff, 0x34, 0x7f, 0xb4, 0xf4, 0x3f, 0x62, 0xa9, 0xe9, 0x22, 0x69, 0xa2, 0xe2, 0x29, 0xb0, 0x7b, 0x3b, 0xf0, 0xbb, 0x70, 0x30, 0xfb, 0xa6, 0x6d, 0x2d, 0xe6, 0xad, 0x66, 0x26, 0xed, 0x9c, 0x57, 0x17, 0xdc, 0x97, 0x5c, 0x1c, 0xd7, 0x8a, 0x41, 0x1, 0xca, 0x81, 0x4a, 0xa, 0xc1, 0xe8, 0x23, 0x63, 0xa8, 0xe3, 0x28, 0x68, 0xa3, 0xfe, 0x35, 0x75, 0xbe, 0xf5, 0x3e, 0x7e, 0xb5, 0xc4, 0xf, 0x4f, 0x84, 0xcf, 0x4, 0x44, 0x8f, 0xd2, 0x19, 0x59, 0x92, 0xd9, 0x12, 0x52, 0x99, 0x7d, 0xb6, 0xf6, 0x3d, 0x76, 0xbd, 0xfd, 0x36, 0x6b, 0xa0, 0xe0, 0x2b, 0x60, 0xab, 0xeb, 0x20, 0x51, 0x9a, 0xda, 0x11, 0x5a, 0x91, 0xd1, 0x1a, 0x47, 0x8c, 0xcc, 0x7, 0x4c, 0x87, 0xc7, 0xc, 0x25, 0xee, 0xae, 0x65, 0x2e, 0xe5, 0xa5, 0x6e, 0x33, 0xf8, 0xb8, 0x73, 0x38, 0xf3, 0xb3, 0x78, 0x9, 0xc2, 0x82, 0x49, 0x2, 0xc9, 0x89, 0x42, 0x1f, 0xd4, 0x94, 0x5f, 0x14, 0xdf, 0x9f, 0x54, 0xcd, 0x6, 0x46, 0x8d, 0xc6, 0xd, 0x4d, 0x86, 0xdb, 0x10, 0x50, 0x9b, 0xd0, 0x1b, 0x5b, 0x90, 0xe1, 0x2a, 0x6a, 0xa1, 0xea, 0x21, 0x61, 0xaa, 0xf7, 0x3c, 0x7c, 0xb7, 0xfc, 0x37, 0x77, 0xbc, 0x95, 0x5e, 0x1e, 0xd5, 0x9e, 0x55, 0x15, 0xde, 0x83, 0x48, 0x8, 0xc3, 0x88, 0x43, 0x3, 0xc8, 0xb9, 0x72, 0x32, 0xf9, 0xb2, 0x79, 0x39, 0xf2, 0xaf, 0x64, 0x24, 0xef, 0xa4, 0x6f, 0x2f, 0xe4},
+ {0x0, 0xcc, 0x85, 0x49, 0x17, 0xdb, 0x92, 0x5e, 0x2e, 0xe2, 0xab, 0x67, 0x39, 0xf5, 0xbc, 0x70, 0x5c, 0x90, 0xd9, 0x15, 0x4b, 0x87, 0xce, 0x2, 0x72, 0xbe, 0xf7, 0x3b, 0x65, 0xa9, 0xe0, 0x2c, 0xb8, 0x74, 0x3d, 0xf1, 0xaf, 0x63, 0x2a, 0xe6, 0x96, 0x5a, 0x13, 0xdf, 0x81, 0x4d, 0x4, 0xc8, 0xe4, 0x28, 0x61, 0xad, 0xf3, 0x3f, 0x76, 0xba, 0xca, 0x6, 0x4f, 0x83, 0xdd, 0x11, 0x58, 0x94, 0x6d, 0xa1, 0xe8, 0x24, 0x7a, 0xb6, 0xff, 0x33, 0x43, 0x8f, 0xc6, 0xa, 0x54, 0x98, 0xd1, 0x1d, 0x31, 0xfd, 0xb4, 0x78, 0x26, 0xea, 0xa3, 0x6f, 0x1f, 0xd3, 0x9a, 0x56, 0x8, 0xc4, 0x8d, 0x41, 0xd5, 0x19, 0x50, 0x9c, 0xc2, 0xe, 0x47, 0x8b, 0xfb, 0x37, 0x7e, 0xb2, 0xec, 0x20, 0x69, 0xa5, 0x89, 0x45, 0xc, 0xc0, 0x9e, 0x52, 0x1b, 0xd7, 0xa7, 0x6b, 0x22, 0xee, 0xb0, 0x7c, 0x35, 0xf9, 0xda, 0x16, 0x5f, 0x93, 0xcd, 0x1, 0x48, 0x84, 0xf4, 0x38, 0x71, 0xbd, 0xe3, 0x2f, 0x66, 0xaa, 0x86, 0x4a, 0x3, 0xcf, 0x91, 0x5d, 0x14, 0xd8, 0xa8, 0x64, 0x2d, 0xe1, 0xbf, 0x73, 0x3a, 0xf6, 0x62, 0xae, 0xe7, 0x2b, 0x75, 0xb9, 0xf0, 0x3c, 0x4c, 0x80, 0xc9, 0x5, 0x5b, 0x97, 0xde, 0x12, 0x3e, 0xf2, 0xbb, 0x77, 0x29, 0xe5, 0xac, 0x60, 0x10, 0xdc, 0x95, 0x59, 0x7, 0xcb, 0x82, 0x4e, 0xb7, 0x7b, 0x32, 0xfe, 0xa0, 0x6c, 0x25, 0xe9, 0x99, 0x55, 0x1c, 0xd0, 0x8e, 0x42, 0xb, 0xc7, 0xeb, 0x27, 0x6e, 0xa2, 0xfc, 0x30, 0x79, 0xb5, 0xc5, 0x9, 0x40, 0x8c, 0xd2, 0x1e, 0x57, 0x9b, 0xf, 0xc3, 0x8a, 0x46, 0x18, 0xd4, 0x9d, 0x51, 0x21, 0xed, 0xa4, 0x68, 0x36, 0xfa, 0xb3, 0x7f, 0x53, 0x9f, 0xd6, 0x1a, 0x44, 0x88, 0xc1, 0xd, 0x7d, 0xb1, 0xf8, 0x34, 0x6a, 0xa6, 0xef, 0x23},
+ {0x0, 0xcd, 0x87, 0x4a, 0x13, 0xde, 0x94, 0x59, 0x26, 0xeb, 0xa1, 0x6c, 0x35, 0xf8, 0xb2, 0x7f, 0x4c, 0x81, 0xcb, 0x6, 0x5f, 0x92, 0xd8, 0x15, 0x6a, 0xa7, 0xed, 0x20, 0x79, 0xb4, 0xfe, 0x33, 0x98, 0x55, 0x1f, 0xd2, 0x8b, 0x46, 0xc, 0xc1, 0xbe, 0x73, 0x39, 0xf4, 0xad, 0x60, 0x2a, 0xe7, 0xd4, 0x19, 0x53, 0x9e, 0xc7, 0xa, 0x40, 0x8d, 0xf2, 0x3f, 0x75, 0xb8, 0xe1, 0x2c, 0x66, 0xab, 0x2d, 0xe0, 0xaa, 0x67, 0x3e, 0xf3, 0xb9, 0x74, 0xb, 0xc6, 0x8c, 0x41, 0x18, 0xd5, 0x9f, 0x52, 0x61, 0xac, 0xe6, 0x2b, 0x72, 0xbf, 0xf5, 0x38, 0x47, 0x8a, 0xc0, 0xd, 0x54, 0x99, 0xd3, 0x1e, 0xb5, 0x78, 0x32, 0xff, 0xa6, 0x6b, 0x21, 0xec, 0x93, 0x5e, 0x14, 0xd9, 0x80, 0x4d, 0x7, 0xca, 0xf9, 0x34, 0x7e, 0xb3, 0xea, 0x27, 0x6d, 0xa0, 0xdf, 0x12, 0x58, 0x95, 0xcc, 0x1, 0x4b, 0x86, 0x5a, 0x97, 0xdd, 0x10, 0x49, 0x84, 0xce, 0x3, 0x7c, 0xb1, 0xfb, 0x36, 0x6f, 0xa2, 0xe8, 0x25, 0x16, 0xdb, 0x91, 0x5c, 0x5, 0xc8, 0x82, 0x4f, 0x30, 0xfd, 0xb7, 0x7a, 0x23, 0xee, 0xa4, 0x69, 0xc2, 0xf, 0x45, 0x88, 0xd1, 0x1c, 0x56, 0x9b, 0xe4, 0x29, 0x63, 0xae, 0xf7, 0x3a, 0x70, 0xbd, 0x8e, 0x43, 0x9, 0xc4, 0x9d, 0x50, 0x1a, 0xd7, 0xa8, 0x65, 0x2f, 0xe2, 0xbb, 0x76, 0x3c, 0xf1, 0x77, 0xba, 0xf0, 0x3d, 0x64, 0xa9, 0xe3, 0x2e, 0x51, 0x9c, 0xd6, 0x1b, 0x42, 0x8f, 0xc5, 0x8, 0x3b, 0xf6, 0xbc, 0x71, 0x28, 0xe5, 0xaf, 0x62, 0x1d, 0xd0, 0x9a, 0x57, 0xe, 0xc3, 0x89, 0x44, 0xef, 0x22, 0x68, 0xa5, 0xfc, 0x31, 0x7b, 0xb6, 0xc9, 0x4, 0x4e, 0x83, 0xda, 0x17, 0x5d, 0x90, 0xa3, 0x6e, 0x24, 0xe9, 0xb0, 0x7d, 0x37, 0xfa, 0x85, 0x48, 0x2, 0xcf, 0x96, 0x5b, 0x11, 0xdc},
+ {0x0, 0xce, 0x81, 0x4f, 0x1f, 0xd1, 0x9e, 0x50, 0x3e, 0xf0, 0xbf, 0x71, 0x21, 0xef, 0xa0, 0x6e, 0x7c, 0xb2, 0xfd, 0x33, 0x63, 0xad, 0xe2, 0x2c, 0x42, 0x8c, 0xc3, 0xd, 0x5d, 0x93, 0xdc, 0x12, 0xf8, 0x36, 0x79, 0xb7, 0xe7, 0x29, 0x66, 0xa8, 0xc6, 0x8, 0x47, 0x89, 0xd9, 0x17, 0x58, 0x96, 0x84, 0x4a, 0x5, 0xcb, 0x9b, 0x55, 0x1a, 0xd4, 0xba, 0x74, 0x3b, 0xf5, 0xa5, 0x6b, 0x24, 0xea, 0xed, 0x23, 0x6c, 0xa2, 0xf2, 0x3c, 0x73, 0xbd, 0xd3, 0x1d, 0x52, 0x9c, 0xcc, 0x2, 0x4d, 0x83, 0x91, 0x5f, 0x10, 0xde, 0x8e, 0x40, 0xf, 0xc1, 0xaf, 0x61, 0x2e, 0xe0, 0xb0, 0x7e, 0x31, 0xff, 0x15, 0xdb, 0x94, 0x5a, 0xa, 0xc4, 0x8b, 0x45, 0x2b, 0xe5, 0xaa, 0x64, 0x34, 0xfa, 0xb5, 0x7b, 0x69, 0xa7, 0xe8, 0x26, 0x76, 0xb8, 0xf7, 0x39, 0x57, 0x99, 0xd6, 0x18, 0x48, 0x86, 0xc9, 0x7, 0xc7, 0x9, 0x46, 0x88, 0xd8, 0x16, 0x59, 0x97, 0xf9, 0x37, 0x78, 0xb6, 0xe6, 0x28, 0x67, 0xa9, 0xbb, 0x75, 0x3a, 0xf4, 0xa4, 0x6a, 0x25, 0xeb, 0x85, 0x4b, 0x4, 0xca, 0x9a, 0x54, 0x1b, 0xd5, 0x3f, 0xf1, 0xbe, 0x70, 0x20, 0xee, 0xa1, 0x6f, 0x1, 0xcf, 0x80, 0x4e, 0x1e, 0xd0, 0x9f, 0x51, 0x43, 0x8d, 0xc2, 0xc, 0x5c, 0x92, 0xdd, 0x13, 0x7d, 0xb3, 0xfc, 0x32, 0x62, 0xac, 0xe3, 0x2d, 0x2a, 0xe4, 0xab, 0x65, 0x35, 0xfb, 0xb4, 0x7a, 0x14, 0xda, 0x95, 0x5b, 0xb, 0xc5, 0x8a, 0x44, 0x56, 0x98, 0xd7, 0x19, 0x49, 0x87, 0xc8, 0x6, 0x68, 0xa6, 0xe9, 0x27, 0x77, 0xb9, 0xf6, 0x38, 0xd2, 0x1c, 0x53, 0x9d, 0xcd, 0x3, 0x4c, 0x82, 0xec, 0x22, 0x6d, 0xa3, 0xf3, 0x3d, 0x72, 0xbc, 0xae, 0x60, 0x2f, 0xe1, 0xb1, 0x7f, 0x30, 0xfe, 0x90, 0x5e, 0x11, 0xdf, 0x8f, 0x41, 0xe, 0xc0},
+ {0x0, 0xcf, 0x83, 0x4c, 0x1b, 0xd4, 0x98, 0x57, 0x36, 0xf9, 0xb5, 0x7a, 0x2d, 0xe2, 0xae, 0x61, 0x6c, 0xa3, 0xef, 0x20, 0x77, 0xb8, 0xf4, 0x3b, 0x5a, 0x95, 0xd9, 0x16, 0x41, 0x8e, 0xc2, 0xd, 0xd8, 0x17, 0x5b, 0x94, 0xc3, 0xc, 0x40, 0x8f, 0xee, 0x21, 0x6d, 0xa2, 0xf5, 0x3a, 0x76, 0xb9, 0xb4, 0x7b, 0x37, 0xf8, 0xaf, 0x60, 0x2c, 0xe3, 0x82, 0x4d, 0x1, 0xce, 0x99, 0x56, 0x1a, 0xd5, 0xad, 0x62, 0x2e, 0xe1, 0xb6, 0x79, 0x35, 0xfa, 0x9b, 0x54, 0x18, 0xd7, 0x80, 0x4f, 0x3, 0xcc, 0xc1, 0xe, 0x42, 0x8d, 0xda, 0x15, 0x59, 0x96, 0xf7, 0x38, 0x74, 0xbb, 0xec, 0x23, 0x6f, 0xa0, 0x75, 0xba, 0xf6, 0x39, 0x6e, 0xa1, 0xed, 0x22, 0x43, 0x8c, 0xc0, 0xf, 0x58, 0x97, 0xdb, 0x14, 0x19, 0xd6, 0x9a, 0x55, 0x2, 0xcd, 0x81, 0x4e, 0x2f, 0xe0, 0xac, 0x63, 0x34, 0xfb, 0xb7, 0x78, 0x47, 0x88, 0xc4, 0xb, 0x5c, 0x93, 0xdf, 0x10, 0x71, 0xbe, 0xf2, 0x3d, 0x6a, 0xa5, 0xe9, 0x26, 0x2b, 0xe4, 0xa8, 0x67, 0x30, 0xff, 0xb3, 0x7c, 0x1d, 0xd2, 0x9e, 0x51, 0x6, 0xc9, 0x85, 0x4a, 0x9f, 0x50, 0x1c, 0xd3, 0x84, 0x4b, 0x7, 0xc8, 0xa9, 0x66, 0x2a, 0xe5, 0xb2, 0x7d, 0x31, 0xfe, 0xf3, 0x3c, 0x70, 0xbf, 0xe8, 0x27, 0x6b, 0xa4, 0xc5, 0xa, 0x46, 0x89, 0xde, 0x11, 0x5d, 0x92, 0xea, 0x25, 0x69, 0xa6, 0xf1, 0x3e, 0x72, 0xbd, 0xdc, 0x13, 0x5f, 0x90, 0xc7, 0x8, 0x44, 0x8b, 0x86, 0x49, 0x5, 0xca, 0x9d, 0x52, 0x1e, 0xd1, 0xb0, 0x7f, 0x33, 0xfc, 0xab, 0x64, 0x28, 0xe7, 0x32, 0xfd, 0xb1, 0x7e, 0x29, 0xe6, 0xaa, 0x65, 0x4, 0xcb, 0x87, 0x48, 0x1f, 0xd0, 0x9c, 0x53, 0x5e, 0x91, 0xdd, 0x12, 0x45, 0x8a, 0xc6, 0x9, 0x68, 0xa7, 0xeb, 0x24, 0x73, 0xbc, 0xf0, 0x3f},
+ {0x0, 0xd0, 0xbd, 0x6d, 0x67, 0xb7, 0xda, 0xa, 0xce, 0x1e, 0x73, 0xa3, 0xa9, 0x79, 0x14, 0xc4, 0x81, 0x51, 0x3c, 0xec, 0xe6, 0x36, 0x5b, 0x8b, 0x4f, 0x9f, 0xf2, 0x22, 0x28, 0xf8, 0x95, 0x45, 0x1f, 0xcf, 0xa2, 0x72, 0x78, 0xa8, 0xc5, 0x15, 0xd1, 0x1, 0x6c, 0xbc, 0xb6, 0x66, 0xb, 0xdb, 0x9e, 0x4e, 0x23, 0xf3, 0xf9, 0x29, 0x44, 0x94, 0x50, 0x80, 0xed, 0x3d, 0x37, 0xe7, 0x8a, 0x5a, 0x3e, 0xee, 0x83, 0x53, 0x59, 0x89, 0xe4, 0x34, 0xf0, 0x20, 0x4d, 0x9d, 0x97, 0x47, 0x2a, 0xfa, 0xbf, 0x6f, 0x2, 0xd2, 0xd8, 0x8, 0x65, 0xb5, 0x71, 0xa1, 0xcc, 0x1c, 0x16, 0xc6, 0xab, 0x7b, 0x21, 0xf1, 0x9c, 0x4c, 0x46, 0x96, 0xfb, 0x2b, 0xef, 0x3f, 0x52, 0x82, 0x88, 0x58, 0x35, 0xe5, 0xa0, 0x70, 0x1d, 0xcd, 0xc7, 0x17, 0x7a, 0xaa, 0x6e, 0xbe, 0xd3, 0x3, 0x9, 0xd9, 0xb4, 0x64, 0x7c, 0xac, 0xc1, 0x11, 0x1b, 0xcb, 0xa6, 0x76, 0xb2, 0x62, 0xf, 0xdf, 0xd5, 0x5, 0x68, 0xb8, 0xfd, 0x2d, 0x40, 0x90, 0x9a, 0x4a, 0x27, 0xf7, 0x33, 0xe3, 0x8e, 0x5e, 0x54, 0x84, 0xe9, 0x39, 0x63, 0xb3, 0xde, 0xe, 0x4, 0xd4, 0xb9, 0x69, 0xad, 0x7d, 0x10, 0xc0, 0xca, 0x1a, 0x77, 0xa7, 0xe2, 0x32, 0x5f, 0x8f, 0x85, 0x55, 0x38, 0xe8, 0x2c, 0xfc, 0x91, 0x41, 0x4b, 0x9b, 0xf6, 0x26, 0x42, 0x92, 0xff, 0x2f, 0x25, 0xf5, 0x98, 0x48, 0x8c, 0x5c, 0x31, 0xe1, 0xeb, 0x3b, 0x56, 0x86, 0xc3, 0x13, 0x7e, 0xae, 0xa4, 0x74, 0x19, 0xc9, 0xd, 0xdd, 0xb0, 0x60, 0x6a, 0xba, 0xd7, 0x7, 0x5d, 0x8d, 0xe0, 0x30, 0x3a, 0xea, 0x87, 0x57, 0x93, 0x43, 0x2e, 0xfe, 0xf4, 0x24, 0x49, 0x99, 0xdc, 0xc, 0x61, 0xb1, 0xbb, 0x6b, 0x6, 0xd6, 0x12, 0xc2, 0xaf, 0x7f, 0x75, 0xa5, 0xc8, 0x18},
+ {0x0, 0xd1, 0xbf, 0x6e, 0x63, 0xb2, 0xdc, 0xd, 0xc6, 0x17, 0x79, 0xa8, 0xa5, 0x74, 0x1a, 0xcb, 0x91, 0x40, 0x2e, 0xff, 0xf2, 0x23, 0x4d, 0x9c, 0x57, 0x86, 0xe8, 0x39, 0x34, 0xe5, 0x8b, 0x5a, 0x3f, 0xee, 0x80, 0x51, 0x5c, 0x8d, 0xe3, 0x32, 0xf9, 0x28, 0x46, 0x97, 0x9a, 0x4b, 0x25, 0xf4, 0xae, 0x7f, 0x11, 0xc0, 0xcd, 0x1c, 0x72, 0xa3, 0x68, 0xb9, 0xd7, 0x6, 0xb, 0xda, 0xb4, 0x65, 0x7e, 0xaf, 0xc1, 0x10, 0x1d, 0xcc, 0xa2, 0x73, 0xb8, 0x69, 0x7, 0xd6, 0xdb, 0xa, 0x64, 0xb5, 0xef, 0x3e, 0x50, 0x81, 0x8c, 0x5d, 0x33, 0xe2, 0x29, 0xf8, 0x96, 0x47, 0x4a, 0x9b, 0xf5, 0x24, 0x41, 0x90, 0xfe, 0x2f, 0x22, 0xf3, 0x9d, 0x4c, 0x87, 0x56, 0x38, 0xe9, 0xe4, 0x35, 0x5b, 0x8a, 0xd0, 0x1, 0x6f, 0xbe, 0xb3, 0x62, 0xc, 0xdd, 0x16, 0xc7, 0xa9, 0x78, 0x75, 0xa4, 0xca, 0x1b, 0xfc, 0x2d, 0x43, 0x92, 0x9f, 0x4e, 0x20, 0xf1, 0x3a, 0xeb, 0x85, 0x54, 0x59, 0x88, 0xe6, 0x37, 0x6d, 0xbc, 0xd2, 0x3, 0xe, 0xdf, 0xb1, 0x60, 0xab, 0x7a, 0x14, 0xc5, 0xc8, 0x19, 0x77, 0xa6, 0xc3, 0x12, 0x7c, 0xad, 0xa0, 0x71, 0x1f, 0xce, 0x5, 0xd4, 0xba, 0x6b, 0x66, 0xb7, 0xd9, 0x8, 0x52, 0x83, 0xed, 0x3c, 0x31, 0xe0, 0x8e, 0x5f, 0x94, 0x45, 0x2b, 0xfa, 0xf7, 0x26, 0x48, 0x99, 0x82, 0x53, 0x3d, 0xec, 0xe1, 0x30, 0x5e, 0x8f, 0x44, 0x95, 0xfb, 0x2a, 0x27, 0xf6, 0x98, 0x49, 0x13, 0xc2, 0xac, 0x7d, 0x70, 0xa1, 0xcf, 0x1e, 0xd5, 0x4, 0x6a, 0xbb, 0xb6, 0x67, 0x9, 0xd8, 0xbd, 0x6c, 0x2, 0xd3, 0xde, 0xf, 0x61, 0xb0, 0x7b, 0xaa, 0xc4, 0x15, 0x18, 0xc9, 0xa7, 0x76, 0x2c, 0xfd, 0x93, 0x42, 0x4f, 0x9e, 0xf0, 0x21, 0xea, 0x3b, 0x55, 0x84, 0x89, 0x58, 0x36, 0xe7},
+ {0x0, 0xd2, 0xb9, 0x6b, 0x6f, 0xbd, 0xd6, 0x4, 0xde, 0xc, 0x67, 0xb5, 0xb1, 0x63, 0x8, 0xda, 0xa1, 0x73, 0x18, 0xca, 0xce, 0x1c, 0x77, 0xa5, 0x7f, 0xad, 0xc6, 0x14, 0x10, 0xc2, 0xa9, 0x7b, 0x5f, 0x8d, 0xe6, 0x34, 0x30, 0xe2, 0x89, 0x5b, 0x81, 0x53, 0x38, 0xea, 0xee, 0x3c, 0x57, 0x85, 0xfe, 0x2c, 0x47, 0x95, 0x91, 0x43, 0x28, 0xfa, 0x20, 0xf2, 0x99, 0x4b, 0x4f, 0x9d, 0xf6, 0x24, 0xbe, 0x6c, 0x7, 0xd5, 0xd1, 0x3, 0x68, 0xba, 0x60, 0xb2, 0xd9, 0xb, 0xf, 0xdd, 0xb6, 0x64, 0x1f, 0xcd, 0xa6, 0x74, 0x70, 0xa2, 0xc9, 0x1b, 0xc1, 0x13, 0x78, 0xaa, 0xae, 0x7c, 0x17, 0xc5, 0xe1, 0x33, 0x58, 0x8a, 0x8e, 0x5c, 0x37, 0xe5, 0x3f, 0xed, 0x86, 0x54, 0x50, 0x82, 0xe9, 0x3b, 0x40, 0x92, 0xf9, 0x2b, 0x2f, 0xfd, 0x96, 0x44, 0x9e, 0x4c, 0x27, 0xf5, 0xf1, 0x23, 0x48, 0x9a, 0x61, 0xb3, 0xd8, 0xa, 0xe, 0xdc, 0xb7, 0x65, 0xbf, 0x6d, 0x6, 0xd4, 0xd0, 0x2, 0x69, 0xbb, 0xc0, 0x12, 0x79, 0xab, 0xaf, 0x7d, 0x16, 0xc4, 0x1e, 0xcc, 0xa7, 0x75, 0x71, 0xa3, 0xc8, 0x1a, 0x3e, 0xec, 0x87, 0x55, 0x51, 0x83, 0xe8, 0x3a, 0xe0, 0x32, 0x59, 0x8b, 0x8f, 0x5d, 0x36, 0xe4, 0x9f, 0x4d, 0x26, 0xf4, 0xf0, 0x22, 0x49, 0x9b, 0x41, 0x93, 0xf8, 0x2a, 0x2e, 0xfc, 0x97, 0x45, 0xdf, 0xd, 0x66, 0xb4, 0xb0, 0x62, 0x9, 0xdb, 0x1, 0xd3, 0xb8, 0x6a, 0x6e, 0xbc, 0xd7, 0x5, 0x7e, 0xac, 0xc7, 0x15, 0x11, 0xc3, 0xa8, 0x7a, 0xa0, 0x72, 0x19, 0xcb, 0xcf, 0x1d, 0x76, 0xa4, 0x80, 0x52, 0x39, 0xeb, 0xef, 0x3d, 0x56, 0x84, 0x5e, 0x8c, 0xe7, 0x35, 0x31, 0xe3, 0x88, 0x5a, 0x21, 0xf3, 0x98, 0x4a, 0x4e, 0x9c, 0xf7, 0x25, 0xff, 0x2d, 0x46, 0x94, 0x90, 0x42, 0x29, 0xfb},
+ {0x0, 0xd3, 0xbb, 0x68, 0x6b, 0xb8, 0xd0, 0x3, 0xd6, 0x5, 0x6d, 0xbe, 0xbd, 0x6e, 0x6, 0xd5, 0xb1, 0x62, 0xa, 0xd9, 0xda, 0x9, 0x61, 0xb2, 0x67, 0xb4, 0xdc, 0xf, 0xc, 0xdf, 0xb7, 0x64, 0x7f, 0xac, 0xc4, 0x17, 0x14, 0xc7, 0xaf, 0x7c, 0xa9, 0x7a, 0x12, 0xc1, 0xc2, 0x11, 0x79, 0xaa, 0xce, 0x1d, 0x75, 0xa6, 0xa5, 0x76, 0x1e, 0xcd, 0x18, 0xcb, 0xa3, 0x70, 0x73, 0xa0, 0xc8, 0x1b, 0xfe, 0x2d, 0x45, 0x96, 0x95, 0x46, 0x2e, 0xfd, 0x28, 0xfb, 0x93, 0x40, 0x43, 0x90, 0xf8, 0x2b, 0x4f, 0x9c, 0xf4, 0x27, 0x24, 0xf7, 0x9f, 0x4c, 0x99, 0x4a, 0x22, 0xf1, 0xf2, 0x21, 0x49, 0x9a, 0x81, 0x52, 0x3a, 0xe9, 0xea, 0x39, 0x51, 0x82, 0x57, 0x84, 0xec, 0x3f, 0x3c, 0xef, 0x87, 0x54, 0x30, 0xe3, 0x8b, 0x58, 0x5b, 0x88, 0xe0, 0x33, 0xe6, 0x35, 0x5d, 0x8e, 0x8d, 0x5e, 0x36, 0xe5, 0xe1, 0x32, 0x5a, 0x89, 0x8a, 0x59, 0x31, 0xe2, 0x37, 0xe4, 0x8c, 0x5f, 0x5c, 0x8f, 0xe7, 0x34, 0x50, 0x83, 0xeb, 0x38, 0x3b, 0xe8, 0x80, 0x53, 0x86, 0x55, 0x3d, 0xee, 0xed, 0x3e, 0x56, 0x85, 0x9e, 0x4d, 0x25, 0xf6, 0xf5, 0x26, 0x4e, 0x9d, 0x48, 0x9b, 0xf3, 0x20, 0x23, 0xf0, 0x98, 0x4b, 0x2f, 0xfc, 0x94, 0x47, 0x44, 0x97, 0xff, 0x2c, 0xf9, 0x2a, 0x42, 0x91, 0x92, 0x41, 0x29, 0xfa, 0x1f, 0xcc, 0xa4, 0x77, 0x74, 0xa7, 0xcf, 0x1c, 0xc9, 0x1a, 0x72, 0xa1, 0xa2, 0x71, 0x19, 0xca, 0xae, 0x7d, 0x15, 0xc6, 0xc5, 0x16, 0x7e, 0xad, 0x78, 0xab, 0xc3, 0x10, 0x13, 0xc0, 0xa8, 0x7b, 0x60, 0xb3, 0xdb, 0x8, 0xb, 0xd8, 0xb0, 0x63, 0xb6, 0x65, 0xd, 0xde, 0xdd, 0xe, 0x66, 0xb5, 0xd1, 0x2, 0x6a, 0xb9, 0xba, 0x69, 0x1, 0xd2, 0x7, 0xd4, 0xbc, 0x6f, 0x6c, 0xbf, 0xd7, 0x4},
+ {0x0, 0xd4, 0xb5, 0x61, 0x77, 0xa3, 0xc2, 0x16, 0xee, 0x3a, 0x5b, 0x8f, 0x99, 0x4d, 0x2c, 0xf8, 0xc1, 0x15, 0x74, 0xa0, 0xb6, 0x62, 0x3, 0xd7, 0x2f, 0xfb, 0x9a, 0x4e, 0x58, 0x8c, 0xed, 0x39, 0x9f, 0x4b, 0x2a, 0xfe, 0xe8, 0x3c, 0x5d, 0x89, 0x71, 0xa5, 0xc4, 0x10, 0x6, 0xd2, 0xb3, 0x67, 0x5e, 0x8a, 0xeb, 0x3f, 0x29, 0xfd, 0x9c, 0x48, 0xb0, 0x64, 0x5, 0xd1, 0xc7, 0x13, 0x72, 0xa6, 0x23, 0xf7, 0x96, 0x42, 0x54, 0x80, 0xe1, 0x35, 0xcd, 0x19, 0x78, 0xac, 0xba, 0x6e, 0xf, 0xdb, 0xe2, 0x36, 0x57, 0x83, 0x95, 0x41, 0x20, 0xf4, 0xc, 0xd8, 0xb9, 0x6d, 0x7b, 0xaf, 0xce, 0x1a, 0xbc, 0x68, 0x9, 0xdd, 0xcb, 0x1f, 0x7e, 0xaa, 0x52, 0x86, 0xe7, 0x33, 0x25, 0xf1, 0x90, 0x44, 0x7d, 0xa9, 0xc8, 0x1c, 0xa, 0xde, 0xbf, 0x6b, 0x93, 0x47, 0x26, 0xf2, 0xe4, 0x30, 0x51, 0x85, 0x46, 0x92, 0xf3, 0x27, 0x31, 0xe5, 0x84, 0x50, 0xa8, 0x7c, 0x1d, 0xc9, 0xdf, 0xb, 0x6a, 0xbe, 0x87, 0x53, 0x32, 0xe6, 0xf0, 0x24, 0x45, 0x91, 0x69, 0xbd, 0xdc, 0x8, 0x1e, 0xca, 0xab, 0x7f, 0xd9, 0xd, 0x6c, 0xb8, 0xae, 0x7a, 0x1b, 0xcf, 0x37, 0xe3, 0x82, 0x56, 0x40, 0x94, 0xf5, 0x21, 0x18, 0xcc, 0xad, 0x79, 0x6f, 0xbb, 0xda, 0xe, 0xf6, 0x22, 0x43, 0x97, 0x81, 0x55, 0x34, 0xe0, 0x65, 0xb1, 0xd0, 0x4, 0x12, 0xc6, 0xa7, 0x73, 0x8b, 0x5f, 0x3e, 0xea, 0xfc, 0x28, 0x49, 0x9d, 0xa4, 0x70, 0x11, 0xc5, 0xd3, 0x7, 0x66, 0xb2, 0x4a, 0x9e, 0xff, 0x2b, 0x3d, 0xe9, 0x88, 0x5c, 0xfa, 0x2e, 0x4f, 0x9b, 0x8d, 0x59, 0x38, 0xec, 0x14, 0xc0, 0xa1, 0x75, 0x63, 0xb7, 0xd6, 0x2, 0x3b, 0xef, 0x8e, 0x5a, 0x4c, 0x98, 0xf9, 0x2d, 0xd5, 0x1, 0x60, 0xb4, 0xa2, 0x76, 0x17, 0xc3},
+ {0x0, 0xd5, 0xb7, 0x62, 0x73, 0xa6, 0xc4, 0x11, 0xe6, 0x33, 0x51, 0x84, 0x95, 0x40, 0x22, 0xf7, 0xd1, 0x4, 0x66, 0xb3, 0xa2, 0x77, 0x15, 0xc0, 0x37, 0xe2, 0x80, 0x55, 0x44, 0x91, 0xf3, 0x26, 0xbf, 0x6a, 0x8, 0xdd, 0xcc, 0x19, 0x7b, 0xae, 0x59, 0x8c, 0xee, 0x3b, 0x2a, 0xff, 0x9d, 0x48, 0x6e, 0xbb, 0xd9, 0xc, 0x1d, 0xc8, 0xaa, 0x7f, 0x88, 0x5d, 0x3f, 0xea, 0xfb, 0x2e, 0x4c, 0x99, 0x63, 0xb6, 0xd4, 0x1, 0x10, 0xc5, 0xa7, 0x72, 0x85, 0x50, 0x32, 0xe7, 0xf6, 0x23, 0x41, 0x94, 0xb2, 0x67, 0x5, 0xd0, 0xc1, 0x14, 0x76, 0xa3, 0x54, 0x81, 0xe3, 0x36, 0x27, 0xf2, 0x90, 0x45, 0xdc, 0x9, 0x6b, 0xbe, 0xaf, 0x7a, 0x18, 0xcd, 0x3a, 0xef, 0x8d, 0x58, 0x49, 0x9c, 0xfe, 0x2b, 0xd, 0xd8, 0xba, 0x6f, 0x7e, 0xab, 0xc9, 0x1c, 0xeb, 0x3e, 0x5c, 0x89, 0x98, 0x4d, 0x2f, 0xfa, 0xc6, 0x13, 0x71, 0xa4, 0xb5, 0x60, 0x2, 0xd7, 0x20, 0xf5, 0x97, 0x42, 0x53, 0x86, 0xe4, 0x31, 0x17, 0xc2, 0xa0, 0x75, 0x64, 0xb1, 0xd3, 0x6, 0xf1, 0x24, 0x46, 0x93, 0x82, 0x57, 0x35, 0xe0, 0x79, 0xac, 0xce, 0x1b, 0xa, 0xdf, 0xbd, 0x68, 0x9f, 0x4a, 0x28, 0xfd, 0xec, 0x39, 0x5b, 0x8e, 0xa8, 0x7d, 0x1f, 0xca, 0xdb, 0xe, 0x6c, 0xb9, 0x4e, 0x9b, 0xf9, 0x2c, 0x3d, 0xe8, 0x8a, 0x5f, 0xa5, 0x70, 0x12, 0xc7, 0xd6, 0x3, 0x61, 0xb4, 0x43, 0x96, 0xf4, 0x21, 0x30, 0xe5, 0x87, 0x52, 0x74, 0xa1, 0xc3, 0x16, 0x7, 0xd2, 0xb0, 0x65, 0x92, 0x47, 0x25, 0xf0, 0xe1, 0x34, 0x56, 0x83, 0x1a, 0xcf, 0xad, 0x78, 0x69, 0xbc, 0xde, 0xb, 0xfc, 0x29, 0x4b, 0x9e, 0x8f, 0x5a, 0x38, 0xed, 0xcb, 0x1e, 0x7c, 0xa9, 0xb8, 0x6d, 0xf, 0xda, 0x2d, 0xf8, 0x9a, 0x4f, 0x5e, 0x8b, 0xe9, 0x3c},
+ {0x0, 0xd6, 0xb1, 0x67, 0x7f, 0xa9, 0xce, 0x18, 0xfe, 0x28, 0x4f, 0x99, 0x81, 0x57, 0x30, 0xe6, 0xe1, 0x37, 0x50, 0x86, 0x9e, 0x48, 0x2f, 0xf9, 0x1f, 0xc9, 0xae, 0x78, 0x60, 0xb6, 0xd1, 0x7, 0xdf, 0x9, 0x6e, 0xb8, 0xa0, 0x76, 0x11, 0xc7, 0x21, 0xf7, 0x90, 0x46, 0x5e, 0x88, 0xef, 0x39, 0x3e, 0xe8, 0x8f, 0x59, 0x41, 0x97, 0xf0, 0x26, 0xc0, 0x16, 0x71, 0xa7, 0xbf, 0x69, 0xe, 0xd8, 0xa3, 0x75, 0x12, 0xc4, 0xdc, 0xa, 0x6d, 0xbb, 0x5d, 0x8b, 0xec, 0x3a, 0x22, 0xf4, 0x93, 0x45, 0x42, 0x94, 0xf3, 0x25, 0x3d, 0xeb, 0x8c, 0x5a, 0xbc, 0x6a, 0xd, 0xdb, 0xc3, 0x15, 0x72, 0xa4, 0x7c, 0xaa, 0xcd, 0x1b, 0x3, 0xd5, 0xb2, 0x64, 0x82, 0x54, 0x33, 0xe5, 0xfd, 0x2b, 0x4c, 0x9a, 0x9d, 0x4b, 0x2c, 0xfa, 0xe2, 0x34, 0x53, 0x85, 0x63, 0xb5, 0xd2, 0x4, 0x1c, 0xca, 0xad, 0x7b, 0x5b, 0x8d, 0xea, 0x3c, 0x24, 0xf2, 0x95, 0x43, 0xa5, 0x73, 0x14, 0xc2, 0xda, 0xc, 0x6b, 0xbd, 0xba, 0x6c, 0xb, 0xdd, 0xc5, 0x13, 0x74, 0xa2, 0x44, 0x92, 0xf5, 0x23, 0x3b, 0xed, 0x8a, 0x5c, 0x84, 0x52, 0x35, 0xe3, 0xfb, 0x2d, 0x4a, 0x9c, 0x7a, 0xac, 0xcb, 0x1d, 0x5, 0xd3, 0xb4, 0x62, 0x65, 0xb3, 0xd4, 0x2, 0x1a, 0xcc, 0xab, 0x7d, 0x9b, 0x4d, 0x2a, 0xfc, 0xe4, 0x32, 0x55, 0x83, 0xf8, 0x2e, 0x49, 0x9f, 0x87, 0x51, 0x36, 0xe0, 0x6, 0xd0, 0xb7, 0x61, 0x79, 0xaf, 0xc8, 0x1e, 0x19, 0xcf, 0xa8, 0x7e, 0x66, 0xb0, 0xd7, 0x1, 0xe7, 0x31, 0x56, 0x80, 0x98, 0x4e, 0x29, 0xff, 0x27, 0xf1, 0x96, 0x40, 0x58, 0x8e, 0xe9, 0x3f, 0xd9, 0xf, 0x68, 0xbe, 0xa6, 0x70, 0x17, 0xc1, 0xc6, 0x10, 0x77, 0xa1, 0xb9, 0x6f, 0x8, 0xde, 0x38, 0xee, 0x89, 0x5f, 0x47, 0x91, 0xf6, 0x20},
+ {0x0, 0xd7, 0xb3, 0x64, 0x7b, 0xac, 0xc8, 0x1f, 0xf6, 0x21, 0x45, 0x92, 0x8d, 0x5a, 0x3e, 0xe9, 0xf1, 0x26, 0x42, 0x95, 0x8a, 0x5d, 0x39, 0xee, 0x7, 0xd0, 0xb4, 0x63, 0x7c, 0xab, 0xcf, 0x18, 0xff, 0x28, 0x4c, 0x9b, 0x84, 0x53, 0x37, 0xe0, 0x9, 0xde, 0xba, 0x6d, 0x72, 0xa5, 0xc1, 0x16, 0xe, 0xd9, 0xbd, 0x6a, 0x75, 0xa2, 0xc6, 0x11, 0xf8, 0x2f, 0x4b, 0x9c, 0x83, 0x54, 0x30, 0xe7, 0xe3, 0x34, 0x50, 0x87, 0x98, 0x4f, 0x2b, 0xfc, 0x15, 0xc2, 0xa6, 0x71, 0x6e, 0xb9, 0xdd, 0xa, 0x12, 0xc5, 0xa1, 0x76, 0x69, 0xbe, 0xda, 0xd, 0xe4, 0x33, 0x57, 0x80, 0x9f, 0x48, 0x2c, 0xfb, 0x1c, 0xcb, 0xaf, 0x78, 0x67, 0xb0, 0xd4, 0x3, 0xea, 0x3d, 0x59, 0x8e, 0x91, 0x46, 0x22, 0xf5, 0xed, 0x3a, 0x5e, 0x89, 0x96, 0x41, 0x25, 0xf2, 0x1b, 0xcc, 0xa8, 0x7f, 0x60, 0xb7, 0xd3, 0x4, 0xdb, 0xc, 0x68, 0xbf, 0xa0, 0x77, 0x13, 0xc4, 0x2d, 0xfa, 0x9e, 0x49, 0x56, 0x81, 0xe5, 0x32, 0x2a, 0xfd, 0x99, 0x4e, 0x51, 0x86, 0xe2, 0x35, 0xdc, 0xb, 0x6f, 0xb8, 0xa7, 0x70, 0x14, 0xc3, 0x24, 0xf3, 0x97, 0x40, 0x5f, 0x88, 0xec, 0x3b, 0xd2, 0x5, 0x61, 0xb6, 0xa9, 0x7e, 0x1a, 0xcd, 0xd5, 0x2, 0x66, 0xb1, 0xae, 0x79, 0x1d, 0xca, 0x23, 0xf4, 0x90, 0x47, 0x58, 0x8f, 0xeb, 0x3c, 0x38, 0xef, 0x8b, 0x5c, 0x43, 0x94, 0xf0, 0x27, 0xce, 0x19, 0x7d, 0xaa, 0xb5, 0x62, 0x6, 0xd1, 0xc9, 0x1e, 0x7a, 0xad, 0xb2, 0x65, 0x1, 0xd6, 0x3f, 0xe8, 0x8c, 0x5b, 0x44, 0x93, 0xf7, 0x20, 0xc7, 0x10, 0x74, 0xa3, 0xbc, 0x6b, 0xf, 0xd8, 0x31, 0xe6, 0x82, 0x55, 0x4a, 0x9d, 0xf9, 0x2e, 0x36, 0xe1, 0x85, 0x52, 0x4d, 0x9a, 0xfe, 0x29, 0xc0, 0x17, 0x73, 0xa4, 0xbb, 0x6c, 0x8, 0xdf},
+ {0x0, 0xd8, 0xad, 0x75, 0x47, 0x9f, 0xea, 0x32, 0x8e, 0x56, 0x23, 0xfb, 0xc9, 0x11, 0x64, 0xbc, 0x1, 0xd9, 0xac, 0x74, 0x46, 0x9e, 0xeb, 0x33, 0x8f, 0x57, 0x22, 0xfa, 0xc8, 0x10, 0x65, 0xbd, 0x2, 0xda, 0xaf, 0x77, 0x45, 0x9d, 0xe8, 0x30, 0x8c, 0x54, 0x21, 0xf9, 0xcb, 0x13, 0x66, 0xbe, 0x3, 0xdb, 0xae, 0x76, 0x44, 0x9c, 0xe9, 0x31, 0x8d, 0x55, 0x20, 0xf8, 0xca, 0x12, 0x67, 0xbf, 0x4, 0xdc, 0xa9, 0x71, 0x43, 0x9b, 0xee, 0x36, 0x8a, 0x52, 0x27, 0xff, 0xcd, 0x15, 0x60, 0xb8, 0x5, 0xdd, 0xa8, 0x70, 0x42, 0x9a, 0xef, 0x37, 0x8b, 0x53, 0x26, 0xfe, 0xcc, 0x14, 0x61, 0xb9, 0x6, 0xde, 0xab, 0x73, 0x41, 0x99, 0xec, 0x34, 0x88, 0x50, 0x25, 0xfd, 0xcf, 0x17, 0x62, 0xba, 0x7, 0xdf, 0xaa, 0x72, 0x40, 0x98, 0xed, 0x35, 0x89, 0x51, 0x24, 0xfc, 0xce, 0x16, 0x63, 0xbb, 0x8, 0xd0, 0xa5, 0x7d, 0x4f, 0x97, 0xe2, 0x3a, 0x86, 0x5e, 0x2b, 0xf3, 0xc1, 0x19, 0x6c, 0xb4, 0x9, 0xd1, 0xa4, 0x7c, 0x4e, 0x96, 0xe3, 0x3b, 0x87, 0x5f, 0x2a, 0xf2, 0xc0, 0x18, 0x6d, 0xb5, 0xa, 0xd2, 0xa7, 0x7f, 0x4d, 0x95, 0xe0, 0x38, 0x84, 0x5c, 0x29, 0xf1, 0xc3, 0x1b, 0x6e, 0xb6, 0xb, 0xd3, 0xa6, 0x7e, 0x4c, 0x94, 0xe1, 0x39, 0x85, 0x5d, 0x28, 0xf0, 0xc2, 0x1a, 0x6f, 0xb7, 0xc, 0xd4, 0xa1, 0x79, 0x4b, 0x93, 0xe6, 0x3e, 0x82, 0x5a, 0x2f, 0xf7, 0xc5, 0x1d, 0x68, 0xb0, 0xd, 0xd5, 0xa0, 0x78, 0x4a, 0x92, 0xe7, 0x3f, 0x83, 0x5b, 0x2e, 0xf6, 0xc4, 0x1c, 0x69, 0xb1, 0xe, 0xd6, 0xa3, 0x7b, 0x49, 0x91, 0xe4, 0x3c, 0x80, 0x58, 0x2d, 0xf5, 0xc7, 0x1f, 0x6a, 0xb2, 0xf, 0xd7, 0xa2, 0x7a, 0x48, 0x90, 0xe5, 0x3d, 0x81, 0x59, 0x2c, 0xf4, 0xc6, 0x1e, 0x6b, 0xb3},
+ {0x0, 0xd9, 0xaf, 0x76, 0x43, 0x9a, 0xec, 0x35, 0x86, 0x5f, 0x29, 0xf0, 0xc5, 0x1c, 0x6a, 0xb3, 0x11, 0xc8, 0xbe, 0x67, 0x52, 0x8b, 0xfd, 0x24, 0x97, 0x4e, 0x38, 0xe1, 0xd4, 0xd, 0x7b, 0xa2, 0x22, 0xfb, 0x8d, 0x54, 0x61, 0xb8, 0xce, 0x17, 0xa4, 0x7d, 0xb, 0xd2, 0xe7, 0x3e, 0x48, 0x91, 0x33, 0xea, 0x9c, 0x45, 0x70, 0xa9, 0xdf, 0x6, 0xb5, 0x6c, 0x1a, 0xc3, 0xf6, 0x2f, 0x59, 0x80, 0x44, 0x9d, 0xeb, 0x32, 0x7, 0xde, 0xa8, 0x71, 0xc2, 0x1b, 0x6d, 0xb4, 0x81, 0x58, 0x2e, 0xf7, 0x55, 0x8c, 0xfa, 0x23, 0x16, 0xcf, 0xb9, 0x60, 0xd3, 0xa, 0x7c, 0xa5, 0x90, 0x49, 0x3f, 0xe6, 0x66, 0xbf, 0xc9, 0x10, 0x25, 0xfc, 0x8a, 0x53, 0xe0, 0x39, 0x4f, 0x96, 0xa3, 0x7a, 0xc, 0xd5, 0x77, 0xae, 0xd8, 0x1, 0x34, 0xed, 0x9b, 0x42, 0xf1, 0x28, 0x5e, 0x87, 0xb2, 0x6b, 0x1d, 0xc4, 0x88, 0x51, 0x27, 0xfe, 0xcb, 0x12, 0x64, 0xbd, 0xe, 0xd7, 0xa1, 0x78, 0x4d, 0x94, 0xe2, 0x3b, 0x99, 0x40, 0x36, 0xef, 0xda, 0x3, 0x75, 0xac, 0x1f, 0xc6, 0xb0, 0x69, 0x5c, 0x85, 0xf3, 0x2a, 0xaa, 0x73, 0x5, 0xdc, 0xe9, 0x30, 0x46, 0x9f, 0x2c, 0xf5, 0x83, 0x5a, 0x6f, 0xb6, 0xc0, 0x19, 0xbb, 0x62, 0x14, 0xcd, 0xf8, 0x21, 0x57, 0x8e, 0x3d, 0xe4, 0x92, 0x4b, 0x7e, 0xa7, 0xd1, 0x8, 0xcc, 0x15, 0x63, 0xba, 0x8f, 0x56, 0x20, 0xf9, 0x4a, 0x93, 0xe5, 0x3c, 0x9, 0xd0, 0xa6, 0x7f, 0xdd, 0x4, 0x72, 0xab, 0x9e, 0x47, 0x31, 0xe8, 0x5b, 0x82, 0xf4, 0x2d, 0x18, 0xc1, 0xb7, 0x6e, 0xee, 0x37, 0x41, 0x98, 0xad, 0x74, 0x2, 0xdb, 0x68, 0xb1, 0xc7, 0x1e, 0x2b, 0xf2, 0x84, 0x5d, 0xff, 0x26, 0x50, 0x89, 0xbc, 0x65, 0x13, 0xca, 0x79, 0xa0, 0xd6, 0xf, 0x3a, 0xe3, 0x95, 0x4c},
+ {0x0, 0xda, 0xa9, 0x73, 0x4f, 0x95, 0xe6, 0x3c, 0x9e, 0x44, 0x37, 0xed, 0xd1, 0xb, 0x78, 0xa2, 0x21, 0xfb, 0x88, 0x52, 0x6e, 0xb4, 0xc7, 0x1d, 0xbf, 0x65, 0x16, 0xcc, 0xf0, 0x2a, 0x59, 0x83, 0x42, 0x98, 0xeb, 0x31, 0xd, 0xd7, 0xa4, 0x7e, 0xdc, 0x6, 0x75, 0xaf, 0x93, 0x49, 0x3a, 0xe0, 0x63, 0xb9, 0xca, 0x10, 0x2c, 0xf6, 0x85, 0x5f, 0xfd, 0x27, 0x54, 0x8e, 0xb2, 0x68, 0x1b, 0xc1, 0x84, 0x5e, 0x2d, 0xf7, 0xcb, 0x11, 0x62, 0xb8, 0x1a, 0xc0, 0xb3, 0x69, 0x55, 0x8f, 0xfc, 0x26, 0xa5, 0x7f, 0xc, 0xd6, 0xea, 0x30, 0x43, 0x99, 0x3b, 0xe1, 0x92, 0x48, 0x74, 0xae, 0xdd, 0x7, 0xc6, 0x1c, 0x6f, 0xb5, 0x89, 0x53, 0x20, 0xfa, 0x58, 0x82, 0xf1, 0x2b, 0x17, 0xcd, 0xbe, 0x64, 0xe7, 0x3d, 0x4e, 0x94, 0xa8, 0x72, 0x1, 0xdb, 0x79, 0xa3, 0xd0, 0xa, 0x36, 0xec, 0x9f, 0x45, 0x15, 0xcf, 0xbc, 0x66, 0x5a, 0x80, 0xf3, 0x29, 0x8b, 0x51, 0x22, 0xf8, 0xc4, 0x1e, 0x6d, 0xb7, 0x34, 0xee, 0x9d, 0x47, 0x7b, 0xa1, 0xd2, 0x8, 0xaa, 0x70, 0x3, 0xd9, 0xe5, 0x3f, 0x4c, 0x96, 0x57, 0x8d, 0xfe, 0x24, 0x18, 0xc2, 0xb1, 0x6b, 0xc9, 0x13, 0x60, 0xba, 0x86, 0x5c, 0x2f, 0xf5, 0x76, 0xac, 0xdf, 0x5, 0x39, 0xe3, 0x90, 0x4a, 0xe8, 0x32, 0x41, 0x9b, 0xa7, 0x7d, 0xe, 0xd4, 0x91, 0x4b, 0x38, 0xe2, 0xde, 0x4, 0x77, 0xad, 0xf, 0xd5, 0xa6, 0x7c, 0x40, 0x9a, 0xe9, 0x33, 0xb0, 0x6a, 0x19, 0xc3, 0xff, 0x25, 0x56, 0x8c, 0x2e, 0xf4, 0x87, 0x5d, 0x61, 0xbb, 0xc8, 0x12, 0xd3, 0x9, 0x7a, 0xa0, 0x9c, 0x46, 0x35, 0xef, 0x4d, 0x97, 0xe4, 0x3e, 0x2, 0xd8, 0xab, 0x71, 0xf2, 0x28, 0x5b, 0x81, 0xbd, 0x67, 0x14, 0xce, 0x6c, 0xb6, 0xc5, 0x1f, 0x23, 0xf9, 0x8a, 0x50},
+ {0x0, 0xdb, 0xab, 0x70, 0x4b, 0x90, 0xe0, 0x3b, 0x96, 0x4d, 0x3d, 0xe6, 0xdd, 0x6, 0x76, 0xad, 0x31, 0xea, 0x9a, 0x41, 0x7a, 0xa1, 0xd1, 0xa, 0xa7, 0x7c, 0xc, 0xd7, 0xec, 0x37, 0x47, 0x9c, 0x62, 0xb9, 0xc9, 0x12, 0x29, 0xf2, 0x82, 0x59, 0xf4, 0x2f, 0x5f, 0x84, 0xbf, 0x64, 0x14, 0xcf, 0x53, 0x88, 0xf8, 0x23, 0x18, 0xc3, 0xb3, 0x68, 0xc5, 0x1e, 0x6e, 0xb5, 0x8e, 0x55, 0x25, 0xfe, 0xc4, 0x1f, 0x6f, 0xb4, 0x8f, 0x54, 0x24, 0xff, 0x52, 0x89, 0xf9, 0x22, 0x19, 0xc2, 0xb2, 0x69, 0xf5, 0x2e, 0x5e, 0x85, 0xbe, 0x65, 0x15, 0xce, 0x63, 0xb8, 0xc8, 0x13, 0x28, 0xf3, 0x83, 0x58, 0xa6, 0x7d, 0xd, 0xd6, 0xed, 0x36, 0x46, 0x9d, 0x30, 0xeb, 0x9b, 0x40, 0x7b, 0xa0, 0xd0, 0xb, 0x97, 0x4c, 0x3c, 0xe7, 0xdc, 0x7, 0x77, 0xac, 0x1, 0xda, 0xaa, 0x71, 0x4a, 0x91, 0xe1, 0x3a, 0x95, 0x4e, 0x3e, 0xe5, 0xde, 0x5, 0x75, 0xae, 0x3, 0xd8, 0xa8, 0x73, 0x48, 0x93, 0xe3, 0x38, 0xa4, 0x7f, 0xf, 0xd4, 0xef, 0x34, 0x44, 0x9f, 0x32, 0xe9, 0x99, 0x42, 0x79, 0xa2, 0xd2, 0x9, 0xf7, 0x2c, 0x5c, 0x87, 0xbc, 0x67, 0x17, 0xcc, 0x61, 0xba, 0xca, 0x11, 0x2a, 0xf1, 0x81, 0x5a, 0xc6, 0x1d, 0x6d, 0xb6, 0x8d, 0x56, 0x26, 0xfd, 0x50, 0x8b, 0xfb, 0x20, 0x1b, 0xc0, 0xb0, 0x6b, 0x51, 0x8a, 0xfa, 0x21, 0x1a, 0xc1, 0xb1, 0x6a, 0xc7, 0x1c, 0x6c, 0xb7, 0x8c, 0x57, 0x27, 0xfc, 0x60, 0xbb, 0xcb, 0x10, 0x2b, 0xf0, 0x80, 0x5b, 0xf6, 0x2d, 0x5d, 0x86, 0xbd, 0x66, 0x16, 0xcd, 0x33, 0xe8, 0x98, 0x43, 0x78, 0xa3, 0xd3, 0x8, 0xa5, 0x7e, 0xe, 0xd5, 0xee, 0x35, 0x45, 0x9e, 0x2, 0xd9, 0xa9, 0x72, 0x49, 0x92, 0xe2, 0x39, 0x94, 0x4f, 0x3f, 0xe4, 0xdf, 0x4, 0x74, 0xaf},
+ {0x0, 0xdc, 0xa5, 0x79, 0x57, 0x8b, 0xf2, 0x2e, 0xae, 0x72, 0xb, 0xd7, 0xf9, 0x25, 0x5c, 0x80, 0x41, 0x9d, 0xe4, 0x38, 0x16, 0xca, 0xb3, 0x6f, 0xef, 0x33, 0x4a, 0x96, 0xb8, 0x64, 0x1d, 0xc1, 0x82, 0x5e, 0x27, 0xfb, 0xd5, 0x9, 0x70, 0xac, 0x2c, 0xf0, 0x89, 0x55, 0x7b, 0xa7, 0xde, 0x2, 0xc3, 0x1f, 0x66, 0xba, 0x94, 0x48, 0x31, 0xed, 0x6d, 0xb1, 0xc8, 0x14, 0x3a, 0xe6, 0x9f, 0x43, 0x19, 0xc5, 0xbc, 0x60, 0x4e, 0x92, 0xeb, 0x37, 0xb7, 0x6b, 0x12, 0xce, 0xe0, 0x3c, 0x45, 0x99, 0x58, 0x84, 0xfd, 0x21, 0xf, 0xd3, 0xaa, 0x76, 0xf6, 0x2a, 0x53, 0x8f, 0xa1, 0x7d, 0x4, 0xd8, 0x9b, 0x47, 0x3e, 0xe2, 0xcc, 0x10, 0x69, 0xb5, 0x35, 0xe9, 0x90, 0x4c, 0x62, 0xbe, 0xc7, 0x1b, 0xda, 0x6, 0x7f, 0xa3, 0x8d, 0x51, 0x28, 0xf4, 0x74, 0xa8, 0xd1, 0xd, 0x23, 0xff, 0x86, 0x5a, 0x32, 0xee, 0x97, 0x4b, 0x65, 0xb9, 0xc0, 0x1c, 0x9c, 0x40, 0x39, 0xe5, 0xcb, 0x17, 0x6e, 0xb2, 0x73, 0xaf, 0xd6, 0xa, 0x24, 0xf8, 0x81, 0x5d, 0xdd, 0x1, 0x78, 0xa4, 0x8a, 0x56, 0x2f, 0xf3, 0xb0, 0x6c, 0x15, 0xc9, 0xe7, 0x3b, 0x42, 0x9e, 0x1e, 0xc2, 0xbb, 0x67, 0x49, 0x95, 0xec, 0x30, 0xf1, 0x2d, 0x54, 0x88, 0xa6, 0x7a, 0x3, 0xdf, 0x5f, 0x83, 0xfa, 0x26, 0x8, 0xd4, 0xad, 0x71, 0x2b, 0xf7, 0x8e, 0x52, 0x7c, 0xa0, 0xd9, 0x5, 0x85, 0x59, 0x20, 0xfc, 0xd2, 0xe, 0x77, 0xab, 0x6a, 0xb6, 0xcf, 0x13, 0x3d, 0xe1, 0x98, 0x44, 0xc4, 0x18, 0x61, 0xbd, 0x93, 0x4f, 0x36, 0xea, 0xa9, 0x75, 0xc, 0xd0, 0xfe, 0x22, 0x5b, 0x87, 0x7, 0xdb, 0xa2, 0x7e, 0x50, 0x8c, 0xf5, 0x29, 0xe8, 0x34, 0x4d, 0x91, 0xbf, 0x63, 0x1a, 0xc6, 0x46, 0x9a, 0xe3, 0x3f, 0x11, 0xcd, 0xb4, 0x68},
+ {0x0, 0xdd, 0xa7, 0x7a, 0x53, 0x8e, 0xf4, 0x29, 0xa6, 0x7b, 0x1, 0xdc, 0xf5, 0x28, 0x52, 0x8f, 0x51, 0x8c, 0xf6, 0x2b, 0x2, 0xdf, 0xa5, 0x78, 0xf7, 0x2a, 0x50, 0x8d, 0xa4, 0x79, 0x3, 0xde, 0xa2, 0x7f, 0x5, 0xd8, 0xf1, 0x2c, 0x56, 0x8b, 0x4, 0xd9, 0xa3, 0x7e, 0x57, 0x8a, 0xf0, 0x2d, 0xf3, 0x2e, 0x54, 0x89, 0xa0, 0x7d, 0x7, 0xda, 0x55, 0x88, 0xf2, 0x2f, 0x6, 0xdb, 0xa1, 0x7c, 0x59, 0x84, 0xfe, 0x23, 0xa, 0xd7, 0xad, 0x70, 0xff, 0x22, 0x58, 0x85, 0xac, 0x71, 0xb, 0xd6, 0x8, 0xd5, 0xaf, 0x72, 0x5b, 0x86, 0xfc, 0x21, 0xae, 0x73, 0x9, 0xd4, 0xfd, 0x20, 0x5a, 0x87, 0xfb, 0x26, 0x5c, 0x81, 0xa8, 0x75, 0xf, 0xd2, 0x5d, 0x80, 0xfa, 0x27, 0xe, 0xd3, 0xa9, 0x74, 0xaa, 0x77, 0xd, 0xd0, 0xf9, 0x24, 0x5e, 0x83, 0xc, 0xd1, 0xab, 0x76, 0x5f, 0x82, 0xf8, 0x25, 0xb2, 0x6f, 0x15, 0xc8, 0xe1, 0x3c, 0x46, 0x9b, 0x14, 0xc9, 0xb3, 0x6e, 0x47, 0x9a, 0xe0, 0x3d, 0xe3, 0x3e, 0x44, 0x99, 0xb0, 0x6d, 0x17, 0xca, 0x45, 0x98, 0xe2, 0x3f, 0x16, 0xcb, 0xb1, 0x6c, 0x10, 0xcd, 0xb7, 0x6a, 0x43, 0x9e, 0xe4, 0x39, 0xb6, 0x6b, 0x11, 0xcc, 0xe5, 0x38, 0x42, 0x9f, 0x41, 0x9c, 0xe6, 0x3b, 0x12, 0xcf, 0xb5, 0x68, 0xe7, 0x3a, 0x40, 0x9d, 0xb4, 0x69, 0x13, 0xce, 0xeb, 0x36, 0x4c, 0x91, 0xb8, 0x65, 0x1f, 0xc2, 0x4d, 0x90, 0xea, 0x37, 0x1e, 0xc3, 0xb9, 0x64, 0xba, 0x67, 0x1d, 0xc0, 0xe9, 0x34, 0x4e, 0x93, 0x1c, 0xc1, 0xbb, 0x66, 0x4f, 0x92, 0xe8, 0x35, 0x49, 0x94, 0xee, 0x33, 0x1a, 0xc7, 0xbd, 0x60, 0xef, 0x32, 0x48, 0x95, 0xbc, 0x61, 0x1b, 0xc6, 0x18, 0xc5, 0xbf, 0x62, 0x4b, 0x96, 0xec, 0x31, 0xbe, 0x63, 0x19, 0xc4, 0xed, 0x30, 0x4a, 0x97},
+ {0x0, 0xde, 0xa1, 0x7f, 0x5f, 0x81, 0xfe, 0x20, 0xbe, 0x60, 0x1f, 0xc1, 0xe1, 0x3f, 0x40, 0x9e, 0x61, 0xbf, 0xc0, 0x1e, 0x3e, 0xe0, 0x9f, 0x41, 0xdf, 0x1, 0x7e, 0xa0, 0x80, 0x5e, 0x21, 0xff, 0xc2, 0x1c, 0x63, 0xbd, 0x9d, 0x43, 0x3c, 0xe2, 0x7c, 0xa2, 0xdd, 0x3, 0x23, 0xfd, 0x82, 0x5c, 0xa3, 0x7d, 0x2, 0xdc, 0xfc, 0x22, 0x5d, 0x83, 0x1d, 0xc3, 0xbc, 0x62, 0x42, 0x9c, 0xe3, 0x3d, 0x99, 0x47, 0x38, 0xe6, 0xc6, 0x18, 0x67, 0xb9, 0x27, 0xf9, 0x86, 0x58, 0x78, 0xa6, 0xd9, 0x7, 0xf8, 0x26, 0x59, 0x87, 0xa7, 0x79, 0x6, 0xd8, 0x46, 0x98, 0xe7, 0x39, 0x19, 0xc7, 0xb8, 0x66, 0x5b, 0x85, 0xfa, 0x24, 0x4, 0xda, 0xa5, 0x7b, 0xe5, 0x3b, 0x44, 0x9a, 0xba, 0x64, 0x1b, 0xc5, 0x3a, 0xe4, 0x9b, 0x45, 0x65, 0xbb, 0xc4, 0x1a, 0x84, 0x5a, 0x25, 0xfb, 0xdb, 0x5, 0x7a, 0xa4, 0x2f, 0xf1, 0x8e, 0x50, 0x70, 0xae, 0xd1, 0xf, 0x91, 0x4f, 0x30, 0xee, 0xce, 0x10, 0x6f, 0xb1, 0x4e, 0x90, 0xef, 0x31, 0x11, 0xcf, 0xb0, 0x6e, 0xf0, 0x2e, 0x51, 0x8f, 0xaf, 0x71, 0xe, 0xd0, 0xed, 0x33, 0x4c, 0x92, 0xb2, 0x6c, 0x13, 0xcd, 0x53, 0x8d, 0xf2, 0x2c, 0xc, 0xd2, 0xad, 0x73, 0x8c, 0x52, 0x2d, 0xf3, 0xd3, 0xd, 0x72, 0xac, 0x32, 0xec, 0x93, 0x4d, 0x6d, 0xb3, 0xcc, 0x12, 0xb6, 0x68, 0x17, 0xc9, 0xe9, 0x37, 0x48, 0x96, 0x8, 0xd6, 0xa9, 0x77, 0x57, 0x89, 0xf6, 0x28, 0xd7, 0x9, 0x76, 0xa8, 0x88, 0x56, 0x29, 0xf7, 0x69, 0xb7, 0xc8, 0x16, 0x36, 0xe8, 0x97, 0x49, 0x74, 0xaa, 0xd5, 0xb, 0x2b, 0xf5, 0x8a, 0x54, 0xca, 0x14, 0x6b, 0xb5, 0x95, 0x4b, 0x34, 0xea, 0x15, 0xcb, 0xb4, 0x6a, 0x4a, 0x94, 0xeb, 0x35, 0xab, 0x75, 0xa, 0xd4, 0xf4, 0x2a, 0x55, 0x8b},
+ {0x0, 0xdf, 0xa3, 0x7c, 0x5b, 0x84, 0xf8, 0x27, 0xb6, 0x69, 0x15, 0xca, 0xed, 0x32, 0x4e, 0x91, 0x71, 0xae, 0xd2, 0xd, 0x2a, 0xf5, 0x89, 0x56, 0xc7, 0x18, 0x64, 0xbb, 0x9c, 0x43, 0x3f, 0xe0, 0xe2, 0x3d, 0x41, 0x9e, 0xb9, 0x66, 0x1a, 0xc5, 0x54, 0x8b, 0xf7, 0x28, 0xf, 0xd0, 0xac, 0x73, 0x93, 0x4c, 0x30, 0xef, 0xc8, 0x17, 0x6b, 0xb4, 0x25, 0xfa, 0x86, 0x59, 0x7e, 0xa1, 0xdd, 0x2, 0xd9, 0x6, 0x7a, 0xa5, 0x82, 0x5d, 0x21, 0xfe, 0x6f, 0xb0, 0xcc, 0x13, 0x34, 0xeb, 0x97, 0x48, 0xa8, 0x77, 0xb, 0xd4, 0xf3, 0x2c, 0x50, 0x8f, 0x1e, 0xc1, 0xbd, 0x62, 0x45, 0x9a, 0xe6, 0x39, 0x3b, 0xe4, 0x98, 0x47, 0x60, 0xbf, 0xc3, 0x1c, 0x8d, 0x52, 0x2e, 0xf1, 0xd6, 0x9, 0x75, 0xaa, 0x4a, 0x95, 0xe9, 0x36, 0x11, 0xce, 0xb2, 0x6d, 0xfc, 0x23, 0x5f, 0x80, 0xa7, 0x78, 0x4, 0xdb, 0xaf, 0x70, 0xc, 0xd3, 0xf4, 0x2b, 0x57, 0x88, 0x19, 0xc6, 0xba, 0x65, 0x42, 0x9d, 0xe1, 0x3e, 0xde, 0x1, 0x7d, 0xa2, 0x85, 0x5a, 0x26, 0xf9, 0x68, 0xb7, 0xcb, 0x14, 0x33, 0xec, 0x90, 0x4f, 0x4d, 0x92, 0xee, 0x31, 0x16, 0xc9, 0xb5, 0x6a, 0xfb, 0x24, 0x58, 0x87, 0xa0, 0x7f, 0x3, 0xdc, 0x3c, 0xe3, 0x9f, 0x40, 0x67, 0xb8, 0xc4, 0x1b, 0x8a, 0x55, 0x29, 0xf6, 0xd1, 0xe, 0x72, 0xad, 0x76, 0xa9, 0xd5, 0xa, 0x2d, 0xf2, 0x8e, 0x51, 0xc0, 0x1f, 0x63, 0xbc, 0x9b, 0x44, 0x38, 0xe7, 0x7, 0xd8, 0xa4, 0x7b, 0x5c, 0x83, 0xff, 0x20, 0xb1, 0x6e, 0x12, 0xcd, 0xea, 0x35, 0x49, 0x96, 0x94, 0x4b, 0x37, 0xe8, 0xcf, 0x10, 0x6c, 0xb3, 0x22, 0xfd, 0x81, 0x5e, 0x79, 0xa6, 0xda, 0x5, 0xe5, 0x3a, 0x46, 0x99, 0xbe, 0x61, 0x1d, 0xc2, 0x53, 0x8c, 0xf0, 0x2f, 0x8, 0xd7, 0xab, 0x74},
+ {0x0, 0xe0, 0xdd, 0x3d, 0xa7, 0x47, 0x7a, 0x9a, 0x53, 0xb3, 0x8e, 0x6e, 0xf4, 0x14, 0x29, 0xc9, 0xa6, 0x46, 0x7b, 0x9b, 0x1, 0xe1, 0xdc, 0x3c, 0xf5, 0x15, 0x28, 0xc8, 0x52, 0xb2, 0x8f, 0x6f, 0x51, 0xb1, 0x8c, 0x6c, 0xf6, 0x16, 0x2b, 0xcb, 0x2, 0xe2, 0xdf, 0x3f, 0xa5, 0x45, 0x78, 0x98, 0xf7, 0x17, 0x2a, 0xca, 0x50, 0xb0, 0x8d, 0x6d, 0xa4, 0x44, 0x79, 0x99, 0x3, 0xe3, 0xde, 0x3e, 0xa2, 0x42, 0x7f, 0x9f, 0x5, 0xe5, 0xd8, 0x38, 0xf1, 0x11, 0x2c, 0xcc, 0x56, 0xb6, 0x8b, 0x6b, 0x4, 0xe4, 0xd9, 0x39, 0xa3, 0x43, 0x7e, 0x9e, 0x57, 0xb7, 0x8a, 0x6a, 0xf0, 0x10, 0x2d, 0xcd, 0xf3, 0x13, 0x2e, 0xce, 0x54, 0xb4, 0x89, 0x69, 0xa0, 0x40, 0x7d, 0x9d, 0x7, 0xe7, 0xda, 0x3a, 0x55, 0xb5, 0x88, 0x68, 0xf2, 0x12, 0x2f, 0xcf, 0x6, 0xe6, 0xdb, 0x3b, 0xa1, 0x41, 0x7c, 0x9c, 0x59, 0xb9, 0x84, 0x64, 0xfe, 0x1e, 0x23, 0xc3, 0xa, 0xea, 0xd7, 0x37, 0xad, 0x4d, 0x70, 0x90, 0xff, 0x1f, 0x22, 0xc2, 0x58, 0xb8, 0x85, 0x65, 0xac, 0x4c, 0x71, 0x91, 0xb, 0xeb, 0xd6, 0x36, 0x8, 0xe8, 0xd5, 0x35, 0xaf, 0x4f, 0x72, 0x92, 0x5b, 0xbb, 0x86, 0x66, 0xfc, 0x1c, 0x21, 0xc1, 0xae, 0x4e, 0x73, 0x93, 0x9, 0xe9, 0xd4, 0x34, 0xfd, 0x1d, 0x20, 0xc0, 0x5a, 0xba, 0x87, 0x67, 0xfb, 0x1b, 0x26, 0xc6, 0x5c, 0xbc, 0x81, 0x61, 0xa8, 0x48, 0x75, 0x95, 0xf, 0xef, 0xd2, 0x32, 0x5d, 0xbd, 0x80, 0x60, 0xfa, 0x1a, 0x27, 0xc7, 0xe, 0xee, 0xd3, 0x33, 0xa9, 0x49, 0x74, 0x94, 0xaa, 0x4a, 0x77, 0x97, 0xd, 0xed, 0xd0, 0x30, 0xf9, 0x19, 0x24, 0xc4, 0x5e, 0xbe, 0x83, 0x63, 0xc, 0xec, 0xd1, 0x31, 0xab, 0x4b, 0x76, 0x96, 0x5f, 0xbf, 0x82, 0x62, 0xf8, 0x18, 0x25, 0xc5},
+ {0x0, 0xe1, 0xdf, 0x3e, 0xa3, 0x42, 0x7c, 0x9d, 0x5b, 0xba, 0x84, 0x65, 0xf8, 0x19, 0x27, 0xc6, 0xb6, 0x57, 0x69, 0x88, 0x15, 0xf4, 0xca, 0x2b, 0xed, 0xc, 0x32, 0xd3, 0x4e, 0xaf, 0x91, 0x70, 0x71, 0x90, 0xae, 0x4f, 0xd2, 0x33, 0xd, 0xec, 0x2a, 0xcb, 0xf5, 0x14, 0x89, 0x68, 0x56, 0xb7, 0xc7, 0x26, 0x18, 0xf9, 0x64, 0x85, 0xbb, 0x5a, 0x9c, 0x7d, 0x43, 0xa2, 0x3f, 0xde, 0xe0, 0x1, 0xe2, 0x3, 0x3d, 0xdc, 0x41, 0xa0, 0x9e, 0x7f, 0xb9, 0x58, 0x66, 0x87, 0x1a, 0xfb, 0xc5, 0x24, 0x54, 0xb5, 0x8b, 0x6a, 0xf7, 0x16, 0x28, 0xc9, 0xf, 0xee, 0xd0, 0x31, 0xac, 0x4d, 0x73, 0x92, 0x93, 0x72, 0x4c, 0xad, 0x30, 0xd1, 0xef, 0xe, 0xc8, 0x29, 0x17, 0xf6, 0x6b, 0x8a, 0xb4, 0x55, 0x25, 0xc4, 0xfa, 0x1b, 0x86, 0x67, 0x59, 0xb8, 0x7e, 0x9f, 0xa1, 0x40, 0xdd, 0x3c, 0x2, 0xe3, 0xd9, 0x38, 0x6, 0xe7, 0x7a, 0x9b, 0xa5, 0x44, 0x82, 0x63, 0x5d, 0xbc, 0x21, 0xc0, 0xfe, 0x1f, 0x6f, 0x8e, 0xb0, 0x51, 0xcc, 0x2d, 0x13, 0xf2, 0x34, 0xd5, 0xeb, 0xa, 0x97, 0x76, 0x48, 0xa9, 0xa8, 0x49, 0x77, 0x96, 0xb, 0xea, 0xd4, 0x35, 0xf3, 0x12, 0x2c, 0xcd, 0x50, 0xb1, 0x8f, 0x6e, 0x1e, 0xff, 0xc1, 0x20, 0xbd, 0x5c, 0x62, 0x83, 0x45, 0xa4, 0x9a, 0x7b, 0xe6, 0x7, 0x39, 0xd8, 0x3b, 0xda, 0xe4, 0x5, 0x98, 0x79, 0x47, 0xa6, 0x60, 0x81, 0xbf, 0x5e, 0xc3, 0x22, 0x1c, 0xfd, 0x8d, 0x6c, 0x52, 0xb3, 0x2e, 0xcf, 0xf1, 0x10, 0xd6, 0x37, 0x9, 0xe8, 0x75, 0x94, 0xaa, 0x4b, 0x4a, 0xab, 0x95, 0x74, 0xe9, 0x8, 0x36, 0xd7, 0x11, 0xf0, 0xce, 0x2f, 0xb2, 0x53, 0x6d, 0x8c, 0xfc, 0x1d, 0x23, 0xc2, 0x5f, 0xbe, 0x80, 0x61, 0xa7, 0x46, 0x78, 0x99, 0x4, 0xe5, 0xdb, 0x3a},
+ {0x0, 0xe2, 0xd9, 0x3b, 0xaf, 0x4d, 0x76, 0x94, 0x43, 0xa1, 0x9a, 0x78, 0xec, 0xe, 0x35, 0xd7, 0x86, 0x64, 0x5f, 0xbd, 0x29, 0xcb, 0xf0, 0x12, 0xc5, 0x27, 0x1c, 0xfe, 0x6a, 0x88, 0xb3, 0x51, 0x11, 0xf3, 0xc8, 0x2a, 0xbe, 0x5c, 0x67, 0x85, 0x52, 0xb0, 0x8b, 0x69, 0xfd, 0x1f, 0x24, 0xc6, 0x97, 0x75, 0x4e, 0xac, 0x38, 0xda, 0xe1, 0x3, 0xd4, 0x36, 0xd, 0xef, 0x7b, 0x99, 0xa2, 0x40, 0x22, 0xc0, 0xfb, 0x19, 0x8d, 0x6f, 0x54, 0xb6, 0x61, 0x83, 0xb8, 0x5a, 0xce, 0x2c, 0x17, 0xf5, 0xa4, 0x46, 0x7d, 0x9f, 0xb, 0xe9, 0xd2, 0x30, 0xe7, 0x5, 0x3e, 0xdc, 0x48, 0xaa, 0x91, 0x73, 0x33, 0xd1, 0xea, 0x8, 0x9c, 0x7e, 0x45, 0xa7, 0x70, 0x92, 0xa9, 0x4b, 0xdf, 0x3d, 0x6, 0xe4, 0xb5, 0x57, 0x6c, 0x8e, 0x1a, 0xf8, 0xc3, 0x21, 0xf6, 0x14, 0x2f, 0xcd, 0x59, 0xbb, 0x80, 0x62, 0x44, 0xa6, 0x9d, 0x7f, 0xeb, 0x9, 0x32, 0xd0, 0x7, 0xe5, 0xde, 0x3c, 0xa8, 0x4a, 0x71, 0x93, 0xc2, 0x20, 0x1b, 0xf9, 0x6d, 0x8f, 0xb4, 0x56, 0x81, 0x63, 0x58, 0xba, 0x2e, 0xcc, 0xf7, 0x15, 0x55, 0xb7, 0x8c, 0x6e, 0xfa, 0x18, 0x23, 0xc1, 0x16, 0xf4, 0xcf, 0x2d, 0xb9, 0x5b, 0x60, 0x82, 0xd3, 0x31, 0xa, 0xe8, 0x7c, 0x9e, 0xa5, 0x47, 0x90, 0x72, 0x49, 0xab, 0x3f, 0xdd, 0xe6, 0x4, 0x66, 0x84, 0xbf, 0x5d, 0xc9, 0x2b, 0x10, 0xf2, 0x25, 0xc7, 0xfc, 0x1e, 0x8a, 0x68, 0x53, 0xb1, 0xe0, 0x2, 0x39, 0xdb, 0x4f, 0xad, 0x96, 0x74, 0xa3, 0x41, 0x7a, 0x98, 0xc, 0xee, 0xd5, 0x37, 0x77, 0x95, 0xae, 0x4c, 0xd8, 0x3a, 0x1, 0xe3, 0x34, 0xd6, 0xed, 0xf, 0x9b, 0x79, 0x42, 0xa0, 0xf1, 0x13, 0x28, 0xca, 0x5e, 0xbc, 0x87, 0x65, 0xb2, 0x50, 0x6b, 0x89, 0x1d, 0xff, 0xc4, 0x26},
+ {0x0, 0xe3, 0xdb, 0x38, 0xab, 0x48, 0x70, 0x93, 0x4b, 0xa8, 0x90, 0x73, 0xe0, 0x3, 0x3b, 0xd8, 0x96, 0x75, 0x4d, 0xae, 0x3d, 0xde, 0xe6, 0x5, 0xdd, 0x3e, 0x6, 0xe5, 0x76, 0x95, 0xad, 0x4e, 0x31, 0xd2, 0xea, 0x9, 0x9a, 0x79, 0x41, 0xa2, 0x7a, 0x99, 0xa1, 0x42, 0xd1, 0x32, 0xa, 0xe9, 0xa7, 0x44, 0x7c, 0x9f, 0xc, 0xef, 0xd7, 0x34, 0xec, 0xf, 0x37, 0xd4, 0x47, 0xa4, 0x9c, 0x7f, 0x62, 0x81, 0xb9, 0x5a, 0xc9, 0x2a, 0x12, 0xf1, 0x29, 0xca, 0xf2, 0x11, 0x82, 0x61, 0x59, 0xba, 0xf4, 0x17, 0x2f, 0xcc, 0x5f, 0xbc, 0x84, 0x67, 0xbf, 0x5c, 0x64, 0x87, 0x14, 0xf7, 0xcf, 0x2c, 0x53, 0xb0, 0x88, 0x6b, 0xf8, 0x1b, 0x23, 0xc0, 0x18, 0xfb, 0xc3, 0x20, 0xb3, 0x50, 0x68, 0x8b, 0xc5, 0x26, 0x1e, 0xfd, 0x6e, 0x8d, 0xb5, 0x56, 0x8e, 0x6d, 0x55, 0xb6, 0x25, 0xc6, 0xfe, 0x1d, 0xc4, 0x27, 0x1f, 0xfc, 0x6f, 0x8c, 0xb4, 0x57, 0x8f, 0x6c, 0x54, 0xb7, 0x24, 0xc7, 0xff, 0x1c, 0x52, 0xb1, 0x89, 0x6a, 0xf9, 0x1a, 0x22, 0xc1, 0x19, 0xfa, 0xc2, 0x21, 0xb2, 0x51, 0x69, 0x8a, 0xf5, 0x16, 0x2e, 0xcd, 0x5e, 0xbd, 0x85, 0x66, 0xbe, 0x5d, 0x65, 0x86, 0x15, 0xf6, 0xce, 0x2d, 0x63, 0x80, 0xb8, 0x5b, 0xc8, 0x2b, 0x13, 0xf0, 0x28, 0xcb, 0xf3, 0x10, 0x83, 0x60, 0x58, 0xbb, 0xa6, 0x45, 0x7d, 0x9e, 0xd, 0xee, 0xd6, 0x35, 0xed, 0xe, 0x36, 0xd5, 0x46, 0xa5, 0x9d, 0x7e, 0x30, 0xd3, 0xeb, 0x8, 0x9b, 0x78, 0x40, 0xa3, 0x7b, 0x98, 0xa0, 0x43, 0xd0, 0x33, 0xb, 0xe8, 0x97, 0x74, 0x4c, 0xaf, 0x3c, 0xdf, 0xe7, 0x4, 0xdc, 0x3f, 0x7, 0xe4, 0x77, 0x94, 0xac, 0x4f, 0x1, 0xe2, 0xda, 0x39, 0xaa, 0x49, 0x71, 0x92, 0x4a, 0xa9, 0x91, 0x72, 0xe1, 0x2, 0x3a, 0xd9},
+ {0x0, 0xe4, 0xd5, 0x31, 0xb7, 0x53, 0x62, 0x86, 0x73, 0x97, 0xa6, 0x42, 0xc4, 0x20, 0x11, 0xf5, 0xe6, 0x2, 0x33, 0xd7, 0x51, 0xb5, 0x84, 0x60, 0x95, 0x71, 0x40, 0xa4, 0x22, 0xc6, 0xf7, 0x13, 0xd1, 0x35, 0x4, 0xe0, 0x66, 0x82, 0xb3, 0x57, 0xa2, 0x46, 0x77, 0x93, 0x15, 0xf1, 0xc0, 0x24, 0x37, 0xd3, 0xe2, 0x6, 0x80, 0x64, 0x55, 0xb1, 0x44, 0xa0, 0x91, 0x75, 0xf3, 0x17, 0x26, 0xc2, 0xbf, 0x5b, 0x6a, 0x8e, 0x8, 0xec, 0xdd, 0x39, 0xcc, 0x28, 0x19, 0xfd, 0x7b, 0x9f, 0xae, 0x4a, 0x59, 0xbd, 0x8c, 0x68, 0xee, 0xa, 0x3b, 0xdf, 0x2a, 0xce, 0xff, 0x1b, 0x9d, 0x79, 0x48, 0xac, 0x6e, 0x8a, 0xbb, 0x5f, 0xd9, 0x3d, 0xc, 0xe8, 0x1d, 0xf9, 0xc8, 0x2c, 0xaa, 0x4e, 0x7f, 0x9b, 0x88, 0x6c, 0x5d, 0xb9, 0x3f, 0xdb, 0xea, 0xe, 0xfb, 0x1f, 0x2e, 0xca, 0x4c, 0xa8, 0x99, 0x7d, 0x63, 0x87, 0xb6, 0x52, 0xd4, 0x30, 0x1, 0xe5, 0x10, 0xf4, 0xc5, 0x21, 0xa7, 0x43, 0x72, 0x96, 0x85, 0x61, 0x50, 0xb4, 0x32, 0xd6, 0xe7, 0x3, 0xf6, 0x12, 0x23, 0xc7, 0x41, 0xa5, 0x94, 0x70, 0xb2, 0x56, 0x67, 0x83, 0x5, 0xe1, 0xd0, 0x34, 0xc1, 0x25, 0x14, 0xf0, 0x76, 0x92, 0xa3, 0x47, 0x54, 0xb0, 0x81, 0x65, 0xe3, 0x7, 0x36, 0xd2, 0x27, 0xc3, 0xf2, 0x16, 0x90, 0x74, 0x45, 0xa1, 0xdc, 0x38, 0x9, 0xed, 0x6b, 0x8f, 0xbe, 0x5a, 0xaf, 0x4b, 0x7a, 0x9e, 0x18, 0xfc, 0xcd, 0x29, 0x3a, 0xde, 0xef, 0xb, 0x8d, 0x69, 0x58, 0xbc, 0x49, 0xad, 0x9c, 0x78, 0xfe, 0x1a, 0x2b, 0xcf, 0xd, 0xe9, 0xd8, 0x3c, 0xba, 0x5e, 0x6f, 0x8b, 0x7e, 0x9a, 0xab, 0x4f, 0xc9, 0x2d, 0x1c, 0xf8, 0xeb, 0xf, 0x3e, 0xda, 0x5c, 0xb8, 0x89, 0x6d, 0x98, 0x7c, 0x4d, 0xa9, 0x2f, 0xcb, 0xfa, 0x1e},
+ {0x0, 0xe5, 0xd7, 0x32, 0xb3, 0x56, 0x64, 0x81, 0x7b, 0x9e, 0xac, 0x49, 0xc8, 0x2d, 0x1f, 0xfa, 0xf6, 0x13, 0x21, 0xc4, 0x45, 0xa0, 0x92, 0x77, 0x8d, 0x68, 0x5a, 0xbf, 0x3e, 0xdb, 0xe9, 0xc, 0xf1, 0x14, 0x26, 0xc3, 0x42, 0xa7, 0x95, 0x70, 0x8a, 0x6f, 0x5d, 0xb8, 0x39, 0xdc, 0xee, 0xb, 0x7, 0xe2, 0xd0, 0x35, 0xb4, 0x51, 0x63, 0x86, 0x7c, 0x99, 0xab, 0x4e, 0xcf, 0x2a, 0x18, 0xfd, 0xff, 0x1a, 0x28, 0xcd, 0x4c, 0xa9, 0x9b, 0x7e, 0x84, 0x61, 0x53, 0xb6, 0x37, 0xd2, 0xe0, 0x5, 0x9, 0xec, 0xde, 0x3b, 0xba, 0x5f, 0x6d, 0x88, 0x72, 0x97, 0xa5, 0x40, 0xc1, 0x24, 0x16, 0xf3, 0xe, 0xeb, 0xd9, 0x3c, 0xbd, 0x58, 0x6a, 0x8f, 0x75, 0x90, 0xa2, 0x47, 0xc6, 0x23, 0x11, 0xf4, 0xf8, 0x1d, 0x2f, 0xca, 0x4b, 0xae, 0x9c, 0x79, 0x83, 0x66, 0x54, 0xb1, 0x30, 0xd5, 0xe7, 0x2, 0xe3, 0x6, 0x34, 0xd1, 0x50, 0xb5, 0x87, 0x62, 0x98, 0x7d, 0x4f, 0xaa, 0x2b, 0xce, 0xfc, 0x19, 0x15, 0xf0, 0xc2, 0x27, 0xa6, 0x43, 0x71, 0x94, 0x6e, 0x8b, 0xb9, 0x5c, 0xdd, 0x38, 0xa, 0xef, 0x12, 0xf7, 0xc5, 0x20, 0xa1, 0x44, 0x76, 0x93, 0x69, 0x8c, 0xbe, 0x5b, 0xda, 0x3f, 0xd, 0xe8, 0xe4, 0x1, 0x33, 0xd6, 0x57, 0xb2, 0x80, 0x65, 0x9f, 0x7a, 0x48, 0xad, 0x2c, 0xc9, 0xfb, 0x1e, 0x1c, 0xf9, 0xcb, 0x2e, 0xaf, 0x4a, 0x78, 0x9d, 0x67, 0x82, 0xb0, 0x55, 0xd4, 0x31, 0x3, 0xe6, 0xea, 0xf, 0x3d, 0xd8, 0x59, 0xbc, 0x8e, 0x6b, 0x91, 0x74, 0x46, 0xa3, 0x22, 0xc7, 0xf5, 0x10, 0xed, 0x8, 0x3a, 0xdf, 0x5e, 0xbb, 0x89, 0x6c, 0x96, 0x73, 0x41, 0xa4, 0x25, 0xc0, 0xf2, 0x17, 0x1b, 0xfe, 0xcc, 0x29, 0xa8, 0x4d, 0x7f, 0x9a, 0x60, 0x85, 0xb7, 0x52, 0xd3, 0x36, 0x4, 0xe1},
+ {0x0, 0xe6, 0xd1, 0x37, 0xbf, 0x59, 0x6e, 0x88, 0x63, 0x85, 0xb2, 0x54, 0xdc, 0x3a, 0xd, 0xeb, 0xc6, 0x20, 0x17, 0xf1, 0x79, 0x9f, 0xa8, 0x4e, 0xa5, 0x43, 0x74, 0x92, 0x1a, 0xfc, 0xcb, 0x2d, 0x91, 0x77, 0x40, 0xa6, 0x2e, 0xc8, 0xff, 0x19, 0xf2, 0x14, 0x23, 0xc5, 0x4d, 0xab, 0x9c, 0x7a, 0x57, 0xb1, 0x86, 0x60, 0xe8, 0xe, 0x39, 0xdf, 0x34, 0xd2, 0xe5, 0x3, 0x8b, 0x6d, 0x5a, 0xbc, 0x3f, 0xd9, 0xee, 0x8, 0x80, 0x66, 0x51, 0xb7, 0x5c, 0xba, 0x8d, 0x6b, 0xe3, 0x5, 0x32, 0xd4, 0xf9, 0x1f, 0x28, 0xce, 0x46, 0xa0, 0x97, 0x71, 0x9a, 0x7c, 0x4b, 0xad, 0x25, 0xc3, 0xf4, 0x12, 0xae, 0x48, 0x7f, 0x99, 0x11, 0xf7, 0xc0, 0x26, 0xcd, 0x2b, 0x1c, 0xfa, 0x72, 0x94, 0xa3, 0x45, 0x68, 0x8e, 0xb9, 0x5f, 0xd7, 0x31, 0x6, 0xe0, 0xb, 0xed, 0xda, 0x3c, 0xb4, 0x52, 0x65, 0x83, 0x7e, 0x98, 0xaf, 0x49, 0xc1, 0x27, 0x10, 0xf6, 0x1d, 0xfb, 0xcc, 0x2a, 0xa2, 0x44, 0x73, 0x95, 0xb8, 0x5e, 0x69, 0x8f, 0x7, 0xe1, 0xd6, 0x30, 0xdb, 0x3d, 0xa, 0xec, 0x64, 0x82, 0xb5, 0x53, 0xef, 0x9, 0x3e, 0xd8, 0x50, 0xb6, 0x81, 0x67, 0x8c, 0x6a, 0x5d, 0xbb, 0x33, 0xd5, 0xe2, 0x4, 0x29, 0xcf, 0xf8, 0x1e, 0x96, 0x70, 0x47, 0xa1, 0x4a, 0xac, 0x9b, 0x7d, 0xf5, 0x13, 0x24, 0xc2, 0x41, 0xa7, 0x90, 0x76, 0xfe, 0x18, 0x2f, 0xc9, 0x22, 0xc4, 0xf3, 0x15, 0x9d, 0x7b, 0x4c, 0xaa, 0x87, 0x61, 0x56, 0xb0, 0x38, 0xde, 0xe9, 0xf, 0xe4, 0x2, 0x35, 0xd3, 0x5b, 0xbd, 0x8a, 0x6c, 0xd0, 0x36, 0x1, 0xe7, 0x6f, 0x89, 0xbe, 0x58, 0xb3, 0x55, 0x62, 0x84, 0xc, 0xea, 0xdd, 0x3b, 0x16, 0xf0, 0xc7, 0x21, 0xa9, 0x4f, 0x78, 0x9e, 0x75, 0x93, 0xa4, 0x42, 0xca, 0x2c, 0x1b, 0xfd},
+ {0x0, 0xe7, 0xd3, 0x34, 0xbb, 0x5c, 0x68, 0x8f, 0x6b, 0x8c, 0xb8, 0x5f, 0xd0, 0x37, 0x3, 0xe4, 0xd6, 0x31, 0x5, 0xe2, 0x6d, 0x8a, 0xbe, 0x59, 0xbd, 0x5a, 0x6e, 0x89, 0x6, 0xe1, 0xd5, 0x32, 0xb1, 0x56, 0x62, 0x85, 0xa, 0xed, 0xd9, 0x3e, 0xda, 0x3d, 0x9, 0xee, 0x61, 0x86, 0xb2, 0x55, 0x67, 0x80, 0xb4, 0x53, 0xdc, 0x3b, 0xf, 0xe8, 0xc, 0xeb, 0xdf, 0x38, 0xb7, 0x50, 0x64, 0x83, 0x7f, 0x98, 0xac, 0x4b, 0xc4, 0x23, 0x17, 0xf0, 0x14, 0xf3, 0xc7, 0x20, 0xaf, 0x48, 0x7c, 0x9b, 0xa9, 0x4e, 0x7a, 0x9d, 0x12, 0xf5, 0xc1, 0x26, 0xc2, 0x25, 0x11, 0xf6, 0x79, 0x9e, 0xaa, 0x4d, 0xce, 0x29, 0x1d, 0xfa, 0x75, 0x92, 0xa6, 0x41, 0xa5, 0x42, 0x76, 0x91, 0x1e, 0xf9, 0xcd, 0x2a, 0x18, 0xff, 0xcb, 0x2c, 0xa3, 0x44, 0x70, 0x97, 0x73, 0x94, 0xa0, 0x47, 0xc8, 0x2f, 0x1b, 0xfc, 0xfe, 0x19, 0x2d, 0xca, 0x45, 0xa2, 0x96, 0x71, 0x95, 0x72, 0x46, 0xa1, 0x2e, 0xc9, 0xfd, 0x1a, 0x28, 0xcf, 0xfb, 0x1c, 0x93, 0x74, 0x40, 0xa7, 0x43, 0xa4, 0x90, 0x77, 0xf8, 0x1f, 0x2b, 0xcc, 0x4f, 0xa8, 0x9c, 0x7b, 0xf4, 0x13, 0x27, 0xc0, 0x24, 0xc3, 0xf7, 0x10, 0x9f, 0x78, 0x4c, 0xab, 0x99, 0x7e, 0x4a, 0xad, 0x22, 0xc5, 0xf1, 0x16, 0xf2, 0x15, 0x21, 0xc6, 0x49, 0xae, 0x9a, 0x7d, 0x81, 0x66, 0x52, 0xb5, 0x3a, 0xdd, 0xe9, 0xe, 0xea, 0xd, 0x39, 0xde, 0x51, 0xb6, 0x82, 0x65, 0x57, 0xb0, 0x84, 0x63, 0xec, 0xb, 0x3f, 0xd8, 0x3c, 0xdb, 0xef, 0x8, 0x87, 0x60, 0x54, 0xb3, 0x30, 0xd7, 0xe3, 0x4, 0x8b, 0x6c, 0x58, 0xbf, 0x5b, 0xbc, 0x88, 0x6f, 0xe0, 0x7, 0x33, 0xd4, 0xe6, 0x1, 0x35, 0xd2, 0x5d, 0xba, 0x8e, 0x69, 0x8d, 0x6a, 0x5e, 0xb9, 0x36, 0xd1, 0xe5, 0x2},
+ {0x0, 0xe8, 0xcd, 0x25, 0x87, 0x6f, 0x4a, 0xa2, 0x13, 0xfb, 0xde, 0x36, 0x94, 0x7c, 0x59, 0xb1, 0x26, 0xce, 0xeb, 0x3, 0xa1, 0x49, 0x6c, 0x84, 0x35, 0xdd, 0xf8, 0x10, 0xb2, 0x5a, 0x7f, 0x97, 0x4c, 0xa4, 0x81, 0x69, 0xcb, 0x23, 0x6, 0xee, 0x5f, 0xb7, 0x92, 0x7a, 0xd8, 0x30, 0x15, 0xfd, 0x6a, 0x82, 0xa7, 0x4f, 0xed, 0x5, 0x20, 0xc8, 0x79, 0x91, 0xb4, 0x5c, 0xfe, 0x16, 0x33, 0xdb, 0x98, 0x70, 0x55, 0xbd, 0x1f, 0xf7, 0xd2, 0x3a, 0x8b, 0x63, 0x46, 0xae, 0xc, 0xe4, 0xc1, 0x29, 0xbe, 0x56, 0x73, 0x9b, 0x39, 0xd1, 0xf4, 0x1c, 0xad, 0x45, 0x60, 0x88, 0x2a, 0xc2, 0xe7, 0xf, 0xd4, 0x3c, 0x19, 0xf1, 0x53, 0xbb, 0x9e, 0x76, 0xc7, 0x2f, 0xa, 0xe2, 0x40, 0xa8, 0x8d, 0x65, 0xf2, 0x1a, 0x3f, 0xd7, 0x75, 0x9d, 0xb8, 0x50, 0xe1, 0x9, 0x2c, 0xc4, 0x66, 0x8e, 0xab, 0x43, 0x2d, 0xc5, 0xe0, 0x8, 0xaa, 0x42, 0x67, 0x8f, 0x3e, 0xd6, 0xf3, 0x1b, 0xb9, 0x51, 0x74, 0x9c, 0xb, 0xe3, 0xc6, 0x2e, 0x8c, 0x64, 0x41, 0xa9, 0x18, 0xf0, 0xd5, 0x3d, 0x9f, 0x77, 0x52, 0xba, 0x61, 0x89, 0xac, 0x44, 0xe6, 0xe, 0x2b, 0xc3, 0x72, 0x9a, 0xbf, 0x57, 0xf5, 0x1d, 0x38, 0xd0, 0x47, 0xaf, 0x8a, 0x62, 0xc0, 0x28, 0xd, 0xe5, 0x54, 0xbc, 0x99, 0x71, 0xd3, 0x3b, 0x1e, 0xf6, 0xb5, 0x5d, 0x78, 0x90, 0x32, 0xda, 0xff, 0x17, 0xa6, 0x4e, 0x6b, 0x83, 0x21, 0xc9, 0xec, 0x4, 0x93, 0x7b, 0x5e, 0xb6, 0x14, 0xfc, 0xd9, 0x31, 0x80, 0x68, 0x4d, 0xa5, 0x7, 0xef, 0xca, 0x22, 0xf9, 0x11, 0x34, 0xdc, 0x7e, 0x96, 0xb3, 0x5b, 0xea, 0x2, 0x27, 0xcf, 0x6d, 0x85, 0xa0, 0x48, 0xdf, 0x37, 0x12, 0xfa, 0x58, 0xb0, 0x95, 0x7d, 0xcc, 0x24, 0x1, 0xe9, 0x4b, 0xa3, 0x86, 0x6e},
+ {0x0, 0xe9, 0xcf, 0x26, 0x83, 0x6a, 0x4c, 0xa5, 0x1b, 0xf2, 0xd4, 0x3d, 0x98, 0x71, 0x57, 0xbe, 0x36, 0xdf, 0xf9, 0x10, 0xb5, 0x5c, 0x7a, 0x93, 0x2d, 0xc4, 0xe2, 0xb, 0xae, 0x47, 0x61, 0x88, 0x6c, 0x85, 0xa3, 0x4a, 0xef, 0x6, 0x20, 0xc9, 0x77, 0x9e, 0xb8, 0x51, 0xf4, 0x1d, 0x3b, 0xd2, 0x5a, 0xb3, 0x95, 0x7c, 0xd9, 0x30, 0x16, 0xff, 0x41, 0xa8, 0x8e, 0x67, 0xc2, 0x2b, 0xd, 0xe4, 0xd8, 0x31, 0x17, 0xfe, 0x5b, 0xb2, 0x94, 0x7d, 0xc3, 0x2a, 0xc, 0xe5, 0x40, 0xa9, 0x8f, 0x66, 0xee, 0x7, 0x21, 0xc8, 0x6d, 0x84, 0xa2, 0x4b, 0xf5, 0x1c, 0x3a, 0xd3, 0x76, 0x9f, 0xb9, 0x50, 0xb4, 0x5d, 0x7b, 0x92, 0x37, 0xde, 0xf8, 0x11, 0xaf, 0x46, 0x60, 0x89, 0x2c, 0xc5, 0xe3, 0xa, 0x82, 0x6b, 0x4d, 0xa4, 0x1, 0xe8, 0xce, 0x27, 0x99, 0x70, 0x56, 0xbf, 0x1a, 0xf3, 0xd5, 0x3c, 0xad, 0x44, 0x62, 0x8b, 0x2e, 0xc7, 0xe1, 0x8, 0xb6, 0x5f, 0x79, 0x90, 0x35, 0xdc, 0xfa, 0x13, 0x9b, 0x72, 0x54, 0xbd, 0x18, 0xf1, 0xd7, 0x3e, 0x80, 0x69, 0x4f, 0xa6, 0x3, 0xea, 0xcc, 0x25, 0xc1, 0x28, 0xe, 0xe7, 0x42, 0xab, 0x8d, 0x64, 0xda, 0x33, 0x15, 0xfc, 0x59, 0xb0, 0x96, 0x7f, 0xf7, 0x1e, 0x38, 0xd1, 0x74, 0x9d, 0xbb, 0x52, 0xec, 0x5, 0x23, 0xca, 0x6f, 0x86, 0xa0, 0x49, 0x75, 0x9c, 0xba, 0x53, 0xf6, 0x1f, 0x39, 0xd0, 0x6e, 0x87, 0xa1, 0x48, 0xed, 0x4, 0x22, 0xcb, 0x43, 0xaa, 0x8c, 0x65, 0xc0, 0x29, 0xf, 0xe6, 0x58, 0xb1, 0x97, 0x7e, 0xdb, 0x32, 0x14, 0xfd, 0x19, 0xf0, 0xd6, 0x3f, 0x9a, 0x73, 0x55, 0xbc, 0x2, 0xeb, 0xcd, 0x24, 0x81, 0x68, 0x4e, 0xa7, 0x2f, 0xc6, 0xe0, 0x9, 0xac, 0x45, 0x63, 0x8a, 0x34, 0xdd, 0xfb, 0x12, 0xb7, 0x5e, 0x78, 0x91},
+ {0x0, 0xea, 0xc9, 0x23, 0x8f, 0x65, 0x46, 0xac, 0x3, 0xe9, 0xca, 0x20, 0x8c, 0x66, 0x45, 0xaf, 0x6, 0xec, 0xcf, 0x25, 0x89, 0x63, 0x40, 0xaa, 0x5, 0xef, 0xcc, 0x26, 0x8a, 0x60, 0x43, 0xa9, 0xc, 0xe6, 0xc5, 0x2f, 0x83, 0x69, 0x4a, 0xa0, 0xf, 0xe5, 0xc6, 0x2c, 0x80, 0x6a, 0x49, 0xa3, 0xa, 0xe0, 0xc3, 0x29, 0x85, 0x6f, 0x4c, 0xa6, 0x9, 0xe3, 0xc0, 0x2a, 0x86, 0x6c, 0x4f, 0xa5, 0x18, 0xf2, 0xd1, 0x3b, 0x97, 0x7d, 0x5e, 0xb4, 0x1b, 0xf1, 0xd2, 0x38, 0x94, 0x7e, 0x5d, 0xb7, 0x1e, 0xf4, 0xd7, 0x3d, 0x91, 0x7b, 0x58, 0xb2, 0x1d, 0xf7, 0xd4, 0x3e, 0x92, 0x78, 0x5b, 0xb1, 0x14, 0xfe, 0xdd, 0x37, 0x9b, 0x71, 0x52, 0xb8, 0x17, 0xfd, 0xde, 0x34, 0x98, 0x72, 0x51, 0xbb, 0x12, 0xf8, 0xdb, 0x31, 0x9d, 0x77, 0x54, 0xbe, 0x11, 0xfb, 0xd8, 0x32, 0x9e, 0x74, 0x57, 0xbd, 0x30, 0xda, 0xf9, 0x13, 0xbf, 0x55, 0x76, 0x9c, 0x33, 0xd9, 0xfa, 0x10, 0xbc, 0x56, 0x75, 0x9f, 0x36, 0xdc, 0xff, 0x15, 0xb9, 0x53, 0x70, 0x9a, 0x35, 0xdf, 0xfc, 0x16, 0xba, 0x50, 0x73, 0x99, 0x3c, 0xd6, 0xf5, 0x1f, 0xb3, 0x59, 0x7a, 0x90, 0x3f, 0xd5, 0xf6, 0x1c, 0xb0, 0x5a, 0x79, 0x93, 0x3a, 0xd0, 0xf3, 0x19, 0xb5, 0x5f, 0x7c, 0x96, 0x39, 0xd3, 0xf0, 0x1a, 0xb6, 0x5c, 0x7f, 0x95, 0x28, 0xc2, 0xe1, 0xb, 0xa7, 0x4d, 0x6e, 0x84, 0x2b, 0xc1, 0xe2, 0x8, 0xa4, 0x4e, 0x6d, 0x87, 0x2e, 0xc4, 0xe7, 0xd, 0xa1, 0x4b, 0x68, 0x82, 0x2d, 0xc7, 0xe4, 0xe, 0xa2, 0x48, 0x6b, 0x81, 0x24, 0xce, 0xed, 0x7, 0xab, 0x41, 0x62, 0x88, 0x27, 0xcd, 0xee, 0x4, 0xa8, 0x42, 0x61, 0x8b, 0x22, 0xc8, 0xeb, 0x1, 0xad, 0x47, 0x64, 0x8e, 0x21, 0xcb, 0xe8, 0x2, 0xae, 0x44, 0x67, 0x8d},
+ {0x0, 0xeb, 0xcb, 0x20, 0x8b, 0x60, 0x40, 0xab, 0xb, 0xe0, 0xc0, 0x2b, 0x80, 0x6b, 0x4b, 0xa0, 0x16, 0xfd, 0xdd, 0x36, 0x9d, 0x76, 0x56, 0xbd, 0x1d, 0xf6, 0xd6, 0x3d, 0x96, 0x7d, 0x5d, 0xb6, 0x2c, 0xc7, 0xe7, 0xc, 0xa7, 0x4c, 0x6c, 0x87, 0x27, 0xcc, 0xec, 0x7, 0xac, 0x47, 0x67, 0x8c, 0x3a, 0xd1, 0xf1, 0x1a, 0xb1, 0x5a, 0x7a, 0x91, 0x31, 0xda, 0xfa, 0x11, 0xba, 0x51, 0x71, 0x9a, 0x58, 0xb3, 0x93, 0x78, 0xd3, 0x38, 0x18, 0xf3, 0x53, 0xb8, 0x98, 0x73, 0xd8, 0x33, 0x13, 0xf8, 0x4e, 0xa5, 0x85, 0x6e, 0xc5, 0x2e, 0xe, 0xe5, 0x45, 0xae, 0x8e, 0x65, 0xce, 0x25, 0x5, 0xee, 0x74, 0x9f, 0xbf, 0x54, 0xff, 0x14, 0x34, 0xdf, 0x7f, 0x94, 0xb4, 0x5f, 0xf4, 0x1f, 0x3f, 0xd4, 0x62, 0x89, 0xa9, 0x42, 0xe9, 0x2, 0x22, 0xc9, 0x69, 0x82, 0xa2, 0x49, 0xe2, 0x9, 0x29, 0xc2, 0xb0, 0x5b, 0x7b, 0x90, 0x3b, 0xd0, 0xf0, 0x1b, 0xbb, 0x50, 0x70, 0x9b, 0x30, 0xdb, 0xfb, 0x10, 0xa6, 0x4d, 0x6d, 0x86, 0x2d, 0xc6, 0xe6, 0xd, 0xad, 0x46, 0x66, 0x8d, 0x26, 0xcd, 0xed, 0x6, 0x9c, 0x77, 0x57, 0xbc, 0x17, 0xfc, 0xdc, 0x37, 0x97, 0x7c, 0x5c, 0xb7, 0x1c, 0xf7, 0xd7, 0x3c, 0x8a, 0x61, 0x41, 0xaa, 0x1, 0xea, 0xca, 0x21, 0x81, 0x6a, 0x4a, 0xa1, 0xa, 0xe1, 0xc1, 0x2a, 0xe8, 0x3, 0x23, 0xc8, 0x63, 0x88, 0xa8, 0x43, 0xe3, 0x8, 0x28, 0xc3, 0x68, 0x83, 0xa3, 0x48, 0xfe, 0x15, 0x35, 0xde, 0x75, 0x9e, 0xbe, 0x55, 0xf5, 0x1e, 0x3e, 0xd5, 0x7e, 0x95, 0xb5, 0x5e, 0xc4, 0x2f, 0xf, 0xe4, 0x4f, 0xa4, 0x84, 0x6f, 0xcf, 0x24, 0x4, 0xef, 0x44, 0xaf, 0x8f, 0x64, 0xd2, 0x39, 0x19, 0xf2, 0x59, 0xb2, 0x92, 0x79, 0xd9, 0x32, 0x12, 0xf9, 0x52, 0xb9, 0x99, 0x72},
+ {0x0, 0xec, 0xc5, 0x29, 0x97, 0x7b, 0x52, 0xbe, 0x33, 0xdf, 0xf6, 0x1a, 0xa4, 0x48, 0x61, 0x8d, 0x66, 0x8a, 0xa3, 0x4f, 0xf1, 0x1d, 0x34, 0xd8, 0x55, 0xb9, 0x90, 0x7c, 0xc2, 0x2e, 0x7, 0xeb, 0xcc, 0x20, 0x9, 0xe5, 0x5b, 0xb7, 0x9e, 0x72, 0xff, 0x13, 0x3a, 0xd6, 0x68, 0x84, 0xad, 0x41, 0xaa, 0x46, 0x6f, 0x83, 0x3d, 0xd1, 0xf8, 0x14, 0x99, 0x75, 0x5c, 0xb0, 0xe, 0xe2, 0xcb, 0x27, 0x85, 0x69, 0x40, 0xac, 0x12, 0xfe, 0xd7, 0x3b, 0xb6, 0x5a, 0x73, 0x9f, 0x21, 0xcd, 0xe4, 0x8, 0xe3, 0xf, 0x26, 0xca, 0x74, 0x98, 0xb1, 0x5d, 0xd0, 0x3c, 0x15, 0xf9, 0x47, 0xab, 0x82, 0x6e, 0x49, 0xa5, 0x8c, 0x60, 0xde, 0x32, 0x1b, 0xf7, 0x7a, 0x96, 0xbf, 0x53, 0xed, 0x1, 0x28, 0xc4, 0x2f, 0xc3, 0xea, 0x6, 0xb8, 0x54, 0x7d, 0x91, 0x1c, 0xf0, 0xd9, 0x35, 0x8b, 0x67, 0x4e, 0xa2, 0x17, 0xfb, 0xd2, 0x3e, 0x80, 0x6c, 0x45, 0xa9, 0x24, 0xc8, 0xe1, 0xd, 0xb3, 0x5f, 0x76, 0x9a, 0x71, 0x9d, 0xb4, 0x58, 0xe6, 0xa, 0x23, 0xcf, 0x42, 0xae, 0x87, 0x6b, 0xd5, 0x39, 0x10, 0xfc, 0xdb, 0x37, 0x1e, 0xf2, 0x4c, 0xa0, 0x89, 0x65, 0xe8, 0x4, 0x2d, 0xc1, 0x7f, 0x93, 0xba, 0x56, 0xbd, 0x51, 0x78, 0x94, 0x2a, 0xc6, 0xef, 0x3, 0x8e, 0x62, 0x4b, 0xa7, 0x19, 0xf5, 0xdc, 0x30, 0x92, 0x7e, 0x57, 0xbb, 0x5, 0xe9, 0xc0, 0x2c, 0xa1, 0x4d, 0x64, 0x88, 0x36, 0xda, 0xf3, 0x1f, 0xf4, 0x18, 0x31, 0xdd, 0x63, 0x8f, 0xa6, 0x4a, 0xc7, 0x2b, 0x2, 0xee, 0x50, 0xbc, 0x95, 0x79, 0x5e, 0xb2, 0x9b, 0x77, 0xc9, 0x25, 0xc, 0xe0, 0x6d, 0x81, 0xa8, 0x44, 0xfa, 0x16, 0x3f, 0xd3, 0x38, 0xd4, 0xfd, 0x11, 0xaf, 0x43, 0x6a, 0x86, 0xb, 0xe7, 0xce, 0x22, 0x9c, 0x70, 0x59, 0xb5},
+ {0x0, 0xed, 0xc7, 0x2a, 0x93, 0x7e, 0x54, 0xb9, 0x3b, 0xd6, 0xfc, 0x11, 0xa8, 0x45, 0x6f, 0x82, 0x76, 0x9b, 0xb1, 0x5c, 0xe5, 0x8, 0x22, 0xcf, 0x4d, 0xa0, 0x8a, 0x67, 0xde, 0x33, 0x19, 0xf4, 0xec, 0x1, 0x2b, 0xc6, 0x7f, 0x92, 0xb8, 0x55, 0xd7, 0x3a, 0x10, 0xfd, 0x44, 0xa9, 0x83, 0x6e, 0x9a, 0x77, 0x5d, 0xb0, 0x9, 0xe4, 0xce, 0x23, 0xa1, 0x4c, 0x66, 0x8b, 0x32, 0xdf, 0xf5, 0x18, 0xc5, 0x28, 0x2, 0xef, 0x56, 0xbb, 0x91, 0x7c, 0xfe, 0x13, 0x39, 0xd4, 0x6d, 0x80, 0xaa, 0x47, 0xb3, 0x5e, 0x74, 0x99, 0x20, 0xcd, 0xe7, 0xa, 0x88, 0x65, 0x4f, 0xa2, 0x1b, 0xf6, 0xdc, 0x31, 0x29, 0xc4, 0xee, 0x3, 0xba, 0x57, 0x7d, 0x90, 0x12, 0xff, 0xd5, 0x38, 0x81, 0x6c, 0x46, 0xab, 0x5f, 0xb2, 0x98, 0x75, 0xcc, 0x21, 0xb, 0xe6, 0x64, 0x89, 0xa3, 0x4e, 0xf7, 0x1a, 0x30, 0xdd, 0x97, 0x7a, 0x50, 0xbd, 0x4, 0xe9, 0xc3, 0x2e, 0xac, 0x41, 0x6b, 0x86, 0x3f, 0xd2, 0xf8, 0x15, 0xe1, 0xc, 0x26, 0xcb, 0x72, 0x9f, 0xb5, 0x58, 0xda, 0x37, 0x1d, 0xf0, 0x49, 0xa4, 0x8e, 0x63, 0x7b, 0x96, 0xbc, 0x51, 0xe8, 0x5, 0x2f, 0xc2, 0x40, 0xad, 0x87, 0x6a, 0xd3, 0x3e, 0x14, 0xf9, 0xd, 0xe0, 0xca, 0x27, 0x9e, 0x73, 0x59, 0xb4, 0x36, 0xdb, 0xf1, 0x1c, 0xa5, 0x48, 0x62, 0x8f, 0x52, 0xbf, 0x95, 0x78, 0xc1, 0x2c, 0x6, 0xeb, 0x69, 0x84, 0xae, 0x43, 0xfa, 0x17, 0x3d, 0xd0, 0x24, 0xc9, 0xe3, 0xe, 0xb7, 0x5a, 0x70, 0x9d, 0x1f, 0xf2, 0xd8, 0x35, 0x8c, 0x61, 0x4b, 0xa6, 0xbe, 0x53, 0x79, 0x94, 0x2d, 0xc0, 0xea, 0x7, 0x85, 0x68, 0x42, 0xaf, 0x16, 0xfb, 0xd1, 0x3c, 0xc8, 0x25, 0xf, 0xe2, 0x5b, 0xb6, 0x9c, 0x71, 0xf3, 0x1e, 0x34, 0xd9, 0x60, 0x8d, 0xa7, 0x4a},
+ {0x0, 0xee, 0xc1, 0x2f, 0x9f, 0x71, 0x5e, 0xb0, 0x23, 0xcd, 0xe2, 0xc, 0xbc, 0x52, 0x7d, 0x93, 0x46, 0xa8, 0x87, 0x69, 0xd9, 0x37, 0x18, 0xf6, 0x65, 0x8b, 0xa4, 0x4a, 0xfa, 0x14, 0x3b, 0xd5, 0x8c, 0x62, 0x4d, 0xa3, 0x13, 0xfd, 0xd2, 0x3c, 0xaf, 0x41, 0x6e, 0x80, 0x30, 0xde, 0xf1, 0x1f, 0xca, 0x24, 0xb, 0xe5, 0x55, 0xbb, 0x94, 0x7a, 0xe9, 0x7, 0x28, 0xc6, 0x76, 0x98, 0xb7, 0x59, 0x5, 0xeb, 0xc4, 0x2a, 0x9a, 0x74, 0x5b, 0xb5, 0x26, 0xc8, 0xe7, 0x9, 0xb9, 0x57, 0x78, 0x96, 0x43, 0xad, 0x82, 0x6c, 0xdc, 0x32, 0x1d, 0xf3, 0x60, 0x8e, 0xa1, 0x4f, 0xff, 0x11, 0x3e, 0xd0, 0x89, 0x67, 0x48, 0xa6, 0x16, 0xf8, 0xd7, 0x39, 0xaa, 0x44, 0x6b, 0x85, 0x35, 0xdb, 0xf4, 0x1a, 0xcf, 0x21, 0xe, 0xe0, 0x50, 0xbe, 0x91, 0x7f, 0xec, 0x2, 0x2d, 0xc3, 0x73, 0x9d, 0xb2, 0x5c, 0xa, 0xe4, 0xcb, 0x25, 0x95, 0x7b, 0x54, 0xba, 0x29, 0xc7, 0xe8, 0x6, 0xb6, 0x58, 0x77, 0x99, 0x4c, 0xa2, 0x8d, 0x63, 0xd3, 0x3d, 0x12, 0xfc, 0x6f, 0x81, 0xae, 0x40, 0xf0, 0x1e, 0x31, 0xdf, 0x86, 0x68, 0x47, 0xa9, 0x19, 0xf7, 0xd8, 0x36, 0xa5, 0x4b, 0x64, 0x8a, 0x3a, 0xd4, 0xfb, 0x15, 0xc0, 0x2e, 0x1, 0xef, 0x5f, 0xb1, 0x9e, 0x70, 0xe3, 0xd, 0x22, 0xcc, 0x7c, 0x92, 0xbd, 0x53, 0xf, 0xe1, 0xce, 0x20, 0x90, 0x7e, 0x51, 0xbf, 0x2c, 0xc2, 0xed, 0x3, 0xb3, 0x5d, 0x72, 0x9c, 0x49, 0xa7, 0x88, 0x66, 0xd6, 0x38, 0x17, 0xf9, 0x6a, 0x84, 0xab, 0x45, 0xf5, 0x1b, 0x34, 0xda, 0x83, 0x6d, 0x42, 0xac, 0x1c, 0xf2, 0xdd, 0x33, 0xa0, 0x4e, 0x61, 0x8f, 0x3f, 0xd1, 0xfe, 0x10, 0xc5, 0x2b, 0x4, 0xea, 0x5a, 0xb4, 0x9b, 0x75, 0xe6, 0x8, 0x27, 0xc9, 0x79, 0x97, 0xb8, 0x56},
+ {0x0, 0xef, 0xc3, 0x2c, 0x9b, 0x74, 0x58, 0xb7, 0x2b, 0xc4, 0xe8, 0x7, 0xb0, 0x5f, 0x73, 0x9c, 0x56, 0xb9, 0x95, 0x7a, 0xcd, 0x22, 0xe, 0xe1, 0x7d, 0x92, 0xbe, 0x51, 0xe6, 0x9, 0x25, 0xca, 0xac, 0x43, 0x6f, 0x80, 0x37, 0xd8, 0xf4, 0x1b, 0x87, 0x68, 0x44, 0xab, 0x1c, 0xf3, 0xdf, 0x30, 0xfa, 0x15, 0x39, 0xd6, 0x61, 0x8e, 0xa2, 0x4d, 0xd1, 0x3e, 0x12, 0xfd, 0x4a, 0xa5, 0x89, 0x66, 0x45, 0xaa, 0x86, 0x69, 0xde, 0x31, 0x1d, 0xf2, 0x6e, 0x81, 0xad, 0x42, 0xf5, 0x1a, 0x36, 0xd9, 0x13, 0xfc, 0xd0, 0x3f, 0x88, 0x67, 0x4b, 0xa4, 0x38, 0xd7, 0xfb, 0x14, 0xa3, 0x4c, 0x60, 0x8f, 0xe9, 0x6, 0x2a, 0xc5, 0x72, 0x9d, 0xb1, 0x5e, 0xc2, 0x2d, 0x1, 0xee, 0x59, 0xb6, 0x9a, 0x75, 0xbf, 0x50, 0x7c, 0x93, 0x24, 0xcb, 0xe7, 0x8, 0x94, 0x7b, 0x57, 0xb8, 0xf, 0xe0, 0xcc, 0x23, 0x8a, 0x65, 0x49, 0xa6, 0x11, 0xfe, 0xd2, 0x3d, 0xa1, 0x4e, 0x62, 0x8d, 0x3a, 0xd5, 0xf9, 0x16, 0xdc, 0x33, 0x1f, 0xf0, 0x47, 0xa8, 0x84, 0x6b, 0xf7, 0x18, 0x34, 0xdb, 0x6c, 0x83, 0xaf, 0x40, 0x26, 0xc9, 0xe5, 0xa, 0xbd, 0x52, 0x7e, 0x91, 0xd, 0xe2, 0xce, 0x21, 0x96, 0x79, 0x55, 0xba, 0x70, 0x9f, 0xb3, 0x5c, 0xeb, 0x4, 0x28, 0xc7, 0x5b, 0xb4, 0x98, 0x77, 0xc0, 0x2f, 0x3, 0xec, 0xcf, 0x20, 0xc, 0xe3, 0x54, 0xbb, 0x97, 0x78, 0xe4, 0xb, 0x27, 0xc8, 0x7f, 0x90, 0xbc, 0x53, 0x99, 0x76, 0x5a, 0xb5, 0x2, 0xed, 0xc1, 0x2e, 0xb2, 0x5d, 0x71, 0x9e, 0x29, 0xc6, 0xea, 0x5, 0x63, 0x8c, 0xa0, 0x4f, 0xf8, 0x17, 0x3b, 0xd4, 0x48, 0xa7, 0x8b, 0x64, 0xd3, 0x3c, 0x10, 0xff, 0x35, 0xda, 0xf6, 0x19, 0xae, 0x41, 0x6d, 0x82, 0x1e, 0xf1, 0xdd, 0x32, 0x85, 0x6a, 0x46, 0xa9},
+ {0x0, 0xf0, 0xfd, 0xd, 0xe7, 0x17, 0x1a, 0xea, 0xd3, 0x23, 0x2e, 0xde, 0x34, 0xc4, 0xc9, 0x39, 0xbb, 0x4b, 0x46, 0xb6, 0x5c, 0xac, 0xa1, 0x51, 0x68, 0x98, 0x95, 0x65, 0x8f, 0x7f, 0x72, 0x82, 0x6b, 0x9b, 0x96, 0x66, 0x8c, 0x7c, 0x71, 0x81, 0xb8, 0x48, 0x45, 0xb5, 0x5f, 0xaf, 0xa2, 0x52, 0xd0, 0x20, 0x2d, 0xdd, 0x37, 0xc7, 0xca, 0x3a, 0x3, 0xf3, 0xfe, 0xe, 0xe4, 0x14, 0x19, 0xe9, 0xd6, 0x26, 0x2b, 0xdb, 0x31, 0xc1, 0xcc, 0x3c, 0x5, 0xf5, 0xf8, 0x8, 0xe2, 0x12, 0x1f, 0xef, 0x6d, 0x9d, 0x90, 0x60, 0x8a, 0x7a, 0x77, 0x87, 0xbe, 0x4e, 0x43, 0xb3, 0x59, 0xa9, 0xa4, 0x54, 0xbd, 0x4d, 0x40, 0xb0, 0x5a, 0xaa, 0xa7, 0x57, 0x6e, 0x9e, 0x93, 0x63, 0x89, 0x79, 0x74, 0x84, 0x6, 0xf6, 0xfb, 0xb, 0xe1, 0x11, 0x1c, 0xec, 0xd5, 0x25, 0x28, 0xd8, 0x32, 0xc2, 0xcf, 0x3f, 0xb1, 0x41, 0x4c, 0xbc, 0x56, 0xa6, 0xab, 0x5b, 0x62, 0x92, 0x9f, 0x6f, 0x85, 0x75, 0x78, 0x88, 0xa, 0xfa, 0xf7, 0x7, 0xed, 0x1d, 0x10, 0xe0, 0xd9, 0x29, 0x24, 0xd4, 0x3e, 0xce, 0xc3, 0x33, 0xda, 0x2a, 0x27, 0xd7, 0x3d, 0xcd, 0xc0, 0x30, 0x9, 0xf9, 0xf4, 0x4, 0xee, 0x1e, 0x13, 0xe3, 0x61, 0x91, 0x9c, 0x6c, 0x86, 0x76, 0x7b, 0x8b, 0xb2, 0x42, 0x4f, 0xbf, 0x55, 0xa5, 0xa8, 0x58, 0x67, 0x97, 0x9a, 0x6a, 0x80, 0x70, 0x7d, 0x8d, 0xb4, 0x44, 0x49, 0xb9, 0x53, 0xa3, 0xae, 0x5e, 0xdc, 0x2c, 0x21, 0xd1, 0x3b, 0xcb, 0xc6, 0x36, 0xf, 0xff, 0xf2, 0x2, 0xe8, 0x18, 0x15, 0xe5, 0xc, 0xfc, 0xf1, 0x1, 0xeb, 0x1b, 0x16, 0xe6, 0xdf, 0x2f, 0x22, 0xd2, 0x38, 0xc8, 0xc5, 0x35, 0xb7, 0x47, 0x4a, 0xba, 0x50, 0xa0, 0xad, 0x5d, 0x64, 0x94, 0x99, 0x69, 0x83, 0x73, 0x7e, 0x8e},
+ {0x0, 0xf1, 0xff, 0xe, 0xe3, 0x12, 0x1c, 0xed, 0xdb, 0x2a, 0x24, 0xd5, 0x38, 0xc9, 0xc7, 0x36, 0xab, 0x5a, 0x54, 0xa5, 0x48, 0xb9, 0xb7, 0x46, 0x70, 0x81, 0x8f, 0x7e, 0x93, 0x62, 0x6c, 0x9d, 0x4b, 0xba, 0xb4, 0x45, 0xa8, 0x59, 0x57, 0xa6, 0x90, 0x61, 0x6f, 0x9e, 0x73, 0x82, 0x8c, 0x7d, 0xe0, 0x11, 0x1f, 0xee, 0x3, 0xf2, 0xfc, 0xd, 0x3b, 0xca, 0xc4, 0x35, 0xd8, 0x29, 0x27, 0xd6, 0x96, 0x67, 0x69, 0x98, 0x75, 0x84, 0x8a, 0x7b, 0x4d, 0xbc, 0xb2, 0x43, 0xae, 0x5f, 0x51, 0xa0, 0x3d, 0xcc, 0xc2, 0x33, 0xde, 0x2f, 0x21, 0xd0, 0xe6, 0x17, 0x19, 0xe8, 0x5, 0xf4, 0xfa, 0xb, 0xdd, 0x2c, 0x22, 0xd3, 0x3e, 0xcf, 0xc1, 0x30, 0x6, 0xf7, 0xf9, 0x8, 0xe5, 0x14, 0x1a, 0xeb, 0x76, 0x87, 0x89, 0x78, 0x95, 0x64, 0x6a, 0x9b, 0xad, 0x5c, 0x52, 0xa3, 0x4e, 0xbf, 0xb1, 0x40, 0x31, 0xc0, 0xce, 0x3f, 0xd2, 0x23, 0x2d, 0xdc, 0xea, 0x1b, 0x15, 0xe4, 0x9, 0xf8, 0xf6, 0x7, 0x9a, 0x6b, 0x65, 0x94, 0x79, 0x88, 0x86, 0x77, 0x41, 0xb0, 0xbe, 0x4f, 0xa2, 0x53, 0x5d, 0xac, 0x7a, 0x8b, 0x85, 0x74, 0x99, 0x68, 0x66, 0x97, 0xa1, 0x50, 0x5e, 0xaf, 0x42, 0xb3, 0xbd, 0x4c, 0xd1, 0x20, 0x2e, 0xdf, 0x32, 0xc3, 0xcd, 0x3c, 0xa, 0xfb, 0xf5, 0x4, 0xe9, 0x18, 0x16, 0xe7, 0xa7, 0x56, 0x58, 0xa9, 0x44, 0xb5, 0xbb, 0x4a, 0x7c, 0x8d, 0x83, 0x72, 0x9f, 0x6e, 0x60, 0x91, 0xc, 0xfd, 0xf3, 0x2, 0xef, 0x1e, 0x10, 0xe1, 0xd7, 0x26, 0x28, 0xd9, 0x34, 0xc5, 0xcb, 0x3a, 0xec, 0x1d, 0x13, 0xe2, 0xf, 0xfe, 0xf0, 0x1, 0x37, 0xc6, 0xc8, 0x39, 0xd4, 0x25, 0x2b, 0xda, 0x47, 0xb6, 0xb8, 0x49, 0xa4, 0x55, 0x5b, 0xaa, 0x9c, 0x6d, 0x63, 0x92, 0x7f, 0x8e, 0x80, 0x71},
+ {0x0, 0xf2, 0xf9, 0xb, 0xef, 0x1d, 0x16, 0xe4, 0xc3, 0x31, 0x3a, 0xc8, 0x2c, 0xde, 0xd5, 0x27, 0x9b, 0x69, 0x62, 0x90, 0x74, 0x86, 0x8d, 0x7f, 0x58, 0xaa, 0xa1, 0x53, 0xb7, 0x45, 0x4e, 0xbc, 0x2b, 0xd9, 0xd2, 0x20, 0xc4, 0x36, 0x3d, 0xcf, 0xe8, 0x1a, 0x11, 0xe3, 0x7, 0xf5, 0xfe, 0xc, 0xb0, 0x42, 0x49, 0xbb, 0x5f, 0xad, 0xa6, 0x54, 0x73, 0x81, 0x8a, 0x78, 0x9c, 0x6e, 0x65, 0x97, 0x56, 0xa4, 0xaf, 0x5d, 0xb9, 0x4b, 0x40, 0xb2, 0x95, 0x67, 0x6c, 0x9e, 0x7a, 0x88, 0x83, 0x71, 0xcd, 0x3f, 0x34, 0xc6, 0x22, 0xd0, 0xdb, 0x29, 0xe, 0xfc, 0xf7, 0x5, 0xe1, 0x13, 0x18, 0xea, 0x7d, 0x8f, 0x84, 0x76, 0x92, 0x60, 0x6b, 0x99, 0xbe, 0x4c, 0x47, 0xb5, 0x51, 0xa3, 0xa8, 0x5a, 0xe6, 0x14, 0x1f, 0xed, 0x9, 0xfb, 0xf0, 0x2, 0x25, 0xd7, 0xdc, 0x2e, 0xca, 0x38, 0x33, 0xc1, 0xac, 0x5e, 0x55, 0xa7, 0x43, 0xb1, 0xba, 0x48, 0x6f, 0x9d, 0x96, 0x64, 0x80, 0x72, 0x79, 0x8b, 0x37, 0xc5, 0xce, 0x3c, 0xd8, 0x2a, 0x21, 0xd3, 0xf4, 0x6, 0xd, 0xff, 0x1b, 0xe9, 0xe2, 0x10, 0x87, 0x75, 0x7e, 0x8c, 0x68, 0x9a, 0x91, 0x63, 0x44, 0xb6, 0xbd, 0x4f, 0xab, 0x59, 0x52, 0xa0, 0x1c, 0xee, 0xe5, 0x17, 0xf3, 0x1, 0xa, 0xf8, 0xdf, 0x2d, 0x26, 0xd4, 0x30, 0xc2, 0xc9, 0x3b, 0xfa, 0x8, 0x3, 0xf1, 0x15, 0xe7, 0xec, 0x1e, 0x39, 0xcb, 0xc0, 0x32, 0xd6, 0x24, 0x2f, 0xdd, 0x61, 0x93, 0x98, 0x6a, 0x8e, 0x7c, 0x77, 0x85, 0xa2, 0x50, 0x5b, 0xa9, 0x4d, 0xbf, 0xb4, 0x46, 0xd1, 0x23, 0x28, 0xda, 0x3e, 0xcc, 0xc7, 0x35, 0x12, 0xe0, 0xeb, 0x19, 0xfd, 0xf, 0x4, 0xf6, 0x4a, 0xb8, 0xb3, 0x41, 0xa5, 0x57, 0x5c, 0xae, 0x89, 0x7b, 0x70, 0x82, 0x66, 0x94, 0x9f, 0x6d},
+ {0x0, 0xf3, 0xfb, 0x8, 0xeb, 0x18, 0x10, 0xe3, 0xcb, 0x38, 0x30, 0xc3, 0x20, 0xd3, 0xdb, 0x28, 0x8b, 0x78, 0x70, 0x83, 0x60, 0x93, 0x9b, 0x68, 0x40, 0xb3, 0xbb, 0x48, 0xab, 0x58, 0x50, 0xa3, 0xb, 0xf8, 0xf0, 0x3, 0xe0, 0x13, 0x1b, 0xe8, 0xc0, 0x33, 0x3b, 0xc8, 0x2b, 0xd8, 0xd0, 0x23, 0x80, 0x73, 0x7b, 0x88, 0x6b, 0x98, 0x90, 0x63, 0x4b, 0xb8, 0xb0, 0x43, 0xa0, 0x53, 0x5b, 0xa8, 0x16, 0xe5, 0xed, 0x1e, 0xfd, 0xe, 0x6, 0xf5, 0xdd, 0x2e, 0x26, 0xd5, 0x36, 0xc5, 0xcd, 0x3e, 0x9d, 0x6e, 0x66, 0x95, 0x76, 0x85, 0x8d, 0x7e, 0x56, 0xa5, 0xad, 0x5e, 0xbd, 0x4e, 0x46, 0xb5, 0x1d, 0xee, 0xe6, 0x15, 0xf6, 0x5, 0xd, 0xfe, 0xd6, 0x25, 0x2d, 0xde, 0x3d, 0xce, 0xc6, 0x35, 0x96, 0x65, 0x6d, 0x9e, 0x7d, 0x8e, 0x86, 0x75, 0x5d, 0xae, 0xa6, 0x55, 0xb6, 0x45, 0x4d, 0xbe, 0x2c, 0xdf, 0xd7, 0x24, 0xc7, 0x34, 0x3c, 0xcf, 0xe7, 0x14, 0x1c, 0xef, 0xc, 0xff, 0xf7, 0x4, 0xa7, 0x54, 0x5c, 0xaf, 0x4c, 0xbf, 0xb7, 0x44, 0x6c, 0x9f, 0x97, 0x64, 0x87, 0x74, 0x7c, 0x8f, 0x27, 0xd4, 0xdc, 0x2f, 0xcc, 0x3f, 0x37, 0xc4, 0xec, 0x1f, 0x17, 0xe4, 0x7, 0xf4, 0xfc, 0xf, 0xac, 0x5f, 0x57, 0xa4, 0x47, 0xb4, 0xbc, 0x4f, 0x67, 0x94, 0x9c, 0x6f, 0x8c, 0x7f, 0x77, 0x84, 0x3a, 0xc9, 0xc1, 0x32, 0xd1, 0x22, 0x2a, 0xd9, 0xf1, 0x2, 0xa, 0xf9, 0x1a, 0xe9, 0xe1, 0x12, 0xb1, 0x42, 0x4a, 0xb9, 0x5a, 0xa9, 0xa1, 0x52, 0x7a, 0x89, 0x81, 0x72, 0x91, 0x62, 0x6a, 0x99, 0x31, 0xc2, 0xca, 0x39, 0xda, 0x29, 0x21, 0xd2, 0xfa, 0x9, 0x1, 0xf2, 0x11, 0xe2, 0xea, 0x19, 0xba, 0x49, 0x41, 0xb2, 0x51, 0xa2, 0xaa, 0x59, 0x71, 0x82, 0x8a, 0x79, 0x9a, 0x69, 0x61, 0x92},
+ {0x0, 0xf4, 0xf5, 0x1, 0xf7, 0x3, 0x2, 0xf6, 0xf3, 0x7, 0x6, 0xf2, 0x4, 0xf0, 0xf1, 0x5, 0xfb, 0xf, 0xe, 0xfa, 0xc, 0xf8, 0xf9, 0xd, 0x8, 0xfc, 0xfd, 0x9, 0xff, 0xb, 0xa, 0xfe, 0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee, 0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15, 0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce, 0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35, 0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25, 0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde, 0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e, 0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75, 0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65, 0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e, 0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45, 0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe, 0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae, 0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55},
+ {0x0, 0xf5, 0xf7, 0x2, 0xf3, 0x6, 0x4, 0xf1, 0xfb, 0xe, 0xc, 0xf9, 0x8, 0xfd, 0xff, 0xa, 0xeb, 0x1e, 0x1c, 0xe9, 0x18, 0xed, 0xef, 0x1a, 0x10, 0xe5, 0xe7, 0x12, 0xe3, 0x16, 0x14, 0xe1, 0xcb, 0x3e, 0x3c, 0xc9, 0x38, 0xcd, 0xcf, 0x3a, 0x30, 0xc5, 0xc7, 0x32, 0xc3, 0x36, 0x34, 0xc1, 0x20, 0xd5, 0xd7, 0x22, 0xd3, 0x26, 0x24, 0xd1, 0xdb, 0x2e, 0x2c, 0xd9, 0x28, 0xdd, 0xdf, 0x2a, 0x8b, 0x7e, 0x7c, 0x89, 0x78, 0x8d, 0x8f, 0x7a, 0x70, 0x85, 0x87, 0x72, 0x83, 0x76, 0x74, 0x81, 0x60, 0x95, 0x97, 0x62, 0x93, 0x66, 0x64, 0x91, 0x9b, 0x6e, 0x6c, 0x99, 0x68, 0x9d, 0x9f, 0x6a, 0x40, 0xb5, 0xb7, 0x42, 0xb3, 0x46, 0x44, 0xb1, 0xbb, 0x4e, 0x4c, 0xb9, 0x48, 0xbd, 0xbf, 0x4a, 0xab, 0x5e, 0x5c, 0xa9, 0x58, 0xad, 0xaf, 0x5a, 0x50, 0xa5, 0xa7, 0x52, 0xa3, 0x56, 0x54, 0xa1, 0xb, 0xfe, 0xfc, 0x9, 0xf8, 0xd, 0xf, 0xfa, 0xf0, 0x5, 0x7, 0xf2, 0x3, 0xf6, 0xf4, 0x1, 0xe0, 0x15, 0x17, 0xe2, 0x13, 0xe6, 0xe4, 0x11, 0x1b, 0xee, 0xec, 0x19, 0xe8, 0x1d, 0x1f, 0xea, 0xc0, 0x35, 0x37, 0xc2, 0x33, 0xc6, 0xc4, 0x31, 0x3b, 0xce, 0xcc, 0x39, 0xc8, 0x3d, 0x3f, 0xca, 0x2b, 0xde, 0xdc, 0x29, 0xd8, 0x2d, 0x2f, 0xda, 0xd0, 0x25, 0x27, 0xd2, 0x23, 0xd6, 0xd4, 0x21, 0x80, 0x75, 0x77, 0x82, 0x73, 0x86, 0x84, 0x71, 0x7b, 0x8e, 0x8c, 0x79, 0x88, 0x7d, 0x7f, 0x8a, 0x6b, 0x9e, 0x9c, 0x69, 0x98, 0x6d, 0x6f, 0x9a, 0x90, 0x65, 0x67, 0x92, 0x63, 0x96, 0x94, 0x61, 0x4b, 0xbe, 0xbc, 0x49, 0xb8, 0x4d, 0x4f, 0xba, 0xb0, 0x45, 0x47, 0xb2, 0x43, 0xb6, 0xb4, 0x41, 0xa0, 0x55, 0x57, 0xa2, 0x53, 0xa6, 0xa4, 0x51, 0x5b, 0xae, 0xac, 0x59, 0xa8, 0x5d, 0x5f, 0xaa},
+ {0x0, 0xf6, 0xf1, 0x7, 0xff, 0x9, 0xe, 0xf8, 0xe3, 0x15, 0x12, 0xe4, 0x1c, 0xea, 0xed, 0x1b, 0xdb, 0x2d, 0x2a, 0xdc, 0x24, 0xd2, 0xd5, 0x23, 0x38, 0xce, 0xc9, 0x3f, 0xc7, 0x31, 0x36, 0xc0, 0xab, 0x5d, 0x5a, 0xac, 0x54, 0xa2, 0xa5, 0x53, 0x48, 0xbe, 0xb9, 0x4f, 0xb7, 0x41, 0x46, 0xb0, 0x70, 0x86, 0x81, 0x77, 0x8f, 0x79, 0x7e, 0x88, 0x93, 0x65, 0x62, 0x94, 0x6c, 0x9a, 0x9d, 0x6b, 0x4b, 0xbd, 0xba, 0x4c, 0xb4, 0x42, 0x45, 0xb3, 0xa8, 0x5e, 0x59, 0xaf, 0x57, 0xa1, 0xa6, 0x50, 0x90, 0x66, 0x61, 0x97, 0x6f, 0x99, 0x9e, 0x68, 0x73, 0x85, 0x82, 0x74, 0x8c, 0x7a, 0x7d, 0x8b, 0xe0, 0x16, 0x11, 0xe7, 0x1f, 0xe9, 0xee, 0x18, 0x3, 0xf5, 0xf2, 0x4, 0xfc, 0xa, 0xd, 0xfb, 0x3b, 0xcd, 0xca, 0x3c, 0xc4, 0x32, 0x35, 0xc3, 0xd8, 0x2e, 0x29, 0xdf, 0x27, 0xd1, 0xd6, 0x20, 0x96, 0x60, 0x67, 0x91, 0x69, 0x9f, 0x98, 0x6e, 0x75, 0x83, 0x84, 0x72, 0x8a, 0x7c, 0x7b, 0x8d, 0x4d, 0xbb, 0xbc, 0x4a, 0xb2, 0x44, 0x43, 0xb5, 0xae, 0x58, 0x5f, 0xa9, 0x51, 0xa7, 0xa0, 0x56, 0x3d, 0xcb, 0xcc, 0x3a, 0xc2, 0x34, 0x33, 0xc5, 0xde, 0x28, 0x2f, 0xd9, 0x21, 0xd7, 0xd0, 0x26, 0xe6, 0x10, 0x17, 0xe1, 0x19, 0xef, 0xe8, 0x1e, 0x5, 0xf3, 0xf4, 0x2, 0xfa, 0xc, 0xb, 0xfd, 0xdd, 0x2b, 0x2c, 0xda, 0x22, 0xd4, 0xd3, 0x25, 0x3e, 0xc8, 0xcf, 0x39, 0xc1, 0x37, 0x30, 0xc6, 0x6, 0xf0, 0xf7, 0x1, 0xf9, 0xf, 0x8, 0xfe, 0xe5, 0x13, 0x14, 0xe2, 0x1a, 0xec, 0xeb, 0x1d, 0x76, 0x80, 0x87, 0x71, 0x89, 0x7f, 0x78, 0x8e, 0x95, 0x63, 0x64, 0x92, 0x6a, 0x9c, 0x9b, 0x6d, 0xad, 0x5b, 0x5c, 0xaa, 0x52, 0xa4, 0xa3, 0x55, 0x4e, 0xb8, 0xbf, 0x49, 0xb1, 0x47, 0x40, 0xb6},
+ {0x0, 0xf7, 0xf3, 0x4, 0xfb, 0xc, 0x8, 0xff, 0xeb, 0x1c, 0x18, 0xef, 0x10, 0xe7, 0xe3, 0x14, 0xcb, 0x3c, 0x38, 0xcf, 0x30, 0xc7, 0xc3, 0x34, 0x20, 0xd7, 0xd3, 0x24, 0xdb, 0x2c, 0x28, 0xdf, 0x8b, 0x7c, 0x78, 0x8f, 0x70, 0x87, 0x83, 0x74, 0x60, 0x97, 0x93, 0x64, 0x9b, 0x6c, 0x68, 0x9f, 0x40, 0xb7, 0xb3, 0x44, 0xbb, 0x4c, 0x48, 0xbf, 0xab, 0x5c, 0x58, 0xaf, 0x50, 0xa7, 0xa3, 0x54, 0xb, 0xfc, 0xf8, 0xf, 0xf0, 0x7, 0x3, 0xf4, 0xe0, 0x17, 0x13, 0xe4, 0x1b, 0xec, 0xe8, 0x1f, 0xc0, 0x37, 0x33, 0xc4, 0x3b, 0xcc, 0xc8, 0x3f, 0x2b, 0xdc, 0xd8, 0x2f, 0xd0, 0x27, 0x23, 0xd4, 0x80, 0x77, 0x73, 0x84, 0x7b, 0x8c, 0x88, 0x7f, 0x6b, 0x9c, 0x98, 0x6f, 0x90, 0x67, 0x63, 0x94, 0x4b, 0xbc, 0xb8, 0x4f, 0xb0, 0x47, 0x43, 0xb4, 0xa0, 0x57, 0x53, 0xa4, 0x5b, 0xac, 0xa8, 0x5f, 0x16, 0xe1, 0xe5, 0x12, 0xed, 0x1a, 0x1e, 0xe9, 0xfd, 0xa, 0xe, 0xf9, 0x6, 0xf1, 0xf5, 0x2, 0xdd, 0x2a, 0x2e, 0xd9, 0x26, 0xd1, 0xd5, 0x22, 0x36, 0xc1, 0xc5, 0x32, 0xcd, 0x3a, 0x3e, 0xc9, 0x9d, 0x6a, 0x6e, 0x99, 0x66, 0x91, 0x95, 0x62, 0x76, 0x81, 0x85, 0x72, 0x8d, 0x7a, 0x7e, 0x89, 0x56, 0xa1, 0xa5, 0x52, 0xad, 0x5a, 0x5e, 0xa9, 0xbd, 0x4a, 0x4e, 0xb9, 0x46, 0xb1, 0xb5, 0x42, 0x1d, 0xea, 0xee, 0x19, 0xe6, 0x11, 0x15, 0xe2, 0xf6, 0x1, 0x5, 0xf2, 0xd, 0xfa, 0xfe, 0x9, 0xd6, 0x21, 0x25, 0xd2, 0x2d, 0xda, 0xde, 0x29, 0x3d, 0xca, 0xce, 0x39, 0xc6, 0x31, 0x35, 0xc2, 0x96, 0x61, 0x65, 0x92, 0x6d, 0x9a, 0x9e, 0x69, 0x7d, 0x8a, 0x8e, 0x79, 0x86, 0x71, 0x75, 0x82, 0x5d, 0xaa, 0xae, 0x59, 0xa6, 0x51, 0x55, 0xa2, 0xb6, 0x41, 0x45, 0xb2, 0x4d, 0xba, 0xbe, 0x49},
+ {0x0, 0xf8, 0xed, 0x15, 0xc7, 0x3f, 0x2a, 0xd2, 0x93, 0x6b, 0x7e, 0x86, 0x54, 0xac, 0xb9, 0x41, 0x3b, 0xc3, 0xd6, 0x2e, 0xfc, 0x4, 0x11, 0xe9, 0xa8, 0x50, 0x45, 0xbd, 0x6f, 0x97, 0x82, 0x7a, 0x76, 0x8e, 0x9b, 0x63, 0xb1, 0x49, 0x5c, 0xa4, 0xe5, 0x1d, 0x8, 0xf0, 0x22, 0xda, 0xcf, 0x37, 0x4d, 0xb5, 0xa0, 0x58, 0x8a, 0x72, 0x67, 0x9f, 0xde, 0x26, 0x33, 0xcb, 0x19, 0xe1, 0xf4, 0xc, 0xec, 0x14, 0x1, 0xf9, 0x2b, 0xd3, 0xc6, 0x3e, 0x7f, 0x87, 0x92, 0x6a, 0xb8, 0x40, 0x55, 0xad, 0xd7, 0x2f, 0x3a, 0xc2, 0x10, 0xe8, 0xfd, 0x5, 0x44, 0xbc, 0xa9, 0x51, 0x83, 0x7b, 0x6e, 0x96, 0x9a, 0x62, 0x77, 0x8f, 0x5d, 0xa5, 0xb0, 0x48, 0x9, 0xf1, 0xe4, 0x1c, 0xce, 0x36, 0x23, 0xdb, 0xa1, 0x59, 0x4c, 0xb4, 0x66, 0x9e, 0x8b, 0x73, 0x32, 0xca, 0xdf, 0x27, 0xf5, 0xd, 0x18, 0xe0, 0xc5, 0x3d, 0x28, 0xd0, 0x2, 0xfa, 0xef, 0x17, 0x56, 0xae, 0xbb, 0x43, 0x91, 0x69, 0x7c, 0x84, 0xfe, 0x6, 0x13, 0xeb, 0x39, 0xc1, 0xd4, 0x2c, 0x6d, 0x95, 0x80, 0x78, 0xaa, 0x52, 0x47, 0xbf, 0xb3, 0x4b, 0x5e, 0xa6, 0x74, 0x8c, 0x99, 0x61, 0x20, 0xd8, 0xcd, 0x35, 0xe7, 0x1f, 0xa, 0xf2, 0x88, 0x70, 0x65, 0x9d, 0x4f, 0xb7, 0xa2, 0x5a, 0x1b, 0xe3, 0xf6, 0xe, 0xdc, 0x24, 0x31, 0xc9, 0x29, 0xd1, 0xc4, 0x3c, 0xee, 0x16, 0x3, 0xfb, 0xba, 0x42, 0x57, 0xaf, 0x7d, 0x85, 0x90, 0x68, 0x12, 0xea, 0xff, 0x7, 0xd5, 0x2d, 0x38, 0xc0, 0x81, 0x79, 0x6c, 0x94, 0x46, 0xbe, 0xab, 0x53, 0x5f, 0xa7, 0xb2, 0x4a, 0x98, 0x60, 0x75, 0x8d, 0xcc, 0x34, 0x21, 0xd9, 0xb, 0xf3, 0xe6, 0x1e, 0x64, 0x9c, 0x89, 0x71, 0xa3, 0x5b, 0x4e, 0xb6, 0xf7, 0xf, 0x1a, 0xe2, 0x30, 0xc8, 0xdd, 0x25},
+ {0x0, 0xf9, 0xef, 0x16, 0xc3, 0x3a, 0x2c, 0xd5, 0x9b, 0x62, 0x74, 0x8d, 0x58, 0xa1, 0xb7, 0x4e, 0x2b, 0xd2, 0xc4, 0x3d, 0xe8, 0x11, 0x7, 0xfe, 0xb0, 0x49, 0x5f, 0xa6, 0x73, 0x8a, 0x9c, 0x65, 0x56, 0xaf, 0xb9, 0x40, 0x95, 0x6c, 0x7a, 0x83, 0xcd, 0x34, 0x22, 0xdb, 0xe, 0xf7, 0xe1, 0x18, 0x7d, 0x84, 0x92, 0x6b, 0xbe, 0x47, 0x51, 0xa8, 0xe6, 0x1f, 0x9, 0xf0, 0x25, 0xdc, 0xca, 0x33, 0xac, 0x55, 0x43, 0xba, 0x6f, 0x96, 0x80, 0x79, 0x37, 0xce, 0xd8, 0x21, 0xf4, 0xd, 0x1b, 0xe2, 0x87, 0x7e, 0x68, 0x91, 0x44, 0xbd, 0xab, 0x52, 0x1c, 0xe5, 0xf3, 0xa, 0xdf, 0x26, 0x30, 0xc9, 0xfa, 0x3, 0x15, 0xec, 0x39, 0xc0, 0xd6, 0x2f, 0x61, 0x98, 0x8e, 0x77, 0xa2, 0x5b, 0x4d, 0xb4, 0xd1, 0x28, 0x3e, 0xc7, 0x12, 0xeb, 0xfd, 0x4, 0x4a, 0xb3, 0xa5, 0x5c, 0x89, 0x70, 0x66, 0x9f, 0x45, 0xbc, 0xaa, 0x53, 0x86, 0x7f, 0x69, 0x90, 0xde, 0x27, 0x31, 0xc8, 0x1d, 0xe4, 0xf2, 0xb, 0x6e, 0x97, 0x81, 0x78, 0xad, 0x54, 0x42, 0xbb, 0xf5, 0xc, 0x1a, 0xe3, 0x36, 0xcf, 0xd9, 0x20, 0x13, 0xea, 0xfc, 0x5, 0xd0, 0x29, 0x3f, 0xc6, 0x88, 0x71, 0x67, 0x9e, 0x4b, 0xb2, 0xa4, 0x5d, 0x38, 0xc1, 0xd7, 0x2e, 0xfb, 0x2, 0x14, 0xed, 0xa3, 0x5a, 0x4c, 0xb5, 0x60, 0x99, 0x8f, 0x76, 0xe9, 0x10, 0x6, 0xff, 0x2a, 0xd3, 0xc5, 0x3c, 0x72, 0x8b, 0x9d, 0x64, 0xb1, 0x48, 0x5e, 0xa7, 0xc2, 0x3b, 0x2d, 0xd4, 0x1, 0xf8, 0xee, 0x17, 0x59, 0xa0, 0xb6, 0x4f, 0x9a, 0x63, 0x75, 0x8c, 0xbf, 0x46, 0x50, 0xa9, 0x7c, 0x85, 0x93, 0x6a, 0x24, 0xdd, 0xcb, 0x32, 0xe7, 0x1e, 0x8, 0xf1, 0x94, 0x6d, 0x7b, 0x82, 0x57, 0xae, 0xb8, 0x41, 0xf, 0xf6, 0xe0, 0x19, 0xcc, 0x35, 0x23, 0xda},
+ {0x0, 0xfa, 0xe9, 0x13, 0xcf, 0x35, 0x26, 0xdc, 0x83, 0x79, 0x6a, 0x90, 0x4c, 0xb6, 0xa5, 0x5f, 0x1b, 0xe1, 0xf2, 0x8, 0xd4, 0x2e, 0x3d, 0xc7, 0x98, 0x62, 0x71, 0x8b, 0x57, 0xad, 0xbe, 0x44, 0x36, 0xcc, 0xdf, 0x25, 0xf9, 0x3, 0x10, 0xea, 0xb5, 0x4f, 0x5c, 0xa6, 0x7a, 0x80, 0x93, 0x69, 0x2d, 0xd7, 0xc4, 0x3e, 0xe2, 0x18, 0xb, 0xf1, 0xae, 0x54, 0x47, 0xbd, 0x61, 0x9b, 0x88, 0x72, 0x6c, 0x96, 0x85, 0x7f, 0xa3, 0x59, 0x4a, 0xb0, 0xef, 0x15, 0x6, 0xfc, 0x20, 0xda, 0xc9, 0x33, 0x77, 0x8d, 0x9e, 0x64, 0xb8, 0x42, 0x51, 0xab, 0xf4, 0xe, 0x1d, 0xe7, 0x3b, 0xc1, 0xd2, 0x28, 0x5a, 0xa0, 0xb3, 0x49, 0x95, 0x6f, 0x7c, 0x86, 0xd9, 0x23, 0x30, 0xca, 0x16, 0xec, 0xff, 0x5, 0x41, 0xbb, 0xa8, 0x52, 0x8e, 0x74, 0x67, 0x9d, 0xc2, 0x38, 0x2b, 0xd1, 0xd, 0xf7, 0xe4, 0x1e, 0xd8, 0x22, 0x31, 0xcb, 0x17, 0xed, 0xfe, 0x4, 0x5b, 0xa1, 0xb2, 0x48, 0x94, 0x6e, 0x7d, 0x87, 0xc3, 0x39, 0x2a, 0xd0, 0xc, 0xf6, 0xe5, 0x1f, 0x40, 0xba, 0xa9, 0x53, 0x8f, 0x75, 0x66, 0x9c, 0xee, 0x14, 0x7, 0xfd, 0x21, 0xdb, 0xc8, 0x32, 0x6d, 0x97, 0x84, 0x7e, 0xa2, 0x58, 0x4b, 0xb1, 0xf5, 0xf, 0x1c, 0xe6, 0x3a, 0xc0, 0xd3, 0x29, 0x76, 0x8c, 0x9f, 0x65, 0xb9, 0x43, 0x50, 0xaa, 0xb4, 0x4e, 0x5d, 0xa7, 0x7b, 0x81, 0x92, 0x68, 0x37, 0xcd, 0xde, 0x24, 0xf8, 0x2, 0x11, 0xeb, 0xaf, 0x55, 0x46, 0xbc, 0x60, 0x9a, 0x89, 0x73, 0x2c, 0xd6, 0xc5, 0x3f, 0xe3, 0x19, 0xa, 0xf0, 0x82, 0x78, 0x6b, 0x91, 0x4d, 0xb7, 0xa4, 0x5e, 0x1, 0xfb, 0xe8, 0x12, 0xce, 0x34, 0x27, 0xdd, 0x99, 0x63, 0x70, 0x8a, 0x56, 0xac, 0xbf, 0x45, 0x1a, 0xe0, 0xf3, 0x9, 0xd5, 0x2f, 0x3c, 0xc6},
+ {0x0, 0xfb, 0xeb, 0x10, 0xcb, 0x30, 0x20, 0xdb, 0x8b, 0x70, 0x60, 0x9b, 0x40, 0xbb, 0xab, 0x50, 0xb, 0xf0, 0xe0, 0x1b, 0xc0, 0x3b, 0x2b, 0xd0, 0x80, 0x7b, 0x6b, 0x90, 0x4b, 0xb0, 0xa0, 0x5b, 0x16, 0xed, 0xfd, 0x6, 0xdd, 0x26, 0x36, 0xcd, 0x9d, 0x66, 0x76, 0x8d, 0x56, 0xad, 0xbd, 0x46, 0x1d, 0xe6, 0xf6, 0xd, 0xd6, 0x2d, 0x3d, 0xc6, 0x96, 0x6d, 0x7d, 0x86, 0x5d, 0xa6, 0xb6, 0x4d, 0x2c, 0xd7, 0xc7, 0x3c, 0xe7, 0x1c, 0xc, 0xf7, 0xa7, 0x5c, 0x4c, 0xb7, 0x6c, 0x97, 0x87, 0x7c, 0x27, 0xdc, 0xcc, 0x37, 0xec, 0x17, 0x7, 0xfc, 0xac, 0x57, 0x47, 0xbc, 0x67, 0x9c, 0x8c, 0x77, 0x3a, 0xc1, 0xd1, 0x2a, 0xf1, 0xa, 0x1a, 0xe1, 0xb1, 0x4a, 0x5a, 0xa1, 0x7a, 0x81, 0x91, 0x6a, 0x31, 0xca, 0xda, 0x21, 0xfa, 0x1, 0x11, 0xea, 0xba, 0x41, 0x51, 0xaa, 0x71, 0x8a, 0x9a, 0x61, 0x58, 0xa3, 0xb3, 0x48, 0x93, 0x68, 0x78, 0x83, 0xd3, 0x28, 0x38, 0xc3, 0x18, 0xe3, 0xf3, 0x8, 0x53, 0xa8, 0xb8, 0x43, 0x98, 0x63, 0x73, 0x88, 0xd8, 0x23, 0x33, 0xc8, 0x13, 0xe8, 0xf8, 0x3, 0x4e, 0xb5, 0xa5, 0x5e, 0x85, 0x7e, 0x6e, 0x95, 0xc5, 0x3e, 0x2e, 0xd5, 0xe, 0xf5, 0xe5, 0x1e, 0x45, 0xbe, 0xae, 0x55, 0x8e, 0x75, 0x65, 0x9e, 0xce, 0x35, 0x25, 0xde, 0x5, 0xfe, 0xee, 0x15, 0x74, 0x8f, 0x9f, 0x64, 0xbf, 0x44, 0x54, 0xaf, 0xff, 0x4, 0x14, 0xef, 0x34, 0xcf, 0xdf, 0x24, 0x7f, 0x84, 0x94, 0x6f, 0xb4, 0x4f, 0x5f, 0xa4, 0xf4, 0xf, 0x1f, 0xe4, 0x3f, 0xc4, 0xd4, 0x2f, 0x62, 0x99, 0x89, 0x72, 0xa9, 0x52, 0x42, 0xb9, 0xe9, 0x12, 0x2, 0xf9, 0x22, 0xd9, 0xc9, 0x32, 0x69, 0x92, 0x82, 0x79, 0xa2, 0x59, 0x49, 0xb2, 0xe2, 0x19, 0x9, 0xf2, 0x29, 0xd2, 0xc2, 0x39},
+ {0x0, 0xfc, 0xe5, 0x19, 0xd7, 0x2b, 0x32, 0xce, 0xb3, 0x4f, 0x56, 0xaa, 0x64, 0x98, 0x81, 0x7d, 0x7b, 0x87, 0x9e, 0x62, 0xac, 0x50, 0x49, 0xb5, 0xc8, 0x34, 0x2d, 0xd1, 0x1f, 0xe3, 0xfa, 0x6, 0xf6, 0xa, 0x13, 0xef, 0x21, 0xdd, 0xc4, 0x38, 0x45, 0xb9, 0xa0, 0x5c, 0x92, 0x6e, 0x77, 0x8b, 0x8d, 0x71, 0x68, 0x94, 0x5a, 0xa6, 0xbf, 0x43, 0x3e, 0xc2, 0xdb, 0x27, 0xe9, 0x15, 0xc, 0xf0, 0xf1, 0xd, 0x14, 0xe8, 0x26, 0xda, 0xc3, 0x3f, 0x42, 0xbe, 0xa7, 0x5b, 0x95, 0x69, 0x70, 0x8c, 0x8a, 0x76, 0x6f, 0x93, 0x5d, 0xa1, 0xb8, 0x44, 0x39, 0xc5, 0xdc, 0x20, 0xee, 0x12, 0xb, 0xf7, 0x7, 0xfb, 0xe2, 0x1e, 0xd0, 0x2c, 0x35, 0xc9, 0xb4, 0x48, 0x51, 0xad, 0x63, 0x9f, 0x86, 0x7a, 0x7c, 0x80, 0x99, 0x65, 0xab, 0x57, 0x4e, 0xb2, 0xcf, 0x33, 0x2a, 0xd6, 0x18, 0xe4, 0xfd, 0x1, 0xff, 0x3, 0x1a, 0xe6, 0x28, 0xd4, 0xcd, 0x31, 0x4c, 0xb0, 0xa9, 0x55, 0x9b, 0x67, 0x7e, 0x82, 0x84, 0x78, 0x61, 0x9d, 0x53, 0xaf, 0xb6, 0x4a, 0x37, 0xcb, 0xd2, 0x2e, 0xe0, 0x1c, 0x5, 0xf9, 0x9, 0xf5, 0xec, 0x10, 0xde, 0x22, 0x3b, 0xc7, 0xba, 0x46, 0x5f, 0xa3, 0x6d, 0x91, 0x88, 0x74, 0x72, 0x8e, 0x97, 0x6b, 0xa5, 0x59, 0x40, 0xbc, 0xc1, 0x3d, 0x24, 0xd8, 0x16, 0xea, 0xf3, 0xf, 0xe, 0xf2, 0xeb, 0x17, 0xd9, 0x25, 0x3c, 0xc0, 0xbd, 0x41, 0x58, 0xa4, 0x6a, 0x96, 0x8f, 0x73, 0x75, 0x89, 0x90, 0x6c, 0xa2, 0x5e, 0x47, 0xbb, 0xc6, 0x3a, 0x23, 0xdf, 0x11, 0xed, 0xf4, 0x8, 0xf8, 0x4, 0x1d, 0xe1, 0x2f, 0xd3, 0xca, 0x36, 0x4b, 0xb7, 0xae, 0x52, 0x9c, 0x60, 0x79, 0x85, 0x83, 0x7f, 0x66, 0x9a, 0x54, 0xa8, 0xb1, 0x4d, 0x30, 0xcc, 0xd5, 0x29, 0xe7, 0x1b, 0x2, 0xfe},
+ {0x0, 0xfd, 0xe7, 0x1a, 0xd3, 0x2e, 0x34, 0xc9, 0xbb, 0x46, 0x5c, 0xa1, 0x68, 0x95, 0x8f, 0x72, 0x6b, 0x96, 0x8c, 0x71, 0xb8, 0x45, 0x5f, 0xa2, 0xd0, 0x2d, 0x37, 0xca, 0x3, 0xfe, 0xe4, 0x19, 0xd6, 0x2b, 0x31, 0xcc, 0x5, 0xf8, 0xe2, 0x1f, 0x6d, 0x90, 0x8a, 0x77, 0xbe, 0x43, 0x59, 0xa4, 0xbd, 0x40, 0x5a, 0xa7, 0x6e, 0x93, 0x89, 0x74, 0x6, 0xfb, 0xe1, 0x1c, 0xd5, 0x28, 0x32, 0xcf, 0xb1, 0x4c, 0x56, 0xab, 0x62, 0x9f, 0x85, 0x78, 0xa, 0xf7, 0xed, 0x10, 0xd9, 0x24, 0x3e, 0xc3, 0xda, 0x27, 0x3d, 0xc0, 0x9, 0xf4, 0xee, 0x13, 0x61, 0x9c, 0x86, 0x7b, 0xb2, 0x4f, 0x55, 0xa8, 0x67, 0x9a, 0x80, 0x7d, 0xb4, 0x49, 0x53, 0xae, 0xdc, 0x21, 0x3b, 0xc6, 0xf, 0xf2, 0xe8, 0x15, 0xc, 0xf1, 0xeb, 0x16, 0xdf, 0x22, 0x38, 0xc5, 0xb7, 0x4a, 0x50, 0xad, 0x64, 0x99, 0x83, 0x7e, 0x7f, 0x82, 0x98, 0x65, 0xac, 0x51, 0x4b, 0xb6, 0xc4, 0x39, 0x23, 0xde, 0x17, 0xea, 0xf0, 0xd, 0x14, 0xe9, 0xf3, 0xe, 0xc7, 0x3a, 0x20, 0xdd, 0xaf, 0x52, 0x48, 0xb5, 0x7c, 0x81, 0x9b, 0x66, 0xa9, 0x54, 0x4e, 0xb3, 0x7a, 0x87, 0x9d, 0x60, 0x12, 0xef, 0xf5, 0x8, 0xc1, 0x3c, 0x26, 0xdb, 0xc2, 0x3f, 0x25, 0xd8, 0x11, 0xec, 0xf6, 0xb, 0x79, 0x84, 0x9e, 0x63, 0xaa, 0x57, 0x4d, 0xb0, 0xce, 0x33, 0x29, 0xd4, 0x1d, 0xe0, 0xfa, 0x7, 0x75, 0x88, 0x92, 0x6f, 0xa6, 0x5b, 0x41, 0xbc, 0xa5, 0x58, 0x42, 0xbf, 0x76, 0x8b, 0x91, 0x6c, 0x1e, 0xe3, 0xf9, 0x4, 0xcd, 0x30, 0x2a, 0xd7, 0x18, 0xe5, 0xff, 0x2, 0xcb, 0x36, 0x2c, 0xd1, 0xa3, 0x5e, 0x44, 0xb9, 0x70, 0x8d, 0x97, 0x6a, 0x73, 0x8e, 0x94, 0x69, 0xa0, 0x5d, 0x47, 0xba, 0xc8, 0x35, 0x2f, 0xd2, 0x1b, 0xe6, 0xfc, 0x1},
+ {0x0, 0xfe, 0xe1, 0x1f, 0xdf, 0x21, 0x3e, 0xc0, 0xa3, 0x5d, 0x42, 0xbc, 0x7c, 0x82, 0x9d, 0x63, 0x5b, 0xa5, 0xba, 0x44, 0x84, 0x7a, 0x65, 0x9b, 0xf8, 0x6, 0x19, 0xe7, 0x27, 0xd9, 0xc6, 0x38, 0xb6, 0x48, 0x57, 0xa9, 0x69, 0x97, 0x88, 0x76, 0x15, 0xeb, 0xf4, 0xa, 0xca, 0x34, 0x2b, 0xd5, 0xed, 0x13, 0xc, 0xf2, 0x32, 0xcc, 0xd3, 0x2d, 0x4e, 0xb0, 0xaf, 0x51, 0x91, 0x6f, 0x70, 0x8e, 0x71, 0x8f, 0x90, 0x6e, 0xae, 0x50, 0x4f, 0xb1, 0xd2, 0x2c, 0x33, 0xcd, 0xd, 0xf3, 0xec, 0x12, 0x2a, 0xd4, 0xcb, 0x35, 0xf5, 0xb, 0x14, 0xea, 0x89, 0x77, 0x68, 0x96, 0x56, 0xa8, 0xb7, 0x49, 0xc7, 0x39, 0x26, 0xd8, 0x18, 0xe6, 0xf9, 0x7, 0x64, 0x9a, 0x85, 0x7b, 0xbb, 0x45, 0x5a, 0xa4, 0x9c, 0x62, 0x7d, 0x83, 0x43, 0xbd, 0xa2, 0x5c, 0x3f, 0xc1, 0xde, 0x20, 0xe0, 0x1e, 0x1, 0xff, 0xe2, 0x1c, 0x3, 0xfd, 0x3d, 0xc3, 0xdc, 0x22, 0x41, 0xbf, 0xa0, 0x5e, 0x9e, 0x60, 0x7f, 0x81, 0xb9, 0x47, 0x58, 0xa6, 0x66, 0x98, 0x87, 0x79, 0x1a, 0xe4, 0xfb, 0x5, 0xc5, 0x3b, 0x24, 0xda, 0x54, 0xaa, 0xb5, 0x4b, 0x8b, 0x75, 0x6a, 0x94, 0xf7, 0x9, 0x16, 0xe8, 0x28, 0xd6, 0xc9, 0x37, 0xf, 0xf1, 0xee, 0x10, 0xd0, 0x2e, 0x31, 0xcf, 0xac, 0x52, 0x4d, 0xb3, 0x73, 0x8d, 0x92, 0x6c, 0x93, 0x6d, 0x72, 0x8c, 0x4c, 0xb2, 0xad, 0x53, 0x30, 0xce, 0xd1, 0x2f, 0xef, 0x11, 0xe, 0xf0, 0xc8, 0x36, 0x29, 0xd7, 0x17, 0xe9, 0xf6, 0x8, 0x6b, 0x95, 0x8a, 0x74, 0xb4, 0x4a, 0x55, 0xab, 0x25, 0xdb, 0xc4, 0x3a, 0xfa, 0x4, 0x1b, 0xe5, 0x86, 0x78, 0x67, 0x99, 0x59, 0xa7, 0xb8, 0x46, 0x7e, 0x80, 0x9f, 0x61, 0xa1, 0x5f, 0x40, 0xbe, 0xdd, 0x23, 0x3c, 0xc2, 0x2, 0xfc, 0xe3, 0x1d},
+ {0x0, 0xff, 0xe3, 0x1c, 0xdb, 0x24, 0x38, 0xc7, 0xab, 0x54, 0x48, 0xb7, 0x70, 0x8f, 0x93, 0x6c, 0x4b, 0xb4, 0xa8, 0x57, 0x90, 0x6f, 0x73, 0x8c, 0xe0, 0x1f, 0x3, 0xfc, 0x3b, 0xc4, 0xd8, 0x27, 0x96, 0x69, 0x75, 0x8a, 0x4d, 0xb2, 0xae, 0x51, 0x3d, 0xc2, 0xde, 0x21, 0xe6, 0x19, 0x5, 0xfa, 0xdd, 0x22, 0x3e, 0xc1, 0x6, 0xf9, 0xe5, 0x1a, 0x76, 0x89, 0x95, 0x6a, 0xad, 0x52, 0x4e, 0xb1, 0x31, 0xce, 0xd2, 0x2d, 0xea, 0x15, 0x9, 0xf6, 0x9a, 0x65, 0x79, 0x86, 0x41, 0xbe, 0xa2, 0x5d, 0x7a, 0x85, 0x99, 0x66, 0xa1, 0x5e, 0x42, 0xbd, 0xd1, 0x2e, 0x32, 0xcd, 0xa, 0xf5, 0xe9, 0x16, 0xa7, 0x58, 0x44, 0xbb, 0x7c, 0x83, 0x9f, 0x60, 0xc, 0xf3, 0xef, 0x10, 0xd7, 0x28, 0x34, 0xcb, 0xec, 0x13, 0xf, 0xf0, 0x37, 0xc8, 0xd4, 0x2b, 0x47, 0xb8, 0xa4, 0x5b, 0x9c, 0x63, 0x7f, 0x80, 0x62, 0x9d, 0x81, 0x7e, 0xb9, 0x46, 0x5a, 0xa5, 0xc9, 0x36, 0x2a, 0xd5, 0x12, 0xed, 0xf1, 0xe, 0x29, 0xd6, 0xca, 0x35, 0xf2, 0xd, 0x11, 0xee, 0x82, 0x7d, 0x61, 0x9e, 0x59, 0xa6, 0xba, 0x45, 0xf4, 0xb, 0x17, 0xe8, 0x2f, 0xd0, 0xcc, 0x33, 0x5f, 0xa0, 0xbc, 0x43, 0x84, 0x7b, 0x67, 0x98, 0xbf, 0x40, 0x5c, 0xa3, 0x64, 0x9b, 0x87, 0x78, 0x14, 0xeb, 0xf7, 0x8, 0xcf, 0x30, 0x2c, 0xd3, 0x53, 0xac, 0xb0, 0x4f, 0x88, 0x77, 0x6b, 0x94, 0xf8, 0x7, 0x1b, 0xe4, 0x23, 0xdc, 0xc0, 0x3f, 0x18, 0xe7, 0xfb, 0x4, 0xc3, 0x3c, 0x20, 0xdf, 0xb3, 0x4c, 0x50, 0xaf, 0x68, 0x97, 0x8b, 0x74, 0xc5, 0x3a, 0x26, 0xd9, 0x1e, 0xe1, 0xfd, 0x2, 0x6e, 0x91, 0x8d, 0x72, 0xb5, 0x4a, 0x56, 0xa9, 0x8e, 0x71, 0x6d, 0x92, 0x55, 0xaa, 0xb6, 0x49, 0x25, 0xda, 0xc6, 0x39, 0xfe, 0x1, 0x1d, 0xe2}}
+
+var mulTableLow = [256][16]uint8{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf},
+ {0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e},
+ {0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11},
+ {0x0, 0x4, 0x8, 0xc, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c},
+ {0x0, 0x5, 0xa, 0xf, 0x14, 0x11, 0x1e, 0x1b, 0x28, 0x2d, 0x22, 0x27, 0x3c, 0x39, 0x36, 0x33},
+ {0x0, 0x6, 0xc, 0xa, 0x18, 0x1e, 0x14, 0x12, 0x30, 0x36, 0x3c, 0x3a, 0x28, 0x2e, 0x24, 0x22},
+ {0x0, 0x7, 0xe, 0x9, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d},
+ {0x0, 0x8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78},
+ {0x0, 0x9, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77},
+ {0x0, 0xa, 0x14, 0x1e, 0x28, 0x22, 0x3c, 0x36, 0x50, 0x5a, 0x44, 0x4e, 0x78, 0x72, 0x6c, 0x66},
+ {0x0, 0xb, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69},
+ {0x0, 0xc, 0x18, 0x14, 0x30, 0x3c, 0x28, 0x24, 0x60, 0x6c, 0x78, 0x74, 0x50, 0x5c, 0x48, 0x44},
+ {0x0, 0xd, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b},
+ {0x0, 0xe, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a},
+ {0x0, 0xf, 0x1e, 0x11, 0x3c, 0x33, 0x22, 0x2d, 0x78, 0x77, 0x66, 0x69, 0x44, 0x4b, 0x5a, 0x55},
+ {0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0},
+ {0x0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+ {0x0, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee},
+ {0x0, 0x13, 0x26, 0x35, 0x4c, 0x5f, 0x6a, 0x79, 0x98, 0x8b, 0xbe, 0xad, 0xd4, 0xc7, 0xf2, 0xe1},
+ {0x0, 0x14, 0x28, 0x3c, 0x50, 0x44, 0x78, 0x6c, 0xa0, 0xb4, 0x88, 0x9c, 0xf0, 0xe4, 0xd8, 0xcc},
+ {0x0, 0x15, 0x2a, 0x3f, 0x54, 0x41, 0x7e, 0x6b, 0xa8, 0xbd, 0x82, 0x97, 0xfc, 0xe9, 0xd6, 0xc3},
+ {0x0, 0x16, 0x2c, 0x3a, 0x58, 0x4e, 0x74, 0x62, 0xb0, 0xa6, 0x9c, 0x8a, 0xe8, 0xfe, 0xc4, 0xd2},
+ {0x0, 0x17, 0x2e, 0x39, 0x5c, 0x4b, 0x72, 0x65, 0xb8, 0xaf, 0x96, 0x81, 0xe4, 0xf3, 0xca, 0xdd},
+ {0x0, 0x18, 0x30, 0x28, 0x60, 0x78, 0x50, 0x48, 0xc0, 0xd8, 0xf0, 0xe8, 0xa0, 0xb8, 0x90, 0x88},
+ {0x0, 0x19, 0x32, 0x2b, 0x64, 0x7d, 0x56, 0x4f, 0xc8, 0xd1, 0xfa, 0xe3, 0xac, 0xb5, 0x9e, 0x87},
+ {0x0, 0x1a, 0x34, 0x2e, 0x68, 0x72, 0x5c, 0x46, 0xd0, 0xca, 0xe4, 0xfe, 0xb8, 0xa2, 0x8c, 0x96},
+ {0x0, 0x1b, 0x36, 0x2d, 0x6c, 0x77, 0x5a, 0x41, 0xd8, 0xc3, 0xee, 0xf5, 0xb4, 0xaf, 0x82, 0x99},
+ {0x0, 0x1c, 0x38, 0x24, 0x70, 0x6c, 0x48, 0x54, 0xe0, 0xfc, 0xd8, 0xc4, 0x90, 0x8c, 0xa8, 0xb4},
+ {0x0, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb},
+ {0x0, 0x1e, 0x3c, 0x22, 0x78, 0x66, 0x44, 0x5a, 0xf0, 0xee, 0xcc, 0xd2, 0x88, 0x96, 0xb4, 0xaa},
+ {0x0, 0x1f, 0x3e, 0x21, 0x7c, 0x63, 0x42, 0x5d, 0xf8, 0xe7, 0xc6, 0xd9, 0x84, 0x9b, 0xba, 0xa5},
+ {0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x1d, 0x3d, 0x5d, 0x7d, 0x9d, 0xbd, 0xdd, 0xfd},
+ {0x0, 0x21, 0x42, 0x63, 0x84, 0xa5, 0xc6, 0xe7, 0x15, 0x34, 0x57, 0x76, 0x91, 0xb0, 0xd3, 0xf2},
+ {0x0, 0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0xd, 0x2f, 0x49, 0x6b, 0x85, 0xa7, 0xc1, 0xe3},
+ {0x0, 0x23, 0x46, 0x65, 0x8c, 0xaf, 0xca, 0xe9, 0x5, 0x26, 0x43, 0x60, 0x89, 0xaa, 0xcf, 0xec},
+ {0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0xd8, 0xfc, 0x3d, 0x19, 0x75, 0x51, 0xad, 0x89, 0xe5, 0xc1},
+ {0x0, 0x25, 0x4a, 0x6f, 0x94, 0xb1, 0xde, 0xfb, 0x35, 0x10, 0x7f, 0x5a, 0xa1, 0x84, 0xeb, 0xce},
+ {0x0, 0x26, 0x4c, 0x6a, 0x98, 0xbe, 0xd4, 0xf2, 0x2d, 0xb, 0x61, 0x47, 0xb5, 0x93, 0xf9, 0xdf},
+ {0x0, 0x27, 0x4e, 0x69, 0x9c, 0xbb, 0xd2, 0xf5, 0x25, 0x2, 0x6b, 0x4c, 0xb9, 0x9e, 0xf7, 0xd0},
+ {0x0, 0x28, 0x50, 0x78, 0xa0, 0x88, 0xf0, 0xd8, 0x5d, 0x75, 0xd, 0x25, 0xfd, 0xd5, 0xad, 0x85},
+ {0x0, 0x29, 0x52, 0x7b, 0xa4, 0x8d, 0xf6, 0xdf, 0x55, 0x7c, 0x7, 0x2e, 0xf1, 0xd8, 0xa3, 0x8a},
+ {0x0, 0x2a, 0x54, 0x7e, 0xa8, 0x82, 0xfc, 0xd6, 0x4d, 0x67, 0x19, 0x33, 0xe5, 0xcf, 0xb1, 0x9b},
+ {0x0, 0x2b, 0x56, 0x7d, 0xac, 0x87, 0xfa, 0xd1, 0x45, 0x6e, 0x13, 0x38, 0xe9, 0xc2, 0xbf, 0x94},
+ {0x0, 0x2c, 0x58, 0x74, 0xb0, 0x9c, 0xe8, 0xc4, 0x7d, 0x51, 0x25, 0x9, 0xcd, 0xe1, 0x95, 0xb9},
+ {0x0, 0x2d, 0x5a, 0x77, 0xb4, 0x99, 0xee, 0xc3, 0x75, 0x58, 0x2f, 0x2, 0xc1, 0xec, 0x9b, 0xb6},
+ {0x0, 0x2e, 0x5c, 0x72, 0xb8, 0x96, 0xe4, 0xca, 0x6d, 0x43, 0x31, 0x1f, 0xd5, 0xfb, 0x89, 0xa7},
+ {0x0, 0x2f, 0x5e, 0x71, 0xbc, 0x93, 0xe2, 0xcd, 0x65, 0x4a, 0x3b, 0x14, 0xd9, 0xf6, 0x87, 0xa8},
+ {0x0, 0x30, 0x60, 0x50, 0xc0, 0xf0, 0xa0, 0x90, 0x9d, 0xad, 0xfd, 0xcd, 0x5d, 0x6d, 0x3d, 0xd},
+ {0x0, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, 0x95, 0xa4, 0xf7, 0xc6, 0x51, 0x60, 0x33, 0x2},
+ {0x0, 0x32, 0x64, 0x56, 0xc8, 0xfa, 0xac, 0x9e, 0x8d, 0xbf, 0xe9, 0xdb, 0x45, 0x77, 0x21, 0x13},
+ {0x0, 0x33, 0x66, 0x55, 0xcc, 0xff, 0xaa, 0x99, 0x85, 0xb6, 0xe3, 0xd0, 0x49, 0x7a, 0x2f, 0x1c},
+ {0x0, 0x34, 0x68, 0x5c, 0xd0, 0xe4, 0xb8, 0x8c, 0xbd, 0x89, 0xd5, 0xe1, 0x6d, 0x59, 0x5, 0x31},
+ {0x0, 0x35, 0x6a, 0x5f, 0xd4, 0xe1, 0xbe, 0x8b, 0xb5, 0x80, 0xdf, 0xea, 0x61, 0x54, 0xb, 0x3e},
+ {0x0, 0x36, 0x6c, 0x5a, 0xd8, 0xee, 0xb4, 0x82, 0xad, 0x9b, 0xc1, 0xf7, 0x75, 0x43, 0x19, 0x2f},
+ {0x0, 0x37, 0x6e, 0x59, 0xdc, 0xeb, 0xb2, 0x85, 0xa5, 0x92, 0xcb, 0xfc, 0x79, 0x4e, 0x17, 0x20},
+ {0x0, 0x38, 0x70, 0x48, 0xe0, 0xd8, 0x90, 0xa8, 0xdd, 0xe5, 0xad, 0x95, 0x3d, 0x5, 0x4d, 0x75},
+ {0x0, 0x39, 0x72, 0x4b, 0xe4, 0xdd, 0x96, 0xaf, 0xd5, 0xec, 0xa7, 0x9e, 0x31, 0x8, 0x43, 0x7a},
+ {0x0, 0x3a, 0x74, 0x4e, 0xe8, 0xd2, 0x9c, 0xa6, 0xcd, 0xf7, 0xb9, 0x83, 0x25, 0x1f, 0x51, 0x6b},
+ {0x0, 0x3b, 0x76, 0x4d, 0xec, 0xd7, 0x9a, 0xa1, 0xc5, 0xfe, 0xb3, 0x88, 0x29, 0x12, 0x5f, 0x64},
+ {0x0, 0x3c, 0x78, 0x44, 0xf0, 0xcc, 0x88, 0xb4, 0xfd, 0xc1, 0x85, 0xb9, 0xd, 0x31, 0x75, 0x49},
+ {0x0, 0x3d, 0x7a, 0x47, 0xf4, 0xc9, 0x8e, 0xb3, 0xf5, 0xc8, 0x8f, 0xb2, 0x1, 0x3c, 0x7b, 0x46},
+ {0x0, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0xed, 0xd3, 0x91, 0xaf, 0x15, 0x2b, 0x69, 0x57},
+ {0x0, 0x3f, 0x7e, 0x41, 0xfc, 0xc3, 0x82, 0xbd, 0xe5, 0xda, 0x9b, 0xa4, 0x19, 0x26, 0x67, 0x58},
+ {0x0, 0x40, 0x80, 0xc0, 0x1d, 0x5d, 0x9d, 0xdd, 0x3a, 0x7a, 0xba, 0xfa, 0x27, 0x67, 0xa7, 0xe7},
+ {0x0, 0x41, 0x82, 0xc3, 0x19, 0x58, 0x9b, 0xda, 0x32, 0x73, 0xb0, 0xf1, 0x2b, 0x6a, 0xa9, 0xe8},
+ {0x0, 0x42, 0x84, 0xc6, 0x15, 0x57, 0x91, 0xd3, 0x2a, 0x68, 0xae, 0xec, 0x3f, 0x7d, 0xbb, 0xf9},
+ {0x0, 0x43, 0x86, 0xc5, 0x11, 0x52, 0x97, 0xd4, 0x22, 0x61, 0xa4, 0xe7, 0x33, 0x70, 0xb5, 0xf6},
+ {0x0, 0x44, 0x88, 0xcc, 0xd, 0x49, 0x85, 0xc1, 0x1a, 0x5e, 0x92, 0xd6, 0x17, 0x53, 0x9f, 0xdb},
+ {0x0, 0x45, 0x8a, 0xcf, 0x9, 0x4c, 0x83, 0xc6, 0x12, 0x57, 0x98, 0xdd, 0x1b, 0x5e, 0x91, 0xd4},
+ {0x0, 0x46, 0x8c, 0xca, 0x5, 0x43, 0x89, 0xcf, 0xa, 0x4c, 0x86, 0xc0, 0xf, 0x49, 0x83, 0xc5},
+ {0x0, 0x47, 0x8e, 0xc9, 0x1, 0x46, 0x8f, 0xc8, 0x2, 0x45, 0x8c, 0xcb, 0x3, 0x44, 0x8d, 0xca},
+ {0x0, 0x48, 0x90, 0xd8, 0x3d, 0x75, 0xad, 0xe5, 0x7a, 0x32, 0xea, 0xa2, 0x47, 0xf, 0xd7, 0x9f},
+ {0x0, 0x49, 0x92, 0xdb, 0x39, 0x70, 0xab, 0xe2, 0x72, 0x3b, 0xe0, 0xa9, 0x4b, 0x2, 0xd9, 0x90},
+ {0x0, 0x4a, 0x94, 0xde, 0x35, 0x7f, 0xa1, 0xeb, 0x6a, 0x20, 0xfe, 0xb4, 0x5f, 0x15, 0xcb, 0x81},
+ {0x0, 0x4b, 0x96, 0xdd, 0x31, 0x7a, 0xa7, 0xec, 0x62, 0x29, 0xf4, 0xbf, 0x53, 0x18, 0xc5, 0x8e},
+ {0x0, 0x4c, 0x98, 0xd4, 0x2d, 0x61, 0xb5, 0xf9, 0x5a, 0x16, 0xc2, 0x8e, 0x77, 0x3b, 0xef, 0xa3},
+ {0x0, 0x4d, 0x9a, 0xd7, 0x29, 0x64, 0xb3, 0xfe, 0x52, 0x1f, 0xc8, 0x85, 0x7b, 0x36, 0xe1, 0xac},
+ {0x0, 0x4e, 0x9c, 0xd2, 0x25, 0x6b, 0xb9, 0xf7, 0x4a, 0x4, 0xd6, 0x98, 0x6f, 0x21, 0xf3, 0xbd},
+ {0x0, 0x4f, 0x9e, 0xd1, 0x21, 0x6e, 0xbf, 0xf0, 0x42, 0xd, 0xdc, 0x93, 0x63, 0x2c, 0xfd, 0xb2},
+ {0x0, 0x50, 0xa0, 0xf0, 0x5d, 0xd, 0xfd, 0xad, 0xba, 0xea, 0x1a, 0x4a, 0xe7, 0xb7, 0x47, 0x17},
+ {0x0, 0x51, 0xa2, 0xf3, 0x59, 0x8, 0xfb, 0xaa, 0xb2, 0xe3, 0x10, 0x41, 0xeb, 0xba, 0x49, 0x18},
+ {0x0, 0x52, 0xa4, 0xf6, 0x55, 0x7, 0xf1, 0xa3, 0xaa, 0xf8, 0xe, 0x5c, 0xff, 0xad, 0x5b, 0x9},
+ {0x0, 0x53, 0xa6, 0xf5, 0x51, 0x2, 0xf7, 0xa4, 0xa2, 0xf1, 0x4, 0x57, 0xf3, 0xa0, 0x55, 0x6},
+ {0x0, 0x54, 0xa8, 0xfc, 0x4d, 0x19, 0xe5, 0xb1, 0x9a, 0xce, 0x32, 0x66, 0xd7, 0x83, 0x7f, 0x2b},
+ {0x0, 0x55, 0xaa, 0xff, 0x49, 0x1c, 0xe3, 0xb6, 0x92, 0xc7, 0x38, 0x6d, 0xdb, 0x8e, 0x71, 0x24},
+ {0x0, 0x56, 0xac, 0xfa, 0x45, 0x13, 0xe9, 0xbf, 0x8a, 0xdc, 0x26, 0x70, 0xcf, 0x99, 0x63, 0x35},
+ {0x0, 0x57, 0xae, 0xf9, 0x41, 0x16, 0xef, 0xb8, 0x82, 0xd5, 0x2c, 0x7b, 0xc3, 0x94, 0x6d, 0x3a},
+ {0x0, 0x58, 0xb0, 0xe8, 0x7d, 0x25, 0xcd, 0x95, 0xfa, 0xa2, 0x4a, 0x12, 0x87, 0xdf, 0x37, 0x6f},
+ {0x0, 0x59, 0xb2, 0xeb, 0x79, 0x20, 0xcb, 0x92, 0xf2, 0xab, 0x40, 0x19, 0x8b, 0xd2, 0x39, 0x60},
+ {0x0, 0x5a, 0xb4, 0xee, 0x75, 0x2f, 0xc1, 0x9b, 0xea, 0xb0, 0x5e, 0x4, 0x9f, 0xc5, 0x2b, 0x71},
+ {0x0, 0x5b, 0xb6, 0xed, 0x71, 0x2a, 0xc7, 0x9c, 0xe2, 0xb9, 0x54, 0xf, 0x93, 0xc8, 0x25, 0x7e},
+ {0x0, 0x5c, 0xb8, 0xe4, 0x6d, 0x31, 0xd5, 0x89, 0xda, 0x86, 0x62, 0x3e, 0xb7, 0xeb, 0xf, 0x53},
+ {0x0, 0x5d, 0xba, 0xe7, 0x69, 0x34, 0xd3, 0x8e, 0xd2, 0x8f, 0x68, 0x35, 0xbb, 0xe6, 0x1, 0x5c},
+ {0x0, 0x5e, 0xbc, 0xe2, 0x65, 0x3b, 0xd9, 0x87, 0xca, 0x94, 0x76, 0x28, 0xaf, 0xf1, 0x13, 0x4d},
+ {0x0, 0x5f, 0xbe, 0xe1, 0x61, 0x3e, 0xdf, 0x80, 0xc2, 0x9d, 0x7c, 0x23, 0xa3, 0xfc, 0x1d, 0x42},
+ {0x0, 0x60, 0xc0, 0xa0, 0x9d, 0xfd, 0x5d, 0x3d, 0x27, 0x47, 0xe7, 0x87, 0xba, 0xda, 0x7a, 0x1a},
+ {0x0, 0x61, 0xc2, 0xa3, 0x99, 0xf8, 0x5b, 0x3a, 0x2f, 0x4e, 0xed, 0x8c, 0xb6, 0xd7, 0x74, 0x15},
+ {0x0, 0x62, 0xc4, 0xa6, 0x95, 0xf7, 0x51, 0x33, 0x37, 0x55, 0xf3, 0x91, 0xa2, 0xc0, 0x66, 0x4},
+ {0x0, 0x63, 0xc6, 0xa5, 0x91, 0xf2, 0x57, 0x34, 0x3f, 0x5c, 0xf9, 0x9a, 0xae, 0xcd, 0x68, 0xb},
+ {0x0, 0x64, 0xc8, 0xac, 0x8d, 0xe9, 0x45, 0x21, 0x7, 0x63, 0xcf, 0xab, 0x8a, 0xee, 0x42, 0x26},
+ {0x0, 0x65, 0xca, 0xaf, 0x89, 0xec, 0x43, 0x26, 0xf, 0x6a, 0xc5, 0xa0, 0x86, 0xe3, 0x4c, 0x29},
+ {0x0, 0x66, 0xcc, 0xaa, 0x85, 0xe3, 0x49, 0x2f, 0x17, 0x71, 0xdb, 0xbd, 0x92, 0xf4, 0x5e, 0x38},
+ {0x0, 0x67, 0xce, 0xa9, 0x81, 0xe6, 0x4f, 0x28, 0x1f, 0x78, 0xd1, 0xb6, 0x9e, 0xf9, 0x50, 0x37},
+ {0x0, 0x68, 0xd0, 0xb8, 0xbd, 0xd5, 0x6d, 0x5, 0x67, 0xf, 0xb7, 0xdf, 0xda, 0xb2, 0xa, 0x62},
+ {0x0, 0x69, 0xd2, 0xbb, 0xb9, 0xd0, 0x6b, 0x2, 0x6f, 0x6, 0xbd, 0xd4, 0xd6, 0xbf, 0x4, 0x6d},
+ {0x0, 0x6a, 0xd4, 0xbe, 0xb5, 0xdf, 0x61, 0xb, 0x77, 0x1d, 0xa3, 0xc9, 0xc2, 0xa8, 0x16, 0x7c},
+ {0x0, 0x6b, 0xd6, 0xbd, 0xb1, 0xda, 0x67, 0xc, 0x7f, 0x14, 0xa9, 0xc2, 0xce, 0xa5, 0x18, 0x73},
+ {0x0, 0x6c, 0xd8, 0xb4, 0xad, 0xc1, 0x75, 0x19, 0x47, 0x2b, 0x9f, 0xf3, 0xea, 0x86, 0x32, 0x5e},
+ {0x0, 0x6d, 0xda, 0xb7, 0xa9, 0xc4, 0x73, 0x1e, 0x4f, 0x22, 0x95, 0xf8, 0xe6, 0x8b, 0x3c, 0x51},
+ {0x0, 0x6e, 0xdc, 0xb2, 0xa5, 0xcb, 0x79, 0x17, 0x57, 0x39, 0x8b, 0xe5, 0xf2, 0x9c, 0x2e, 0x40},
+ {0x0, 0x6f, 0xde, 0xb1, 0xa1, 0xce, 0x7f, 0x10, 0x5f, 0x30, 0x81, 0xee, 0xfe, 0x91, 0x20, 0x4f},
+ {0x0, 0x70, 0xe0, 0x90, 0xdd, 0xad, 0x3d, 0x4d, 0xa7, 0xd7, 0x47, 0x37, 0x7a, 0xa, 0x9a, 0xea},
+ {0x0, 0x71, 0xe2, 0x93, 0xd9, 0xa8, 0x3b, 0x4a, 0xaf, 0xde, 0x4d, 0x3c, 0x76, 0x7, 0x94, 0xe5},
+ {0x0, 0x72, 0xe4, 0x96, 0xd5, 0xa7, 0x31, 0x43, 0xb7, 0xc5, 0x53, 0x21, 0x62, 0x10, 0x86, 0xf4},
+ {0x0, 0x73, 0xe6, 0x95, 0xd1, 0xa2, 0x37, 0x44, 0xbf, 0xcc, 0x59, 0x2a, 0x6e, 0x1d, 0x88, 0xfb},
+ {0x0, 0x74, 0xe8, 0x9c, 0xcd, 0xb9, 0x25, 0x51, 0x87, 0xf3, 0x6f, 0x1b, 0x4a, 0x3e, 0xa2, 0xd6},
+ {0x0, 0x75, 0xea, 0x9f, 0xc9, 0xbc, 0x23, 0x56, 0x8f, 0xfa, 0x65, 0x10, 0x46, 0x33, 0xac, 0xd9},
+ {0x0, 0x76, 0xec, 0x9a, 0xc5, 0xb3, 0x29, 0x5f, 0x97, 0xe1, 0x7b, 0xd, 0x52, 0x24, 0xbe, 0xc8},
+ {0x0, 0x77, 0xee, 0x99, 0xc1, 0xb6, 0x2f, 0x58, 0x9f, 0xe8, 0x71, 0x6, 0x5e, 0x29, 0xb0, 0xc7},
+ {0x0, 0x78, 0xf0, 0x88, 0xfd, 0x85, 0xd, 0x75, 0xe7, 0x9f, 0x17, 0x6f, 0x1a, 0x62, 0xea, 0x92},
+ {0x0, 0x79, 0xf2, 0x8b, 0xf9, 0x80, 0xb, 0x72, 0xef, 0x96, 0x1d, 0x64, 0x16, 0x6f, 0xe4, 0x9d},
+ {0x0, 0x7a, 0xf4, 0x8e, 0xf5, 0x8f, 0x1, 0x7b, 0xf7, 0x8d, 0x3, 0x79, 0x2, 0x78, 0xf6, 0x8c},
+ {0x0, 0x7b, 0xf6, 0x8d, 0xf1, 0x8a, 0x7, 0x7c, 0xff, 0x84, 0x9, 0x72, 0xe, 0x75, 0xf8, 0x83},
+ {0x0, 0x7c, 0xf8, 0x84, 0xed, 0x91, 0x15, 0x69, 0xc7, 0xbb, 0x3f, 0x43, 0x2a, 0x56, 0xd2, 0xae},
+ {0x0, 0x7d, 0xfa, 0x87, 0xe9, 0x94, 0x13, 0x6e, 0xcf, 0xb2, 0x35, 0x48, 0x26, 0x5b, 0xdc, 0xa1},
+ {0x0, 0x7e, 0xfc, 0x82, 0xe5, 0x9b, 0x19, 0x67, 0xd7, 0xa9, 0x2b, 0x55, 0x32, 0x4c, 0xce, 0xb0},
+ {0x0, 0x7f, 0xfe, 0x81, 0xe1, 0x9e, 0x1f, 0x60, 0xdf, 0xa0, 0x21, 0x5e, 0x3e, 0x41, 0xc0, 0xbf},
+ {0x0, 0x80, 0x1d, 0x9d, 0x3a, 0xba, 0x27, 0xa7, 0x74, 0xf4, 0x69, 0xe9, 0x4e, 0xce, 0x53, 0xd3},
+ {0x0, 0x81, 0x1f, 0x9e, 0x3e, 0xbf, 0x21, 0xa0, 0x7c, 0xfd, 0x63, 0xe2, 0x42, 0xc3, 0x5d, 0xdc},
+ {0x0, 0x82, 0x19, 0x9b, 0x32, 0xb0, 0x2b, 0xa9, 0x64, 0xe6, 0x7d, 0xff, 0x56, 0xd4, 0x4f, 0xcd},
+ {0x0, 0x83, 0x1b, 0x98, 0x36, 0xb5, 0x2d, 0xae, 0x6c, 0xef, 0x77, 0xf4, 0x5a, 0xd9, 0x41, 0xc2},
+ {0x0, 0x84, 0x15, 0x91, 0x2a, 0xae, 0x3f, 0xbb, 0x54, 0xd0, 0x41, 0xc5, 0x7e, 0xfa, 0x6b, 0xef},
+ {0x0, 0x85, 0x17, 0x92, 0x2e, 0xab, 0x39, 0xbc, 0x5c, 0xd9, 0x4b, 0xce, 0x72, 0xf7, 0x65, 0xe0},
+ {0x0, 0x86, 0x11, 0x97, 0x22, 0xa4, 0x33, 0xb5, 0x44, 0xc2, 0x55, 0xd3, 0x66, 0xe0, 0x77, 0xf1},
+ {0x0, 0x87, 0x13, 0x94, 0x26, 0xa1, 0x35, 0xb2, 0x4c, 0xcb, 0x5f, 0xd8, 0x6a, 0xed, 0x79, 0xfe},
+ {0x0, 0x88, 0xd, 0x85, 0x1a, 0x92, 0x17, 0x9f, 0x34, 0xbc, 0x39, 0xb1, 0x2e, 0xa6, 0x23, 0xab},
+ {0x0, 0x89, 0xf, 0x86, 0x1e, 0x97, 0x11, 0x98, 0x3c, 0xb5, 0x33, 0xba, 0x22, 0xab, 0x2d, 0xa4},
+ {0x0, 0x8a, 0x9, 0x83, 0x12, 0x98, 0x1b, 0x91, 0x24, 0xae, 0x2d, 0xa7, 0x36, 0xbc, 0x3f, 0xb5},
+ {0x0, 0x8b, 0xb, 0x80, 0x16, 0x9d, 0x1d, 0x96, 0x2c, 0xa7, 0x27, 0xac, 0x3a, 0xb1, 0x31, 0xba},
+ {0x0, 0x8c, 0x5, 0x89, 0xa, 0x86, 0xf, 0x83, 0x14, 0x98, 0x11, 0x9d, 0x1e, 0x92, 0x1b, 0x97},
+ {0x0, 0x8d, 0x7, 0x8a, 0xe, 0x83, 0x9, 0x84, 0x1c, 0x91, 0x1b, 0x96, 0x12, 0x9f, 0x15, 0x98},
+ {0x0, 0x8e, 0x1, 0x8f, 0x2, 0x8c, 0x3, 0x8d, 0x4, 0x8a, 0x5, 0x8b, 0x6, 0x88, 0x7, 0x89},
+ {0x0, 0x8f, 0x3, 0x8c, 0x6, 0x89, 0x5, 0x8a, 0xc, 0x83, 0xf, 0x80, 0xa, 0x85, 0x9, 0x86},
+ {0x0, 0x90, 0x3d, 0xad, 0x7a, 0xea, 0x47, 0xd7, 0xf4, 0x64, 0xc9, 0x59, 0x8e, 0x1e, 0xb3, 0x23},
+ {0x0, 0x91, 0x3f, 0xae, 0x7e, 0xef, 0x41, 0xd0, 0xfc, 0x6d, 0xc3, 0x52, 0x82, 0x13, 0xbd, 0x2c},
+ {0x0, 0x92, 0x39, 0xab, 0x72, 0xe0, 0x4b, 0xd9, 0xe4, 0x76, 0xdd, 0x4f, 0x96, 0x4, 0xaf, 0x3d},
+ {0x0, 0x93, 0x3b, 0xa8, 0x76, 0xe5, 0x4d, 0xde, 0xec, 0x7f, 0xd7, 0x44, 0x9a, 0x9, 0xa1, 0x32},
+ {0x0, 0x94, 0x35, 0xa1, 0x6a, 0xfe, 0x5f, 0xcb, 0xd4, 0x40, 0xe1, 0x75, 0xbe, 0x2a, 0x8b, 0x1f},
+ {0x0, 0x95, 0x37, 0xa2, 0x6e, 0xfb, 0x59, 0xcc, 0xdc, 0x49, 0xeb, 0x7e, 0xb2, 0x27, 0x85, 0x10},
+ {0x0, 0x96, 0x31, 0xa7, 0x62, 0xf4, 0x53, 0xc5, 0xc4, 0x52, 0xf5, 0x63, 0xa6, 0x30, 0x97, 0x1},
+ {0x0, 0x97, 0x33, 0xa4, 0x66, 0xf1, 0x55, 0xc2, 0xcc, 0x5b, 0xff, 0x68, 0xaa, 0x3d, 0x99, 0xe},
+ {0x0, 0x98, 0x2d, 0xb5, 0x5a, 0xc2, 0x77, 0xef, 0xb4, 0x2c, 0x99, 0x1, 0xee, 0x76, 0xc3, 0x5b},
+ {0x0, 0x99, 0x2f, 0xb6, 0x5e, 0xc7, 0x71, 0xe8, 0xbc, 0x25, 0x93, 0xa, 0xe2, 0x7b, 0xcd, 0x54},
+ {0x0, 0x9a, 0x29, 0xb3, 0x52, 0xc8, 0x7b, 0xe1, 0xa4, 0x3e, 0x8d, 0x17, 0xf6, 0x6c, 0xdf, 0x45},
+ {0x0, 0x9b, 0x2b, 0xb0, 0x56, 0xcd, 0x7d, 0xe6, 0xac, 0x37, 0x87, 0x1c, 0xfa, 0x61, 0xd1, 0x4a},
+ {0x0, 0x9c, 0x25, 0xb9, 0x4a, 0xd6, 0x6f, 0xf3, 0x94, 0x8, 0xb1, 0x2d, 0xde, 0x42, 0xfb, 0x67},
+ {0x0, 0x9d, 0x27, 0xba, 0x4e, 0xd3, 0x69, 0xf4, 0x9c, 0x1, 0xbb, 0x26, 0xd2, 0x4f, 0xf5, 0x68},
+ {0x0, 0x9e, 0x21, 0xbf, 0x42, 0xdc, 0x63, 0xfd, 0x84, 0x1a, 0xa5, 0x3b, 0xc6, 0x58, 0xe7, 0x79},
+ {0x0, 0x9f, 0x23, 0xbc, 0x46, 0xd9, 0x65, 0xfa, 0x8c, 0x13, 0xaf, 0x30, 0xca, 0x55, 0xe9, 0x76},
+ {0x0, 0xa0, 0x5d, 0xfd, 0xba, 0x1a, 0xe7, 0x47, 0x69, 0xc9, 0x34, 0x94, 0xd3, 0x73, 0x8e, 0x2e},
+ {0x0, 0xa1, 0x5f, 0xfe, 0xbe, 0x1f, 0xe1, 0x40, 0x61, 0xc0, 0x3e, 0x9f, 0xdf, 0x7e, 0x80, 0x21},
+ {0x0, 0xa2, 0x59, 0xfb, 0xb2, 0x10, 0xeb, 0x49, 0x79, 0xdb, 0x20, 0x82, 0xcb, 0x69, 0x92, 0x30},
+ {0x0, 0xa3, 0x5b, 0xf8, 0xb6, 0x15, 0xed, 0x4e, 0x71, 0xd2, 0x2a, 0x89, 0xc7, 0x64, 0x9c, 0x3f},
+ {0x0, 0xa4, 0x55, 0xf1, 0xaa, 0xe, 0xff, 0x5b, 0x49, 0xed, 0x1c, 0xb8, 0xe3, 0x47, 0xb6, 0x12},
+ {0x0, 0xa5, 0x57, 0xf2, 0xae, 0xb, 0xf9, 0x5c, 0x41, 0xe4, 0x16, 0xb3, 0xef, 0x4a, 0xb8, 0x1d},
+ {0x0, 0xa6, 0x51, 0xf7, 0xa2, 0x4, 0xf3, 0x55, 0x59, 0xff, 0x8, 0xae, 0xfb, 0x5d, 0xaa, 0xc},
+ {0x0, 0xa7, 0x53, 0xf4, 0xa6, 0x1, 0xf5, 0x52, 0x51, 0xf6, 0x2, 0xa5, 0xf7, 0x50, 0xa4, 0x3},
+ {0x0, 0xa8, 0x4d, 0xe5, 0x9a, 0x32, 0xd7, 0x7f, 0x29, 0x81, 0x64, 0xcc, 0xb3, 0x1b, 0xfe, 0x56},
+ {0x0, 0xa9, 0x4f, 0xe6, 0x9e, 0x37, 0xd1, 0x78, 0x21, 0x88, 0x6e, 0xc7, 0xbf, 0x16, 0xf0, 0x59},
+ {0x0, 0xaa, 0x49, 0xe3, 0x92, 0x38, 0xdb, 0x71, 0x39, 0x93, 0x70, 0xda, 0xab, 0x1, 0xe2, 0x48},
+ {0x0, 0xab, 0x4b, 0xe0, 0x96, 0x3d, 0xdd, 0x76, 0x31, 0x9a, 0x7a, 0xd1, 0xa7, 0xc, 0xec, 0x47},
+ {0x0, 0xac, 0x45, 0xe9, 0x8a, 0x26, 0xcf, 0x63, 0x9, 0xa5, 0x4c, 0xe0, 0x83, 0x2f, 0xc6, 0x6a},
+ {0x0, 0xad, 0x47, 0xea, 0x8e, 0x23, 0xc9, 0x64, 0x1, 0xac, 0x46, 0xeb, 0x8f, 0x22, 0xc8, 0x65},
+ {0x0, 0xae, 0x41, 0xef, 0x82, 0x2c, 0xc3, 0x6d, 0x19, 0xb7, 0x58, 0xf6, 0x9b, 0x35, 0xda, 0x74},
+ {0x0, 0xaf, 0x43, 0xec, 0x86, 0x29, 0xc5, 0x6a, 0x11, 0xbe, 0x52, 0xfd, 0x97, 0x38, 0xd4, 0x7b},
+ {0x0, 0xb0, 0x7d, 0xcd, 0xfa, 0x4a, 0x87, 0x37, 0xe9, 0x59, 0x94, 0x24, 0x13, 0xa3, 0x6e, 0xde},
+ {0x0, 0xb1, 0x7f, 0xce, 0xfe, 0x4f, 0x81, 0x30, 0xe1, 0x50, 0x9e, 0x2f, 0x1f, 0xae, 0x60, 0xd1},
+ {0x0, 0xb2, 0x79, 0xcb, 0xf2, 0x40, 0x8b, 0x39, 0xf9, 0x4b, 0x80, 0x32, 0xb, 0xb9, 0x72, 0xc0},
+ {0x0, 0xb3, 0x7b, 0xc8, 0xf6, 0x45, 0x8d, 0x3e, 0xf1, 0x42, 0x8a, 0x39, 0x7, 0xb4, 0x7c, 0xcf},
+ {0x0, 0xb4, 0x75, 0xc1, 0xea, 0x5e, 0x9f, 0x2b, 0xc9, 0x7d, 0xbc, 0x8, 0x23, 0x97, 0x56, 0xe2},
+ {0x0, 0xb5, 0x77, 0xc2, 0xee, 0x5b, 0x99, 0x2c, 0xc1, 0x74, 0xb6, 0x3, 0x2f, 0x9a, 0x58, 0xed},
+ {0x0, 0xb6, 0x71, 0xc7, 0xe2, 0x54, 0x93, 0x25, 0xd9, 0x6f, 0xa8, 0x1e, 0x3b, 0x8d, 0x4a, 0xfc},
+ {0x0, 0xb7, 0x73, 0xc4, 0xe6, 0x51, 0x95, 0x22, 0xd1, 0x66, 0xa2, 0x15, 0x37, 0x80, 0x44, 0xf3},
+ {0x0, 0xb8, 0x6d, 0xd5, 0xda, 0x62, 0xb7, 0xf, 0xa9, 0x11, 0xc4, 0x7c, 0x73, 0xcb, 0x1e, 0xa6},
+ {0x0, 0xb9, 0x6f, 0xd6, 0xde, 0x67, 0xb1, 0x8, 0xa1, 0x18, 0xce, 0x77, 0x7f, 0xc6, 0x10, 0xa9},
+ {0x0, 0xba, 0x69, 0xd3, 0xd2, 0x68, 0xbb, 0x1, 0xb9, 0x3, 0xd0, 0x6a, 0x6b, 0xd1, 0x2, 0xb8},
+ {0x0, 0xbb, 0x6b, 0xd0, 0xd6, 0x6d, 0xbd, 0x6, 0xb1, 0xa, 0xda, 0x61, 0x67, 0xdc, 0xc, 0xb7},
+ {0x0, 0xbc, 0x65, 0xd9, 0xca, 0x76, 0xaf, 0x13, 0x89, 0x35, 0xec, 0x50, 0x43, 0xff, 0x26, 0x9a},
+ {0x0, 0xbd, 0x67, 0xda, 0xce, 0x73, 0xa9, 0x14, 0x81, 0x3c, 0xe6, 0x5b, 0x4f, 0xf2, 0x28, 0x95},
+ {0x0, 0xbe, 0x61, 0xdf, 0xc2, 0x7c, 0xa3, 0x1d, 0x99, 0x27, 0xf8, 0x46, 0x5b, 0xe5, 0x3a, 0x84},
+ {0x0, 0xbf, 0x63, 0xdc, 0xc6, 0x79, 0xa5, 0x1a, 0x91, 0x2e, 0xf2, 0x4d, 0x57, 0xe8, 0x34, 0x8b},
+ {0x0, 0xc0, 0x9d, 0x5d, 0x27, 0xe7, 0xba, 0x7a, 0x4e, 0x8e, 0xd3, 0x13, 0x69, 0xa9, 0xf4, 0x34},
+ {0x0, 0xc1, 0x9f, 0x5e, 0x23, 0xe2, 0xbc, 0x7d, 0x46, 0x87, 0xd9, 0x18, 0x65, 0xa4, 0xfa, 0x3b},
+ {0x0, 0xc2, 0x99, 0x5b, 0x2f, 0xed, 0xb6, 0x74, 0x5e, 0x9c, 0xc7, 0x5, 0x71, 0xb3, 0xe8, 0x2a},
+ {0x0, 0xc3, 0x9b, 0x58, 0x2b, 0xe8, 0xb0, 0x73, 0x56, 0x95, 0xcd, 0xe, 0x7d, 0xbe, 0xe6, 0x25},
+ {0x0, 0xc4, 0x95, 0x51, 0x37, 0xf3, 0xa2, 0x66, 0x6e, 0xaa, 0xfb, 0x3f, 0x59, 0x9d, 0xcc, 0x8},
+ {0x0, 0xc5, 0x97, 0x52, 0x33, 0xf6, 0xa4, 0x61, 0x66, 0xa3, 0xf1, 0x34, 0x55, 0x90, 0xc2, 0x7},
+ {0x0, 0xc6, 0x91, 0x57, 0x3f, 0xf9, 0xae, 0x68, 0x7e, 0xb8, 0xef, 0x29, 0x41, 0x87, 0xd0, 0x16},
+ {0x0, 0xc7, 0x93, 0x54, 0x3b, 0xfc, 0xa8, 0x6f, 0x76, 0xb1, 0xe5, 0x22, 0x4d, 0x8a, 0xde, 0x19},
+ {0x0, 0xc8, 0x8d, 0x45, 0x7, 0xcf, 0x8a, 0x42, 0xe, 0xc6, 0x83, 0x4b, 0x9, 0xc1, 0x84, 0x4c},
+ {0x0, 0xc9, 0x8f, 0x46, 0x3, 0xca, 0x8c, 0x45, 0x6, 0xcf, 0x89, 0x40, 0x5, 0xcc, 0x8a, 0x43},
+ {0x0, 0xca, 0x89, 0x43, 0xf, 0xc5, 0x86, 0x4c, 0x1e, 0xd4, 0x97, 0x5d, 0x11, 0xdb, 0x98, 0x52},
+ {0x0, 0xcb, 0x8b, 0x40, 0xb, 0xc0, 0x80, 0x4b, 0x16, 0xdd, 0x9d, 0x56, 0x1d, 0xd6, 0x96, 0x5d},
+ {0x0, 0xcc, 0x85, 0x49, 0x17, 0xdb, 0x92, 0x5e, 0x2e, 0xe2, 0xab, 0x67, 0x39, 0xf5, 0xbc, 0x70},
+ {0x0, 0xcd, 0x87, 0x4a, 0x13, 0xde, 0x94, 0x59, 0x26, 0xeb, 0xa1, 0x6c, 0x35, 0xf8, 0xb2, 0x7f},
+ {0x0, 0xce, 0x81, 0x4f, 0x1f, 0xd1, 0x9e, 0x50, 0x3e, 0xf0, 0xbf, 0x71, 0x21, 0xef, 0xa0, 0x6e},
+ {0x0, 0xcf, 0x83, 0x4c, 0x1b, 0xd4, 0x98, 0x57, 0x36, 0xf9, 0xb5, 0x7a, 0x2d, 0xe2, 0xae, 0x61},
+ {0x0, 0xd0, 0xbd, 0x6d, 0x67, 0xb7, 0xda, 0xa, 0xce, 0x1e, 0x73, 0xa3, 0xa9, 0x79, 0x14, 0xc4},
+ {0x0, 0xd1, 0xbf, 0x6e, 0x63, 0xb2, 0xdc, 0xd, 0xc6, 0x17, 0x79, 0xa8, 0xa5, 0x74, 0x1a, 0xcb},
+ {0x0, 0xd2, 0xb9, 0x6b, 0x6f, 0xbd, 0xd6, 0x4, 0xde, 0xc, 0x67, 0xb5, 0xb1, 0x63, 0x8, 0xda},
+ {0x0, 0xd3, 0xbb, 0x68, 0x6b, 0xb8, 0xd0, 0x3, 0xd6, 0x5, 0x6d, 0xbe, 0xbd, 0x6e, 0x6, 0xd5},
+ {0x0, 0xd4, 0xb5, 0x61, 0x77, 0xa3, 0xc2, 0x16, 0xee, 0x3a, 0x5b, 0x8f, 0x99, 0x4d, 0x2c, 0xf8},
+ {0x0, 0xd5, 0xb7, 0x62, 0x73, 0xa6, 0xc4, 0x11, 0xe6, 0x33, 0x51, 0x84, 0x95, 0x40, 0x22, 0xf7},
+ {0x0, 0xd6, 0xb1, 0x67, 0x7f, 0xa9, 0xce, 0x18, 0xfe, 0x28, 0x4f, 0x99, 0x81, 0x57, 0x30, 0xe6},
+ {0x0, 0xd7, 0xb3, 0x64, 0x7b, 0xac, 0xc8, 0x1f, 0xf6, 0x21, 0x45, 0x92, 0x8d, 0x5a, 0x3e, 0xe9},
+ {0x0, 0xd8, 0xad, 0x75, 0x47, 0x9f, 0xea, 0x32, 0x8e, 0x56, 0x23, 0xfb, 0xc9, 0x11, 0x64, 0xbc},
+ {0x0, 0xd9, 0xaf, 0x76, 0x43, 0x9a, 0xec, 0x35, 0x86, 0x5f, 0x29, 0xf0, 0xc5, 0x1c, 0x6a, 0xb3},
+ {0x0, 0xda, 0xa9, 0x73, 0x4f, 0x95, 0xe6, 0x3c, 0x9e, 0x44, 0x37, 0xed, 0xd1, 0xb, 0x78, 0xa2},
+ {0x0, 0xdb, 0xab, 0x70, 0x4b, 0x90, 0xe0, 0x3b, 0x96, 0x4d, 0x3d, 0xe6, 0xdd, 0x6, 0x76, 0xad},
+ {0x0, 0xdc, 0xa5, 0x79, 0x57, 0x8b, 0xf2, 0x2e, 0xae, 0x72, 0xb, 0xd7, 0xf9, 0x25, 0x5c, 0x80},
+ {0x0, 0xdd, 0xa7, 0x7a, 0x53, 0x8e, 0xf4, 0x29, 0xa6, 0x7b, 0x1, 0xdc, 0xf5, 0x28, 0x52, 0x8f},
+ {0x0, 0xde, 0xa1, 0x7f, 0x5f, 0x81, 0xfe, 0x20, 0xbe, 0x60, 0x1f, 0xc1, 0xe1, 0x3f, 0x40, 0x9e},
+ {0x0, 0xdf, 0xa3, 0x7c, 0x5b, 0x84, 0xf8, 0x27, 0xb6, 0x69, 0x15, 0xca, 0xed, 0x32, 0x4e, 0x91},
+ {0x0, 0xe0, 0xdd, 0x3d, 0xa7, 0x47, 0x7a, 0x9a, 0x53, 0xb3, 0x8e, 0x6e, 0xf4, 0x14, 0x29, 0xc9},
+ {0x0, 0xe1, 0xdf, 0x3e, 0xa3, 0x42, 0x7c, 0x9d, 0x5b, 0xba, 0x84, 0x65, 0xf8, 0x19, 0x27, 0xc6},
+ {0x0, 0xe2, 0xd9, 0x3b, 0xaf, 0x4d, 0x76, 0x94, 0x43, 0xa1, 0x9a, 0x78, 0xec, 0xe, 0x35, 0xd7},
+ {0x0, 0xe3, 0xdb, 0x38, 0xab, 0x48, 0x70, 0x93, 0x4b, 0xa8, 0x90, 0x73, 0xe0, 0x3, 0x3b, 0xd8},
+ {0x0, 0xe4, 0xd5, 0x31, 0xb7, 0x53, 0x62, 0x86, 0x73, 0x97, 0xa6, 0x42, 0xc4, 0x20, 0x11, 0xf5},
+ {0x0, 0xe5, 0xd7, 0x32, 0xb3, 0x56, 0x64, 0x81, 0x7b, 0x9e, 0xac, 0x49, 0xc8, 0x2d, 0x1f, 0xfa},
+ {0x0, 0xe6, 0xd1, 0x37, 0xbf, 0x59, 0x6e, 0x88, 0x63, 0x85, 0xb2, 0x54, 0xdc, 0x3a, 0xd, 0xeb},
+ {0x0, 0xe7, 0xd3, 0x34, 0xbb, 0x5c, 0x68, 0x8f, 0x6b, 0x8c, 0xb8, 0x5f, 0xd0, 0x37, 0x3, 0xe4},
+ {0x0, 0xe8, 0xcd, 0x25, 0x87, 0x6f, 0x4a, 0xa2, 0x13, 0xfb, 0xde, 0x36, 0x94, 0x7c, 0x59, 0xb1},
+ {0x0, 0xe9, 0xcf, 0x26, 0x83, 0x6a, 0x4c, 0xa5, 0x1b, 0xf2, 0xd4, 0x3d, 0x98, 0x71, 0x57, 0xbe},
+ {0x0, 0xea, 0xc9, 0x23, 0x8f, 0x65, 0x46, 0xac, 0x3, 0xe9, 0xca, 0x20, 0x8c, 0x66, 0x45, 0xaf},
+ {0x0, 0xeb, 0xcb, 0x20, 0x8b, 0x60, 0x40, 0xab, 0xb, 0xe0, 0xc0, 0x2b, 0x80, 0x6b, 0x4b, 0xa0},
+ {0x0, 0xec, 0xc5, 0x29, 0x97, 0x7b, 0x52, 0xbe, 0x33, 0xdf, 0xf6, 0x1a, 0xa4, 0x48, 0x61, 0x8d},
+ {0x0, 0xed, 0xc7, 0x2a, 0x93, 0x7e, 0x54, 0xb9, 0x3b, 0xd6, 0xfc, 0x11, 0xa8, 0x45, 0x6f, 0x82},
+ {0x0, 0xee, 0xc1, 0x2f, 0x9f, 0x71, 0x5e, 0xb0, 0x23, 0xcd, 0xe2, 0xc, 0xbc, 0x52, 0x7d, 0x93},
+ {0x0, 0xef, 0xc3, 0x2c, 0x9b, 0x74, 0x58, 0xb7, 0x2b, 0xc4, 0xe8, 0x7, 0xb0, 0x5f, 0x73, 0x9c},
+ {0x0, 0xf0, 0xfd, 0xd, 0xe7, 0x17, 0x1a, 0xea, 0xd3, 0x23, 0x2e, 0xde, 0x34, 0xc4, 0xc9, 0x39},
+ {0x0, 0xf1, 0xff, 0xe, 0xe3, 0x12, 0x1c, 0xed, 0xdb, 0x2a, 0x24, 0xd5, 0x38, 0xc9, 0xc7, 0x36},
+ {0x0, 0xf2, 0xf9, 0xb, 0xef, 0x1d, 0x16, 0xe4, 0xc3, 0x31, 0x3a, 0xc8, 0x2c, 0xde, 0xd5, 0x27},
+ {0x0, 0xf3, 0xfb, 0x8, 0xeb, 0x18, 0x10, 0xe3, 0xcb, 0x38, 0x30, 0xc3, 0x20, 0xd3, 0xdb, 0x28},
+ {0x0, 0xf4, 0xf5, 0x1, 0xf7, 0x3, 0x2, 0xf6, 0xf3, 0x7, 0x6, 0xf2, 0x4, 0xf0, 0xf1, 0x5},
+ {0x0, 0xf5, 0xf7, 0x2, 0xf3, 0x6, 0x4, 0xf1, 0xfb, 0xe, 0xc, 0xf9, 0x8, 0xfd, 0xff, 0xa},
+ {0x0, 0xf6, 0xf1, 0x7, 0xff, 0x9, 0xe, 0xf8, 0xe3, 0x15, 0x12, 0xe4, 0x1c, 0xea, 0xed, 0x1b},
+ {0x0, 0xf7, 0xf3, 0x4, 0xfb, 0xc, 0x8, 0xff, 0xeb, 0x1c, 0x18, 0xef, 0x10, 0xe7, 0xe3, 0x14},
+ {0x0, 0xf8, 0xed, 0x15, 0xc7, 0x3f, 0x2a, 0xd2, 0x93, 0x6b, 0x7e, 0x86, 0x54, 0xac, 0xb9, 0x41},
+ {0x0, 0xf9, 0xef, 0x16, 0xc3, 0x3a, 0x2c, 0xd5, 0x9b, 0x62, 0x74, 0x8d, 0x58, 0xa1, 0xb7, 0x4e},
+ {0x0, 0xfa, 0xe9, 0x13, 0xcf, 0x35, 0x26, 0xdc, 0x83, 0x79, 0x6a, 0x90, 0x4c, 0xb6, 0xa5, 0x5f},
+ {0x0, 0xfb, 0xeb, 0x10, 0xcb, 0x30, 0x20, 0xdb, 0x8b, 0x70, 0x60, 0x9b, 0x40, 0xbb, 0xab, 0x50},
+ {0x0, 0xfc, 0xe5, 0x19, 0xd7, 0x2b, 0x32, 0xce, 0xb3, 0x4f, 0x56, 0xaa, 0x64, 0x98, 0x81, 0x7d},
+ {0x0, 0xfd, 0xe7, 0x1a, 0xd3, 0x2e, 0x34, 0xc9, 0xbb, 0x46, 0x5c, 0xa1, 0x68, 0x95, 0x8f, 0x72},
+ {0x0, 0xfe, 0xe1, 0x1f, 0xdf, 0x21, 0x3e, 0xc0, 0xa3, 0x5d, 0x42, 0xbc, 0x7c, 0x82, 0x9d, 0x63},
+ {0x0, 0xff, 0xe3, 0x1c, 0xdb, 0x24, 0x38, 0xc7, 0xab, 0x54, 0x48, 0xb7, 0x70, 0x8f, 0x93, 0x6c}}
+var mulTableHigh = [256][16]uint8{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
+ {0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0},
+ {0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x1d, 0x3d, 0x5d, 0x7d, 0x9d, 0xbd, 0xdd, 0xfd},
+ {0x0, 0x30, 0x60, 0x50, 0xc0, 0xf0, 0xa0, 0x90, 0x9d, 0xad, 0xfd, 0xcd, 0x5d, 0x6d, 0x3d, 0xd},
+ {0x0, 0x40, 0x80, 0xc0, 0x1d, 0x5d, 0x9d, 0xdd, 0x3a, 0x7a, 0xba, 0xfa, 0x27, 0x67, 0xa7, 0xe7},
+ {0x0, 0x50, 0xa0, 0xf0, 0x5d, 0xd, 0xfd, 0xad, 0xba, 0xea, 0x1a, 0x4a, 0xe7, 0xb7, 0x47, 0x17},
+ {0x0, 0x60, 0xc0, 0xa0, 0x9d, 0xfd, 0x5d, 0x3d, 0x27, 0x47, 0xe7, 0x87, 0xba, 0xda, 0x7a, 0x1a},
+ {0x0, 0x70, 0xe0, 0x90, 0xdd, 0xad, 0x3d, 0x4d, 0xa7, 0xd7, 0x47, 0x37, 0x7a, 0xa, 0x9a, 0xea},
+ {0x0, 0x80, 0x1d, 0x9d, 0x3a, 0xba, 0x27, 0xa7, 0x74, 0xf4, 0x69, 0xe9, 0x4e, 0xce, 0x53, 0xd3},
+ {0x0, 0x90, 0x3d, 0xad, 0x7a, 0xea, 0x47, 0xd7, 0xf4, 0x64, 0xc9, 0x59, 0x8e, 0x1e, 0xb3, 0x23},
+ {0x0, 0xa0, 0x5d, 0xfd, 0xba, 0x1a, 0xe7, 0x47, 0x69, 0xc9, 0x34, 0x94, 0xd3, 0x73, 0x8e, 0x2e},
+ {0x0, 0xb0, 0x7d, 0xcd, 0xfa, 0x4a, 0x87, 0x37, 0xe9, 0x59, 0x94, 0x24, 0x13, 0xa3, 0x6e, 0xde},
+ {0x0, 0xc0, 0x9d, 0x5d, 0x27, 0xe7, 0xba, 0x7a, 0x4e, 0x8e, 0xd3, 0x13, 0x69, 0xa9, 0xf4, 0x34},
+ {0x0, 0xd0, 0xbd, 0x6d, 0x67, 0xb7, 0xda, 0xa, 0xce, 0x1e, 0x73, 0xa3, 0xa9, 0x79, 0x14, 0xc4},
+ {0x0, 0xe0, 0xdd, 0x3d, 0xa7, 0x47, 0x7a, 0x9a, 0x53, 0xb3, 0x8e, 0x6e, 0xf4, 0x14, 0x29, 0xc9},
+ {0x0, 0xf0, 0xfd, 0xd, 0xe7, 0x17, 0x1a, 0xea, 0xd3, 0x23, 0x2e, 0xde, 0x34, 0xc4, 0xc9, 0x39},
+ {0x0, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb},
+ {0x0, 0xd, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b},
+ {0x0, 0x3d, 0x7a, 0x47, 0xf4, 0xc9, 0x8e, 0xb3, 0xf5, 0xc8, 0x8f, 0xb2, 0x1, 0x3c, 0x7b, 0x46},
+ {0x0, 0x2d, 0x5a, 0x77, 0xb4, 0x99, 0xee, 0xc3, 0x75, 0x58, 0x2f, 0x2, 0xc1, 0xec, 0x9b, 0xb6},
+ {0x0, 0x5d, 0xba, 0xe7, 0x69, 0x34, 0xd3, 0x8e, 0xd2, 0x8f, 0x68, 0x35, 0xbb, 0xe6, 0x1, 0x5c},
+ {0x0, 0x4d, 0x9a, 0xd7, 0x29, 0x64, 0xb3, 0xfe, 0x52, 0x1f, 0xc8, 0x85, 0x7b, 0x36, 0xe1, 0xac},
+ {0x0, 0x7d, 0xfa, 0x87, 0xe9, 0x94, 0x13, 0x6e, 0xcf, 0xb2, 0x35, 0x48, 0x26, 0x5b, 0xdc, 0xa1},
+ {0x0, 0x6d, 0xda, 0xb7, 0xa9, 0xc4, 0x73, 0x1e, 0x4f, 0x22, 0x95, 0xf8, 0xe6, 0x8b, 0x3c, 0x51},
+ {0x0, 0x9d, 0x27, 0xba, 0x4e, 0xd3, 0x69, 0xf4, 0x9c, 0x1, 0xbb, 0x26, 0xd2, 0x4f, 0xf5, 0x68},
+ {0x0, 0x8d, 0x7, 0x8a, 0xe, 0x83, 0x9, 0x84, 0x1c, 0x91, 0x1b, 0x96, 0x12, 0x9f, 0x15, 0x98},
+ {0x0, 0xbd, 0x67, 0xda, 0xce, 0x73, 0xa9, 0x14, 0x81, 0x3c, 0xe6, 0x5b, 0x4f, 0xf2, 0x28, 0x95},
+ {0x0, 0xad, 0x47, 0xea, 0x8e, 0x23, 0xc9, 0x64, 0x1, 0xac, 0x46, 0xeb, 0x8f, 0x22, 0xc8, 0x65},
+ {0x0, 0xdd, 0xa7, 0x7a, 0x53, 0x8e, 0xf4, 0x29, 0xa6, 0x7b, 0x1, 0xdc, 0xf5, 0x28, 0x52, 0x8f},
+ {0x0, 0xcd, 0x87, 0x4a, 0x13, 0xde, 0x94, 0x59, 0x26, 0xeb, 0xa1, 0x6c, 0x35, 0xf8, 0xb2, 0x7f},
+ {0x0, 0xfd, 0xe7, 0x1a, 0xd3, 0x2e, 0x34, 0xc9, 0xbb, 0x46, 0x5c, 0xa1, 0x68, 0x95, 0x8f, 0x72},
+ {0x0, 0xed, 0xc7, 0x2a, 0x93, 0x7e, 0x54, 0xb9, 0x3b, 0xd6, 0xfc, 0x11, 0xa8, 0x45, 0x6f, 0x82},
+ {0x0, 0x3a, 0x74, 0x4e, 0xe8, 0xd2, 0x9c, 0xa6, 0xcd, 0xf7, 0xb9, 0x83, 0x25, 0x1f, 0x51, 0x6b},
+ {0x0, 0x2a, 0x54, 0x7e, 0xa8, 0x82, 0xfc, 0xd6, 0x4d, 0x67, 0x19, 0x33, 0xe5, 0xcf, 0xb1, 0x9b},
+ {0x0, 0x1a, 0x34, 0x2e, 0x68, 0x72, 0x5c, 0x46, 0xd0, 0xca, 0xe4, 0xfe, 0xb8, 0xa2, 0x8c, 0x96},
+ {0x0, 0xa, 0x14, 0x1e, 0x28, 0x22, 0x3c, 0x36, 0x50, 0x5a, 0x44, 0x4e, 0x78, 0x72, 0x6c, 0x66},
+ {0x0, 0x7a, 0xf4, 0x8e, 0xf5, 0x8f, 0x1, 0x7b, 0xf7, 0x8d, 0x3, 0x79, 0x2, 0x78, 0xf6, 0x8c},
+ {0x0, 0x6a, 0xd4, 0xbe, 0xb5, 0xdf, 0x61, 0xb, 0x77, 0x1d, 0xa3, 0xc9, 0xc2, 0xa8, 0x16, 0x7c},
+ {0x0, 0x5a, 0xb4, 0xee, 0x75, 0x2f, 0xc1, 0x9b, 0xea, 0xb0, 0x5e, 0x4, 0x9f, 0xc5, 0x2b, 0x71},
+ {0x0, 0x4a, 0x94, 0xde, 0x35, 0x7f, 0xa1, 0xeb, 0x6a, 0x20, 0xfe, 0xb4, 0x5f, 0x15, 0xcb, 0x81},
+ {0x0, 0xba, 0x69, 0xd3, 0xd2, 0x68, 0xbb, 0x1, 0xb9, 0x3, 0xd0, 0x6a, 0x6b, 0xd1, 0x2, 0xb8},
+ {0x0, 0xaa, 0x49, 0xe3, 0x92, 0x38, 0xdb, 0x71, 0x39, 0x93, 0x70, 0xda, 0xab, 0x1, 0xe2, 0x48},
+ {0x0, 0x9a, 0x29, 0xb3, 0x52, 0xc8, 0x7b, 0xe1, 0xa4, 0x3e, 0x8d, 0x17, 0xf6, 0x6c, 0xdf, 0x45},
+ {0x0, 0x8a, 0x9, 0x83, 0x12, 0x98, 0x1b, 0x91, 0x24, 0xae, 0x2d, 0xa7, 0x36, 0xbc, 0x3f, 0xb5},
+ {0x0, 0xfa, 0xe9, 0x13, 0xcf, 0x35, 0x26, 0xdc, 0x83, 0x79, 0x6a, 0x90, 0x4c, 0xb6, 0xa5, 0x5f},
+ {0x0, 0xea, 0xc9, 0x23, 0x8f, 0x65, 0x46, 0xac, 0x3, 0xe9, 0xca, 0x20, 0x8c, 0x66, 0x45, 0xaf},
+ {0x0, 0xda, 0xa9, 0x73, 0x4f, 0x95, 0xe6, 0x3c, 0x9e, 0x44, 0x37, 0xed, 0xd1, 0xb, 0x78, 0xa2},
+ {0x0, 0xca, 0x89, 0x43, 0xf, 0xc5, 0x86, 0x4c, 0x1e, 0xd4, 0x97, 0x5d, 0x11, 0xdb, 0x98, 0x52},
+ {0x0, 0x27, 0x4e, 0x69, 0x9c, 0xbb, 0xd2, 0xf5, 0x25, 0x2, 0x6b, 0x4c, 0xb9, 0x9e, 0xf7, 0xd0},
+ {0x0, 0x37, 0x6e, 0x59, 0xdc, 0xeb, 0xb2, 0x85, 0xa5, 0x92, 0xcb, 0xfc, 0x79, 0x4e, 0x17, 0x20},
+ {0x0, 0x7, 0xe, 0x9, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d},
+ {0x0, 0x17, 0x2e, 0x39, 0x5c, 0x4b, 0x72, 0x65, 0xb8, 0xaf, 0x96, 0x81, 0xe4, 0xf3, 0xca, 0xdd},
+ {0x0, 0x67, 0xce, 0xa9, 0x81, 0xe6, 0x4f, 0x28, 0x1f, 0x78, 0xd1, 0xb6, 0x9e, 0xf9, 0x50, 0x37},
+ {0x0, 0x77, 0xee, 0x99, 0xc1, 0xb6, 0x2f, 0x58, 0x9f, 0xe8, 0x71, 0x6, 0x5e, 0x29, 0xb0, 0xc7},
+ {0x0, 0x47, 0x8e, 0xc9, 0x1, 0x46, 0x8f, 0xc8, 0x2, 0x45, 0x8c, 0xcb, 0x3, 0x44, 0x8d, 0xca},
+ {0x0, 0x57, 0xae, 0xf9, 0x41, 0x16, 0xef, 0xb8, 0x82, 0xd5, 0x2c, 0x7b, 0xc3, 0x94, 0x6d, 0x3a},
+ {0x0, 0xa7, 0x53, 0xf4, 0xa6, 0x1, 0xf5, 0x52, 0x51, 0xf6, 0x2, 0xa5, 0xf7, 0x50, 0xa4, 0x3},
+ {0x0, 0xb7, 0x73, 0xc4, 0xe6, 0x51, 0x95, 0x22, 0xd1, 0x66, 0xa2, 0x15, 0x37, 0x80, 0x44, 0xf3},
+ {0x0, 0x87, 0x13, 0x94, 0x26, 0xa1, 0x35, 0xb2, 0x4c, 0xcb, 0x5f, 0xd8, 0x6a, 0xed, 0x79, 0xfe},
+ {0x0, 0x97, 0x33, 0xa4, 0x66, 0xf1, 0x55, 0xc2, 0xcc, 0x5b, 0xff, 0x68, 0xaa, 0x3d, 0x99, 0xe},
+ {0x0, 0xe7, 0xd3, 0x34, 0xbb, 0x5c, 0x68, 0x8f, 0x6b, 0x8c, 0xb8, 0x5f, 0xd0, 0x37, 0x3, 0xe4},
+ {0x0, 0xf7, 0xf3, 0x4, 0xfb, 0xc, 0x8, 0xff, 0xeb, 0x1c, 0x18, 0xef, 0x10, 0xe7, 0xe3, 0x14},
+ {0x0, 0xc7, 0x93, 0x54, 0x3b, 0xfc, 0xa8, 0x6f, 0x76, 0xb1, 0xe5, 0x22, 0x4d, 0x8a, 0xde, 0x19},
+ {0x0, 0xd7, 0xb3, 0x64, 0x7b, 0xac, 0xc8, 0x1f, 0xf6, 0x21, 0x45, 0x92, 0x8d, 0x5a, 0x3e, 0xe9},
+ {0x0, 0x74, 0xe8, 0x9c, 0xcd, 0xb9, 0x25, 0x51, 0x87, 0xf3, 0x6f, 0x1b, 0x4a, 0x3e, 0xa2, 0xd6},
+ {0x0, 0x64, 0xc8, 0xac, 0x8d, 0xe9, 0x45, 0x21, 0x7, 0x63, 0xcf, 0xab, 0x8a, 0xee, 0x42, 0x26},
+ {0x0, 0x54, 0xa8, 0xfc, 0x4d, 0x19, 0xe5, 0xb1, 0x9a, 0xce, 0x32, 0x66, 0xd7, 0x83, 0x7f, 0x2b},
+ {0x0, 0x44, 0x88, 0xcc, 0xd, 0x49, 0x85, 0xc1, 0x1a, 0x5e, 0x92, 0xd6, 0x17, 0x53, 0x9f, 0xdb},
+ {0x0, 0x34, 0x68, 0x5c, 0xd0, 0xe4, 0xb8, 0x8c, 0xbd, 0x89, 0xd5, 0xe1, 0x6d, 0x59, 0x5, 0x31},
+ {0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0xd8, 0xfc, 0x3d, 0x19, 0x75, 0x51, 0xad, 0x89, 0xe5, 0xc1},
+ {0x0, 0x14, 0x28, 0x3c, 0x50, 0x44, 0x78, 0x6c, 0xa0, 0xb4, 0x88, 0x9c, 0xf0, 0xe4, 0xd8, 0xcc},
+ {0x0, 0x4, 0x8, 0xc, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c},
+ {0x0, 0xf4, 0xf5, 0x1, 0xf7, 0x3, 0x2, 0xf6, 0xf3, 0x7, 0x6, 0xf2, 0x4, 0xf0, 0xf1, 0x5},
+ {0x0, 0xe4, 0xd5, 0x31, 0xb7, 0x53, 0x62, 0x86, 0x73, 0x97, 0xa6, 0x42, 0xc4, 0x20, 0x11, 0xf5},
+ {0x0, 0xd4, 0xb5, 0x61, 0x77, 0xa3, 0xc2, 0x16, 0xee, 0x3a, 0x5b, 0x8f, 0x99, 0x4d, 0x2c, 0xf8},
+ {0x0, 0xc4, 0x95, 0x51, 0x37, 0xf3, 0xa2, 0x66, 0x6e, 0xaa, 0xfb, 0x3f, 0x59, 0x9d, 0xcc, 0x8},
+ {0x0, 0xb4, 0x75, 0xc1, 0xea, 0x5e, 0x9f, 0x2b, 0xc9, 0x7d, 0xbc, 0x8, 0x23, 0x97, 0x56, 0xe2},
+ {0x0, 0xa4, 0x55, 0xf1, 0xaa, 0xe, 0xff, 0x5b, 0x49, 0xed, 0x1c, 0xb8, 0xe3, 0x47, 0xb6, 0x12},
+ {0x0, 0x94, 0x35, 0xa1, 0x6a, 0xfe, 0x5f, 0xcb, 0xd4, 0x40, 0xe1, 0x75, 0xbe, 0x2a, 0x8b, 0x1f},
+ {0x0, 0x84, 0x15, 0x91, 0x2a, 0xae, 0x3f, 0xbb, 0x54, 0xd0, 0x41, 0xc5, 0x7e, 0xfa, 0x6b, 0xef},
+ {0x0, 0x69, 0xd2, 0xbb, 0xb9, 0xd0, 0x6b, 0x2, 0x6f, 0x6, 0xbd, 0xd4, 0xd6, 0xbf, 0x4, 0x6d},
+ {0x0, 0x79, 0xf2, 0x8b, 0xf9, 0x80, 0xb, 0x72, 0xef, 0x96, 0x1d, 0x64, 0x16, 0x6f, 0xe4, 0x9d},
+ {0x0, 0x49, 0x92, 0xdb, 0x39, 0x70, 0xab, 0xe2, 0x72, 0x3b, 0xe0, 0xa9, 0x4b, 0x2, 0xd9, 0x90},
+ {0x0, 0x59, 0xb2, 0xeb, 0x79, 0x20, 0xcb, 0x92, 0xf2, 0xab, 0x40, 0x19, 0x8b, 0xd2, 0x39, 0x60},
+ {0x0, 0x29, 0x52, 0x7b, 0xa4, 0x8d, 0xf6, 0xdf, 0x55, 0x7c, 0x7, 0x2e, 0xf1, 0xd8, 0xa3, 0x8a},
+ {0x0, 0x39, 0x72, 0x4b, 0xe4, 0xdd, 0x96, 0xaf, 0xd5, 0xec, 0xa7, 0x9e, 0x31, 0x8, 0x43, 0x7a},
+ {0x0, 0x9, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77},
+ {0x0, 0x19, 0x32, 0x2b, 0x64, 0x7d, 0x56, 0x4f, 0xc8, 0xd1, 0xfa, 0xe3, 0xac, 0xb5, 0x9e, 0x87},
+ {0x0, 0xe9, 0xcf, 0x26, 0x83, 0x6a, 0x4c, 0xa5, 0x1b, 0xf2, 0xd4, 0x3d, 0x98, 0x71, 0x57, 0xbe},
+ {0x0, 0xf9, 0xef, 0x16, 0xc3, 0x3a, 0x2c, 0xd5, 0x9b, 0x62, 0x74, 0x8d, 0x58, 0xa1, 0xb7, 0x4e},
+ {0x0, 0xc9, 0x8f, 0x46, 0x3, 0xca, 0x8c, 0x45, 0x6, 0xcf, 0x89, 0x40, 0x5, 0xcc, 0x8a, 0x43},
+ {0x0, 0xd9, 0xaf, 0x76, 0x43, 0x9a, 0xec, 0x35, 0x86, 0x5f, 0x29, 0xf0, 0xc5, 0x1c, 0x6a, 0xb3},
+ {0x0, 0xa9, 0x4f, 0xe6, 0x9e, 0x37, 0xd1, 0x78, 0x21, 0x88, 0x6e, 0xc7, 0xbf, 0x16, 0xf0, 0x59},
+ {0x0, 0xb9, 0x6f, 0xd6, 0xde, 0x67, 0xb1, 0x8, 0xa1, 0x18, 0xce, 0x77, 0x7f, 0xc6, 0x10, 0xa9},
+ {0x0, 0x89, 0xf, 0x86, 0x1e, 0x97, 0x11, 0x98, 0x3c, 0xb5, 0x33, 0xba, 0x22, 0xab, 0x2d, 0xa4},
+ {0x0, 0x99, 0x2f, 0xb6, 0x5e, 0xc7, 0x71, 0xe8, 0xbc, 0x25, 0x93, 0xa, 0xe2, 0x7b, 0xcd, 0x54},
+ {0x0, 0x4e, 0x9c, 0xd2, 0x25, 0x6b, 0xb9, 0xf7, 0x4a, 0x4, 0xd6, 0x98, 0x6f, 0x21, 0xf3, 0xbd},
+ {0x0, 0x5e, 0xbc, 0xe2, 0x65, 0x3b, 0xd9, 0x87, 0xca, 0x94, 0x76, 0x28, 0xaf, 0xf1, 0x13, 0x4d},
+ {0x0, 0x6e, 0xdc, 0xb2, 0xa5, 0xcb, 0x79, 0x17, 0x57, 0x39, 0x8b, 0xe5, 0xf2, 0x9c, 0x2e, 0x40},
+ {0x0, 0x7e, 0xfc, 0x82, 0xe5, 0x9b, 0x19, 0x67, 0xd7, 0xa9, 0x2b, 0x55, 0x32, 0x4c, 0xce, 0xb0},
+ {0x0, 0xe, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a},
+ {0x0, 0x1e, 0x3c, 0x22, 0x78, 0x66, 0x44, 0x5a, 0xf0, 0xee, 0xcc, 0xd2, 0x88, 0x96, 0xb4, 0xaa},
+ {0x0, 0x2e, 0x5c, 0x72, 0xb8, 0x96, 0xe4, 0xca, 0x6d, 0x43, 0x31, 0x1f, 0xd5, 0xfb, 0x89, 0xa7},
+ {0x0, 0x3e, 0x7c, 0x42, 0xf8, 0xc6, 0x84, 0xba, 0xed, 0xd3, 0x91, 0xaf, 0x15, 0x2b, 0x69, 0x57},
+ {0x0, 0xce, 0x81, 0x4f, 0x1f, 0xd1, 0x9e, 0x50, 0x3e, 0xf0, 0xbf, 0x71, 0x21, 0xef, 0xa0, 0x6e},
+ {0x0, 0xde, 0xa1, 0x7f, 0x5f, 0x81, 0xfe, 0x20, 0xbe, 0x60, 0x1f, 0xc1, 0xe1, 0x3f, 0x40, 0x9e},
+ {0x0, 0xee, 0xc1, 0x2f, 0x9f, 0x71, 0x5e, 0xb0, 0x23, 0xcd, 0xe2, 0xc, 0xbc, 0x52, 0x7d, 0x93},
+ {0x0, 0xfe, 0xe1, 0x1f, 0xdf, 0x21, 0x3e, 0xc0, 0xa3, 0x5d, 0x42, 0xbc, 0x7c, 0x82, 0x9d, 0x63},
+ {0x0, 0x8e, 0x1, 0x8f, 0x2, 0x8c, 0x3, 0x8d, 0x4, 0x8a, 0x5, 0x8b, 0x6, 0x88, 0x7, 0x89},
+ {0x0, 0x9e, 0x21, 0xbf, 0x42, 0xdc, 0x63, 0xfd, 0x84, 0x1a, 0xa5, 0x3b, 0xc6, 0x58, 0xe7, 0x79},
+ {0x0, 0xae, 0x41, 0xef, 0x82, 0x2c, 0xc3, 0x6d, 0x19, 0xb7, 0x58, 0xf6, 0x9b, 0x35, 0xda, 0x74},
+ {0x0, 0xbe, 0x61, 0xdf, 0xc2, 0x7c, 0xa3, 0x1d, 0x99, 0x27, 0xf8, 0x46, 0x5b, 0xe5, 0x3a, 0x84},
+ {0x0, 0x53, 0xa6, 0xf5, 0x51, 0x2, 0xf7, 0xa4, 0xa2, 0xf1, 0x4, 0x57, 0xf3, 0xa0, 0x55, 0x6},
+ {0x0, 0x43, 0x86, 0xc5, 0x11, 0x52, 0x97, 0xd4, 0x22, 0x61, 0xa4, 0xe7, 0x33, 0x70, 0xb5, 0xf6},
+ {0x0, 0x73, 0xe6, 0x95, 0xd1, 0xa2, 0x37, 0x44, 0xbf, 0xcc, 0x59, 0x2a, 0x6e, 0x1d, 0x88, 0xfb},
+ {0x0, 0x63, 0xc6, 0xa5, 0x91, 0xf2, 0x57, 0x34, 0x3f, 0x5c, 0xf9, 0x9a, 0xae, 0xcd, 0x68, 0xb},
+ {0x0, 0x13, 0x26, 0x35, 0x4c, 0x5f, 0x6a, 0x79, 0x98, 0x8b, 0xbe, 0xad, 0xd4, 0xc7, 0xf2, 0xe1},
+ {0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11},
+ {0x0, 0x33, 0x66, 0x55, 0xcc, 0xff, 0xaa, 0x99, 0x85, 0xb6, 0xe3, 0xd0, 0x49, 0x7a, 0x2f, 0x1c},
+ {0x0, 0x23, 0x46, 0x65, 0x8c, 0xaf, 0xca, 0xe9, 0x5, 0x26, 0x43, 0x60, 0x89, 0xaa, 0xcf, 0xec},
+ {0x0, 0xd3, 0xbb, 0x68, 0x6b, 0xb8, 0xd0, 0x3, 0xd6, 0x5, 0x6d, 0xbe, 0xbd, 0x6e, 0x6, 0xd5},
+ {0x0, 0xc3, 0x9b, 0x58, 0x2b, 0xe8, 0xb0, 0x73, 0x56, 0x95, 0xcd, 0xe, 0x7d, 0xbe, 0xe6, 0x25},
+ {0x0, 0xf3, 0xfb, 0x8, 0xeb, 0x18, 0x10, 0xe3, 0xcb, 0x38, 0x30, 0xc3, 0x20, 0xd3, 0xdb, 0x28},
+ {0x0, 0xe3, 0xdb, 0x38, 0xab, 0x48, 0x70, 0x93, 0x4b, 0xa8, 0x90, 0x73, 0xe0, 0x3, 0x3b, 0xd8},
+ {0x0, 0x93, 0x3b, 0xa8, 0x76, 0xe5, 0x4d, 0xde, 0xec, 0x7f, 0xd7, 0x44, 0x9a, 0x9, 0xa1, 0x32},
+ {0x0, 0x83, 0x1b, 0x98, 0x36, 0xb5, 0x2d, 0xae, 0x6c, 0xef, 0x77, 0xf4, 0x5a, 0xd9, 0x41, 0xc2},
+ {0x0, 0xb3, 0x7b, 0xc8, 0xf6, 0x45, 0x8d, 0x3e, 0xf1, 0x42, 0x8a, 0x39, 0x7, 0xb4, 0x7c, 0xcf},
+ {0x0, 0xa3, 0x5b, 0xf8, 0xb6, 0x15, 0xed, 0x4e, 0x71, 0xd2, 0x2a, 0x89, 0xc7, 0x64, 0x9c, 0x3f},
+ {0x0, 0xe8, 0xcd, 0x25, 0x87, 0x6f, 0x4a, 0xa2, 0x13, 0xfb, 0xde, 0x36, 0x94, 0x7c, 0x59, 0xb1},
+ {0x0, 0xf8, 0xed, 0x15, 0xc7, 0x3f, 0x2a, 0xd2, 0x93, 0x6b, 0x7e, 0x86, 0x54, 0xac, 0xb9, 0x41},
+ {0x0, 0xc8, 0x8d, 0x45, 0x7, 0xcf, 0x8a, 0x42, 0xe, 0xc6, 0x83, 0x4b, 0x9, 0xc1, 0x84, 0x4c},
+ {0x0, 0xd8, 0xad, 0x75, 0x47, 0x9f, 0xea, 0x32, 0x8e, 0x56, 0x23, 0xfb, 0xc9, 0x11, 0x64, 0xbc},
+ {0x0, 0xa8, 0x4d, 0xe5, 0x9a, 0x32, 0xd7, 0x7f, 0x29, 0x81, 0x64, 0xcc, 0xb3, 0x1b, 0xfe, 0x56},
+ {0x0, 0xb8, 0x6d, 0xd5, 0xda, 0x62, 0xb7, 0xf, 0xa9, 0x11, 0xc4, 0x7c, 0x73, 0xcb, 0x1e, 0xa6},
+ {0x0, 0x88, 0xd, 0x85, 0x1a, 0x92, 0x17, 0x9f, 0x34, 0xbc, 0x39, 0xb1, 0x2e, 0xa6, 0x23, 0xab},
+ {0x0, 0x98, 0x2d, 0xb5, 0x5a, 0xc2, 0x77, 0xef, 0xb4, 0x2c, 0x99, 0x1, 0xee, 0x76, 0xc3, 0x5b},
+ {0x0, 0x68, 0xd0, 0xb8, 0xbd, 0xd5, 0x6d, 0x5, 0x67, 0xf, 0xb7, 0xdf, 0xda, 0xb2, 0xa, 0x62},
+ {0x0, 0x78, 0xf0, 0x88, 0xfd, 0x85, 0xd, 0x75, 0xe7, 0x9f, 0x17, 0x6f, 0x1a, 0x62, 0xea, 0x92},
+ {0x0, 0x48, 0x90, 0xd8, 0x3d, 0x75, 0xad, 0xe5, 0x7a, 0x32, 0xea, 0xa2, 0x47, 0xf, 0xd7, 0x9f},
+ {0x0, 0x58, 0xb0, 0xe8, 0x7d, 0x25, 0xcd, 0x95, 0xfa, 0xa2, 0x4a, 0x12, 0x87, 0xdf, 0x37, 0x6f},
+ {0x0, 0x28, 0x50, 0x78, 0xa0, 0x88, 0xf0, 0xd8, 0x5d, 0x75, 0xd, 0x25, 0xfd, 0xd5, 0xad, 0x85},
+ {0x0, 0x38, 0x70, 0x48, 0xe0, 0xd8, 0x90, 0xa8, 0xdd, 0xe5, 0xad, 0x95, 0x3d, 0x5, 0x4d, 0x75},
+ {0x0, 0x8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78},
+ {0x0, 0x18, 0x30, 0x28, 0x60, 0x78, 0x50, 0x48, 0xc0, 0xd8, 0xf0, 0xe8, 0xa0, 0xb8, 0x90, 0x88},
+ {0x0, 0xf5, 0xf7, 0x2, 0xf3, 0x6, 0x4, 0xf1, 0xfb, 0xe, 0xc, 0xf9, 0x8, 0xfd, 0xff, 0xa},
+ {0x0, 0xe5, 0xd7, 0x32, 0xb3, 0x56, 0x64, 0x81, 0x7b, 0x9e, 0xac, 0x49, 0xc8, 0x2d, 0x1f, 0xfa},
+ {0x0, 0xd5, 0xb7, 0x62, 0x73, 0xa6, 0xc4, 0x11, 0xe6, 0x33, 0x51, 0x84, 0x95, 0x40, 0x22, 0xf7},
+ {0x0, 0xc5, 0x97, 0x52, 0x33, 0xf6, 0xa4, 0x61, 0x66, 0xa3, 0xf1, 0x34, 0x55, 0x90, 0xc2, 0x7},
+ {0x0, 0xb5, 0x77, 0xc2, 0xee, 0x5b, 0x99, 0x2c, 0xc1, 0x74, 0xb6, 0x3, 0x2f, 0x9a, 0x58, 0xed},
+ {0x0, 0xa5, 0x57, 0xf2, 0xae, 0xb, 0xf9, 0x5c, 0x41, 0xe4, 0x16, 0xb3, 0xef, 0x4a, 0xb8, 0x1d},
+ {0x0, 0x95, 0x37, 0xa2, 0x6e, 0xfb, 0x59, 0xcc, 0xdc, 0x49, 0xeb, 0x7e, 0xb2, 0x27, 0x85, 0x10},
+ {0x0, 0x85, 0x17, 0x92, 0x2e, 0xab, 0x39, 0xbc, 0x5c, 0xd9, 0x4b, 0xce, 0x72, 0xf7, 0x65, 0xe0},
+ {0x0, 0x75, 0xea, 0x9f, 0xc9, 0xbc, 0x23, 0x56, 0x8f, 0xfa, 0x65, 0x10, 0x46, 0x33, 0xac, 0xd9},
+ {0x0, 0x65, 0xca, 0xaf, 0x89, 0xec, 0x43, 0x26, 0xf, 0x6a, 0xc5, 0xa0, 0x86, 0xe3, 0x4c, 0x29},
+ {0x0, 0x55, 0xaa, 0xff, 0x49, 0x1c, 0xe3, 0xb6, 0x92, 0xc7, 0x38, 0x6d, 0xdb, 0x8e, 0x71, 0x24},
+ {0x0, 0x45, 0x8a, 0xcf, 0x9, 0x4c, 0x83, 0xc6, 0x12, 0x57, 0x98, 0xdd, 0x1b, 0x5e, 0x91, 0xd4},
+ {0x0, 0x35, 0x6a, 0x5f, 0xd4, 0xe1, 0xbe, 0x8b, 0xb5, 0x80, 0xdf, 0xea, 0x61, 0x54, 0xb, 0x3e},
+ {0x0, 0x25, 0x4a, 0x6f, 0x94, 0xb1, 0xde, 0xfb, 0x35, 0x10, 0x7f, 0x5a, 0xa1, 0x84, 0xeb, 0xce},
+ {0x0, 0x15, 0x2a, 0x3f, 0x54, 0x41, 0x7e, 0x6b, 0xa8, 0xbd, 0x82, 0x97, 0xfc, 0xe9, 0xd6, 0xc3},
+ {0x0, 0x5, 0xa, 0xf, 0x14, 0x11, 0x1e, 0x1b, 0x28, 0x2d, 0x22, 0x27, 0x3c, 0x39, 0x36, 0x33},
+ {0x0, 0xd2, 0xb9, 0x6b, 0x6f, 0xbd, 0xd6, 0x4, 0xde, 0xc, 0x67, 0xb5, 0xb1, 0x63, 0x8, 0xda},
+ {0x0, 0xc2, 0x99, 0x5b, 0x2f, 0xed, 0xb6, 0x74, 0x5e, 0x9c, 0xc7, 0x5, 0x71, 0xb3, 0xe8, 0x2a},
+ {0x0, 0xf2, 0xf9, 0xb, 0xef, 0x1d, 0x16, 0xe4, 0xc3, 0x31, 0x3a, 0xc8, 0x2c, 0xde, 0xd5, 0x27},
+ {0x0, 0xe2, 0xd9, 0x3b, 0xaf, 0x4d, 0x76, 0x94, 0x43, 0xa1, 0x9a, 0x78, 0xec, 0xe, 0x35, 0xd7},
+ {0x0, 0x92, 0x39, 0xab, 0x72, 0xe0, 0x4b, 0xd9, 0xe4, 0x76, 0xdd, 0x4f, 0x96, 0x4, 0xaf, 0x3d},
+ {0x0, 0x82, 0x19, 0x9b, 0x32, 0xb0, 0x2b, 0xa9, 0x64, 0xe6, 0x7d, 0xff, 0x56, 0xd4, 0x4f, 0xcd},
+ {0x0, 0xb2, 0x79, 0xcb, 0xf2, 0x40, 0x8b, 0x39, 0xf9, 0x4b, 0x80, 0x32, 0xb, 0xb9, 0x72, 0xc0},
+ {0x0, 0xa2, 0x59, 0xfb, 0xb2, 0x10, 0xeb, 0x49, 0x79, 0xdb, 0x20, 0x82, 0xcb, 0x69, 0x92, 0x30},
+ {0x0, 0x52, 0xa4, 0xf6, 0x55, 0x7, 0xf1, 0xa3, 0xaa, 0xf8, 0xe, 0x5c, 0xff, 0xad, 0x5b, 0x9},
+ {0x0, 0x42, 0x84, 0xc6, 0x15, 0x57, 0x91, 0xd3, 0x2a, 0x68, 0xae, 0xec, 0x3f, 0x7d, 0xbb, 0xf9},
+ {0x0, 0x72, 0xe4, 0x96, 0xd5, 0xa7, 0x31, 0x43, 0xb7, 0xc5, 0x53, 0x21, 0x62, 0x10, 0x86, 0xf4},
+ {0x0, 0x62, 0xc4, 0xa6, 0x95, 0xf7, 0x51, 0x33, 0x37, 0x55, 0xf3, 0x91, 0xa2, 0xc0, 0x66, 0x4},
+ {0x0, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee},
+ {0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e},
+ {0x0, 0x32, 0x64, 0x56, 0xc8, 0xfa, 0xac, 0x9e, 0x8d, 0xbf, 0xe9, 0xdb, 0x45, 0x77, 0x21, 0x13},
+ {0x0, 0x22, 0x44, 0x66, 0x88, 0xaa, 0xcc, 0xee, 0xd, 0x2f, 0x49, 0x6b, 0x85, 0xa7, 0xc1, 0xe3},
+ {0x0, 0xcf, 0x83, 0x4c, 0x1b, 0xd4, 0x98, 0x57, 0x36, 0xf9, 0xb5, 0x7a, 0x2d, 0xe2, 0xae, 0x61},
+ {0x0, 0xdf, 0xa3, 0x7c, 0x5b, 0x84, 0xf8, 0x27, 0xb6, 0x69, 0x15, 0xca, 0xed, 0x32, 0x4e, 0x91},
+ {0x0, 0xef, 0xc3, 0x2c, 0x9b, 0x74, 0x58, 0xb7, 0x2b, 0xc4, 0xe8, 0x7, 0xb0, 0x5f, 0x73, 0x9c},
+ {0x0, 0xff, 0xe3, 0x1c, 0xdb, 0x24, 0x38, 0xc7, 0xab, 0x54, 0x48, 0xb7, 0x70, 0x8f, 0x93, 0x6c},
+ {0x0, 0x8f, 0x3, 0x8c, 0x6, 0x89, 0x5, 0x8a, 0xc, 0x83, 0xf, 0x80, 0xa, 0x85, 0x9, 0x86},
+ {0x0, 0x9f, 0x23, 0xbc, 0x46, 0xd9, 0x65, 0xfa, 0x8c, 0x13, 0xaf, 0x30, 0xca, 0x55, 0xe9, 0x76},
+ {0x0, 0xaf, 0x43, 0xec, 0x86, 0x29, 0xc5, 0x6a, 0x11, 0xbe, 0x52, 0xfd, 0x97, 0x38, 0xd4, 0x7b},
+ {0x0, 0xbf, 0x63, 0xdc, 0xc6, 0x79, 0xa5, 0x1a, 0x91, 0x2e, 0xf2, 0x4d, 0x57, 0xe8, 0x34, 0x8b},
+ {0x0, 0x4f, 0x9e, 0xd1, 0x21, 0x6e, 0xbf, 0xf0, 0x42, 0xd, 0xdc, 0x93, 0x63, 0x2c, 0xfd, 0xb2},
+ {0x0, 0x5f, 0xbe, 0xe1, 0x61, 0x3e, 0xdf, 0x80, 0xc2, 0x9d, 0x7c, 0x23, 0xa3, 0xfc, 0x1d, 0x42},
+ {0x0, 0x6f, 0xde, 0xb1, 0xa1, 0xce, 0x7f, 0x10, 0x5f, 0x30, 0x81, 0xee, 0xfe, 0x91, 0x20, 0x4f},
+ {0x0, 0x7f, 0xfe, 0x81, 0xe1, 0x9e, 0x1f, 0x60, 0xdf, 0xa0, 0x21, 0x5e, 0x3e, 0x41, 0xc0, 0xbf},
+ {0x0, 0xf, 0x1e, 0x11, 0x3c, 0x33, 0x22, 0x2d, 0x78, 0x77, 0x66, 0x69, 0x44, 0x4b, 0x5a, 0x55},
+ {0x0, 0x1f, 0x3e, 0x21, 0x7c, 0x63, 0x42, 0x5d, 0xf8, 0xe7, 0xc6, 0xd9, 0x84, 0x9b, 0xba, 0xa5},
+ {0x0, 0x2f, 0x5e, 0x71, 0xbc, 0x93, 0xe2, 0xcd, 0x65, 0x4a, 0x3b, 0x14, 0xd9, 0xf6, 0x87, 0xa8},
+ {0x0, 0x3f, 0x7e, 0x41, 0xfc, 0xc3, 0x82, 0xbd, 0xe5, 0xda, 0x9b, 0xa4, 0x19, 0x26, 0x67, 0x58},
+ {0x0, 0x9c, 0x25, 0xb9, 0x4a, 0xd6, 0x6f, 0xf3, 0x94, 0x8, 0xb1, 0x2d, 0xde, 0x42, 0xfb, 0x67},
+ {0x0, 0x8c, 0x5, 0x89, 0xa, 0x86, 0xf, 0x83, 0x14, 0x98, 0x11, 0x9d, 0x1e, 0x92, 0x1b, 0x97},
+ {0x0, 0xbc, 0x65, 0xd9, 0xca, 0x76, 0xaf, 0x13, 0x89, 0x35, 0xec, 0x50, 0x43, 0xff, 0x26, 0x9a},
+ {0x0, 0xac, 0x45, 0xe9, 0x8a, 0x26, 0xcf, 0x63, 0x9, 0xa5, 0x4c, 0xe0, 0x83, 0x2f, 0xc6, 0x6a},
+ {0x0, 0xdc, 0xa5, 0x79, 0x57, 0x8b, 0xf2, 0x2e, 0xae, 0x72, 0xb, 0xd7, 0xf9, 0x25, 0x5c, 0x80},
+ {0x0, 0xcc, 0x85, 0x49, 0x17, 0xdb, 0x92, 0x5e, 0x2e, 0xe2, 0xab, 0x67, 0x39, 0xf5, 0xbc, 0x70},
+ {0x0, 0xfc, 0xe5, 0x19, 0xd7, 0x2b, 0x32, 0xce, 0xb3, 0x4f, 0x56, 0xaa, 0x64, 0x98, 0x81, 0x7d},
+ {0x0, 0xec, 0xc5, 0x29, 0x97, 0x7b, 0x52, 0xbe, 0x33, 0xdf, 0xf6, 0x1a, 0xa4, 0x48, 0x61, 0x8d},
+ {0x0, 0x1c, 0x38, 0x24, 0x70, 0x6c, 0x48, 0x54, 0xe0, 0xfc, 0xd8, 0xc4, 0x90, 0x8c, 0xa8, 0xb4},
+ {0x0, 0xc, 0x18, 0x14, 0x30, 0x3c, 0x28, 0x24, 0x60, 0x6c, 0x78, 0x74, 0x50, 0x5c, 0x48, 0x44},
+ {0x0, 0x3c, 0x78, 0x44, 0xf0, 0xcc, 0x88, 0xb4, 0xfd, 0xc1, 0x85, 0xb9, 0xd, 0x31, 0x75, 0x49},
+ {0x0, 0x2c, 0x58, 0x74, 0xb0, 0x9c, 0xe8, 0xc4, 0x7d, 0x51, 0x25, 0x9, 0xcd, 0xe1, 0x95, 0xb9},
+ {0x0, 0x5c, 0xb8, 0xe4, 0x6d, 0x31, 0xd5, 0x89, 0xda, 0x86, 0x62, 0x3e, 0xb7, 0xeb, 0xf, 0x53},
+ {0x0, 0x4c, 0x98, 0xd4, 0x2d, 0x61, 0xb5, 0xf9, 0x5a, 0x16, 0xc2, 0x8e, 0x77, 0x3b, 0xef, 0xa3},
+ {0x0, 0x7c, 0xf8, 0x84, 0xed, 0x91, 0x15, 0x69, 0xc7, 0xbb, 0x3f, 0x43, 0x2a, 0x56, 0xd2, 0xae},
+ {0x0, 0x6c, 0xd8, 0xb4, 0xad, 0xc1, 0x75, 0x19, 0x47, 0x2b, 0x9f, 0xf3, 0xea, 0x86, 0x32, 0x5e},
+ {0x0, 0x81, 0x1f, 0x9e, 0x3e, 0xbf, 0x21, 0xa0, 0x7c, 0xfd, 0x63, 0xe2, 0x42, 0xc3, 0x5d, 0xdc},
+ {0x0, 0x91, 0x3f, 0xae, 0x7e, 0xef, 0x41, 0xd0, 0xfc, 0x6d, 0xc3, 0x52, 0x82, 0x13, 0xbd, 0x2c},
+ {0x0, 0xa1, 0x5f, 0xfe, 0xbe, 0x1f, 0xe1, 0x40, 0x61, 0xc0, 0x3e, 0x9f, 0xdf, 0x7e, 0x80, 0x21},
+ {0x0, 0xb1, 0x7f, 0xce, 0xfe, 0x4f, 0x81, 0x30, 0xe1, 0x50, 0x9e, 0x2f, 0x1f, 0xae, 0x60, 0xd1},
+ {0x0, 0xc1, 0x9f, 0x5e, 0x23, 0xe2, 0xbc, 0x7d, 0x46, 0x87, 0xd9, 0x18, 0x65, 0xa4, 0xfa, 0x3b},
+ {0x0, 0xd1, 0xbf, 0x6e, 0x63, 0xb2, 0xdc, 0xd, 0xc6, 0x17, 0x79, 0xa8, 0xa5, 0x74, 0x1a, 0xcb},
+ {0x0, 0xe1, 0xdf, 0x3e, 0xa3, 0x42, 0x7c, 0x9d, 0x5b, 0xba, 0x84, 0x65, 0xf8, 0x19, 0x27, 0xc6},
+ {0x0, 0xf1, 0xff, 0xe, 0xe3, 0x12, 0x1c, 0xed, 0xdb, 0x2a, 0x24, 0xd5, 0x38, 0xc9, 0xc7, 0x36},
+ {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf},
+ {0x0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+ {0x0, 0x21, 0x42, 0x63, 0x84, 0xa5, 0xc6, 0xe7, 0x15, 0x34, 0x57, 0x76, 0x91, 0xb0, 0xd3, 0xf2},
+ {0x0, 0x31, 0x62, 0x53, 0xc4, 0xf5, 0xa6, 0x97, 0x95, 0xa4, 0xf7, 0xc6, 0x51, 0x60, 0x33, 0x2},
+ {0x0, 0x41, 0x82, 0xc3, 0x19, 0x58, 0x9b, 0xda, 0x32, 0x73, 0xb0, 0xf1, 0x2b, 0x6a, 0xa9, 0xe8},
+ {0x0, 0x51, 0xa2, 0xf3, 0x59, 0x8, 0xfb, 0xaa, 0xb2, 0xe3, 0x10, 0x41, 0xeb, 0xba, 0x49, 0x18},
+ {0x0, 0x61, 0xc2, 0xa3, 0x99, 0xf8, 0x5b, 0x3a, 0x2f, 0x4e, 0xed, 0x8c, 0xb6, 0xd7, 0x74, 0x15},
+ {0x0, 0x71, 0xe2, 0x93, 0xd9, 0xa8, 0x3b, 0x4a, 0xaf, 0xde, 0x4d, 0x3c, 0x76, 0x7, 0x94, 0xe5},
+ {0x0, 0xa6, 0x51, 0xf7, 0xa2, 0x4, 0xf3, 0x55, 0x59, 0xff, 0x8, 0xae, 0xfb, 0x5d, 0xaa, 0xc},
+ {0x0, 0xb6, 0x71, 0xc7, 0xe2, 0x54, 0x93, 0x25, 0xd9, 0x6f, 0xa8, 0x1e, 0x3b, 0x8d, 0x4a, 0xfc},
+ {0x0, 0x86, 0x11, 0x97, 0x22, 0xa4, 0x33, 0xb5, 0x44, 0xc2, 0x55, 0xd3, 0x66, 0xe0, 0x77, 0xf1},
+ {0x0, 0x96, 0x31, 0xa7, 0x62, 0xf4, 0x53, 0xc5, 0xc4, 0x52, 0xf5, 0x63, 0xa6, 0x30, 0x97, 0x1},
+ {0x0, 0xe6, 0xd1, 0x37, 0xbf, 0x59, 0x6e, 0x88, 0x63, 0x85, 0xb2, 0x54, 0xdc, 0x3a, 0xd, 0xeb},
+ {0x0, 0xf6, 0xf1, 0x7, 0xff, 0x9, 0xe, 0xf8, 0xe3, 0x15, 0x12, 0xe4, 0x1c, 0xea, 0xed, 0x1b},
+ {0x0, 0xc6, 0x91, 0x57, 0x3f, 0xf9, 0xae, 0x68, 0x7e, 0xb8, 0xef, 0x29, 0x41, 0x87, 0xd0, 0x16},
+ {0x0, 0xd6, 0xb1, 0x67, 0x7f, 0xa9, 0xce, 0x18, 0xfe, 0x28, 0x4f, 0x99, 0x81, 0x57, 0x30, 0xe6},
+ {0x0, 0x26, 0x4c, 0x6a, 0x98, 0xbe, 0xd4, 0xf2, 0x2d, 0xb, 0x61, 0x47, 0xb5, 0x93, 0xf9, 0xdf},
+ {0x0, 0x36, 0x6c, 0x5a, 0xd8, 0xee, 0xb4, 0x82, 0xad, 0x9b, 0xc1, 0xf7, 0x75, 0x43, 0x19, 0x2f},
+ {0x0, 0x6, 0xc, 0xa, 0x18, 0x1e, 0x14, 0x12, 0x30, 0x36, 0x3c, 0x3a, 0x28, 0x2e, 0x24, 0x22},
+ {0x0, 0x16, 0x2c, 0x3a, 0x58, 0x4e, 0x74, 0x62, 0xb0, 0xa6, 0x9c, 0x8a, 0xe8, 0xfe, 0xc4, 0xd2},
+ {0x0, 0x66, 0xcc, 0xaa, 0x85, 0xe3, 0x49, 0x2f, 0x17, 0x71, 0xdb, 0xbd, 0x92, 0xf4, 0x5e, 0x38},
+ {0x0, 0x76, 0xec, 0x9a, 0xc5, 0xb3, 0x29, 0x5f, 0x97, 0xe1, 0x7b, 0xd, 0x52, 0x24, 0xbe, 0xc8},
+ {0x0, 0x46, 0x8c, 0xca, 0x5, 0x43, 0x89, 0xcf, 0xa, 0x4c, 0x86, 0xc0, 0xf, 0x49, 0x83, 0xc5},
+ {0x0, 0x56, 0xac, 0xfa, 0x45, 0x13, 0xe9, 0xbf, 0x8a, 0xdc, 0x26, 0x70, 0xcf, 0x99, 0x63, 0x35},
+ {0x0, 0xbb, 0x6b, 0xd0, 0xd6, 0x6d, 0xbd, 0x6, 0xb1, 0xa, 0xda, 0x61, 0x67, 0xdc, 0xc, 0xb7},
+ {0x0, 0xab, 0x4b, 0xe0, 0x96, 0x3d, 0xdd, 0x76, 0x31, 0x9a, 0x7a, 0xd1, 0xa7, 0xc, 0xec, 0x47},
+ {0x0, 0x9b, 0x2b, 0xb0, 0x56, 0xcd, 0x7d, 0xe6, 0xac, 0x37, 0x87, 0x1c, 0xfa, 0x61, 0xd1, 0x4a},
+ {0x0, 0x8b, 0xb, 0x80, 0x16, 0x9d, 0x1d, 0x96, 0x2c, 0xa7, 0x27, 0xac, 0x3a, 0xb1, 0x31, 0xba},
+ {0x0, 0xfb, 0xeb, 0x10, 0xcb, 0x30, 0x20, 0xdb, 0x8b, 0x70, 0x60, 0x9b, 0x40, 0xbb, 0xab, 0x50},
+ {0x0, 0xeb, 0xcb, 0x20, 0x8b, 0x60, 0x40, 0xab, 0xb, 0xe0, 0xc0, 0x2b, 0x80, 0x6b, 0x4b, 0xa0},
+ {0x0, 0xdb, 0xab, 0x70, 0x4b, 0x90, 0xe0, 0x3b, 0x96, 0x4d, 0x3d, 0xe6, 0xdd, 0x6, 0x76, 0xad},
+ {0x0, 0xcb, 0x8b, 0x40, 0xb, 0xc0, 0x80, 0x4b, 0x16, 0xdd, 0x9d, 0x56, 0x1d, 0xd6, 0x96, 0x5d},
+ {0x0, 0x3b, 0x76, 0x4d, 0xec, 0xd7, 0x9a, 0xa1, 0xc5, 0xfe, 0xb3, 0x88, 0x29, 0x12, 0x5f, 0x64},
+ {0x0, 0x2b, 0x56, 0x7d, 0xac, 0x87, 0xfa, 0xd1, 0x45, 0x6e, 0x13, 0x38, 0xe9, 0xc2, 0xbf, 0x94},
+ {0x0, 0x1b, 0x36, 0x2d, 0x6c, 0x77, 0x5a, 0x41, 0xd8, 0xc3, 0xee, 0xf5, 0xb4, 0xaf, 0x82, 0x99},
+ {0x0, 0xb, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69},
+ {0x0, 0x7b, 0xf6, 0x8d, 0xf1, 0x8a, 0x7, 0x7c, 0xff, 0x84, 0x9, 0x72, 0xe, 0x75, 0xf8, 0x83},
+ {0x0, 0x6b, 0xd6, 0xbd, 0xb1, 0xda, 0x67, 0xc, 0x7f, 0x14, 0xa9, 0xc2, 0xce, 0xa5, 0x18, 0x73},
+ {0x0, 0x5b, 0xb6, 0xed, 0x71, 0x2a, 0xc7, 0x9c, 0xe2, 0xb9, 0x54, 0xf, 0x93, 0xc8, 0x25, 0x7e},
+ {0x0, 0x4b, 0x96, 0xdd, 0x31, 0x7a, 0xa7, 0xec, 0x62, 0x29, 0xf4, 0xbf, 0x53, 0x18, 0xc5, 0x8e}}
+
+// galMultiply multiplies to elements of the field.
+// Uses lookup table ~40% faster
+func galMultiply(a, b byte) byte {
+ return mulTable[a][b]
+}
+
+// Original function:
+/*
+// galMultiply multiplies to elements of the field.
+func galMultiply(a, b byte) byte {
+ if a == 0 || b == 0 {
+ return 0
+ }
+ logA := int(logTable[a])
+ logB := int(logTable[b])
+ return expTable[logA+logB]
+}
+*/
+
+// galDivide is inverse of galMultiply.
+func galDivide(a, b byte) byte {
+ if a == 0 {
+ return 0
+ }
+ if b == 0 {
+ panic("Argument 'divisor' is 0")
+ }
+ logA := int(logTable[a])
+ logB := int(logTable[b])
+ logResult := logA - logB
+ if logResult < 0 {
+ logResult += 255
+ }
+ return expTable[logResult]
+}
+
+// Computes a**n.
+//
+// The result will be the same as multiplying a times itself n times.
+func galExp(a byte, n int) byte {
+ if n == 0 {
+ return 1
+ }
+ if a == 0 {
+ return 0
+ }
+
+ logA := logTable[a]
+ logResult := int(logA) * n
+ for logResult >= 255 {
+ logResult -= 255
+ }
+ return expTable[logResult]
+}
+
+func genAvx2Matrix(matrixRows [][]byte, inputs, outputs int, dst []byte) []byte {
+ if !avx2CodeGen {
+ panic("codegen not enabled")
+ }
+ total := inputs * outputs
+
+ // Duplicated in+out
+ wantBytes := total * 32 * 2
+ if cap(dst) < wantBytes {
+ dst = make([]byte, wantBytes)
+ } else {
+ dst = dst[:wantBytes]
+ }
+ for i, row := range matrixRows[:outputs] {
+ for j, idx := range row[:inputs] {
+ dstIdx := (j*outputs + i) * 64
+ lo := mulTableLow[idx][:]
+ hi := mulTableHigh[idx][:]
+ copy(dst[dstIdx:], lo)
+ copy(dst[dstIdx+16:], lo)
+ copy(dst[dstIdx+32:], hi)
+ copy(dst[dstIdx+48:], hi)
+ }
+ }
+ return dst
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.go b/vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.go
new file mode 100644
index 0000000..720196f
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.go
@@ -0,0 +1,338 @@
+//+build !noasm
+//+build !appengine
+//+build !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 {
+ 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 {
+ 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 {
+ 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, outputCount, byteCount int) {
+ // Process using no goroutines
+ 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, outputCount, byteCount int) {
+ 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()
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.s b/vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.s
new file mode 100644
index 0000000..97ad420
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galoisAvx512_amd64.s
@@ -0,0 +1,400 @@
+//+build !noasm !appengine !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
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_amd64.go b/vendor/github.com/klauspost/reedsolomon/galois_amd64.go
new file mode 100644
index 0000000..f757f9d
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_amd64.go
@@ -0,0 +1,138 @@
+//+build !noasm
+//+build !appengine
+//+build !gccgo
+
+// Copyright 2015, Klaus Post, see LICENSE for details.
+
+package reedsolomon
+
+//go:noescape
+func galMulSSSE3(low, high, in, out []byte)
+
+//go:noescape
+func galMulSSSE3Xor(low, high, in, out []byte)
+
+//go:noescape
+func galMulAVX2Xor(low, high, in, out []byte)
+
+//go:noescape
+func galMulAVX2(low, high, in, out []byte)
+
+//go:noescape
+func sSE2XorSlice(in, out []byte)
+
+//go:noescape
+func galMulAVX2Xor_64(low, high, in, out []byte)
+
+//go:noescape
+func galMulAVX2_64(low, high, in, out []byte)
+
+//go:noescape
+func sSE2XorSlice_64(in, out []byte)
+
+// This is what the assembler routines do in blocks of 16 bytes:
+/*
+func galMulSSSE3(low, high, in, out []byte) {
+ for n, input := range in {
+ l := input & 0xf
+ h := input >> 4
+ out[n] = low[l] ^ high[h]
+ }
+}
+
+func galMulSSSE3Xor(low, high, in, out []byte) {
+ for n, input := range in {
+ l := input & 0xf
+ h := input >> 4
+ out[n] ^= low[l] ^ high[h]
+ }
+}
+*/
+
+// bigSwitchover is the size where 64 bytes are processed per loop.
+const bigSwitchover = 128
+
+func galMulSlice(c byte, in, out []byte, o *options) {
+ if c == 1 {
+ copy(out, in)
+ return
+ }
+ if o.useAVX2 {
+ if len(in) >= bigSwitchover {
+ galMulAVX2_64(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done := (len(in) >> 6) << 6
+ in = in[done:]
+ out = out[done:]
+ }
+ if len(in) > 32 {
+ galMulAVX2(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done := (len(in) >> 5) << 5
+ in = in[done:]
+ out = out[done:]
+ }
+ } else if o.useSSSE3 {
+ galMulSSSE3(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done := (len(in) >> 4) << 4
+ in = in[done:]
+ out = out[done:]
+ }
+ out = out[:len(in)]
+ mt := mulTable[c][:256]
+ for i := range in {
+ out[i] = mt[in[i]]
+ }
+}
+
+func galMulSliceXor(c byte, in, out []byte, o *options) {
+ if c == 1 {
+ sliceXor(in, out, o)
+ return
+ }
+
+ if o.useAVX2 {
+ if len(in) >= bigSwitchover {
+ galMulAVX2Xor_64(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done := (len(in) >> 6) << 6
+ in = in[done:]
+ out = out[done:]
+ }
+ if len(in) >= 32 {
+ galMulAVX2Xor(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done := (len(in) >> 5) << 5
+ in = in[done:]
+ out = out[done:]
+ }
+ } else if o.useSSSE3 {
+ galMulSSSE3Xor(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done := (len(in) >> 4) << 4
+ in = in[done:]
+ out = out[done:]
+ }
+ out = out[:len(in)]
+ mt := mulTable[c][:256]
+ for i := range in {
+ out[i] ^= mt[in[i]]
+ }
+}
+
+// slice galois add
+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 len(in) >= 16 {
+ sSE2XorSlice(in, out)
+ done := (len(in) >> 4) << 4
+ in = in[done:]
+ out = out[done:]
+ }
+ }
+ out = out[:len(in)]
+ for i := range in {
+ out[i] ^= in[i]
+ }
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_amd64.s b/vendor/github.com/klauspost/reedsolomon/galois_amd64.s
new file mode 100644
index 0000000..3501110
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_amd64.s
@@ -0,0 +1,368 @@
+//+build !noasm !appengine !gccgo
+
+// Copyright 2015, Klaus Post, see LICENSE for details.
+
+// Based on http://www.snia.org/sites/default/files2/SDC2013/presentations/NewThinking/EthanMiller_Screaming_Fast_Galois_Field%20Arithmetic_SIMD%20Instructions.pdf
+// and http://jerasure.org/jerasure/gf-complete/tree/master
+
+// func galMulSSSE3Xor(low, high, in, out []byte)
+TEXT ·galMulSSSE3Xor(SB), 7, $0
+ MOVQ low+0(FP), SI // SI: &low
+ MOVQ high+24(FP), DX // DX: &high
+ MOVOU (SI), X6 // X6 low
+ MOVOU (DX), X7 // X7: high
+ MOVQ $15, BX // BX: low mask
+ MOVQ BX, X8
+ PXOR X5, X5
+ MOVQ in+48(FP), SI // R11: &in
+ MOVQ in_len+56(FP), R9 // R9: len(in)
+ MOVQ out+72(FP), DX // DX: &out
+ PSHUFB X5, X8 // X8: lomask (unpacked)
+ SHRQ $4, R9 // len(in) / 16
+ MOVQ SI, AX
+ MOVQ DX, BX
+ ANDQ $15, AX
+ ANDQ $15, BX
+ CMPQ R9, $0
+ JEQ done_xor
+ ORQ AX, BX
+ CMPQ BX, $0
+ JNZ loopback_xor
+
+loopback_xor_aligned:
+ MOVOA (SI), X0 // in[x]
+ MOVOA (DX), X4 // out[x]
+ MOVOA X0, X1 // in[x]
+ MOVOA X6, X2 // low copy
+ MOVOA X7, X3 // high copy
+ PSRLQ $4, X1 // X1: high input
+ PAND X8, X0 // X0: low input
+ PAND X8, X1 // X0: high input
+ PSHUFB X0, X2 // X2: mul low part
+ PSHUFB X1, X3 // X3: mul high part
+ PXOR X2, X3 // X3: Result
+ PXOR X4, X3 // X3: Result xor existing out
+ MOVOA X3, (DX) // Store
+ ADDQ $16, SI // in+=16
+ ADDQ $16, DX // out+=16
+ SUBQ $1, R9
+ JNZ loopback_xor_aligned
+ JMP done_xor
+
+loopback_xor:
+ MOVOU (SI), X0 // in[x]
+ MOVOU (DX), X4 // out[x]
+ MOVOU X0, X1 // in[x]
+ MOVOU X6, X2 // low copy
+ MOVOU X7, X3 // high copy
+ PSRLQ $4, X1 // X1: high input
+ PAND X8, X0 // X0: low input
+ PAND X8, X1 // X0: high input
+ PSHUFB X0, X2 // X2: mul low part
+ PSHUFB X1, X3 // X3: mul high part
+ PXOR X2, X3 // X3: Result
+ PXOR X4, X3 // X3: Result xor existing out
+ MOVOU X3, (DX) // Store
+ ADDQ $16, SI // in+=16
+ ADDQ $16, DX // out+=16
+ SUBQ $1, R9
+ JNZ loopback_xor
+
+done_xor:
+ RET
+
+// func galMulSSSE3(low, high, in, out []byte)
+TEXT ·galMulSSSE3(SB), 7, $0
+ MOVQ low+0(FP), SI // SI: &low
+ MOVQ high+24(FP), DX // DX: &high
+ MOVOU (SI), X6 // X6 low
+ MOVOU (DX), X7 // X7: high
+ MOVQ $15, BX // BX: low mask
+ MOVQ BX, X8
+ PXOR X5, X5
+ MOVQ in+48(FP), SI // R11: &in
+ MOVQ in_len+56(FP), R9 // R9: len(in)
+ MOVQ out+72(FP), DX // DX: &out
+ PSHUFB X5, X8 // X8: lomask (unpacked)
+ MOVQ SI, AX
+ MOVQ DX, BX
+ SHRQ $4, R9 // len(in) / 16
+ ANDQ $15, AX
+ ANDQ $15, BX
+ CMPQ R9, $0
+ JEQ done
+ ORQ AX, BX
+ CMPQ BX, $0
+ JNZ loopback
+
+loopback_aligned:
+ MOVOA (SI), X0 // in[x]
+ MOVOA X0, X1 // in[x]
+ MOVOA X6, X2 // low copy
+ MOVOA X7, X3 // high copy
+ PSRLQ $4, X1 // X1: high input
+ PAND X8, X0 // X0: low input
+ PAND X8, X1 // X0: high input
+ PSHUFB X0, X2 // X2: mul low part
+ PSHUFB X1, X3 // X3: mul high part
+ PXOR X2, X3 // X3: Result
+ MOVOA X3, (DX) // Store
+ ADDQ $16, SI // in+=16
+ ADDQ $16, DX // out+=16
+ SUBQ $1, R9
+ JNZ loopback_aligned
+ JMP done
+
+loopback:
+ MOVOU (SI), X0 // in[x]
+ MOVOU X0, X1 // in[x]
+ MOVOA X6, X2 // low copy
+ MOVOA X7, X3 // high copy
+ PSRLQ $4, X1 // X1: high input
+ PAND X8, X0 // X0: low input
+ PAND X8, X1 // X0: high input
+ PSHUFB X0, X2 // X2: mul low part
+ PSHUFB X1, X3 // X3: mul high part
+ PXOR X2, X3 // X3: Result
+ MOVOU X3, (DX) // Store
+ ADDQ $16, SI // in+=16
+ ADDQ $16, DX // out+=16
+ SUBQ $1, R9
+ JNZ loopback
+
+done:
+ RET
+
+// func galMulAVX2Xor(low, high, in, out []byte)
+TEXT ·galMulAVX2Xor(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)
+
+ SHRQ $5, R9 // len(in) / 32
+ MOVQ out+72(FP), DX // DX: &out
+ MOVQ in+48(FP), SI // SI: &in
+ TESTQ R9, R9
+ JZ done_xor_avx2
+
+loopback_xor_avx2:
+ VMOVDQU (SI), Y0
+ VMOVDQU (DX), Y4
+ VPSRLQ $4, Y0, Y1 // Y1: high input
+ VPAND Y8, Y0, Y0 // Y0: low input
+ VPAND Y8, Y1, Y1 // Y1: high input
+ VPSHUFB Y0, Y6, Y2 // Y2: mul low part
+ VPSHUFB Y1, Y7, Y3 // Y3: mul high part
+ VPXOR Y3, Y2, Y3 // Y3: Result
+ VPXOR Y4, Y3, Y4 // Y4: Result
+ VMOVDQU Y4, (DX)
+
+ ADDQ $32, SI // in+=32
+ ADDQ $32, DX // out+=32
+ SUBQ $1, R9
+ JNZ loopback_xor_avx2
+
+done_xor_avx2:
+ VZEROUPPER
+ RET
+
+// func galMulAVX2(low, high, in, out []byte)
+TEXT ·galMulAVX2(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)
+
+ SHRQ $5, R9 // len(in) / 32
+ MOVQ out+72(FP), DX // DX: &out
+ MOVQ in+48(FP), SI // SI: &in
+ TESTQ R9, R9
+ JZ done_avx2
+
+loopback_avx2:
+ VMOVDQU (SI), Y0
+ VPSRLQ $4, Y0, Y1 // Y1: high input
+ VPAND Y8, Y0, Y0 // Y0: low input
+ VPAND Y8, Y1, Y1 // Y1: high input
+ VPSHUFB Y0, Y6, Y2 // Y2: mul low part
+ VPSHUFB Y1, Y7, Y3 // Y3: mul high part
+ VPXOR Y3, Y2, Y4 // Y4: Result
+ VMOVDQU Y4, (DX)
+
+ ADDQ $32, SI // in+=32
+ ADDQ $32, DX // out+=32
+ SUBQ $1, R9
+ JNZ loopback_avx2
+
+done_avx2:
+ VZEROUPPER
+ RET
+
+// func sSE2XorSlice(in, out []byte)
+TEXT ·sSE2XorSlice(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 $4, R9 // len(in) / 16
+ CMPQ R9, $0
+ JEQ done_xor_sse2
+
+loopback_xor_sse2:
+ MOVOU (SI), X0 // in[x]
+ MOVOU (DX), X1 // out[x]
+ PXOR X0, X1
+ MOVOU X1, (DX)
+ ADDQ $16, SI // in+=16
+ ADDQ $16, DX // out+=16
+ SUBQ $1, R9
+ JNZ loopback_xor_sse2
+
+done_xor_sse2:
+ RET
+
+// 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)
+
+ VINSERTI128 $1, X6, Y6, Y6 // low
+ VINSERTI128 $1, X7, Y7, Y7 // high
+ VPBROADCASTB X5, Y8 // Y8: lomask (unpacked)
+
+ SHRQ $6, R9 // len(in) / 64
+ MOVQ out+72(FP), DX // DX: &out
+ MOVQ in+48(FP), SI // SI: &in
+ TESTQ R9, R9
+ JZ done_xor_avx2_64
+
+loopback_xor_avx2_64:
+ VMOVDQU (SI), Y0
+ VMOVDQU 32(SI), Y10
+ VMOVDQU (DX), Y4
+ VMOVDQU 32(DX), Y14
+ VPSRLQ $4, Y0, Y1 // Y1: high input
+ VPSRLQ $4, Y10, Y11 // Y11: high input 2
+ VPAND Y8, Y0, Y0 // Y0: low input
+ VPAND Y8, Y10, Y10 // Y10: low input 2
+ VPAND Y8, Y1, Y1 // Y11: high input
+ VPAND Y8, Y11, Y11 // Y11: high input 2
+ VPSHUFB Y0, Y6, Y2 // Y2: mul low part
+ VPSHUFB Y10, Y6, Y12 // Y12: mul low part 2
+ VPSHUFB Y1, Y7, Y3 // Y3: mul high part
+ VPSHUFB Y11, Y7, Y13 // Y13: mul high part 2
+ VPXOR Y3, Y2, Y3 // Y3: Result
+ VPXOR Y13, Y12, Y13 // Y13: Result 2
+ VPXOR Y4, Y3, Y4 // Y4: Result
+ VPXOR Y14, Y13, Y14 // Y4: Result 2
+ VMOVDQU Y4, (DX)
+ VMOVDQU Y14, 32(DX)
+
+ ADDQ $64, SI // in+=64
+ ADDQ $64, DX // out+=64
+ SUBQ $1, R9
+ JNZ loopback_xor_avx2_64
+
+done_xor_avx2_64:
+ VZEROUPPER
+ RET
+
+// 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)
+
+ SHRQ $6, R9 // len(in) / 64
+ MOVQ out+72(FP), DX // DX: &out
+ MOVQ in+48(FP), SI // SI: &in
+ TESTQ R9, R9
+ JZ done_avx2_64
+
+loopback_avx2_64:
+ VMOVDQU (SI), Y0
+ VMOVDQU 32(SI), Y10
+ VPSRLQ $4, Y0, Y1 // Y1: high input
+ VPSRLQ $4, Y10, Y11 // Y11: high input 2
+ VPAND Y8, Y0, Y0 // Y0: low input
+ VPAND Y8, Y10, Y10 // Y10: low input
+ VPAND Y8, Y1, Y1 // Y1: high input
+ VPAND Y8, Y11, Y11 // Y11: high input 2
+ VPSHUFB Y0, Y6, Y2 // Y2: mul low part
+ VPSHUFB Y10, Y6, Y12 // Y12: mul low part 2
+ VPSHUFB Y1, Y7, Y3 // Y3: mul high part
+ VPSHUFB Y11, Y7, Y13 // Y13: mul high part 2
+ VPXOR Y3, Y2, Y4 // Y4: Result
+ VPXOR Y13, Y12, Y14 // Y14: Result 2
+ VMOVDQU Y4, (DX)
+ VMOVDQU Y14, 32(DX)
+
+ ADDQ $64, SI // in+=64
+ ADDQ $64, DX // out+=64
+ SUBQ $1, R9
+ JNZ loopback_avx2_64
+
+done_avx2_64:
+ VZEROUPPER
+ RET
+
+// func sSE2XorSlice_64(in, out []byte)
+TEXT ·sSE2XorSlice_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_sse2_64
+
+loopback_xor_sse2_64:
+ MOVOU (SI), X0 // in[x]
+ MOVOU 16(SI), X2 // in[x]
+ MOVOU 32(SI), X4 // in[x]
+ MOVOU 48(SI), X6 // in[x]
+ MOVOU (DX), X1 // out[x]
+ MOVOU 16(DX), X3 // out[x]
+ MOVOU 32(DX), X5 // out[x]
+ MOVOU 48(DX), X7 // out[x]
+ PXOR X0, X1
+ PXOR X2, X3
+ PXOR X4, X5
+ PXOR X6, X7
+ MOVOU X1, (DX)
+ MOVOU X3, 16(DX)
+ MOVOU X5, 32(DX)
+ MOVOU X7, 48(DX)
+ ADDQ $64, SI // in+=64
+ ADDQ $64, DX // out+=64
+ SUBQ $1, R9
+ JNZ loopback_xor_sse2_64
+
+done_xor_sse2_64:
+ RET
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_arm64.go b/vendor/github.com/klauspost/reedsolomon/galois_arm64.go
new file mode 100644
index 0000000..23a1dd2
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_arm64.go
@@ -0,0 +1,67 @@
+//+build !noasm
+//+build !appengine
+//+build !gccgo
+
+// Copyright 2015, Klaus Post, see LICENSE for details.
+// Copyright 2017, Minio, Inc.
+
+package reedsolomon
+
+//go:noescape
+func galMulNEON(low, high, in, out []byte)
+
+//go:noescape
+func galMulXorNEON(low, high, in, out []byte)
+
+//go:noescape
+func galXorNEON(in, out []byte)
+
+func galMulSlice(c byte, in, out []byte, o *options) {
+ if c == 1 {
+ copy(out, in)
+ return
+ }
+ var done int
+ galMulNEON(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done = (len(in) >> 5) << 5
+
+ remain := len(in) - done
+ if remain > 0 {
+ mt := mulTable[c][:256]
+ for i := done; i < len(in); i++ {
+ out[i] = mt[in[i]]
+ }
+ }
+}
+
+func galMulSliceXor(c byte, in, out []byte, o *options) {
+ if c == 1 {
+ sliceXor(in, out, o)
+ return
+ }
+ var done int
+ galMulXorNEON(mulTableLow[c][:], mulTableHigh[c][:], in, out)
+ done = (len(in) >> 5) << 5
+
+ remain := len(in) - done
+ if remain > 0 {
+ mt := mulTable[c][:256]
+ for i := done; i < len(in); i++ {
+ out[i] ^= mt[in[i]]
+ }
+ }
+}
+
+// slice galois add
+func sliceXor(in, out []byte, o *options) {
+
+ galXorNEON(in, out)
+ done := (len(in) >> 5) << 5
+
+ remain := len(in) - done
+ if remain > 0 {
+ for i := done; i < len(in); i++ {
+ out[i] ^= in[i]
+ }
+ }
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_arm64.s b/vendor/github.com/klauspost/reedsolomon/galois_arm64.s
new file mode 100644
index 0000000..d2cac2c
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_arm64.s
@@ -0,0 +1,125 @@
+//+build !noasm !appengine !gccgo
+
+// Copyright 2015, Klaus Post, see LICENSE for details.
+// Copyright 2017, Minio, Inc.
+
+#define LOAD(LO1, LO2, HI1, HI2) \
+ VLD1.P 32(R1), [LO1.B16, LO2.B16] \
+ \
+ \ // Get low input and high input
+ VUSHR $4, LO1.B16, HI1.B16 \
+ VUSHR $4, LO2.B16, HI2.B16 \
+ VAND V8.B16, LO1.B16, LO1.B16 \
+ VAND V8.B16, LO2.B16, LO2.B16
+
+#define GALOIS_MUL(MUL_LO, MUL_HI, OUT1, OUT2, TMP1, TMP2) \
+ \ // Mul low part and mul high part
+ VTBL V0.B16, [MUL_LO.B16], OUT1.B16 \
+ VTBL V10.B16, [MUL_HI.B16], OUT2.B16 \
+ VTBL V1.B16, [MUL_LO.B16], TMP1.B16 \
+ VTBL V11.B16, [MUL_HI.B16], TMP2.B16 \
+ \
+ \ // Combine results
+ VEOR OUT2.B16, OUT1.B16, OUT1.B16 \
+ VEOR TMP2.B16, TMP1.B16, OUT2.B16
+
+// func galMulNEON(low, high, in, out []byte)
+TEXT ·galMulNEON(SB), 7, $0
+ MOVD in_base+48(FP), R1
+ MOVD in_len+56(FP), R2 // length of message
+ MOVD out_base+72(FP), R5
+ SUBS $32, R2
+ BMI complete
+
+ MOVD low+0(FP), R10 // R10: &low
+ MOVD high+24(FP), R11 // R11: &high
+ VLD1 (R10), [V6.B16]
+ VLD1 (R11), [V7.B16]
+
+ //
+ // Use an extra instruction below since `VDUP R3, V8.B16` generates assembler error
+ // WORD $0x4e010c68 // dup v8.16b, w3
+ //
+ MOVD $0x0f, R3
+ VMOV R3, V8.B[0]
+ VDUP V8.B[0], V8.B16
+
+loop:
+ // Main loop
+ LOAD(V0, V1, V10, V11)
+ GALOIS_MUL(V6, V7, V4, V5, V14, V15)
+
+ // Store result
+ VST1.P [V4.D2, V5.D2], 32(R5)
+
+ SUBS $32, R2
+ BPL loop
+
+complete:
+ RET
+
+// func galMulXorNEON(low, high, in, out []byte)
+TEXT ·galMulXorNEON(SB), 7, $0
+ MOVD in_base+48(FP), R1
+ MOVD in_len+56(FP), R2 // length of message
+ MOVD out_base+72(FP), R5
+ SUBS $32, R2
+ BMI completeXor
+
+ MOVD low+0(FP), R10 // R10: &low
+ MOVD high+24(FP), R11 // R11: &high
+ VLD1 (R10), [V6.B16]
+ VLD1 (R11), [V7.B16]
+
+ //
+ // Use an extra instruction below since `VDUP R3, V8.B16` generates assembler error
+ // WORD $0x4e010c68 // dup v8.16b, w3
+ //
+ MOVD $0x0f, R3
+ VMOV R3, V8.B[0]
+ VDUP V8.B[0], V8.B16
+
+loopXor:
+ // Main loop
+ VLD1 (R5), [V20.B16, V21.B16]
+
+ LOAD(V0, V1, V10, V11)
+ GALOIS_MUL(V6, V7, V4, V5, V14, V15)
+
+ VEOR V20.B16, V4.B16, V4.B16
+ VEOR V21.B16, V5.B16, V5.B16
+
+ // Store result
+ VST1.P [V4.D2, V5.D2], 32(R5)
+
+ SUBS $32, R2
+ BPL loopXor
+
+completeXor:
+ RET
+
+// func galXorNEON(in, out []byte)
+TEXT ·galXorNEON(SB), 7, $0
+ MOVD in_base+0(FP), R1
+ MOVD in_len+8(FP), R2 // length of message
+ MOVD out_base+24(FP), R5
+ SUBS $32, R2
+ BMI completeXor
+
+loopXor:
+ // Main loop
+ VLD1.P 32(R1), [V0.B16, V1.B16]
+ VLD1 (R5), [V20.B16, V21.B16]
+
+ VEOR V20.B16, V0.B16, V4.B16
+ VEOR V21.B16, V1.B16, V5.B16
+
+ // Store result
+ VST1.P [V4.D2, V5.D2], 32(R5)
+
+ SUBS $32, R2
+ BPL loopXor
+
+completeXor:
+ RET
+
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.go b/vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.go
new file mode 100644
index 0000000..edd6376
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.go
@@ -0,0 +1,408 @@
+// Code generated by command: go run gen.go -out galois_gen_amd64.s -stubs galois_gen_amd64.go. DO NOT EDIT.
+
+// +build !appengine
+// +build !noasm
+// +build !nogen
+// +build gc
+
+package reedsolomon
+
+// mulAvxTwo_1x1 takes 1 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_1x2 takes 1 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_1x3 takes 1 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_1x4 takes 1 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_1x5 takes 1 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_1x6 takes 1 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_1x7 takes 1 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_1x8 takes 1 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_1x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x1 takes 2 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x2 takes 2 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x3 takes 2 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x4 takes 2 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x5 takes 2 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x6 takes 2 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x7 takes 2 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_2x8 takes 2 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_2x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x1 takes 3 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x2 takes 3 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x3 takes 3 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x4 takes 3 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x5 takes 3 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x6 takes 3 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x7 takes 3 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_3x8 takes 3 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_3x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x1 takes 4 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x2 takes 4 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x3 takes 4 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x4 takes 4 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x5 takes 4 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x6 takes 4 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x7 takes 4 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_4x8 takes 4 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_4x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x1 takes 5 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x2 takes 5 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x3 takes 5 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x4 takes 5 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x5 takes 5 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x6 takes 5 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x7 takes 5 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_5x8 takes 5 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_5x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x1 takes 6 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x2 takes 6 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x3 takes 6 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x4 takes 6 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x5 takes 6 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x6 takes 6 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x7 takes 6 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_6x8 takes 6 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_6x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x1 takes 7 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x2 takes 7 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x3 takes 7 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x4 takes 7 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x5 takes 7 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x6 takes 7 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x7 takes 7 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_7x8 takes 7 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_7x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x1 takes 8 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x2 takes 8 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x3 takes 8 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x4 takes 8 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x5 takes 8 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x6 takes 8 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x7 takes 8 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_8x8 takes 8 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_8x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x1 takes 9 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x2 takes 9 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x3 takes 9 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x4 takes 9 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x5 takes 9 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x6 takes 9 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x7 takes 9 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_9x8 takes 9 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_9x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x1 takes 10 inputs and produces 1 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x2 takes 10 inputs and produces 2 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x3 takes 10 inputs and produces 3 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x4 takes 10 inputs and produces 4 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x5 takes 10 inputs and produces 5 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x6 takes 10 inputs and produces 6 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x7 takes 10 inputs and produces 7 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+
+// mulAvxTwo_10x8 takes 10 inputs and produces 8 outputs.
+// The output is initialized to 0.
+//go:noescape
+func mulAvxTwo_10x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.s b/vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.s
new file mode 100644
index 0000000..c76db3c
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_gen_amd64.s
@@ -0,0 +1,18526 @@
+// Code generated by command: go run gen.go -out galois_gen_amd64.s -stubs galois_gen_amd64.go. DO NOT EDIT.
+
+// +build !appengine
+// +build !noasm
+// +build !nogen
+// +build gc
+
+// func mulAvxTwo_1x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x1(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 6 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ VMOVDQU (CX), Y1
+ VMOVDQU 32(CX), Y2
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), CX
+ MOVQ $0x0000000f, BX
+ MOVQ BX, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), BX
+
+mulAvxTwo_1x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (CX)(BX*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y3, Y4, Y4
+ VPAND Y3, Y5, Y5
+ VPSHUFB Y4, Y1, Y4
+ VPSHUFB Y5, Y2, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(BX*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, BX
+ DECQ AX
+ JNZ mulAvxTwo_1x1_loop
+ VZEROUPPER
+
+mulAvxTwo_1x1_end:
+ RET
+
+// func mulAvxTwo_1x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x2(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 11 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ VMOVDQU (CX), Y2
+ VMOVDQU 32(CX), Y3
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), CX
+ MOVQ $0x0000000f, BP
+ MOVQ BP, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), BP
+
+mulAvxTwo_1x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (CX)(BP*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VPSHUFB Y9, Y2, Y7
+ VPSHUFB Y10, Y3, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VPSHUFB Y9, Y4, Y7
+ VPSHUFB Y10, Y5, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(BP*1)
+ VMOVDQU Y1, (DX)(BP*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, BP
+ DECQ AX
+ JNZ mulAvxTwo_1x2_loop
+ VZEROUPPER
+
+mulAvxTwo_1x2_end:
+ RET
+
+// func mulAvxTwo_1x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x3(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 14 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), CX
+ MOVQ $0x0000000f, SI
+ MOVQ SI, X9
+ VPBROADCASTB X9, Y9
+ MOVQ start+72(FP), SI
+
+mulAvxTwo_1x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (CX)(SI*1), Y12
+ VPSRLQ $0x04, Y12, Y13
+ VPAND Y9, Y12, Y12
+ VPAND Y9, Y13, Y13
+ VPSHUFB Y12, Y3, Y10
+ VPSHUFB Y13, Y4, Y11
+ VPXOR Y10, Y11, Y10
+ VPXOR Y10, Y0, Y0
+ VPSHUFB Y12, Y5, Y10
+ VPSHUFB Y13, Y6, Y11
+ VPXOR Y10, Y11, Y10
+ VPXOR Y10, Y1, Y1
+ VPSHUFB Y12, Y7, Y10
+ VPSHUFB Y13, Y8, Y11
+ VPXOR Y10, Y11, Y10
+ VPXOR Y10, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(SI*1)
+ VMOVDQU Y1, (BP)(SI*1)
+ VMOVDQU Y2, (DX)(SI*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, SI
+ DECQ AX
+ JNZ mulAvxTwo_1x3_loop
+ VZEROUPPER
+
+mulAvxTwo_1x3_end:
+ RET
+
+// func mulAvxTwo_1x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 17 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), DI
+ MOVQ $0x0000000f, R8
+ MOVQ R8, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R8
+
+mulAvxTwo_1x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (DI)(R8*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R8*1)
+ VMOVDQU Y1, (BP)(R8*1)
+ VMOVDQU Y2, (SI)(R8*1)
+ VMOVDQU Y3, (DX)(R8*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R8
+ DECQ AX
+ JNZ mulAvxTwo_1x4_loop
+ VZEROUPPER
+
+mulAvxTwo_1x4_end:
+ RET
+
+// func mulAvxTwo_1x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 20 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), DX
+ MOVQ in_base+24(FP), R8
+ MOVQ (R8), R8
+ MOVQ $0x0000000f, R9
+ MOVQ R9, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R9
+
+mulAvxTwo_1x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (R8)(R9*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ VMOVDQU Y0, (BX)(R9*1)
+ VMOVDQU Y1, (BP)(R9*1)
+ VMOVDQU Y2, (SI)(R9*1)
+ VMOVDQU Y3, (DI)(R9*1)
+ VMOVDQU Y4, (DX)(R9*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R9
+ DECQ AX
+ JNZ mulAvxTwo_1x5_loop
+ VZEROUPPER
+
+mulAvxTwo_1x5_end:
+ RET
+
+// func mulAvxTwo_1x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 23 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), DX
+ MOVQ in_base+24(FP), R9
+ MOVQ (R9), R9
+ MOVQ $0x0000000f, R10
+ MOVQ R10, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R10
+
+mulAvxTwo_1x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (R9)(R10*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ VMOVDQU Y0, (BX)(R10*1)
+ VMOVDQU Y1, (BP)(R10*1)
+ VMOVDQU Y2, (SI)(R10*1)
+ VMOVDQU Y3, (DI)(R10*1)
+ VMOVDQU Y4, (R8)(R10*1)
+ VMOVDQU Y5, (DX)(R10*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R10
+ DECQ AX
+ JNZ mulAvxTwo_1x6_loop
+ VZEROUPPER
+
+mulAvxTwo_1x6_end:
+ RET
+
+// func mulAvxTwo_1x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 26 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), DX
+ MOVQ in_base+24(FP), R10
+ MOVQ (R10), R10
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_1x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (R10)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ VMOVDQU Y0, (BX)(R11*1)
+ VMOVDQU Y1, (BP)(R11*1)
+ VMOVDQU Y2, (SI)(R11*1)
+ VMOVDQU Y3, (DI)(R11*1)
+ VMOVDQU Y4, (R8)(R11*1)
+ VMOVDQU Y5, (R9)(R11*1)
+ VMOVDQU Y6, (DX)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_1x7_loop
+ VZEROUPPER
+
+mulAvxTwo_1x7_end:
+ RET
+
+// func mulAvxTwo_1x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_1x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 29 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_1x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), R10
+ MOVQ 168(DX), DX
+ MOVQ in_base+24(FP), R11
+ MOVQ (R11), R11
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_1x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (R11)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ VMOVDQU Y0, (BX)(R12*1)
+ VMOVDQU Y1, (BP)(R12*1)
+ VMOVDQU Y2, (SI)(R12*1)
+ VMOVDQU Y3, (DI)(R12*1)
+ VMOVDQU Y4, (R8)(R12*1)
+ VMOVDQU Y5, (R9)(R12*1)
+ VMOVDQU Y6, (R10)(R12*1)
+ VMOVDQU Y7, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_1x8_loop
+ VZEROUPPER
+
+mulAvxTwo_1x8_end:
+ RET
+
+// func mulAvxTwo_2x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x1(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 8 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ VMOVDQU (CX), Y1
+ VMOVDQU 32(CX), Y2
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), BX
+ MOVQ 24(CX), CX
+ MOVQ $0x0000000f, BP
+ MOVQ BP, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), BP
+
+mulAvxTwo_2x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BX)(BP*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y5, Y6, Y6
+ VPAND Y5, Y7, Y7
+ VPSHUFB Y6, Y1, Y6
+ VPSHUFB Y7, Y2, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (CX)(BP*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y5, Y6, Y6
+ VPAND Y5, Y7, Y7
+ VPSHUFB Y6, Y3, Y6
+ VPSHUFB Y7, Y4, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(BP*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, BP
+ DECQ AX
+ JNZ mulAvxTwo_2x1_loop
+ VZEROUPPER
+
+mulAvxTwo_2x1_end:
+ RET
+
+// func mulAvxTwo_2x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x2(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 15 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ VMOVDQU (CX), Y2
+ VMOVDQU 32(CX), Y3
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), BP
+ MOVQ 24(CX), CX
+ MOVQ $0x0000000f, SI
+ MOVQ SI, X10
+ VPBROADCASTB X10, Y10
+ MOVQ start+72(FP), SI
+
+mulAvxTwo_2x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (BP)(SI*1), Y13
+ VPSRLQ $0x04, Y13, Y14
+ VPAND Y10, Y13, Y13
+ VPAND Y10, Y14, Y14
+ VPSHUFB Y13, Y2, Y11
+ VPSHUFB Y14, Y3, Y12
+ VPXOR Y11, Y12, Y11
+ VPXOR Y11, Y0, Y0
+ VPSHUFB Y13, Y4, Y11
+ VPSHUFB Y14, Y5, Y12
+ VPXOR Y11, Y12, Y11
+ VPXOR Y11, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (CX)(SI*1), Y13
+ VPSRLQ $0x04, Y13, Y14
+ VPAND Y10, Y13, Y13
+ VPAND Y10, Y14, Y14
+ VPSHUFB Y13, Y6, Y11
+ VPSHUFB Y14, Y7, Y12
+ VPXOR Y11, Y12, Y11
+ VPXOR Y11, Y0, Y0
+ VPSHUFB Y13, Y8, Y11
+ VPSHUFB Y14, Y9, Y12
+ VPXOR Y11, Y12, Y11
+ VPXOR Y11, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(SI*1)
+ VMOVDQU Y1, (DX)(SI*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, SI
+ DECQ AX
+ JNZ mulAvxTwo_2x2_loop
+ VZEROUPPER
+
+mulAvxTwo_2x2_end:
+ RET
+
+// func mulAvxTwo_2x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 20 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), SI
+ MOVQ $0x0000000f, R8
+ MOVQ R8, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R8
+
+mulAvxTwo_2x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R8*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (SI)(R8*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R8*1)
+ VMOVDQU Y1, (BP)(R8*1)
+ VMOVDQU Y2, (DX)(R8*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R8
+ DECQ AX
+ JNZ mulAvxTwo_2x3_loop
+ VZEROUPPER
+
+mulAvxTwo_2x3_end:
+ RET
+
+// func mulAvxTwo_2x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 25 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), R8
+ MOVQ 24(DI), DI
+ MOVQ $0x0000000f, R9
+ MOVQ R9, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R9
+
+mulAvxTwo_2x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (R8)(R9*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (DI)(R9*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R9*1)
+ VMOVDQU Y1, (BP)(R9*1)
+ VMOVDQU Y2, (SI)(R9*1)
+ VMOVDQU Y3, (DX)(R9*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R9
+ DECQ AX
+ JNZ mulAvxTwo_2x4_loop
+ VZEROUPPER
+
+mulAvxTwo_2x4_end:
+ RET
+
+// func mulAvxTwo_2x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 30 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), DX
+ MOVQ in_base+24(FP), R8
+ MOVQ (R8), R9
+ MOVQ 24(R8), R8
+ MOVQ $0x0000000f, R10
+ MOVQ R10, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R10
+
+mulAvxTwo_2x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (R9)(R10*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (R8)(R10*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ VMOVDQU Y0, (BX)(R10*1)
+ VMOVDQU Y1, (BP)(R10*1)
+ VMOVDQU Y2, (SI)(R10*1)
+ VMOVDQU Y3, (DI)(R10*1)
+ VMOVDQU Y4, (DX)(R10*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R10
+ DECQ AX
+ JNZ mulAvxTwo_2x5_loop
+ VZEROUPPER
+
+mulAvxTwo_2x5_end:
+ RET
+
+// func mulAvxTwo_2x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 35 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), DX
+ MOVQ in_base+24(FP), R9
+ MOVQ (R9), R10
+ MOVQ 24(R9), R9
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_2x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (R10)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (R9)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ VMOVDQU Y0, (BX)(R11*1)
+ VMOVDQU Y1, (BP)(R11*1)
+ VMOVDQU Y2, (SI)(R11*1)
+ VMOVDQU Y3, (DI)(R11*1)
+ VMOVDQU Y4, (R8)(R11*1)
+ VMOVDQU Y5, (DX)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_2x6_loop
+ VZEROUPPER
+
+mulAvxTwo_2x6_end:
+ RET
+
+// func mulAvxTwo_2x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 40 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), DX
+ MOVQ in_base+24(FP), R10
+ MOVQ (R10), R11
+ MOVQ 24(R10), R10
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_2x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (R11)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (R10)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ VMOVDQU Y0, (BX)(R12*1)
+ VMOVDQU Y1, (BP)(R12*1)
+ VMOVDQU Y2, (SI)(R12*1)
+ VMOVDQU Y3, (DI)(R12*1)
+ VMOVDQU Y4, (R8)(R12*1)
+ VMOVDQU Y5, (R9)(R12*1)
+ VMOVDQU Y6, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_2x7_loop
+ VZEROUPPER
+
+mulAvxTwo_2x7_end:
+ RET
+
+// func mulAvxTwo_2x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_2x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 45 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_2x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), R10
+ MOVQ 168(DX), DX
+ MOVQ in_base+24(FP), R11
+ MOVQ (R11), R12
+ MOVQ 24(R11), R11
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_2x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (R12)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (R11)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ VMOVDQU Y0, (BX)(R13*1)
+ VMOVDQU Y1, (BP)(R13*1)
+ VMOVDQU Y2, (SI)(R13*1)
+ VMOVDQU Y3, (DI)(R13*1)
+ VMOVDQU Y4, (R8)(R13*1)
+ VMOVDQU Y5, (R9)(R13*1)
+ VMOVDQU Y6, (R10)(R13*1)
+ VMOVDQU Y7, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_2x8_loop
+ VZEROUPPER
+
+mulAvxTwo_2x8_end:
+ RET
+
+// func mulAvxTwo_3x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x1(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 10 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ VMOVDQU (CX), Y1
+ VMOVDQU 32(CX), Y2
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), BX
+ MOVQ 24(CX), BP
+ MOVQ 48(CX), CX
+ MOVQ $0x0000000f, SI
+ MOVQ SI, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), SI
+
+mulAvxTwo_3x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BX)(SI*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y7, Y8, Y8
+ VPAND Y7, Y9, Y9
+ VPSHUFB Y8, Y1, Y8
+ VPSHUFB Y9, Y2, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (BP)(SI*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y7, Y8, Y8
+ VPAND Y7, Y9, Y9
+ VPSHUFB Y8, Y3, Y8
+ VPSHUFB Y9, Y4, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (CX)(SI*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y7, Y8, Y8
+ VPAND Y7, Y9, Y9
+ VPSHUFB Y8, Y5, Y8
+ VPSHUFB Y9, Y6, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(SI*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, SI
+ DECQ AX
+ JNZ mulAvxTwo_3x1_loop
+ VZEROUPPER
+
+mulAvxTwo_3x1_end:
+ RET
+
+// func mulAvxTwo_3x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 19 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), BP
+ MOVQ $0x0000000f, R8
+ MOVQ R8, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R8
+
+mulAvxTwo_3x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R8*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R8*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (BP)(R8*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R8*1)
+ VMOVDQU Y1, (DX)(R8*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R8
+ DECQ AX
+ JNZ mulAvxTwo_3x2_loop
+ VZEROUPPER
+
+mulAvxTwo_3x2_end:
+ RET
+
+// func mulAvxTwo_3x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 26 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), R8
+ MOVQ 48(SI), SI
+ MOVQ $0x0000000f, R9
+ MOVQ R9, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R9
+
+mulAvxTwo_3x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R9*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (R8)(R9*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (SI)(R9*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R9*1)
+ VMOVDQU Y1, (BP)(R9*1)
+ VMOVDQU Y2, (DX)(R9*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R9
+ DECQ AX
+ JNZ mulAvxTwo_3x3_loop
+ VZEROUPPER
+
+mulAvxTwo_3x3_end:
+ RET
+
+// func mulAvxTwo_3x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 33 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), R8
+ MOVQ 24(DI), R9
+ MOVQ 48(DI), DI
+ MOVQ $0x0000000f, R10
+ MOVQ R10, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R10
+
+mulAvxTwo_3x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (R8)(R10*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (R9)(R10*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (DI)(R10*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R10*1)
+ VMOVDQU Y1, (BP)(R10*1)
+ VMOVDQU Y2, (SI)(R10*1)
+ VMOVDQU Y3, (DX)(R10*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R10
+ DECQ AX
+ JNZ mulAvxTwo_3x4_loop
+ VZEROUPPER
+
+mulAvxTwo_3x4_end:
+ RET
+
+// func mulAvxTwo_3x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 40 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), DX
+ MOVQ in_base+24(FP), R8
+ MOVQ (R8), R9
+ MOVQ 24(R8), R10
+ MOVQ 48(R8), R8
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_3x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (R9)(R11*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (R10)(R11*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (R8)(R11*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ VMOVDQU Y0, (BX)(R11*1)
+ VMOVDQU Y1, (BP)(R11*1)
+ VMOVDQU Y2, (SI)(R11*1)
+ VMOVDQU Y3, (DI)(R11*1)
+ VMOVDQU Y4, (DX)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_3x5_loop
+ VZEROUPPER
+
+mulAvxTwo_3x5_end:
+ RET
+
+// func mulAvxTwo_3x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 47 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), DX
+ MOVQ in_base+24(FP), R9
+ MOVQ (R9), R10
+ MOVQ 24(R9), R11
+ MOVQ 48(R9), R9
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_3x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (R10)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (R11)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (R9)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ VMOVDQU Y0, (BX)(R12*1)
+ VMOVDQU Y1, (BP)(R12*1)
+ VMOVDQU Y2, (SI)(R12*1)
+ VMOVDQU Y3, (DI)(R12*1)
+ VMOVDQU Y4, (R8)(R12*1)
+ VMOVDQU Y5, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_3x6_loop
+ VZEROUPPER
+
+mulAvxTwo_3x6_end:
+ RET
+
+// func mulAvxTwo_3x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 54 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), DX
+ MOVQ in_base+24(FP), R10
+ MOVQ (R10), R11
+ MOVQ 24(R10), R12
+ MOVQ 48(R10), R10
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_3x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (R11)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (R12)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (R10)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ VMOVDQU Y0, (BX)(R13*1)
+ VMOVDQU Y1, (BP)(R13*1)
+ VMOVDQU Y2, (SI)(R13*1)
+ VMOVDQU Y3, (DI)(R13*1)
+ VMOVDQU Y4, (R8)(R13*1)
+ VMOVDQU Y5, (R9)(R13*1)
+ VMOVDQU Y6, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_3x7_loop
+ VZEROUPPER
+
+mulAvxTwo_3x7_end:
+ RET
+
+// func mulAvxTwo_3x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_3x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 61 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_3x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), R10
+ MOVQ 168(DX), DX
+ MOVQ in_base+24(FP), R11
+ MOVQ (R11), R12
+ MOVQ 24(R11), R13
+ MOVQ 48(R11), R11
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_3x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (R12)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (R13)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (R11)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ VMOVDQU Y0, (BX)(R14*1)
+ VMOVDQU Y1, (BP)(R14*1)
+ VMOVDQU Y2, (SI)(R14*1)
+ VMOVDQU Y3, (DI)(R14*1)
+ VMOVDQU Y4, (R8)(R14*1)
+ VMOVDQU Y5, (R9)(R14*1)
+ VMOVDQU Y6, (R10)(R14*1)
+ VMOVDQU Y7, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_3x8_loop
+ VZEROUPPER
+
+mulAvxTwo_3x8_end:
+ RET
+
+// func mulAvxTwo_4x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x1(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 12 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ VMOVDQU (CX), Y1
+ VMOVDQU 32(CX), Y2
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), BX
+ MOVQ 24(CX), BP
+ MOVQ 48(CX), SI
+ MOVQ 72(CX), CX
+ MOVQ $0x0000000f, DI
+ MOVQ DI, X9
+ VPBROADCASTB X9, Y9
+ MOVQ start+72(FP), DI
+
+mulAvxTwo_4x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BX)(DI*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y9, Y10, Y10
+ VPAND Y9, Y11, Y11
+ VPSHUFB Y10, Y1, Y10
+ VPSHUFB Y11, Y2, Y11
+ VPXOR Y10, Y11, Y10
+ VPXOR Y10, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (BP)(DI*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y9, Y10, Y10
+ VPAND Y9, Y11, Y11
+ VPSHUFB Y10, Y3, Y10
+ VPSHUFB Y11, Y4, Y11
+ VPXOR Y10, Y11, Y10
+ VPXOR Y10, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (SI)(DI*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y9, Y10, Y10
+ VPAND Y9, Y11, Y11
+ VPSHUFB Y10, Y5, Y10
+ VPSHUFB Y11, Y6, Y11
+ VPXOR Y10, Y11, Y10
+ VPXOR Y10, Y0, Y0
+
+ // Load and process 32 bytes from input 3 to 1 outputs
+ VMOVDQU (CX)(DI*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y9, Y10, Y10
+ VPAND Y9, Y11, Y11
+ VPSHUFB Y10, Y7, Y10
+ VPSHUFB Y11, Y8, Y11
+ VPXOR Y10, Y11, Y10
+ VPXOR Y10, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(DI*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, DI
+ DECQ AX
+ JNZ mulAvxTwo_4x1_loop
+ VZEROUPPER
+
+mulAvxTwo_4x1_end:
+ RET
+
+// func mulAvxTwo_4x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 23 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), R8
+ MOVQ 72(BP), BP
+ MOVQ $0x0000000f, R9
+ MOVQ R9, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R9
+
+mulAvxTwo_4x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R9*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R9*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (R8)(R9*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 3 to 2 outputs
+ VMOVDQU (BP)(R9*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 384(CX), Y3
+ VMOVDQU 416(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 448(CX), Y3
+ VMOVDQU 480(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R9*1)
+ VMOVDQU Y1, (DX)(R9*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R9
+ DECQ AX
+ JNZ mulAvxTwo_4x2_loop
+ VZEROUPPER
+
+mulAvxTwo_4x2_end:
+ RET
+
+// func mulAvxTwo_4x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 32 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), R8
+ MOVQ 48(SI), R9
+ MOVQ 72(SI), SI
+ MOVQ $0x0000000f, R10
+ MOVQ R10, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R10
+
+mulAvxTwo_4x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R10*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (R8)(R10*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (R9)(R10*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 3 to 3 outputs
+ VMOVDQU (SI)(R10*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 576(CX), Y4
+ VMOVDQU 608(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 640(CX), Y4
+ VMOVDQU 672(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 704(CX), Y4
+ VMOVDQU 736(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R10*1)
+ VMOVDQU Y1, (BP)(R10*1)
+ VMOVDQU Y2, (DX)(R10*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R10
+ DECQ AX
+ JNZ mulAvxTwo_4x3_loop
+ VZEROUPPER
+
+mulAvxTwo_4x3_end:
+ RET
+
+// func mulAvxTwo_4x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 41 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), R8
+ MOVQ 24(DI), R9
+ MOVQ 48(DI), R10
+ MOVQ 72(DI), DI
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_4x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (R8)(R11*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (R9)(R11*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (R10)(R11*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 3 to 4 outputs
+ VMOVDQU (DI)(R11*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 768(CX), Y5
+ VMOVDQU 800(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 832(CX), Y5
+ VMOVDQU 864(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 896(CX), Y5
+ VMOVDQU 928(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 960(CX), Y5
+ VMOVDQU 992(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R11*1)
+ VMOVDQU Y1, (BP)(R11*1)
+ VMOVDQU Y2, (SI)(R11*1)
+ VMOVDQU Y3, (DX)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_4x4_loop
+ VZEROUPPER
+
+mulAvxTwo_4x4_end:
+ RET
+
+// func mulAvxTwo_4x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 50 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), DX
+ MOVQ in_base+24(FP), R8
+ MOVQ (R8), R9
+ MOVQ 24(R8), R10
+ MOVQ 48(R8), R11
+ MOVQ 72(R8), R8
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_4x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (R9)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (R10)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (R11)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 3 to 5 outputs
+ VMOVDQU (R8)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 960(CX), Y6
+ VMOVDQU 992(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1024(CX), Y6
+ VMOVDQU 1056(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1088(CX), Y6
+ VMOVDQU 1120(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1152(CX), Y6
+ VMOVDQU 1184(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1216(CX), Y6
+ VMOVDQU 1248(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ VMOVDQU Y0, (BX)(R12*1)
+ VMOVDQU Y1, (BP)(R12*1)
+ VMOVDQU Y2, (SI)(R12*1)
+ VMOVDQU Y3, (DI)(R12*1)
+ VMOVDQU Y4, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_4x5_loop
+ VZEROUPPER
+
+mulAvxTwo_4x5_end:
+ RET
+
+// func mulAvxTwo_4x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 59 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), DX
+ MOVQ in_base+24(FP), R9
+ MOVQ (R9), R10
+ MOVQ 24(R9), R11
+ MOVQ 48(R9), R12
+ MOVQ 72(R9), R9
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_4x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (R10)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (R11)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (R12)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 3 to 6 outputs
+ VMOVDQU (R9)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1152(CX), Y7
+ VMOVDQU 1184(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1216(CX), Y7
+ VMOVDQU 1248(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1280(CX), Y7
+ VMOVDQU 1312(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1344(CX), Y7
+ VMOVDQU 1376(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1408(CX), Y7
+ VMOVDQU 1440(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1472(CX), Y7
+ VMOVDQU 1504(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ VMOVDQU Y0, (BX)(R13*1)
+ VMOVDQU Y1, (BP)(R13*1)
+ VMOVDQU Y2, (SI)(R13*1)
+ VMOVDQU Y3, (DI)(R13*1)
+ VMOVDQU Y4, (R8)(R13*1)
+ VMOVDQU Y5, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_4x6_loop
+ VZEROUPPER
+
+mulAvxTwo_4x6_end:
+ RET
+
+// func mulAvxTwo_4x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 68 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), DX
+ MOVQ in_base+24(FP), R10
+ MOVQ (R10), R11
+ MOVQ 24(R10), R12
+ MOVQ 48(R10), R13
+ MOVQ 72(R10), R10
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_4x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (R11)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (R12)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (R13)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 3 to 7 outputs
+ VMOVDQU (R10)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1344(CX), Y8
+ VMOVDQU 1376(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1408(CX), Y8
+ VMOVDQU 1440(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1472(CX), Y8
+ VMOVDQU 1504(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1536(CX), Y8
+ VMOVDQU 1568(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1600(CX), Y8
+ VMOVDQU 1632(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1664(CX), Y8
+ VMOVDQU 1696(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1728(CX), Y8
+ VMOVDQU 1760(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ VMOVDQU Y0, (BX)(R14*1)
+ VMOVDQU Y1, (BP)(R14*1)
+ VMOVDQU Y2, (SI)(R14*1)
+ VMOVDQU Y3, (DI)(R14*1)
+ VMOVDQU Y4, (R8)(R14*1)
+ VMOVDQU Y5, (R9)(R14*1)
+ VMOVDQU Y6, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_4x7_loop
+ VZEROUPPER
+
+mulAvxTwo_4x7_end:
+ RET
+
+// func mulAvxTwo_4x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_4x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 77 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_4x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), R10
+ MOVQ 168(DX), DX
+ MOVQ in_base+24(FP), R11
+ MOVQ (R11), R12
+ MOVQ 24(R11), R13
+ MOVQ 48(R11), R14
+ MOVQ 72(R11), R11
+ MOVQ $0x0000000f, R15
+ MOVQ R15, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R15
+
+mulAvxTwo_4x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (R12)(R15*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (R13)(R15*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (R14)(R15*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 3 to 8 outputs
+ VMOVDQU (R11)(R15*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1536(CX), Y9
+ VMOVDQU 1568(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1600(CX), Y9
+ VMOVDQU 1632(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1664(CX), Y9
+ VMOVDQU 1696(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1728(CX), Y9
+ VMOVDQU 1760(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1792(CX), Y9
+ VMOVDQU 1824(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1856(CX), Y9
+ VMOVDQU 1888(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1920(CX), Y9
+ VMOVDQU 1952(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1984(CX), Y9
+ VMOVDQU 2016(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ VMOVDQU Y0, (BX)(R15*1)
+ VMOVDQU Y1, (BP)(R15*1)
+ VMOVDQU Y2, (SI)(R15*1)
+ VMOVDQU Y3, (DI)(R15*1)
+ VMOVDQU Y4, (R8)(R15*1)
+ VMOVDQU Y5, (R9)(R15*1)
+ VMOVDQU Y6, (R10)(R15*1)
+ VMOVDQU Y7, (DX)(R15*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R15
+ DECQ AX
+ JNZ mulAvxTwo_4x8_loop
+ VZEROUPPER
+
+mulAvxTwo_4x8_end:
+ RET
+
+// func mulAvxTwo_5x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x1(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 14 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ VMOVDQU (CX), Y1
+ VMOVDQU 32(CX), Y2
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), BX
+ MOVQ 24(CX), BP
+ MOVQ 48(CX), SI
+ MOVQ 72(CX), DI
+ MOVQ 96(CX), CX
+ MOVQ $0x0000000f, R8
+ MOVQ R8, X11
+ VPBROADCASTB X11, Y11
+ MOVQ start+72(FP), R8
+
+mulAvxTwo_5x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BX)(R8*1), Y12
+ VPSRLQ $0x04, Y12, Y13
+ VPAND Y11, Y12, Y12
+ VPAND Y11, Y13, Y13
+ VPSHUFB Y12, Y1, Y12
+ VPSHUFB Y13, Y2, Y13
+ VPXOR Y12, Y13, Y12
+ VPXOR Y12, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (BP)(R8*1), Y12
+ VPSRLQ $0x04, Y12, Y13
+ VPAND Y11, Y12, Y12
+ VPAND Y11, Y13, Y13
+ VPSHUFB Y12, Y3, Y12
+ VPSHUFB Y13, Y4, Y13
+ VPXOR Y12, Y13, Y12
+ VPXOR Y12, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (SI)(R8*1), Y12
+ VPSRLQ $0x04, Y12, Y13
+ VPAND Y11, Y12, Y12
+ VPAND Y11, Y13, Y13
+ VPSHUFB Y12, Y5, Y12
+ VPSHUFB Y13, Y6, Y13
+ VPXOR Y12, Y13, Y12
+ VPXOR Y12, Y0, Y0
+
+ // Load and process 32 bytes from input 3 to 1 outputs
+ VMOVDQU (DI)(R8*1), Y12
+ VPSRLQ $0x04, Y12, Y13
+ VPAND Y11, Y12, Y12
+ VPAND Y11, Y13, Y13
+ VPSHUFB Y12, Y7, Y12
+ VPSHUFB Y13, Y8, Y13
+ VPXOR Y12, Y13, Y12
+ VPXOR Y12, Y0, Y0
+
+ // Load and process 32 bytes from input 4 to 1 outputs
+ VMOVDQU (CX)(R8*1), Y12
+ VPSRLQ $0x04, Y12, Y13
+ VPAND Y11, Y12, Y12
+ VPAND Y11, Y13, Y13
+ VPSHUFB Y12, Y9, Y12
+ VPSHUFB Y13, Y10, Y13
+ VPXOR Y12, Y13, Y12
+ VPXOR Y12, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(R8*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R8
+ DECQ AX
+ JNZ mulAvxTwo_5x1_loop
+ VZEROUPPER
+
+mulAvxTwo_5x1_end:
+ RET
+
+// func mulAvxTwo_5x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 27 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), R8
+ MOVQ 72(BP), R9
+ MOVQ 96(BP), BP
+ MOVQ $0x0000000f, R10
+ MOVQ R10, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R10
+
+mulAvxTwo_5x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R10*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R10*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (R8)(R10*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 3 to 2 outputs
+ VMOVDQU (R9)(R10*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 384(CX), Y3
+ VMOVDQU 416(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 448(CX), Y3
+ VMOVDQU 480(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 4 to 2 outputs
+ VMOVDQU (BP)(R10*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 512(CX), Y3
+ VMOVDQU 544(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 576(CX), Y3
+ VMOVDQU 608(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R10*1)
+ VMOVDQU Y1, (DX)(R10*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R10
+ DECQ AX
+ JNZ mulAvxTwo_5x2_loop
+ VZEROUPPER
+
+mulAvxTwo_5x2_end:
+ RET
+
+// func mulAvxTwo_5x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 38 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), R8
+ MOVQ 48(SI), R9
+ MOVQ 72(SI), R10
+ MOVQ 96(SI), SI
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_5x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R11*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (R8)(R11*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (R9)(R11*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 3 to 3 outputs
+ VMOVDQU (R10)(R11*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 576(CX), Y4
+ VMOVDQU 608(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 640(CX), Y4
+ VMOVDQU 672(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 704(CX), Y4
+ VMOVDQU 736(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 4 to 3 outputs
+ VMOVDQU (SI)(R11*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 768(CX), Y4
+ VMOVDQU 800(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 832(CX), Y4
+ VMOVDQU 864(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 896(CX), Y4
+ VMOVDQU 928(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R11*1)
+ VMOVDQU Y1, (BP)(R11*1)
+ VMOVDQU Y2, (DX)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_5x3_loop
+ VZEROUPPER
+
+mulAvxTwo_5x3_end:
+ RET
+
+// func mulAvxTwo_5x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 49 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), R8
+ MOVQ 24(DI), R9
+ MOVQ 48(DI), R10
+ MOVQ 72(DI), R11
+ MOVQ 96(DI), DI
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_5x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (R8)(R12*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (R9)(R12*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (R10)(R12*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 3 to 4 outputs
+ VMOVDQU (R11)(R12*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 768(CX), Y5
+ VMOVDQU 800(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 832(CX), Y5
+ VMOVDQU 864(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 896(CX), Y5
+ VMOVDQU 928(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 960(CX), Y5
+ VMOVDQU 992(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 4 to 4 outputs
+ VMOVDQU (DI)(R12*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1024(CX), Y5
+ VMOVDQU 1056(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1088(CX), Y5
+ VMOVDQU 1120(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1152(CX), Y5
+ VMOVDQU 1184(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1216(CX), Y5
+ VMOVDQU 1248(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R12*1)
+ VMOVDQU Y1, (BP)(R12*1)
+ VMOVDQU Y2, (SI)(R12*1)
+ VMOVDQU Y3, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_5x4_loop
+ VZEROUPPER
+
+mulAvxTwo_5x4_end:
+ RET
+
+// func mulAvxTwo_5x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 60 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), DX
+ MOVQ in_base+24(FP), R8
+ MOVQ (R8), R9
+ MOVQ 24(R8), R10
+ MOVQ 48(R8), R11
+ MOVQ 72(R8), R12
+ MOVQ 96(R8), R8
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_5x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (R9)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (R10)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (R11)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 3 to 5 outputs
+ VMOVDQU (R12)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 960(CX), Y6
+ VMOVDQU 992(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1024(CX), Y6
+ VMOVDQU 1056(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1088(CX), Y6
+ VMOVDQU 1120(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1152(CX), Y6
+ VMOVDQU 1184(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1216(CX), Y6
+ VMOVDQU 1248(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 4 to 5 outputs
+ VMOVDQU (R8)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1280(CX), Y6
+ VMOVDQU 1312(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1344(CX), Y6
+ VMOVDQU 1376(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1408(CX), Y6
+ VMOVDQU 1440(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1472(CX), Y6
+ VMOVDQU 1504(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1536(CX), Y6
+ VMOVDQU 1568(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ VMOVDQU Y0, (BX)(R13*1)
+ VMOVDQU Y1, (BP)(R13*1)
+ VMOVDQU Y2, (SI)(R13*1)
+ VMOVDQU Y3, (DI)(R13*1)
+ VMOVDQU Y4, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_5x5_loop
+ VZEROUPPER
+
+mulAvxTwo_5x5_end:
+ RET
+
+// func mulAvxTwo_5x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 71 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), DX
+ MOVQ in_base+24(FP), R9
+ MOVQ (R9), R10
+ MOVQ 24(R9), R11
+ MOVQ 48(R9), R12
+ MOVQ 72(R9), R13
+ MOVQ 96(R9), R9
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_5x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (R10)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (R11)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (R12)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 3 to 6 outputs
+ VMOVDQU (R13)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1152(CX), Y7
+ VMOVDQU 1184(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1216(CX), Y7
+ VMOVDQU 1248(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1280(CX), Y7
+ VMOVDQU 1312(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1344(CX), Y7
+ VMOVDQU 1376(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1408(CX), Y7
+ VMOVDQU 1440(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1472(CX), Y7
+ VMOVDQU 1504(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 4 to 6 outputs
+ VMOVDQU (R9)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1536(CX), Y7
+ VMOVDQU 1568(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1600(CX), Y7
+ VMOVDQU 1632(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1664(CX), Y7
+ VMOVDQU 1696(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1728(CX), Y7
+ VMOVDQU 1760(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1792(CX), Y7
+ VMOVDQU 1824(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1856(CX), Y7
+ VMOVDQU 1888(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ VMOVDQU Y0, (BX)(R14*1)
+ VMOVDQU Y1, (BP)(R14*1)
+ VMOVDQU Y2, (SI)(R14*1)
+ VMOVDQU Y3, (DI)(R14*1)
+ VMOVDQU Y4, (R8)(R14*1)
+ VMOVDQU Y5, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_5x6_loop
+ VZEROUPPER
+
+mulAvxTwo_5x6_end:
+ RET
+
+// func mulAvxTwo_5x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 82 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), R9
+ MOVQ 144(DX), DX
+ MOVQ in_base+24(FP), R10
+ MOVQ (R10), R11
+ MOVQ 24(R10), R12
+ MOVQ 48(R10), R13
+ MOVQ 72(R10), R14
+ MOVQ 96(R10), R10
+ MOVQ $0x0000000f, R15
+ MOVQ R15, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R15
+
+mulAvxTwo_5x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (R11)(R15*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (R12)(R15*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (R13)(R15*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 3 to 7 outputs
+ VMOVDQU (R14)(R15*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1344(CX), Y8
+ VMOVDQU 1376(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1408(CX), Y8
+ VMOVDQU 1440(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1472(CX), Y8
+ VMOVDQU 1504(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1536(CX), Y8
+ VMOVDQU 1568(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1600(CX), Y8
+ VMOVDQU 1632(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1664(CX), Y8
+ VMOVDQU 1696(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1728(CX), Y8
+ VMOVDQU 1760(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 4 to 7 outputs
+ VMOVDQU (R10)(R15*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1792(CX), Y8
+ VMOVDQU 1824(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1856(CX), Y8
+ VMOVDQU 1888(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1920(CX), Y8
+ VMOVDQU 1952(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1984(CX), Y8
+ VMOVDQU 2016(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2048(CX), Y8
+ VMOVDQU 2080(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2112(CX), Y8
+ VMOVDQU 2144(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2176(CX), Y8
+ VMOVDQU 2208(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ VMOVDQU Y0, (BX)(R15*1)
+ VMOVDQU Y1, (BP)(R15*1)
+ VMOVDQU Y2, (SI)(R15*1)
+ VMOVDQU Y3, (DI)(R15*1)
+ VMOVDQU Y4, (R8)(R15*1)
+ VMOVDQU Y5, (R9)(R15*1)
+ VMOVDQU Y6, (DX)(R15*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R15
+ DECQ AX
+ JNZ mulAvxTwo_5x7_loop
+ VZEROUPPER
+
+mulAvxTwo_5x7_end:
+ RET
+
+// func mulAvxTwo_5x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_5x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 93 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_5x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), BX
+ MOVQ $0x0000000f, R9
+ MOVQ R9, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R9
+
+mulAvxTwo_5x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (BP)(R9*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (SI)(R9*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (DI)(R9*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 3 to 8 outputs
+ VMOVDQU (R8)(R9*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1536(CX), Y9
+ VMOVDQU 1568(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1600(CX), Y9
+ VMOVDQU 1632(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1664(CX), Y9
+ VMOVDQU 1696(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1728(CX), Y9
+ VMOVDQU 1760(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1792(CX), Y9
+ VMOVDQU 1824(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1856(CX), Y9
+ VMOVDQU 1888(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1920(CX), Y9
+ VMOVDQU 1952(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1984(CX), Y9
+ VMOVDQU 2016(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 4 to 8 outputs
+ VMOVDQU (BX)(R9*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2048(CX), Y9
+ VMOVDQU 2080(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2112(CX), Y9
+ VMOVDQU 2144(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2176(CX), Y9
+ VMOVDQU 2208(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2240(CX), Y9
+ VMOVDQU 2272(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2304(CX), Y9
+ VMOVDQU 2336(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2368(CX), Y9
+ VMOVDQU 2400(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2432(CX), Y9
+ VMOVDQU 2464(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 2496(CX), Y9
+ VMOVDQU 2528(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ MOVQ (DX), R10
+ VMOVDQU Y0, (R10)(R9*1)
+ MOVQ 24(DX), R10
+ VMOVDQU Y1, (R10)(R9*1)
+ MOVQ 48(DX), R10
+ VMOVDQU Y2, (R10)(R9*1)
+ MOVQ 72(DX), R10
+ VMOVDQU Y3, (R10)(R9*1)
+ MOVQ 96(DX), R10
+ VMOVDQU Y4, (R10)(R9*1)
+ MOVQ 120(DX), R10
+ VMOVDQU Y5, (R10)(R9*1)
+ MOVQ 144(DX), R10
+ VMOVDQU Y6, (R10)(R9*1)
+ MOVQ 168(DX), R10
+ VMOVDQU Y7, (R10)(R9*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R9
+ DECQ AX
+ JNZ mulAvxTwo_5x8_loop
+ VZEROUPPER
+
+mulAvxTwo_5x8_end:
+ RET
+
+// func mulAvxTwo_6x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x1(SB), $0-88
+ // Loading all tables to registers
+ // Full registers estimated 16 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ VMOVDQU (CX), Y1
+ VMOVDQU 32(CX), Y2
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VMOVDQU 320(CX), Y11
+ VMOVDQU 352(CX), Y12
+ MOVQ in_base+24(FP), CX
+ MOVQ (CX), BX
+ MOVQ 24(CX), BP
+ MOVQ 48(CX), SI
+ MOVQ 72(CX), DI
+ MOVQ 96(CX), R8
+ MOVQ 120(CX), CX
+ MOVQ $0x0000000f, R9
+ MOVQ R9, X13
+ VPBROADCASTB X13, Y13
+ MOVQ start+72(FP), R9
+
+mulAvxTwo_6x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BX)(R9*1), Y14
+ VPSRLQ $0x04, Y14, Y15
+ VPAND Y13, Y14, Y14
+ VPAND Y13, Y15, Y15
+ VPSHUFB Y14, Y1, Y14
+ VPSHUFB Y15, Y2, Y15
+ VPXOR Y14, Y15, Y14
+ VPXOR Y14, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (BP)(R9*1), Y14
+ VPSRLQ $0x04, Y14, Y15
+ VPAND Y13, Y14, Y14
+ VPAND Y13, Y15, Y15
+ VPSHUFB Y14, Y3, Y14
+ VPSHUFB Y15, Y4, Y15
+ VPXOR Y14, Y15, Y14
+ VPXOR Y14, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (SI)(R9*1), Y14
+ VPSRLQ $0x04, Y14, Y15
+ VPAND Y13, Y14, Y14
+ VPAND Y13, Y15, Y15
+ VPSHUFB Y14, Y5, Y14
+ VPSHUFB Y15, Y6, Y15
+ VPXOR Y14, Y15, Y14
+ VPXOR Y14, Y0, Y0
+
+ // Load and process 32 bytes from input 3 to 1 outputs
+ VMOVDQU (DI)(R9*1), Y14
+ VPSRLQ $0x04, Y14, Y15
+ VPAND Y13, Y14, Y14
+ VPAND Y13, Y15, Y15
+ VPSHUFB Y14, Y7, Y14
+ VPSHUFB Y15, Y8, Y15
+ VPXOR Y14, Y15, Y14
+ VPXOR Y14, Y0, Y0
+
+ // Load and process 32 bytes from input 4 to 1 outputs
+ VMOVDQU (R8)(R9*1), Y14
+ VPSRLQ $0x04, Y14, Y15
+ VPAND Y13, Y14, Y14
+ VPAND Y13, Y15, Y15
+ VPSHUFB Y14, Y9, Y14
+ VPSHUFB Y15, Y10, Y15
+ VPXOR Y14, Y15, Y14
+ VPXOR Y14, Y0, Y0
+
+ // Load and process 32 bytes from input 5 to 1 outputs
+ VMOVDQU (CX)(R9*1), Y14
+ VPSRLQ $0x04, Y14, Y15
+ VPAND Y13, Y14, Y14
+ VPAND Y13, Y15, Y15
+ VPSHUFB Y14, Y11, Y14
+ VPSHUFB Y15, Y12, Y15
+ VPXOR Y14, Y15, Y14
+ VPXOR Y14, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(R9*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R9
+ DECQ AX
+ JNZ mulAvxTwo_6x1_loop
+ VZEROUPPER
+
+mulAvxTwo_6x1_end:
+ RET
+
+// func mulAvxTwo_6x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 31 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), R8
+ MOVQ 72(BP), R9
+ MOVQ 96(BP), R10
+ MOVQ 120(BP), BP
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_6x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R11*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R11*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (R8)(R11*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 3 to 2 outputs
+ VMOVDQU (R9)(R11*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 384(CX), Y3
+ VMOVDQU 416(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 448(CX), Y3
+ VMOVDQU 480(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 4 to 2 outputs
+ VMOVDQU (R10)(R11*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 512(CX), Y3
+ VMOVDQU 544(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 576(CX), Y3
+ VMOVDQU 608(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 5 to 2 outputs
+ VMOVDQU (BP)(R11*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 640(CX), Y3
+ VMOVDQU 672(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 704(CX), Y3
+ VMOVDQU 736(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R11*1)
+ VMOVDQU Y1, (DX)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_6x2_loop
+ VZEROUPPER
+
+mulAvxTwo_6x2_end:
+ RET
+
+// func mulAvxTwo_6x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 44 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), R8
+ MOVQ 48(SI), R9
+ MOVQ 72(SI), R10
+ MOVQ 96(SI), R11
+ MOVQ 120(SI), SI
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_6x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R12*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (R8)(R12*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (R9)(R12*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 3 to 3 outputs
+ VMOVDQU (R10)(R12*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 576(CX), Y4
+ VMOVDQU 608(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 640(CX), Y4
+ VMOVDQU 672(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 704(CX), Y4
+ VMOVDQU 736(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 4 to 3 outputs
+ VMOVDQU (R11)(R12*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 768(CX), Y4
+ VMOVDQU 800(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 832(CX), Y4
+ VMOVDQU 864(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 896(CX), Y4
+ VMOVDQU 928(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 5 to 3 outputs
+ VMOVDQU (SI)(R12*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 960(CX), Y4
+ VMOVDQU 992(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1024(CX), Y4
+ VMOVDQU 1056(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1088(CX), Y4
+ VMOVDQU 1120(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R12*1)
+ VMOVDQU Y1, (BP)(R12*1)
+ VMOVDQU Y2, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_6x3_loop
+ VZEROUPPER
+
+mulAvxTwo_6x3_end:
+ RET
+
+// func mulAvxTwo_6x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 57 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), R8
+ MOVQ 24(DI), R9
+ MOVQ 48(DI), R10
+ MOVQ 72(DI), R11
+ MOVQ 96(DI), R12
+ MOVQ 120(DI), DI
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_6x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (R8)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (R9)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (R10)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 3 to 4 outputs
+ VMOVDQU (R11)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 768(CX), Y5
+ VMOVDQU 800(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 832(CX), Y5
+ VMOVDQU 864(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 896(CX), Y5
+ VMOVDQU 928(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 960(CX), Y5
+ VMOVDQU 992(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 4 to 4 outputs
+ VMOVDQU (R12)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1024(CX), Y5
+ VMOVDQU 1056(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1088(CX), Y5
+ VMOVDQU 1120(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1152(CX), Y5
+ VMOVDQU 1184(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1216(CX), Y5
+ VMOVDQU 1248(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 5 to 4 outputs
+ VMOVDQU (DI)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1280(CX), Y5
+ VMOVDQU 1312(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1344(CX), Y5
+ VMOVDQU 1376(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1408(CX), Y5
+ VMOVDQU 1440(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1472(CX), Y5
+ VMOVDQU 1504(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R13*1)
+ VMOVDQU Y1, (BP)(R13*1)
+ VMOVDQU Y2, (SI)(R13*1)
+ VMOVDQU Y3, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_6x4_loop
+ VZEROUPPER
+
+mulAvxTwo_6x4_end:
+ RET
+
+// func mulAvxTwo_6x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 70 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), DX
+ MOVQ in_base+24(FP), R8
+ MOVQ (R8), R9
+ MOVQ 24(R8), R10
+ MOVQ 48(R8), R11
+ MOVQ 72(R8), R12
+ MOVQ 96(R8), R13
+ MOVQ 120(R8), R8
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_6x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (R9)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (R10)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (R11)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 3 to 5 outputs
+ VMOVDQU (R12)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 960(CX), Y6
+ VMOVDQU 992(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1024(CX), Y6
+ VMOVDQU 1056(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1088(CX), Y6
+ VMOVDQU 1120(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1152(CX), Y6
+ VMOVDQU 1184(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1216(CX), Y6
+ VMOVDQU 1248(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 4 to 5 outputs
+ VMOVDQU (R13)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1280(CX), Y6
+ VMOVDQU 1312(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1344(CX), Y6
+ VMOVDQU 1376(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1408(CX), Y6
+ VMOVDQU 1440(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1472(CX), Y6
+ VMOVDQU 1504(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1536(CX), Y6
+ VMOVDQU 1568(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 5 to 5 outputs
+ VMOVDQU (R8)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1600(CX), Y6
+ VMOVDQU 1632(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1664(CX), Y6
+ VMOVDQU 1696(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1728(CX), Y6
+ VMOVDQU 1760(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1792(CX), Y6
+ VMOVDQU 1824(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1856(CX), Y6
+ VMOVDQU 1888(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ VMOVDQU Y0, (BX)(R14*1)
+ VMOVDQU Y1, (BP)(R14*1)
+ VMOVDQU Y2, (SI)(R14*1)
+ VMOVDQU Y3, (DI)(R14*1)
+ VMOVDQU Y4, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_6x5_loop
+ VZEROUPPER
+
+mulAvxTwo_6x5_end:
+ RET
+
+// func mulAvxTwo_6x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 83 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), R8
+ MOVQ 120(DX), DX
+ MOVQ in_base+24(FP), R9
+ MOVQ (R9), R10
+ MOVQ 24(R9), R11
+ MOVQ 48(R9), R12
+ MOVQ 72(R9), R13
+ MOVQ 96(R9), R14
+ MOVQ 120(R9), R9
+ MOVQ $0x0000000f, R15
+ MOVQ R15, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R15
+
+mulAvxTwo_6x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (R10)(R15*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (R11)(R15*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (R12)(R15*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 3 to 6 outputs
+ VMOVDQU (R13)(R15*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1152(CX), Y7
+ VMOVDQU 1184(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1216(CX), Y7
+ VMOVDQU 1248(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1280(CX), Y7
+ VMOVDQU 1312(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1344(CX), Y7
+ VMOVDQU 1376(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1408(CX), Y7
+ VMOVDQU 1440(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1472(CX), Y7
+ VMOVDQU 1504(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 4 to 6 outputs
+ VMOVDQU (R14)(R15*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1536(CX), Y7
+ VMOVDQU 1568(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1600(CX), Y7
+ VMOVDQU 1632(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1664(CX), Y7
+ VMOVDQU 1696(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1728(CX), Y7
+ VMOVDQU 1760(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1792(CX), Y7
+ VMOVDQU 1824(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1856(CX), Y7
+ VMOVDQU 1888(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 5 to 6 outputs
+ VMOVDQU (R9)(R15*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1920(CX), Y7
+ VMOVDQU 1952(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1984(CX), Y7
+ VMOVDQU 2016(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2048(CX), Y7
+ VMOVDQU 2080(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2112(CX), Y7
+ VMOVDQU 2144(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2176(CX), Y7
+ VMOVDQU 2208(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2240(CX), Y7
+ VMOVDQU 2272(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ VMOVDQU Y0, (BX)(R15*1)
+ VMOVDQU Y1, (BP)(R15*1)
+ VMOVDQU Y2, (SI)(R15*1)
+ VMOVDQU Y3, (DI)(R15*1)
+ VMOVDQU Y4, (R8)(R15*1)
+ VMOVDQU Y5, (DX)(R15*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R15
+ DECQ AX
+ JNZ mulAvxTwo_6x6_loop
+ VZEROUPPER
+
+mulAvxTwo_6x6_end:
+ RET
+
+// func mulAvxTwo_6x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 96 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), BX
+ MOVQ $0x0000000f, R10
+ MOVQ R10, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R10
+
+mulAvxTwo_6x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (BP)(R10*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (SI)(R10*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (DI)(R10*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 3 to 7 outputs
+ VMOVDQU (R8)(R10*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1344(CX), Y8
+ VMOVDQU 1376(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1408(CX), Y8
+ VMOVDQU 1440(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1472(CX), Y8
+ VMOVDQU 1504(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1536(CX), Y8
+ VMOVDQU 1568(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1600(CX), Y8
+ VMOVDQU 1632(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1664(CX), Y8
+ VMOVDQU 1696(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1728(CX), Y8
+ VMOVDQU 1760(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 4 to 7 outputs
+ VMOVDQU (R9)(R10*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1792(CX), Y8
+ VMOVDQU 1824(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1856(CX), Y8
+ VMOVDQU 1888(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1920(CX), Y8
+ VMOVDQU 1952(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1984(CX), Y8
+ VMOVDQU 2016(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2048(CX), Y8
+ VMOVDQU 2080(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2112(CX), Y8
+ VMOVDQU 2144(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2176(CX), Y8
+ VMOVDQU 2208(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 5 to 7 outputs
+ VMOVDQU (BX)(R10*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2240(CX), Y8
+ VMOVDQU 2272(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2304(CX), Y8
+ VMOVDQU 2336(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2368(CX), Y8
+ VMOVDQU 2400(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2432(CX), Y8
+ VMOVDQU 2464(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2496(CX), Y8
+ VMOVDQU 2528(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2560(CX), Y8
+ VMOVDQU 2592(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2624(CX), Y8
+ VMOVDQU 2656(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ MOVQ (DX), R11
+ VMOVDQU Y0, (R11)(R10*1)
+ MOVQ 24(DX), R11
+ VMOVDQU Y1, (R11)(R10*1)
+ MOVQ 48(DX), R11
+ VMOVDQU Y2, (R11)(R10*1)
+ MOVQ 72(DX), R11
+ VMOVDQU Y3, (R11)(R10*1)
+ MOVQ 96(DX), R11
+ VMOVDQU Y4, (R11)(R10*1)
+ MOVQ 120(DX), R11
+ VMOVDQU Y5, (R11)(R10*1)
+ MOVQ 144(DX), R11
+ VMOVDQU Y6, (R11)(R10*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R10
+ DECQ AX
+ JNZ mulAvxTwo_6x7_loop
+ VZEROUPPER
+
+mulAvxTwo_6x7_end:
+ RET
+
+// func mulAvxTwo_6x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_6x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 109 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_6x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), BX
+ MOVQ $0x0000000f, R10
+ MOVQ R10, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R10
+
+mulAvxTwo_6x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (BP)(R10*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (SI)(R10*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (DI)(R10*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 3 to 8 outputs
+ VMOVDQU (R8)(R10*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1536(CX), Y9
+ VMOVDQU 1568(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1600(CX), Y9
+ VMOVDQU 1632(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1664(CX), Y9
+ VMOVDQU 1696(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1728(CX), Y9
+ VMOVDQU 1760(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1792(CX), Y9
+ VMOVDQU 1824(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1856(CX), Y9
+ VMOVDQU 1888(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1920(CX), Y9
+ VMOVDQU 1952(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1984(CX), Y9
+ VMOVDQU 2016(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 4 to 8 outputs
+ VMOVDQU (R9)(R10*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2048(CX), Y9
+ VMOVDQU 2080(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2112(CX), Y9
+ VMOVDQU 2144(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2176(CX), Y9
+ VMOVDQU 2208(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2240(CX), Y9
+ VMOVDQU 2272(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2304(CX), Y9
+ VMOVDQU 2336(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2368(CX), Y9
+ VMOVDQU 2400(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2432(CX), Y9
+ VMOVDQU 2464(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 2496(CX), Y9
+ VMOVDQU 2528(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 5 to 8 outputs
+ VMOVDQU (BX)(R10*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2560(CX), Y9
+ VMOVDQU 2592(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2624(CX), Y9
+ VMOVDQU 2656(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2688(CX), Y9
+ VMOVDQU 2720(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2752(CX), Y9
+ VMOVDQU 2784(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2816(CX), Y9
+ VMOVDQU 2848(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2880(CX), Y9
+ VMOVDQU 2912(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2944(CX), Y9
+ VMOVDQU 2976(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3008(CX), Y9
+ VMOVDQU 3040(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ MOVQ (DX), R11
+ VMOVDQU Y0, (R11)(R10*1)
+ MOVQ 24(DX), R11
+ VMOVDQU Y1, (R11)(R10*1)
+ MOVQ 48(DX), R11
+ VMOVDQU Y2, (R11)(R10*1)
+ MOVQ 72(DX), R11
+ VMOVDQU Y3, (R11)(R10*1)
+ MOVQ 96(DX), R11
+ VMOVDQU Y4, (R11)(R10*1)
+ MOVQ 120(DX), R11
+ VMOVDQU Y5, (R11)(R10*1)
+ MOVQ 144(DX), R11
+ VMOVDQU Y6, (R11)(R10*1)
+ MOVQ 168(DX), R11
+ VMOVDQU Y7, (R11)(R10*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R10
+ DECQ AX
+ JNZ mulAvxTwo_6x8_loop
+ VZEROUPPER
+
+mulAvxTwo_6x8_end:
+ RET
+
+// func mulAvxTwo_7x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x1(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 18 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), BX
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X1
+ VPBROADCASTB X1, Y1
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_7x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BP)(R11*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU (CX), Y2
+ VMOVDQU 32(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (SI)(R11*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 64(CX), Y2
+ VMOVDQU 96(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (DI)(R11*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 128(CX), Y2
+ VMOVDQU 160(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 3 to 1 outputs
+ VMOVDQU (R8)(R11*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 192(CX), Y2
+ VMOVDQU 224(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 4 to 1 outputs
+ VMOVDQU (R9)(R11*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 256(CX), Y2
+ VMOVDQU 288(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 5 to 1 outputs
+ VMOVDQU (R10)(R11*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 320(CX), Y2
+ VMOVDQU 352(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 6 to 1 outputs
+ VMOVDQU (BX)(R11*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 384(CX), Y2
+ VMOVDQU 416(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_7x1_loop
+ VZEROUPPER
+
+mulAvxTwo_7x1_end:
+ RET
+
+// func mulAvxTwo_7x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 35 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), R8
+ MOVQ 72(BP), R9
+ MOVQ 96(BP), R10
+ MOVQ 120(BP), R11
+ MOVQ 144(BP), BP
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_7x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R12*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R12*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (R8)(R12*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 3 to 2 outputs
+ VMOVDQU (R9)(R12*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 384(CX), Y3
+ VMOVDQU 416(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 448(CX), Y3
+ VMOVDQU 480(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 4 to 2 outputs
+ VMOVDQU (R10)(R12*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 512(CX), Y3
+ VMOVDQU 544(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 576(CX), Y3
+ VMOVDQU 608(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 5 to 2 outputs
+ VMOVDQU (R11)(R12*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 640(CX), Y3
+ VMOVDQU 672(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 704(CX), Y3
+ VMOVDQU 736(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 6 to 2 outputs
+ VMOVDQU (BP)(R12*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 768(CX), Y3
+ VMOVDQU 800(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 832(CX), Y3
+ VMOVDQU 864(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R12*1)
+ VMOVDQU Y1, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_7x2_loop
+ VZEROUPPER
+
+mulAvxTwo_7x2_end:
+ RET
+
+// func mulAvxTwo_7x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 50 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), R8
+ MOVQ 48(SI), R9
+ MOVQ 72(SI), R10
+ MOVQ 96(SI), R11
+ MOVQ 120(SI), R12
+ MOVQ 144(SI), SI
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_7x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R13*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (R8)(R13*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (R9)(R13*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 3 to 3 outputs
+ VMOVDQU (R10)(R13*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 576(CX), Y4
+ VMOVDQU 608(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 640(CX), Y4
+ VMOVDQU 672(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 704(CX), Y4
+ VMOVDQU 736(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 4 to 3 outputs
+ VMOVDQU (R11)(R13*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 768(CX), Y4
+ VMOVDQU 800(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 832(CX), Y4
+ VMOVDQU 864(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 896(CX), Y4
+ VMOVDQU 928(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 5 to 3 outputs
+ VMOVDQU (R12)(R13*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 960(CX), Y4
+ VMOVDQU 992(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1024(CX), Y4
+ VMOVDQU 1056(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1088(CX), Y4
+ VMOVDQU 1120(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 6 to 3 outputs
+ VMOVDQU (SI)(R13*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1152(CX), Y4
+ VMOVDQU 1184(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1216(CX), Y4
+ VMOVDQU 1248(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1280(CX), Y4
+ VMOVDQU 1312(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R13*1)
+ VMOVDQU Y1, (BP)(R13*1)
+ VMOVDQU Y2, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_7x3_loop
+ VZEROUPPER
+
+mulAvxTwo_7x3_end:
+ RET
+
+// func mulAvxTwo_7x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 65 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), R8
+ MOVQ 24(DI), R9
+ MOVQ 48(DI), R10
+ MOVQ 72(DI), R11
+ MOVQ 96(DI), R12
+ MOVQ 120(DI), R13
+ MOVQ 144(DI), DI
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_7x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (R8)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (R9)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (R10)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 3 to 4 outputs
+ VMOVDQU (R11)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 768(CX), Y5
+ VMOVDQU 800(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 832(CX), Y5
+ VMOVDQU 864(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 896(CX), Y5
+ VMOVDQU 928(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 960(CX), Y5
+ VMOVDQU 992(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 4 to 4 outputs
+ VMOVDQU (R12)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1024(CX), Y5
+ VMOVDQU 1056(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1088(CX), Y5
+ VMOVDQU 1120(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1152(CX), Y5
+ VMOVDQU 1184(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1216(CX), Y5
+ VMOVDQU 1248(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 5 to 4 outputs
+ VMOVDQU (R13)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1280(CX), Y5
+ VMOVDQU 1312(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1344(CX), Y5
+ VMOVDQU 1376(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1408(CX), Y5
+ VMOVDQU 1440(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1472(CX), Y5
+ VMOVDQU 1504(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 6 to 4 outputs
+ VMOVDQU (DI)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1536(CX), Y5
+ VMOVDQU 1568(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1600(CX), Y5
+ VMOVDQU 1632(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1664(CX), Y5
+ VMOVDQU 1696(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1728(CX), Y5
+ VMOVDQU 1760(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R14*1)
+ VMOVDQU Y1, (BP)(R14*1)
+ VMOVDQU Y2, (SI)(R14*1)
+ VMOVDQU Y3, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_7x4_loop
+ VZEROUPPER
+
+mulAvxTwo_7x4_end:
+ RET
+
+// func mulAvxTwo_7x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 80 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DI
+ MOVQ 96(DX), DX
+ MOVQ in_base+24(FP), R8
+ MOVQ (R8), R9
+ MOVQ 24(R8), R10
+ MOVQ 48(R8), R11
+ MOVQ 72(R8), R12
+ MOVQ 96(R8), R13
+ MOVQ 120(R8), R14
+ MOVQ 144(R8), R8
+ MOVQ $0x0000000f, R15
+ MOVQ R15, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R15
+
+mulAvxTwo_7x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (R9)(R15*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (R10)(R15*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (R11)(R15*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 3 to 5 outputs
+ VMOVDQU (R12)(R15*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 960(CX), Y6
+ VMOVDQU 992(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1024(CX), Y6
+ VMOVDQU 1056(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1088(CX), Y6
+ VMOVDQU 1120(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1152(CX), Y6
+ VMOVDQU 1184(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1216(CX), Y6
+ VMOVDQU 1248(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 4 to 5 outputs
+ VMOVDQU (R13)(R15*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1280(CX), Y6
+ VMOVDQU 1312(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1344(CX), Y6
+ VMOVDQU 1376(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1408(CX), Y6
+ VMOVDQU 1440(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1472(CX), Y6
+ VMOVDQU 1504(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1536(CX), Y6
+ VMOVDQU 1568(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 5 to 5 outputs
+ VMOVDQU (R14)(R15*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1600(CX), Y6
+ VMOVDQU 1632(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1664(CX), Y6
+ VMOVDQU 1696(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1728(CX), Y6
+ VMOVDQU 1760(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1792(CX), Y6
+ VMOVDQU 1824(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1856(CX), Y6
+ VMOVDQU 1888(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 6 to 5 outputs
+ VMOVDQU (R8)(R15*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1920(CX), Y6
+ VMOVDQU 1952(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1984(CX), Y6
+ VMOVDQU 2016(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2048(CX), Y6
+ VMOVDQU 2080(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2112(CX), Y6
+ VMOVDQU 2144(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2176(CX), Y6
+ VMOVDQU 2208(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ VMOVDQU Y0, (BX)(R15*1)
+ VMOVDQU Y1, (BP)(R15*1)
+ VMOVDQU Y2, (SI)(R15*1)
+ VMOVDQU Y3, (DI)(R15*1)
+ VMOVDQU Y4, (DX)(R15*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R15
+ DECQ AX
+ JNZ mulAvxTwo_7x5_loop
+ VZEROUPPER
+
+mulAvxTwo_7x5_end:
+ RET
+
+// func mulAvxTwo_7x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 95 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), BX
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_7x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (BP)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (SI)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (DI)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 3 to 6 outputs
+ VMOVDQU (R8)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1152(CX), Y7
+ VMOVDQU 1184(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1216(CX), Y7
+ VMOVDQU 1248(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1280(CX), Y7
+ VMOVDQU 1312(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1344(CX), Y7
+ VMOVDQU 1376(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1408(CX), Y7
+ VMOVDQU 1440(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1472(CX), Y7
+ VMOVDQU 1504(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 4 to 6 outputs
+ VMOVDQU (R9)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1536(CX), Y7
+ VMOVDQU 1568(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1600(CX), Y7
+ VMOVDQU 1632(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1664(CX), Y7
+ VMOVDQU 1696(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1728(CX), Y7
+ VMOVDQU 1760(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1792(CX), Y7
+ VMOVDQU 1824(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1856(CX), Y7
+ VMOVDQU 1888(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 5 to 6 outputs
+ VMOVDQU (R10)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1920(CX), Y7
+ VMOVDQU 1952(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1984(CX), Y7
+ VMOVDQU 2016(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2048(CX), Y7
+ VMOVDQU 2080(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2112(CX), Y7
+ VMOVDQU 2144(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2176(CX), Y7
+ VMOVDQU 2208(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2240(CX), Y7
+ VMOVDQU 2272(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 6 to 6 outputs
+ VMOVDQU (BX)(R11*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 2304(CX), Y7
+ VMOVDQU 2336(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 2368(CX), Y7
+ VMOVDQU 2400(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2432(CX), Y7
+ VMOVDQU 2464(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2496(CX), Y7
+ VMOVDQU 2528(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2560(CX), Y7
+ VMOVDQU 2592(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2624(CX), Y7
+ VMOVDQU 2656(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ MOVQ (DX), R12
+ VMOVDQU Y0, (R12)(R11*1)
+ MOVQ 24(DX), R12
+ VMOVDQU Y1, (R12)(R11*1)
+ MOVQ 48(DX), R12
+ VMOVDQU Y2, (R12)(R11*1)
+ MOVQ 72(DX), R12
+ VMOVDQU Y3, (R12)(R11*1)
+ MOVQ 96(DX), R12
+ VMOVDQU Y4, (R12)(R11*1)
+ MOVQ 120(DX), R12
+ VMOVDQU Y5, (R12)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_7x6_loop
+ VZEROUPPER
+
+mulAvxTwo_7x6_end:
+ RET
+
+// func mulAvxTwo_7x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 110 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), BX
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_7x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (BP)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (SI)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (DI)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 3 to 7 outputs
+ VMOVDQU (R8)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1344(CX), Y8
+ VMOVDQU 1376(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1408(CX), Y8
+ VMOVDQU 1440(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1472(CX), Y8
+ VMOVDQU 1504(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1536(CX), Y8
+ VMOVDQU 1568(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1600(CX), Y8
+ VMOVDQU 1632(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1664(CX), Y8
+ VMOVDQU 1696(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1728(CX), Y8
+ VMOVDQU 1760(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 4 to 7 outputs
+ VMOVDQU (R9)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1792(CX), Y8
+ VMOVDQU 1824(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1856(CX), Y8
+ VMOVDQU 1888(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1920(CX), Y8
+ VMOVDQU 1952(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1984(CX), Y8
+ VMOVDQU 2016(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2048(CX), Y8
+ VMOVDQU 2080(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2112(CX), Y8
+ VMOVDQU 2144(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2176(CX), Y8
+ VMOVDQU 2208(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 5 to 7 outputs
+ VMOVDQU (R10)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2240(CX), Y8
+ VMOVDQU 2272(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2304(CX), Y8
+ VMOVDQU 2336(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2368(CX), Y8
+ VMOVDQU 2400(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2432(CX), Y8
+ VMOVDQU 2464(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2496(CX), Y8
+ VMOVDQU 2528(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2560(CX), Y8
+ VMOVDQU 2592(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2624(CX), Y8
+ VMOVDQU 2656(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 6 to 7 outputs
+ VMOVDQU (BX)(R11*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2688(CX), Y8
+ VMOVDQU 2720(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2752(CX), Y8
+ VMOVDQU 2784(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2816(CX), Y8
+ VMOVDQU 2848(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2880(CX), Y8
+ VMOVDQU 2912(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2944(CX), Y8
+ VMOVDQU 2976(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3008(CX), Y8
+ VMOVDQU 3040(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3072(CX), Y8
+ VMOVDQU 3104(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ MOVQ (DX), R12
+ VMOVDQU Y0, (R12)(R11*1)
+ MOVQ 24(DX), R12
+ VMOVDQU Y1, (R12)(R11*1)
+ MOVQ 48(DX), R12
+ VMOVDQU Y2, (R12)(R11*1)
+ MOVQ 72(DX), R12
+ VMOVDQU Y3, (R12)(R11*1)
+ MOVQ 96(DX), R12
+ VMOVDQU Y4, (R12)(R11*1)
+ MOVQ 120(DX), R12
+ VMOVDQU Y5, (R12)(R11*1)
+ MOVQ 144(DX), R12
+ VMOVDQU Y6, (R12)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_7x7_loop
+ VZEROUPPER
+
+mulAvxTwo_7x7_end:
+ RET
+
+// func mulAvxTwo_7x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_7x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 125 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_7x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), BX
+ MOVQ $0x0000000f, R11
+ MOVQ R11, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R11
+
+mulAvxTwo_7x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (BP)(R11*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (SI)(R11*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (DI)(R11*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 3 to 8 outputs
+ VMOVDQU (R8)(R11*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1536(CX), Y9
+ VMOVDQU 1568(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1600(CX), Y9
+ VMOVDQU 1632(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1664(CX), Y9
+ VMOVDQU 1696(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1728(CX), Y9
+ VMOVDQU 1760(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1792(CX), Y9
+ VMOVDQU 1824(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1856(CX), Y9
+ VMOVDQU 1888(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1920(CX), Y9
+ VMOVDQU 1952(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1984(CX), Y9
+ VMOVDQU 2016(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 4 to 8 outputs
+ VMOVDQU (R9)(R11*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2048(CX), Y9
+ VMOVDQU 2080(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2112(CX), Y9
+ VMOVDQU 2144(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2176(CX), Y9
+ VMOVDQU 2208(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2240(CX), Y9
+ VMOVDQU 2272(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2304(CX), Y9
+ VMOVDQU 2336(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2368(CX), Y9
+ VMOVDQU 2400(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2432(CX), Y9
+ VMOVDQU 2464(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 2496(CX), Y9
+ VMOVDQU 2528(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 5 to 8 outputs
+ VMOVDQU (R10)(R11*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2560(CX), Y9
+ VMOVDQU 2592(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2624(CX), Y9
+ VMOVDQU 2656(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2688(CX), Y9
+ VMOVDQU 2720(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2752(CX), Y9
+ VMOVDQU 2784(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2816(CX), Y9
+ VMOVDQU 2848(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2880(CX), Y9
+ VMOVDQU 2912(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2944(CX), Y9
+ VMOVDQU 2976(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3008(CX), Y9
+ VMOVDQU 3040(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 6 to 8 outputs
+ VMOVDQU (BX)(R11*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 3072(CX), Y9
+ VMOVDQU 3104(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 3136(CX), Y9
+ VMOVDQU 3168(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 3200(CX), Y9
+ VMOVDQU 3232(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 3264(CX), Y9
+ VMOVDQU 3296(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 3328(CX), Y9
+ VMOVDQU 3360(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 3392(CX), Y9
+ VMOVDQU 3424(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 3456(CX), Y9
+ VMOVDQU 3488(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3520(CX), Y9
+ VMOVDQU 3552(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ MOVQ (DX), R12
+ VMOVDQU Y0, (R12)(R11*1)
+ MOVQ 24(DX), R12
+ VMOVDQU Y1, (R12)(R11*1)
+ MOVQ 48(DX), R12
+ VMOVDQU Y2, (R12)(R11*1)
+ MOVQ 72(DX), R12
+ VMOVDQU Y3, (R12)(R11*1)
+ MOVQ 96(DX), R12
+ VMOVDQU Y4, (R12)(R11*1)
+ MOVQ 120(DX), R12
+ VMOVDQU Y5, (R12)(R11*1)
+ MOVQ 144(DX), R12
+ VMOVDQU Y6, (R12)(R11*1)
+ MOVQ 168(DX), R12
+ VMOVDQU Y7, (R12)(R11*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R11
+ DECQ AX
+ JNZ mulAvxTwo_7x8_loop
+ VZEROUPPER
+
+mulAvxTwo_7x8_end:
+ RET
+
+// func mulAvxTwo_8x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x1(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 20 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), BX
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X1
+ VPBROADCASTB X1, Y1
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_8x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BP)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU (CX), Y2
+ VMOVDQU 32(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (SI)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 64(CX), Y2
+ VMOVDQU 96(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (DI)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 128(CX), Y2
+ VMOVDQU 160(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 3 to 1 outputs
+ VMOVDQU (R8)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 192(CX), Y2
+ VMOVDQU 224(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 4 to 1 outputs
+ VMOVDQU (R9)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 256(CX), Y2
+ VMOVDQU 288(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 5 to 1 outputs
+ VMOVDQU (R10)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 320(CX), Y2
+ VMOVDQU 352(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 6 to 1 outputs
+ VMOVDQU (R11)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 384(CX), Y2
+ VMOVDQU 416(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 7 to 1 outputs
+ VMOVDQU (BX)(R12*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 448(CX), Y2
+ VMOVDQU 480(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_8x1_loop
+ VZEROUPPER
+
+mulAvxTwo_8x1_end:
+ RET
+
+// func mulAvxTwo_8x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 39 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), R8
+ MOVQ 72(BP), R9
+ MOVQ 96(BP), R10
+ MOVQ 120(BP), R11
+ MOVQ 144(BP), R12
+ MOVQ 168(BP), BP
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_8x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (R8)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 3 to 2 outputs
+ VMOVDQU (R9)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 384(CX), Y3
+ VMOVDQU 416(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 448(CX), Y3
+ VMOVDQU 480(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 4 to 2 outputs
+ VMOVDQU (R10)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 512(CX), Y3
+ VMOVDQU 544(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 576(CX), Y3
+ VMOVDQU 608(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 5 to 2 outputs
+ VMOVDQU (R11)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 640(CX), Y3
+ VMOVDQU 672(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 704(CX), Y3
+ VMOVDQU 736(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 6 to 2 outputs
+ VMOVDQU (R12)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 768(CX), Y3
+ VMOVDQU 800(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 832(CX), Y3
+ VMOVDQU 864(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 7 to 2 outputs
+ VMOVDQU (BP)(R13*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 896(CX), Y3
+ VMOVDQU 928(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 960(CX), Y3
+ VMOVDQU 992(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R13*1)
+ VMOVDQU Y1, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_8x2_loop
+ VZEROUPPER
+
+mulAvxTwo_8x2_end:
+ RET
+
+// func mulAvxTwo_8x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 56 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), R8
+ MOVQ 48(SI), R9
+ MOVQ 72(SI), R10
+ MOVQ 96(SI), R11
+ MOVQ 120(SI), R12
+ MOVQ 144(SI), R13
+ MOVQ 168(SI), SI
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_8x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (R8)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (R9)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 3 to 3 outputs
+ VMOVDQU (R10)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 576(CX), Y4
+ VMOVDQU 608(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 640(CX), Y4
+ VMOVDQU 672(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 704(CX), Y4
+ VMOVDQU 736(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 4 to 3 outputs
+ VMOVDQU (R11)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 768(CX), Y4
+ VMOVDQU 800(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 832(CX), Y4
+ VMOVDQU 864(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 896(CX), Y4
+ VMOVDQU 928(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 5 to 3 outputs
+ VMOVDQU (R12)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 960(CX), Y4
+ VMOVDQU 992(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1024(CX), Y4
+ VMOVDQU 1056(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1088(CX), Y4
+ VMOVDQU 1120(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 6 to 3 outputs
+ VMOVDQU (R13)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1152(CX), Y4
+ VMOVDQU 1184(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1216(CX), Y4
+ VMOVDQU 1248(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1280(CX), Y4
+ VMOVDQU 1312(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 7 to 3 outputs
+ VMOVDQU (SI)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1344(CX), Y4
+ VMOVDQU 1376(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1408(CX), Y4
+ VMOVDQU 1440(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1472(CX), Y4
+ VMOVDQU 1504(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R14*1)
+ VMOVDQU Y1, (BP)(R14*1)
+ VMOVDQU Y2, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_8x3_loop
+ VZEROUPPER
+
+mulAvxTwo_8x3_end:
+ RET
+
+// func mulAvxTwo_8x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 73 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), SI
+ MOVQ 72(DX), DX
+ MOVQ in_base+24(FP), DI
+ MOVQ (DI), R8
+ MOVQ 24(DI), R9
+ MOVQ 48(DI), R10
+ MOVQ 72(DI), R11
+ MOVQ 96(DI), R12
+ MOVQ 120(DI), R13
+ MOVQ 144(DI), R14
+ MOVQ 168(DI), DI
+ MOVQ $0x0000000f, R15
+ MOVQ R15, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R15
+
+mulAvxTwo_8x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (R8)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (R9)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (R10)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 3 to 4 outputs
+ VMOVDQU (R11)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 768(CX), Y5
+ VMOVDQU 800(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 832(CX), Y5
+ VMOVDQU 864(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 896(CX), Y5
+ VMOVDQU 928(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 960(CX), Y5
+ VMOVDQU 992(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 4 to 4 outputs
+ VMOVDQU (R12)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1024(CX), Y5
+ VMOVDQU 1056(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1088(CX), Y5
+ VMOVDQU 1120(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1152(CX), Y5
+ VMOVDQU 1184(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1216(CX), Y5
+ VMOVDQU 1248(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 5 to 4 outputs
+ VMOVDQU (R13)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1280(CX), Y5
+ VMOVDQU 1312(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1344(CX), Y5
+ VMOVDQU 1376(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1408(CX), Y5
+ VMOVDQU 1440(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1472(CX), Y5
+ VMOVDQU 1504(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 6 to 4 outputs
+ VMOVDQU (R14)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1536(CX), Y5
+ VMOVDQU 1568(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1600(CX), Y5
+ VMOVDQU 1632(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1664(CX), Y5
+ VMOVDQU 1696(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1728(CX), Y5
+ VMOVDQU 1760(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 7 to 4 outputs
+ VMOVDQU (DI)(R15*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1792(CX), Y5
+ VMOVDQU 1824(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1856(CX), Y5
+ VMOVDQU 1888(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1920(CX), Y5
+ VMOVDQU 1952(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1984(CX), Y5
+ VMOVDQU 2016(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ VMOVDQU Y0, (BX)(R15*1)
+ VMOVDQU Y1, (BP)(R15*1)
+ VMOVDQU Y2, (SI)(R15*1)
+ VMOVDQU Y3, (DX)(R15*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R15
+ DECQ AX
+ JNZ mulAvxTwo_8x4_loop
+ VZEROUPPER
+
+mulAvxTwo_8x4_end:
+ RET
+
+// func mulAvxTwo_8x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 90 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), BX
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_8x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (BP)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (SI)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (DI)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 3 to 5 outputs
+ VMOVDQU (R8)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 960(CX), Y6
+ VMOVDQU 992(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1024(CX), Y6
+ VMOVDQU 1056(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1088(CX), Y6
+ VMOVDQU 1120(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1152(CX), Y6
+ VMOVDQU 1184(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1216(CX), Y6
+ VMOVDQU 1248(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 4 to 5 outputs
+ VMOVDQU (R9)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1280(CX), Y6
+ VMOVDQU 1312(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1344(CX), Y6
+ VMOVDQU 1376(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1408(CX), Y6
+ VMOVDQU 1440(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1472(CX), Y6
+ VMOVDQU 1504(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1536(CX), Y6
+ VMOVDQU 1568(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 5 to 5 outputs
+ VMOVDQU (R10)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1600(CX), Y6
+ VMOVDQU 1632(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1664(CX), Y6
+ VMOVDQU 1696(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1728(CX), Y6
+ VMOVDQU 1760(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1792(CX), Y6
+ VMOVDQU 1824(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1856(CX), Y6
+ VMOVDQU 1888(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 6 to 5 outputs
+ VMOVDQU (R11)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1920(CX), Y6
+ VMOVDQU 1952(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1984(CX), Y6
+ VMOVDQU 2016(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2048(CX), Y6
+ VMOVDQU 2080(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2112(CX), Y6
+ VMOVDQU 2144(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2176(CX), Y6
+ VMOVDQU 2208(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 7 to 5 outputs
+ VMOVDQU (BX)(R12*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 2240(CX), Y6
+ VMOVDQU 2272(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 2304(CX), Y6
+ VMOVDQU 2336(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2368(CX), Y6
+ VMOVDQU 2400(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2432(CX), Y6
+ VMOVDQU 2464(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2496(CX), Y6
+ VMOVDQU 2528(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ MOVQ (DX), R13
+ VMOVDQU Y0, (R13)(R12*1)
+ MOVQ 24(DX), R13
+ VMOVDQU Y1, (R13)(R12*1)
+ MOVQ 48(DX), R13
+ VMOVDQU Y2, (R13)(R12*1)
+ MOVQ 72(DX), R13
+ VMOVDQU Y3, (R13)(R12*1)
+ MOVQ 96(DX), R13
+ VMOVDQU Y4, (R13)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_8x5_loop
+ VZEROUPPER
+
+mulAvxTwo_8x5_end:
+ RET
+
+// func mulAvxTwo_8x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 107 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), BX
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_8x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (BP)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (SI)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (DI)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 3 to 6 outputs
+ VMOVDQU (R8)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1152(CX), Y7
+ VMOVDQU 1184(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1216(CX), Y7
+ VMOVDQU 1248(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1280(CX), Y7
+ VMOVDQU 1312(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1344(CX), Y7
+ VMOVDQU 1376(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1408(CX), Y7
+ VMOVDQU 1440(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1472(CX), Y7
+ VMOVDQU 1504(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 4 to 6 outputs
+ VMOVDQU (R9)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1536(CX), Y7
+ VMOVDQU 1568(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1600(CX), Y7
+ VMOVDQU 1632(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1664(CX), Y7
+ VMOVDQU 1696(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1728(CX), Y7
+ VMOVDQU 1760(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1792(CX), Y7
+ VMOVDQU 1824(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1856(CX), Y7
+ VMOVDQU 1888(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 5 to 6 outputs
+ VMOVDQU (R10)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1920(CX), Y7
+ VMOVDQU 1952(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1984(CX), Y7
+ VMOVDQU 2016(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2048(CX), Y7
+ VMOVDQU 2080(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2112(CX), Y7
+ VMOVDQU 2144(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2176(CX), Y7
+ VMOVDQU 2208(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2240(CX), Y7
+ VMOVDQU 2272(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 6 to 6 outputs
+ VMOVDQU (R11)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 2304(CX), Y7
+ VMOVDQU 2336(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 2368(CX), Y7
+ VMOVDQU 2400(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2432(CX), Y7
+ VMOVDQU 2464(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2496(CX), Y7
+ VMOVDQU 2528(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2560(CX), Y7
+ VMOVDQU 2592(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2624(CX), Y7
+ VMOVDQU 2656(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 7 to 6 outputs
+ VMOVDQU (BX)(R12*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 2688(CX), Y7
+ VMOVDQU 2720(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 2752(CX), Y7
+ VMOVDQU 2784(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2816(CX), Y7
+ VMOVDQU 2848(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2880(CX), Y7
+ VMOVDQU 2912(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2944(CX), Y7
+ VMOVDQU 2976(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 3008(CX), Y7
+ VMOVDQU 3040(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ MOVQ (DX), R13
+ VMOVDQU Y0, (R13)(R12*1)
+ MOVQ 24(DX), R13
+ VMOVDQU Y1, (R13)(R12*1)
+ MOVQ 48(DX), R13
+ VMOVDQU Y2, (R13)(R12*1)
+ MOVQ 72(DX), R13
+ VMOVDQU Y3, (R13)(R12*1)
+ MOVQ 96(DX), R13
+ VMOVDQU Y4, (R13)(R12*1)
+ MOVQ 120(DX), R13
+ VMOVDQU Y5, (R13)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_8x6_loop
+ VZEROUPPER
+
+mulAvxTwo_8x6_end:
+ RET
+
+// func mulAvxTwo_8x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 124 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), BX
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_8x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (BP)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (SI)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (DI)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 3 to 7 outputs
+ VMOVDQU (R8)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1344(CX), Y8
+ VMOVDQU 1376(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1408(CX), Y8
+ VMOVDQU 1440(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1472(CX), Y8
+ VMOVDQU 1504(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1536(CX), Y8
+ VMOVDQU 1568(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1600(CX), Y8
+ VMOVDQU 1632(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1664(CX), Y8
+ VMOVDQU 1696(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1728(CX), Y8
+ VMOVDQU 1760(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 4 to 7 outputs
+ VMOVDQU (R9)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1792(CX), Y8
+ VMOVDQU 1824(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1856(CX), Y8
+ VMOVDQU 1888(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1920(CX), Y8
+ VMOVDQU 1952(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1984(CX), Y8
+ VMOVDQU 2016(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2048(CX), Y8
+ VMOVDQU 2080(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2112(CX), Y8
+ VMOVDQU 2144(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2176(CX), Y8
+ VMOVDQU 2208(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 5 to 7 outputs
+ VMOVDQU (R10)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2240(CX), Y8
+ VMOVDQU 2272(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2304(CX), Y8
+ VMOVDQU 2336(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2368(CX), Y8
+ VMOVDQU 2400(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2432(CX), Y8
+ VMOVDQU 2464(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2496(CX), Y8
+ VMOVDQU 2528(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2560(CX), Y8
+ VMOVDQU 2592(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2624(CX), Y8
+ VMOVDQU 2656(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 6 to 7 outputs
+ VMOVDQU (R11)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2688(CX), Y8
+ VMOVDQU 2720(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2752(CX), Y8
+ VMOVDQU 2784(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2816(CX), Y8
+ VMOVDQU 2848(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2880(CX), Y8
+ VMOVDQU 2912(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2944(CX), Y8
+ VMOVDQU 2976(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3008(CX), Y8
+ VMOVDQU 3040(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3072(CX), Y8
+ VMOVDQU 3104(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 7 to 7 outputs
+ VMOVDQU (BX)(R12*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 3136(CX), Y8
+ VMOVDQU 3168(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 3200(CX), Y8
+ VMOVDQU 3232(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 3264(CX), Y8
+ VMOVDQU 3296(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 3328(CX), Y8
+ VMOVDQU 3360(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 3392(CX), Y8
+ VMOVDQU 3424(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3456(CX), Y8
+ VMOVDQU 3488(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3520(CX), Y8
+ VMOVDQU 3552(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ MOVQ (DX), R13
+ VMOVDQU Y0, (R13)(R12*1)
+ MOVQ 24(DX), R13
+ VMOVDQU Y1, (R13)(R12*1)
+ MOVQ 48(DX), R13
+ VMOVDQU Y2, (R13)(R12*1)
+ MOVQ 72(DX), R13
+ VMOVDQU Y3, (R13)(R12*1)
+ MOVQ 96(DX), R13
+ VMOVDQU Y4, (R13)(R12*1)
+ MOVQ 120(DX), R13
+ VMOVDQU Y5, (R13)(R12*1)
+ MOVQ 144(DX), R13
+ VMOVDQU Y6, (R13)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_8x7_loop
+ VZEROUPPER
+
+mulAvxTwo_8x7_end:
+ RET
+
+// func mulAvxTwo_8x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_8x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 141 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_8x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), BX
+ MOVQ $0x0000000f, R12
+ MOVQ R12, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R12
+
+mulAvxTwo_8x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (BP)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (SI)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (DI)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 3 to 8 outputs
+ VMOVDQU (R8)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1536(CX), Y9
+ VMOVDQU 1568(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1600(CX), Y9
+ VMOVDQU 1632(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1664(CX), Y9
+ VMOVDQU 1696(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1728(CX), Y9
+ VMOVDQU 1760(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1792(CX), Y9
+ VMOVDQU 1824(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1856(CX), Y9
+ VMOVDQU 1888(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1920(CX), Y9
+ VMOVDQU 1952(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1984(CX), Y9
+ VMOVDQU 2016(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 4 to 8 outputs
+ VMOVDQU (R9)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2048(CX), Y9
+ VMOVDQU 2080(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2112(CX), Y9
+ VMOVDQU 2144(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2176(CX), Y9
+ VMOVDQU 2208(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2240(CX), Y9
+ VMOVDQU 2272(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2304(CX), Y9
+ VMOVDQU 2336(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2368(CX), Y9
+ VMOVDQU 2400(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2432(CX), Y9
+ VMOVDQU 2464(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 2496(CX), Y9
+ VMOVDQU 2528(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 5 to 8 outputs
+ VMOVDQU (R10)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2560(CX), Y9
+ VMOVDQU 2592(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2624(CX), Y9
+ VMOVDQU 2656(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2688(CX), Y9
+ VMOVDQU 2720(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2752(CX), Y9
+ VMOVDQU 2784(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2816(CX), Y9
+ VMOVDQU 2848(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2880(CX), Y9
+ VMOVDQU 2912(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2944(CX), Y9
+ VMOVDQU 2976(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3008(CX), Y9
+ VMOVDQU 3040(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 6 to 8 outputs
+ VMOVDQU (R11)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 3072(CX), Y9
+ VMOVDQU 3104(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 3136(CX), Y9
+ VMOVDQU 3168(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 3200(CX), Y9
+ VMOVDQU 3232(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 3264(CX), Y9
+ VMOVDQU 3296(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 3328(CX), Y9
+ VMOVDQU 3360(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 3392(CX), Y9
+ VMOVDQU 3424(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 3456(CX), Y9
+ VMOVDQU 3488(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3520(CX), Y9
+ VMOVDQU 3552(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 7 to 8 outputs
+ VMOVDQU (BX)(R12*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 3584(CX), Y9
+ VMOVDQU 3616(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 3648(CX), Y9
+ VMOVDQU 3680(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 3712(CX), Y9
+ VMOVDQU 3744(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 3776(CX), Y9
+ VMOVDQU 3808(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 3840(CX), Y9
+ VMOVDQU 3872(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 3904(CX), Y9
+ VMOVDQU 3936(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 3968(CX), Y9
+ VMOVDQU 4000(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 4032(CX), Y9
+ VMOVDQU 4064(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ MOVQ (DX), R13
+ VMOVDQU Y0, (R13)(R12*1)
+ MOVQ 24(DX), R13
+ VMOVDQU Y1, (R13)(R12*1)
+ MOVQ 48(DX), R13
+ VMOVDQU Y2, (R13)(R12*1)
+ MOVQ 72(DX), R13
+ VMOVDQU Y3, (R13)(R12*1)
+ MOVQ 96(DX), R13
+ VMOVDQU Y4, (R13)(R12*1)
+ MOVQ 120(DX), R13
+ VMOVDQU Y5, (R13)(R12*1)
+ MOVQ 144(DX), R13
+ VMOVDQU Y6, (R13)(R12*1)
+ MOVQ 168(DX), R13
+ VMOVDQU Y7, (R13)(R12*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R12
+ DECQ AX
+ JNZ mulAvxTwo_8x8_loop
+ VZEROUPPER
+
+mulAvxTwo_8x8_end:
+ RET
+
+// func mulAvxTwo_9x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x1(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 22 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), BX
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X1
+ VPBROADCASTB X1, Y1
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_9x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BP)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU (CX), Y2
+ VMOVDQU 32(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (SI)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 64(CX), Y2
+ VMOVDQU 96(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (DI)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 128(CX), Y2
+ VMOVDQU 160(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 3 to 1 outputs
+ VMOVDQU (R8)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 192(CX), Y2
+ VMOVDQU 224(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 4 to 1 outputs
+ VMOVDQU (R9)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 256(CX), Y2
+ VMOVDQU 288(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 5 to 1 outputs
+ VMOVDQU (R10)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 320(CX), Y2
+ VMOVDQU 352(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 6 to 1 outputs
+ VMOVDQU (R11)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 384(CX), Y2
+ VMOVDQU 416(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 7 to 1 outputs
+ VMOVDQU (R12)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 448(CX), Y2
+ VMOVDQU 480(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 8 to 1 outputs
+ VMOVDQU (BX)(R13*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 512(CX), Y2
+ VMOVDQU 544(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_9x1_loop
+ VZEROUPPER
+
+mulAvxTwo_9x1_end:
+ RET
+
+// func mulAvxTwo_9x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 43 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), R8
+ MOVQ 72(BP), R9
+ MOVQ 96(BP), R10
+ MOVQ 120(BP), R11
+ MOVQ 144(BP), R12
+ MOVQ 168(BP), R13
+ MOVQ 192(BP), BP
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_9x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (R8)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 3 to 2 outputs
+ VMOVDQU (R9)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 384(CX), Y3
+ VMOVDQU 416(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 448(CX), Y3
+ VMOVDQU 480(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 4 to 2 outputs
+ VMOVDQU (R10)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 512(CX), Y3
+ VMOVDQU 544(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 576(CX), Y3
+ VMOVDQU 608(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 5 to 2 outputs
+ VMOVDQU (R11)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 640(CX), Y3
+ VMOVDQU 672(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 704(CX), Y3
+ VMOVDQU 736(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 6 to 2 outputs
+ VMOVDQU (R12)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 768(CX), Y3
+ VMOVDQU 800(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 832(CX), Y3
+ VMOVDQU 864(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 7 to 2 outputs
+ VMOVDQU (R13)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 896(CX), Y3
+ VMOVDQU 928(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 960(CX), Y3
+ VMOVDQU 992(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 8 to 2 outputs
+ VMOVDQU (BP)(R14*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 1024(CX), Y3
+ VMOVDQU 1056(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 1088(CX), Y3
+ VMOVDQU 1120(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R14*1)
+ VMOVDQU Y1, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_9x2_loop
+ VZEROUPPER
+
+mulAvxTwo_9x2_end:
+ RET
+
+// func mulAvxTwo_9x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 62 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), BP
+ MOVQ 48(DX), DX
+ MOVQ in_base+24(FP), SI
+ MOVQ (SI), DI
+ MOVQ 24(SI), R8
+ MOVQ 48(SI), R9
+ MOVQ 72(SI), R10
+ MOVQ 96(SI), R11
+ MOVQ 120(SI), R12
+ MOVQ 144(SI), R13
+ MOVQ 168(SI), R14
+ MOVQ 192(SI), SI
+ MOVQ $0x0000000f, R15
+ MOVQ R15, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R15
+
+mulAvxTwo_9x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (DI)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (R8)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (R9)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 3 to 3 outputs
+ VMOVDQU (R10)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 576(CX), Y4
+ VMOVDQU 608(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 640(CX), Y4
+ VMOVDQU 672(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 704(CX), Y4
+ VMOVDQU 736(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 4 to 3 outputs
+ VMOVDQU (R11)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 768(CX), Y4
+ VMOVDQU 800(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 832(CX), Y4
+ VMOVDQU 864(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 896(CX), Y4
+ VMOVDQU 928(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 5 to 3 outputs
+ VMOVDQU (R12)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 960(CX), Y4
+ VMOVDQU 992(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1024(CX), Y4
+ VMOVDQU 1056(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1088(CX), Y4
+ VMOVDQU 1120(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 6 to 3 outputs
+ VMOVDQU (R13)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1152(CX), Y4
+ VMOVDQU 1184(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1216(CX), Y4
+ VMOVDQU 1248(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1280(CX), Y4
+ VMOVDQU 1312(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 7 to 3 outputs
+ VMOVDQU (R14)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1344(CX), Y4
+ VMOVDQU 1376(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1408(CX), Y4
+ VMOVDQU 1440(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1472(CX), Y4
+ VMOVDQU 1504(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 8 to 3 outputs
+ VMOVDQU (SI)(R15*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1536(CX), Y4
+ VMOVDQU 1568(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1600(CX), Y4
+ VMOVDQU 1632(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1664(CX), Y4
+ VMOVDQU 1696(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ VMOVDQU Y0, (BX)(R15*1)
+ VMOVDQU Y1, (BP)(R15*1)
+ VMOVDQU Y2, (DX)(R15*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R15
+ DECQ AX
+ JNZ mulAvxTwo_9x3_loop
+ VZEROUPPER
+
+mulAvxTwo_9x3_end:
+ RET
+
+// func mulAvxTwo_9x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 81 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), BX
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_9x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (BP)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (SI)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (DI)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 3 to 4 outputs
+ VMOVDQU (R8)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 768(CX), Y5
+ VMOVDQU 800(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 832(CX), Y5
+ VMOVDQU 864(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 896(CX), Y5
+ VMOVDQU 928(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 960(CX), Y5
+ VMOVDQU 992(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 4 to 4 outputs
+ VMOVDQU (R9)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1024(CX), Y5
+ VMOVDQU 1056(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1088(CX), Y5
+ VMOVDQU 1120(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1152(CX), Y5
+ VMOVDQU 1184(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1216(CX), Y5
+ VMOVDQU 1248(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 5 to 4 outputs
+ VMOVDQU (R10)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1280(CX), Y5
+ VMOVDQU 1312(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1344(CX), Y5
+ VMOVDQU 1376(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1408(CX), Y5
+ VMOVDQU 1440(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1472(CX), Y5
+ VMOVDQU 1504(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 6 to 4 outputs
+ VMOVDQU (R11)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1536(CX), Y5
+ VMOVDQU 1568(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1600(CX), Y5
+ VMOVDQU 1632(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1664(CX), Y5
+ VMOVDQU 1696(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1728(CX), Y5
+ VMOVDQU 1760(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 7 to 4 outputs
+ VMOVDQU (R12)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1792(CX), Y5
+ VMOVDQU 1824(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1856(CX), Y5
+ VMOVDQU 1888(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1920(CX), Y5
+ VMOVDQU 1952(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1984(CX), Y5
+ VMOVDQU 2016(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 8 to 4 outputs
+ VMOVDQU (BX)(R13*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 2048(CX), Y5
+ VMOVDQU 2080(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 2112(CX), Y5
+ VMOVDQU 2144(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 2176(CX), Y5
+ VMOVDQU 2208(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 2240(CX), Y5
+ VMOVDQU 2272(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ MOVQ (DX), R14
+ VMOVDQU Y0, (R14)(R13*1)
+ MOVQ 24(DX), R14
+ VMOVDQU Y1, (R14)(R13*1)
+ MOVQ 48(DX), R14
+ VMOVDQU Y2, (R14)(R13*1)
+ MOVQ 72(DX), R14
+ VMOVDQU Y3, (R14)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_9x4_loop
+ VZEROUPPER
+
+mulAvxTwo_9x4_end:
+ RET
+
+// func mulAvxTwo_9x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 100 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), BX
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_9x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (BP)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (SI)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (DI)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 3 to 5 outputs
+ VMOVDQU (R8)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 960(CX), Y6
+ VMOVDQU 992(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1024(CX), Y6
+ VMOVDQU 1056(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1088(CX), Y6
+ VMOVDQU 1120(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1152(CX), Y6
+ VMOVDQU 1184(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1216(CX), Y6
+ VMOVDQU 1248(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 4 to 5 outputs
+ VMOVDQU (R9)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1280(CX), Y6
+ VMOVDQU 1312(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1344(CX), Y6
+ VMOVDQU 1376(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1408(CX), Y6
+ VMOVDQU 1440(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1472(CX), Y6
+ VMOVDQU 1504(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1536(CX), Y6
+ VMOVDQU 1568(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 5 to 5 outputs
+ VMOVDQU (R10)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1600(CX), Y6
+ VMOVDQU 1632(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1664(CX), Y6
+ VMOVDQU 1696(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1728(CX), Y6
+ VMOVDQU 1760(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1792(CX), Y6
+ VMOVDQU 1824(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1856(CX), Y6
+ VMOVDQU 1888(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 6 to 5 outputs
+ VMOVDQU (R11)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1920(CX), Y6
+ VMOVDQU 1952(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1984(CX), Y6
+ VMOVDQU 2016(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2048(CX), Y6
+ VMOVDQU 2080(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2112(CX), Y6
+ VMOVDQU 2144(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2176(CX), Y6
+ VMOVDQU 2208(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 7 to 5 outputs
+ VMOVDQU (R12)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 2240(CX), Y6
+ VMOVDQU 2272(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 2304(CX), Y6
+ VMOVDQU 2336(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2368(CX), Y6
+ VMOVDQU 2400(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2432(CX), Y6
+ VMOVDQU 2464(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2496(CX), Y6
+ VMOVDQU 2528(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 8 to 5 outputs
+ VMOVDQU (BX)(R13*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 2560(CX), Y6
+ VMOVDQU 2592(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 2624(CX), Y6
+ VMOVDQU 2656(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2688(CX), Y6
+ VMOVDQU 2720(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2752(CX), Y6
+ VMOVDQU 2784(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2816(CX), Y6
+ VMOVDQU 2848(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ MOVQ (DX), R14
+ VMOVDQU Y0, (R14)(R13*1)
+ MOVQ 24(DX), R14
+ VMOVDQU Y1, (R14)(R13*1)
+ MOVQ 48(DX), R14
+ VMOVDQU Y2, (R14)(R13*1)
+ MOVQ 72(DX), R14
+ VMOVDQU Y3, (R14)(R13*1)
+ MOVQ 96(DX), R14
+ VMOVDQU Y4, (R14)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_9x5_loop
+ VZEROUPPER
+
+mulAvxTwo_9x5_end:
+ RET
+
+// func mulAvxTwo_9x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 119 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), BX
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_9x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (BP)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (SI)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (DI)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 3 to 6 outputs
+ VMOVDQU (R8)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1152(CX), Y7
+ VMOVDQU 1184(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1216(CX), Y7
+ VMOVDQU 1248(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1280(CX), Y7
+ VMOVDQU 1312(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1344(CX), Y7
+ VMOVDQU 1376(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1408(CX), Y7
+ VMOVDQU 1440(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1472(CX), Y7
+ VMOVDQU 1504(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 4 to 6 outputs
+ VMOVDQU (R9)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1536(CX), Y7
+ VMOVDQU 1568(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1600(CX), Y7
+ VMOVDQU 1632(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1664(CX), Y7
+ VMOVDQU 1696(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1728(CX), Y7
+ VMOVDQU 1760(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1792(CX), Y7
+ VMOVDQU 1824(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1856(CX), Y7
+ VMOVDQU 1888(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 5 to 6 outputs
+ VMOVDQU (R10)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1920(CX), Y7
+ VMOVDQU 1952(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1984(CX), Y7
+ VMOVDQU 2016(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2048(CX), Y7
+ VMOVDQU 2080(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2112(CX), Y7
+ VMOVDQU 2144(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2176(CX), Y7
+ VMOVDQU 2208(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2240(CX), Y7
+ VMOVDQU 2272(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 6 to 6 outputs
+ VMOVDQU (R11)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 2304(CX), Y7
+ VMOVDQU 2336(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 2368(CX), Y7
+ VMOVDQU 2400(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2432(CX), Y7
+ VMOVDQU 2464(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2496(CX), Y7
+ VMOVDQU 2528(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2560(CX), Y7
+ VMOVDQU 2592(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2624(CX), Y7
+ VMOVDQU 2656(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 7 to 6 outputs
+ VMOVDQU (R12)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 2688(CX), Y7
+ VMOVDQU 2720(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 2752(CX), Y7
+ VMOVDQU 2784(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2816(CX), Y7
+ VMOVDQU 2848(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2880(CX), Y7
+ VMOVDQU 2912(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2944(CX), Y7
+ VMOVDQU 2976(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 3008(CX), Y7
+ VMOVDQU 3040(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 8 to 6 outputs
+ VMOVDQU (BX)(R13*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 3072(CX), Y7
+ VMOVDQU 3104(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 3136(CX), Y7
+ VMOVDQU 3168(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 3200(CX), Y7
+ VMOVDQU 3232(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 3264(CX), Y7
+ VMOVDQU 3296(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 3328(CX), Y7
+ VMOVDQU 3360(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 3392(CX), Y7
+ VMOVDQU 3424(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ MOVQ (DX), R14
+ VMOVDQU Y0, (R14)(R13*1)
+ MOVQ 24(DX), R14
+ VMOVDQU Y1, (R14)(R13*1)
+ MOVQ 48(DX), R14
+ VMOVDQU Y2, (R14)(R13*1)
+ MOVQ 72(DX), R14
+ VMOVDQU Y3, (R14)(R13*1)
+ MOVQ 96(DX), R14
+ VMOVDQU Y4, (R14)(R13*1)
+ MOVQ 120(DX), R14
+ VMOVDQU Y5, (R14)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_9x6_loop
+ VZEROUPPER
+
+mulAvxTwo_9x6_end:
+ RET
+
+// func mulAvxTwo_9x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 138 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), BX
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_9x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (BP)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (SI)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (DI)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 3 to 7 outputs
+ VMOVDQU (R8)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1344(CX), Y8
+ VMOVDQU 1376(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1408(CX), Y8
+ VMOVDQU 1440(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1472(CX), Y8
+ VMOVDQU 1504(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1536(CX), Y8
+ VMOVDQU 1568(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1600(CX), Y8
+ VMOVDQU 1632(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1664(CX), Y8
+ VMOVDQU 1696(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1728(CX), Y8
+ VMOVDQU 1760(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 4 to 7 outputs
+ VMOVDQU (R9)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1792(CX), Y8
+ VMOVDQU 1824(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1856(CX), Y8
+ VMOVDQU 1888(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1920(CX), Y8
+ VMOVDQU 1952(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1984(CX), Y8
+ VMOVDQU 2016(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2048(CX), Y8
+ VMOVDQU 2080(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2112(CX), Y8
+ VMOVDQU 2144(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2176(CX), Y8
+ VMOVDQU 2208(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 5 to 7 outputs
+ VMOVDQU (R10)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2240(CX), Y8
+ VMOVDQU 2272(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2304(CX), Y8
+ VMOVDQU 2336(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2368(CX), Y8
+ VMOVDQU 2400(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2432(CX), Y8
+ VMOVDQU 2464(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2496(CX), Y8
+ VMOVDQU 2528(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2560(CX), Y8
+ VMOVDQU 2592(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2624(CX), Y8
+ VMOVDQU 2656(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 6 to 7 outputs
+ VMOVDQU (R11)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2688(CX), Y8
+ VMOVDQU 2720(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2752(CX), Y8
+ VMOVDQU 2784(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2816(CX), Y8
+ VMOVDQU 2848(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2880(CX), Y8
+ VMOVDQU 2912(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2944(CX), Y8
+ VMOVDQU 2976(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3008(CX), Y8
+ VMOVDQU 3040(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3072(CX), Y8
+ VMOVDQU 3104(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 7 to 7 outputs
+ VMOVDQU (R12)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 3136(CX), Y8
+ VMOVDQU 3168(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 3200(CX), Y8
+ VMOVDQU 3232(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 3264(CX), Y8
+ VMOVDQU 3296(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 3328(CX), Y8
+ VMOVDQU 3360(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 3392(CX), Y8
+ VMOVDQU 3424(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3456(CX), Y8
+ VMOVDQU 3488(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3520(CX), Y8
+ VMOVDQU 3552(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 8 to 7 outputs
+ VMOVDQU (BX)(R13*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 3584(CX), Y8
+ VMOVDQU 3616(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 3648(CX), Y8
+ VMOVDQU 3680(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 3712(CX), Y8
+ VMOVDQU 3744(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 3776(CX), Y8
+ VMOVDQU 3808(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 3840(CX), Y8
+ VMOVDQU 3872(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3904(CX), Y8
+ VMOVDQU 3936(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3968(CX), Y8
+ VMOVDQU 4000(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ MOVQ (DX), R14
+ VMOVDQU Y0, (R14)(R13*1)
+ MOVQ 24(DX), R14
+ VMOVDQU Y1, (R14)(R13*1)
+ MOVQ 48(DX), R14
+ VMOVDQU Y2, (R14)(R13*1)
+ MOVQ 72(DX), R14
+ VMOVDQU Y3, (R14)(R13*1)
+ MOVQ 96(DX), R14
+ VMOVDQU Y4, (R14)(R13*1)
+ MOVQ 120(DX), R14
+ VMOVDQU Y5, (R14)(R13*1)
+ MOVQ 144(DX), R14
+ VMOVDQU Y6, (R14)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_9x7_loop
+ VZEROUPPER
+
+mulAvxTwo_9x7_end:
+ RET
+
+// func mulAvxTwo_9x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_9x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 157 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_9x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), BX
+ MOVQ $0x0000000f, R13
+ MOVQ R13, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R13
+
+mulAvxTwo_9x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (BP)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (SI)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (DI)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 3 to 8 outputs
+ VMOVDQU (R8)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1536(CX), Y9
+ VMOVDQU 1568(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1600(CX), Y9
+ VMOVDQU 1632(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1664(CX), Y9
+ VMOVDQU 1696(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1728(CX), Y9
+ VMOVDQU 1760(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1792(CX), Y9
+ VMOVDQU 1824(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1856(CX), Y9
+ VMOVDQU 1888(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1920(CX), Y9
+ VMOVDQU 1952(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1984(CX), Y9
+ VMOVDQU 2016(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 4 to 8 outputs
+ VMOVDQU (R9)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2048(CX), Y9
+ VMOVDQU 2080(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2112(CX), Y9
+ VMOVDQU 2144(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2176(CX), Y9
+ VMOVDQU 2208(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2240(CX), Y9
+ VMOVDQU 2272(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2304(CX), Y9
+ VMOVDQU 2336(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2368(CX), Y9
+ VMOVDQU 2400(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2432(CX), Y9
+ VMOVDQU 2464(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 2496(CX), Y9
+ VMOVDQU 2528(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 5 to 8 outputs
+ VMOVDQU (R10)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2560(CX), Y9
+ VMOVDQU 2592(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2624(CX), Y9
+ VMOVDQU 2656(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2688(CX), Y9
+ VMOVDQU 2720(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2752(CX), Y9
+ VMOVDQU 2784(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2816(CX), Y9
+ VMOVDQU 2848(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2880(CX), Y9
+ VMOVDQU 2912(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2944(CX), Y9
+ VMOVDQU 2976(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3008(CX), Y9
+ VMOVDQU 3040(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 6 to 8 outputs
+ VMOVDQU (R11)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 3072(CX), Y9
+ VMOVDQU 3104(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 3136(CX), Y9
+ VMOVDQU 3168(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 3200(CX), Y9
+ VMOVDQU 3232(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 3264(CX), Y9
+ VMOVDQU 3296(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 3328(CX), Y9
+ VMOVDQU 3360(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 3392(CX), Y9
+ VMOVDQU 3424(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 3456(CX), Y9
+ VMOVDQU 3488(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3520(CX), Y9
+ VMOVDQU 3552(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 7 to 8 outputs
+ VMOVDQU (R12)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 3584(CX), Y9
+ VMOVDQU 3616(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 3648(CX), Y9
+ VMOVDQU 3680(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 3712(CX), Y9
+ VMOVDQU 3744(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 3776(CX), Y9
+ VMOVDQU 3808(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 3840(CX), Y9
+ VMOVDQU 3872(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 3904(CX), Y9
+ VMOVDQU 3936(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 3968(CX), Y9
+ VMOVDQU 4000(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 4032(CX), Y9
+ VMOVDQU 4064(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 8 to 8 outputs
+ VMOVDQU (BX)(R13*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 4096(CX), Y9
+ VMOVDQU 4128(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 4160(CX), Y9
+ VMOVDQU 4192(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 4224(CX), Y9
+ VMOVDQU 4256(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 4288(CX), Y9
+ VMOVDQU 4320(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 4352(CX), Y9
+ VMOVDQU 4384(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 4416(CX), Y9
+ VMOVDQU 4448(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 4480(CX), Y9
+ VMOVDQU 4512(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 4544(CX), Y9
+ VMOVDQU 4576(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ MOVQ (DX), R14
+ VMOVDQU Y0, (R14)(R13*1)
+ MOVQ 24(DX), R14
+ VMOVDQU Y1, (R14)(R13*1)
+ MOVQ 48(DX), R14
+ VMOVDQU Y2, (R14)(R13*1)
+ MOVQ 72(DX), R14
+ VMOVDQU Y3, (R14)(R13*1)
+ MOVQ 96(DX), R14
+ VMOVDQU Y4, (R14)(R13*1)
+ MOVQ 120(DX), R14
+ VMOVDQU Y5, (R14)(R13*1)
+ MOVQ 144(DX), R14
+ VMOVDQU Y6, (R14)(R13*1)
+ MOVQ 168(DX), R14
+ VMOVDQU Y7, (R14)(R13*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R13
+ DECQ AX
+ JNZ mulAvxTwo_9x8_loop
+ VZEROUPPER
+
+mulAvxTwo_9x8_end:
+ RET
+
+// func mulAvxTwo_10x1(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x1(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 24 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x1_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), R13
+ MOVQ 216(BX), BX
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X1
+ VPBROADCASTB X1, Y1
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_10x1_loop:
+ // Clear 1 outputs
+ VPXOR Y0, Y0, Y0
+
+ // Load and process 32 bytes from input 0 to 1 outputs
+ VMOVDQU (BP)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU (CX), Y2
+ VMOVDQU 32(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 1 to 1 outputs
+ VMOVDQU (SI)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 64(CX), Y2
+ VMOVDQU 96(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 2 to 1 outputs
+ VMOVDQU (DI)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 128(CX), Y2
+ VMOVDQU 160(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 3 to 1 outputs
+ VMOVDQU (R8)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 192(CX), Y2
+ VMOVDQU 224(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 4 to 1 outputs
+ VMOVDQU (R9)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 256(CX), Y2
+ VMOVDQU 288(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 5 to 1 outputs
+ VMOVDQU (R10)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 320(CX), Y2
+ VMOVDQU 352(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 6 to 1 outputs
+ VMOVDQU (R11)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 384(CX), Y2
+ VMOVDQU 416(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 7 to 1 outputs
+ VMOVDQU (R12)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 448(CX), Y2
+ VMOVDQU 480(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 8 to 1 outputs
+ VMOVDQU (R13)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 512(CX), Y2
+ VMOVDQU 544(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Load and process 32 bytes from input 9 to 1 outputs
+ VMOVDQU (BX)(R14*1), Y4
+ VPSRLQ $0x04, Y4, Y5
+ VPAND Y1, Y4, Y4
+ VPAND Y1, Y5, Y5
+ VMOVDQU 576(CX), Y2
+ VMOVDQU 608(CX), Y3
+ VPSHUFB Y4, Y2, Y2
+ VPSHUFB Y5, Y3, Y3
+ VPXOR Y2, Y3, Y2
+ VPXOR Y2, Y0, Y0
+
+ // Store 1 outputs
+ VMOVDQU Y0, (DX)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_10x1_loop
+ VZEROUPPER
+
+mulAvxTwo_10x1_end:
+ RET
+
+// func mulAvxTwo_10x2(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x2(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 47 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x2_end
+ MOVQ out_base+48(FP), DX
+ MOVQ (DX), BX
+ MOVQ 24(DX), DX
+ MOVQ in_base+24(FP), BP
+ MOVQ (BP), SI
+ MOVQ 24(BP), DI
+ MOVQ 48(BP), R8
+ MOVQ 72(BP), R9
+ MOVQ 96(BP), R10
+ MOVQ 120(BP), R11
+ MOVQ 144(BP), R12
+ MOVQ 168(BP), R13
+ MOVQ 192(BP), R14
+ MOVQ 216(BP), BP
+ MOVQ $0x0000000f, R15
+ MOVQ R15, X2
+ VPBROADCASTB X2, Y2
+ MOVQ start+72(FP), R15
+
+mulAvxTwo_10x2_loop:
+ // Clear 2 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+
+ // Load and process 32 bytes from input 0 to 2 outputs
+ VMOVDQU (SI)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU (CX), Y3
+ VMOVDQU 32(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 64(CX), Y3
+ VMOVDQU 96(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 1 to 2 outputs
+ VMOVDQU (DI)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 128(CX), Y3
+ VMOVDQU 160(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 192(CX), Y3
+ VMOVDQU 224(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 2 to 2 outputs
+ VMOVDQU (R8)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 256(CX), Y3
+ VMOVDQU 288(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 320(CX), Y3
+ VMOVDQU 352(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 3 to 2 outputs
+ VMOVDQU (R9)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 384(CX), Y3
+ VMOVDQU 416(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 448(CX), Y3
+ VMOVDQU 480(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 4 to 2 outputs
+ VMOVDQU (R10)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 512(CX), Y3
+ VMOVDQU 544(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 576(CX), Y3
+ VMOVDQU 608(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 5 to 2 outputs
+ VMOVDQU (R11)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 640(CX), Y3
+ VMOVDQU 672(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 704(CX), Y3
+ VMOVDQU 736(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 6 to 2 outputs
+ VMOVDQU (R12)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 768(CX), Y3
+ VMOVDQU 800(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 832(CX), Y3
+ VMOVDQU 864(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 7 to 2 outputs
+ VMOVDQU (R13)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 896(CX), Y3
+ VMOVDQU 928(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 960(CX), Y3
+ VMOVDQU 992(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 8 to 2 outputs
+ VMOVDQU (R14)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 1024(CX), Y3
+ VMOVDQU 1056(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 1088(CX), Y3
+ VMOVDQU 1120(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Load and process 32 bytes from input 9 to 2 outputs
+ VMOVDQU (BP)(R15*1), Y5
+ VPSRLQ $0x04, Y5, Y6
+ VPAND Y2, Y5, Y5
+ VPAND Y2, Y6, Y6
+ VMOVDQU 1152(CX), Y3
+ VMOVDQU 1184(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y0, Y0
+ VMOVDQU 1216(CX), Y3
+ VMOVDQU 1248(CX), Y4
+ VPSHUFB Y5, Y3, Y3
+ VPSHUFB Y6, Y4, Y4
+ VPXOR Y3, Y4, Y3
+ VPXOR Y3, Y1, Y1
+
+ // Store 2 outputs
+ VMOVDQU Y0, (BX)(R15*1)
+ VMOVDQU Y1, (DX)(R15*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R15
+ DECQ AX
+ JNZ mulAvxTwo_10x2_loop
+ VZEROUPPER
+
+mulAvxTwo_10x2_end:
+ RET
+
+// func mulAvxTwo_10x3(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x3(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 68 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x3_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), R13
+ MOVQ 216(BX), BX
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X3
+ VPBROADCASTB X3, Y3
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_10x3_loop:
+ // Clear 3 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+
+ // Load and process 32 bytes from input 0 to 3 outputs
+ VMOVDQU (BP)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU (CX), Y4
+ VMOVDQU 32(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 64(CX), Y4
+ VMOVDQU 96(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 128(CX), Y4
+ VMOVDQU 160(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 1 to 3 outputs
+ VMOVDQU (SI)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 192(CX), Y4
+ VMOVDQU 224(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 256(CX), Y4
+ VMOVDQU 288(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 320(CX), Y4
+ VMOVDQU 352(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 2 to 3 outputs
+ VMOVDQU (DI)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 384(CX), Y4
+ VMOVDQU 416(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 448(CX), Y4
+ VMOVDQU 480(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 512(CX), Y4
+ VMOVDQU 544(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 3 to 3 outputs
+ VMOVDQU (R8)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 576(CX), Y4
+ VMOVDQU 608(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 640(CX), Y4
+ VMOVDQU 672(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 704(CX), Y4
+ VMOVDQU 736(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 4 to 3 outputs
+ VMOVDQU (R9)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 768(CX), Y4
+ VMOVDQU 800(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 832(CX), Y4
+ VMOVDQU 864(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 896(CX), Y4
+ VMOVDQU 928(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 5 to 3 outputs
+ VMOVDQU (R10)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 960(CX), Y4
+ VMOVDQU 992(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1024(CX), Y4
+ VMOVDQU 1056(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1088(CX), Y4
+ VMOVDQU 1120(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 6 to 3 outputs
+ VMOVDQU (R11)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1152(CX), Y4
+ VMOVDQU 1184(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1216(CX), Y4
+ VMOVDQU 1248(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1280(CX), Y4
+ VMOVDQU 1312(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 7 to 3 outputs
+ VMOVDQU (R12)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1344(CX), Y4
+ VMOVDQU 1376(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1408(CX), Y4
+ VMOVDQU 1440(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1472(CX), Y4
+ VMOVDQU 1504(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 8 to 3 outputs
+ VMOVDQU (R13)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1536(CX), Y4
+ VMOVDQU 1568(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1600(CX), Y4
+ VMOVDQU 1632(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1664(CX), Y4
+ VMOVDQU 1696(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Load and process 32 bytes from input 9 to 3 outputs
+ VMOVDQU (BX)(R14*1), Y6
+ VPSRLQ $0x04, Y6, Y7
+ VPAND Y3, Y6, Y6
+ VPAND Y3, Y7, Y7
+ VMOVDQU 1728(CX), Y4
+ VMOVDQU 1760(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y0, Y0
+ VMOVDQU 1792(CX), Y4
+ VMOVDQU 1824(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y1, Y1
+ VMOVDQU 1856(CX), Y4
+ VMOVDQU 1888(CX), Y5
+ VPSHUFB Y6, Y4, Y4
+ VPSHUFB Y7, Y5, Y5
+ VPXOR Y4, Y5, Y4
+ VPXOR Y4, Y2, Y2
+
+ // Store 3 outputs
+ MOVQ (DX), R15
+ VMOVDQU Y0, (R15)(R14*1)
+ MOVQ 24(DX), R15
+ VMOVDQU Y1, (R15)(R14*1)
+ MOVQ 48(DX), R15
+ VMOVDQU Y2, (R15)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_10x3_loop
+ VZEROUPPER
+
+mulAvxTwo_10x3_end:
+ RET
+
+// func mulAvxTwo_10x4(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x4(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 89 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x4_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), R13
+ MOVQ 216(BX), BX
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X4
+ VPBROADCASTB X4, Y4
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_10x4_loop:
+ // Clear 4 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+
+ // Load and process 32 bytes from input 0 to 4 outputs
+ VMOVDQU (BP)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU (CX), Y5
+ VMOVDQU 32(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 64(CX), Y5
+ VMOVDQU 96(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 128(CX), Y5
+ VMOVDQU 160(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 192(CX), Y5
+ VMOVDQU 224(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 1 to 4 outputs
+ VMOVDQU (SI)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 256(CX), Y5
+ VMOVDQU 288(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 320(CX), Y5
+ VMOVDQU 352(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 384(CX), Y5
+ VMOVDQU 416(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 448(CX), Y5
+ VMOVDQU 480(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 2 to 4 outputs
+ VMOVDQU (DI)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 512(CX), Y5
+ VMOVDQU 544(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 576(CX), Y5
+ VMOVDQU 608(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 640(CX), Y5
+ VMOVDQU 672(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 704(CX), Y5
+ VMOVDQU 736(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 3 to 4 outputs
+ VMOVDQU (R8)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 768(CX), Y5
+ VMOVDQU 800(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 832(CX), Y5
+ VMOVDQU 864(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 896(CX), Y5
+ VMOVDQU 928(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 960(CX), Y5
+ VMOVDQU 992(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 4 to 4 outputs
+ VMOVDQU (R9)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1024(CX), Y5
+ VMOVDQU 1056(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1088(CX), Y5
+ VMOVDQU 1120(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1152(CX), Y5
+ VMOVDQU 1184(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1216(CX), Y5
+ VMOVDQU 1248(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 5 to 4 outputs
+ VMOVDQU (R10)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1280(CX), Y5
+ VMOVDQU 1312(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1344(CX), Y5
+ VMOVDQU 1376(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1408(CX), Y5
+ VMOVDQU 1440(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1472(CX), Y5
+ VMOVDQU 1504(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 6 to 4 outputs
+ VMOVDQU (R11)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1536(CX), Y5
+ VMOVDQU 1568(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1600(CX), Y5
+ VMOVDQU 1632(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1664(CX), Y5
+ VMOVDQU 1696(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1728(CX), Y5
+ VMOVDQU 1760(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 7 to 4 outputs
+ VMOVDQU (R12)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 1792(CX), Y5
+ VMOVDQU 1824(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 1856(CX), Y5
+ VMOVDQU 1888(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 1920(CX), Y5
+ VMOVDQU 1952(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 1984(CX), Y5
+ VMOVDQU 2016(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 8 to 4 outputs
+ VMOVDQU (R13)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 2048(CX), Y5
+ VMOVDQU 2080(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 2112(CX), Y5
+ VMOVDQU 2144(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 2176(CX), Y5
+ VMOVDQU 2208(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 2240(CX), Y5
+ VMOVDQU 2272(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Load and process 32 bytes from input 9 to 4 outputs
+ VMOVDQU (BX)(R14*1), Y7
+ VPSRLQ $0x04, Y7, Y8
+ VPAND Y4, Y7, Y7
+ VPAND Y4, Y8, Y8
+ VMOVDQU 2304(CX), Y5
+ VMOVDQU 2336(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y0, Y0
+ VMOVDQU 2368(CX), Y5
+ VMOVDQU 2400(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y1, Y1
+ VMOVDQU 2432(CX), Y5
+ VMOVDQU 2464(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y2, Y2
+ VMOVDQU 2496(CX), Y5
+ VMOVDQU 2528(CX), Y6
+ VPSHUFB Y7, Y5, Y5
+ VPSHUFB Y8, Y6, Y6
+ VPXOR Y5, Y6, Y5
+ VPXOR Y5, Y3, Y3
+
+ // Store 4 outputs
+ MOVQ (DX), R15
+ VMOVDQU Y0, (R15)(R14*1)
+ MOVQ 24(DX), R15
+ VMOVDQU Y1, (R15)(R14*1)
+ MOVQ 48(DX), R15
+ VMOVDQU Y2, (R15)(R14*1)
+ MOVQ 72(DX), R15
+ VMOVDQU Y3, (R15)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_10x4_loop
+ VZEROUPPER
+
+mulAvxTwo_10x4_end:
+ RET
+
+// func mulAvxTwo_10x5(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x5(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 110 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x5_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), R13
+ MOVQ 216(BX), BX
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X5
+ VPBROADCASTB X5, Y5
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_10x5_loop:
+ // Clear 5 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+
+ // Load and process 32 bytes from input 0 to 5 outputs
+ VMOVDQU (BP)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU (CX), Y6
+ VMOVDQU 32(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 64(CX), Y6
+ VMOVDQU 96(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 128(CX), Y6
+ VMOVDQU 160(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 192(CX), Y6
+ VMOVDQU 224(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 256(CX), Y6
+ VMOVDQU 288(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 1 to 5 outputs
+ VMOVDQU (SI)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 320(CX), Y6
+ VMOVDQU 352(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 384(CX), Y6
+ VMOVDQU 416(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 448(CX), Y6
+ VMOVDQU 480(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 512(CX), Y6
+ VMOVDQU 544(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 576(CX), Y6
+ VMOVDQU 608(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 2 to 5 outputs
+ VMOVDQU (DI)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 640(CX), Y6
+ VMOVDQU 672(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 704(CX), Y6
+ VMOVDQU 736(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 768(CX), Y6
+ VMOVDQU 800(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 832(CX), Y6
+ VMOVDQU 864(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 896(CX), Y6
+ VMOVDQU 928(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 3 to 5 outputs
+ VMOVDQU (R8)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 960(CX), Y6
+ VMOVDQU 992(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1024(CX), Y6
+ VMOVDQU 1056(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1088(CX), Y6
+ VMOVDQU 1120(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1152(CX), Y6
+ VMOVDQU 1184(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1216(CX), Y6
+ VMOVDQU 1248(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 4 to 5 outputs
+ VMOVDQU (R9)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1280(CX), Y6
+ VMOVDQU 1312(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1344(CX), Y6
+ VMOVDQU 1376(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1408(CX), Y6
+ VMOVDQU 1440(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1472(CX), Y6
+ VMOVDQU 1504(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1536(CX), Y6
+ VMOVDQU 1568(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 5 to 5 outputs
+ VMOVDQU (R10)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1600(CX), Y6
+ VMOVDQU 1632(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1664(CX), Y6
+ VMOVDQU 1696(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 1728(CX), Y6
+ VMOVDQU 1760(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 1792(CX), Y6
+ VMOVDQU 1824(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 1856(CX), Y6
+ VMOVDQU 1888(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 6 to 5 outputs
+ VMOVDQU (R11)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 1920(CX), Y6
+ VMOVDQU 1952(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 1984(CX), Y6
+ VMOVDQU 2016(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2048(CX), Y6
+ VMOVDQU 2080(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2112(CX), Y6
+ VMOVDQU 2144(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2176(CX), Y6
+ VMOVDQU 2208(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 7 to 5 outputs
+ VMOVDQU (R12)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 2240(CX), Y6
+ VMOVDQU 2272(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 2304(CX), Y6
+ VMOVDQU 2336(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2368(CX), Y6
+ VMOVDQU 2400(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2432(CX), Y6
+ VMOVDQU 2464(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2496(CX), Y6
+ VMOVDQU 2528(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 8 to 5 outputs
+ VMOVDQU (R13)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 2560(CX), Y6
+ VMOVDQU 2592(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 2624(CX), Y6
+ VMOVDQU 2656(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 2688(CX), Y6
+ VMOVDQU 2720(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 2752(CX), Y6
+ VMOVDQU 2784(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 2816(CX), Y6
+ VMOVDQU 2848(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Load and process 32 bytes from input 9 to 5 outputs
+ VMOVDQU (BX)(R14*1), Y8
+ VPSRLQ $0x04, Y8, Y9
+ VPAND Y5, Y8, Y8
+ VPAND Y5, Y9, Y9
+ VMOVDQU 2880(CX), Y6
+ VMOVDQU 2912(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y0, Y0
+ VMOVDQU 2944(CX), Y6
+ VMOVDQU 2976(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y1, Y1
+ VMOVDQU 3008(CX), Y6
+ VMOVDQU 3040(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y2, Y2
+ VMOVDQU 3072(CX), Y6
+ VMOVDQU 3104(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y3, Y3
+ VMOVDQU 3136(CX), Y6
+ VMOVDQU 3168(CX), Y7
+ VPSHUFB Y8, Y6, Y6
+ VPSHUFB Y9, Y7, Y7
+ VPXOR Y6, Y7, Y6
+ VPXOR Y6, Y4, Y4
+
+ // Store 5 outputs
+ MOVQ (DX), R15
+ VMOVDQU Y0, (R15)(R14*1)
+ MOVQ 24(DX), R15
+ VMOVDQU Y1, (R15)(R14*1)
+ MOVQ 48(DX), R15
+ VMOVDQU Y2, (R15)(R14*1)
+ MOVQ 72(DX), R15
+ VMOVDQU Y3, (R15)(R14*1)
+ MOVQ 96(DX), R15
+ VMOVDQU Y4, (R15)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_10x5_loop
+ VZEROUPPER
+
+mulAvxTwo_10x5_end:
+ RET
+
+// func mulAvxTwo_10x6(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x6(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 131 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x6_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), R13
+ MOVQ 216(BX), BX
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X6
+ VPBROADCASTB X6, Y6
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_10x6_loop:
+ // Clear 6 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+
+ // Load and process 32 bytes from input 0 to 6 outputs
+ VMOVDQU (BP)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU (CX), Y7
+ VMOVDQU 32(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 64(CX), Y7
+ VMOVDQU 96(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 128(CX), Y7
+ VMOVDQU 160(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 192(CX), Y7
+ VMOVDQU 224(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 256(CX), Y7
+ VMOVDQU 288(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 320(CX), Y7
+ VMOVDQU 352(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 1 to 6 outputs
+ VMOVDQU (SI)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 384(CX), Y7
+ VMOVDQU 416(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 448(CX), Y7
+ VMOVDQU 480(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 512(CX), Y7
+ VMOVDQU 544(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 576(CX), Y7
+ VMOVDQU 608(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 640(CX), Y7
+ VMOVDQU 672(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 704(CX), Y7
+ VMOVDQU 736(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 2 to 6 outputs
+ VMOVDQU (DI)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 768(CX), Y7
+ VMOVDQU 800(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 832(CX), Y7
+ VMOVDQU 864(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 896(CX), Y7
+ VMOVDQU 928(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 960(CX), Y7
+ VMOVDQU 992(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1024(CX), Y7
+ VMOVDQU 1056(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1088(CX), Y7
+ VMOVDQU 1120(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 3 to 6 outputs
+ VMOVDQU (R8)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1152(CX), Y7
+ VMOVDQU 1184(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1216(CX), Y7
+ VMOVDQU 1248(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1280(CX), Y7
+ VMOVDQU 1312(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1344(CX), Y7
+ VMOVDQU 1376(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1408(CX), Y7
+ VMOVDQU 1440(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1472(CX), Y7
+ VMOVDQU 1504(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 4 to 6 outputs
+ VMOVDQU (R9)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1536(CX), Y7
+ VMOVDQU 1568(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1600(CX), Y7
+ VMOVDQU 1632(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 1664(CX), Y7
+ VMOVDQU 1696(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 1728(CX), Y7
+ VMOVDQU 1760(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 1792(CX), Y7
+ VMOVDQU 1824(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 1856(CX), Y7
+ VMOVDQU 1888(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 5 to 6 outputs
+ VMOVDQU (R10)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 1920(CX), Y7
+ VMOVDQU 1952(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 1984(CX), Y7
+ VMOVDQU 2016(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2048(CX), Y7
+ VMOVDQU 2080(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2112(CX), Y7
+ VMOVDQU 2144(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2176(CX), Y7
+ VMOVDQU 2208(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2240(CX), Y7
+ VMOVDQU 2272(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 6 to 6 outputs
+ VMOVDQU (R11)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 2304(CX), Y7
+ VMOVDQU 2336(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 2368(CX), Y7
+ VMOVDQU 2400(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2432(CX), Y7
+ VMOVDQU 2464(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2496(CX), Y7
+ VMOVDQU 2528(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2560(CX), Y7
+ VMOVDQU 2592(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 2624(CX), Y7
+ VMOVDQU 2656(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 7 to 6 outputs
+ VMOVDQU (R12)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 2688(CX), Y7
+ VMOVDQU 2720(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 2752(CX), Y7
+ VMOVDQU 2784(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 2816(CX), Y7
+ VMOVDQU 2848(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 2880(CX), Y7
+ VMOVDQU 2912(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 2944(CX), Y7
+ VMOVDQU 2976(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 3008(CX), Y7
+ VMOVDQU 3040(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 8 to 6 outputs
+ VMOVDQU (R13)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 3072(CX), Y7
+ VMOVDQU 3104(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 3136(CX), Y7
+ VMOVDQU 3168(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 3200(CX), Y7
+ VMOVDQU 3232(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 3264(CX), Y7
+ VMOVDQU 3296(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 3328(CX), Y7
+ VMOVDQU 3360(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 3392(CX), Y7
+ VMOVDQU 3424(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Load and process 32 bytes from input 9 to 6 outputs
+ VMOVDQU (BX)(R14*1), Y9
+ VPSRLQ $0x04, Y9, Y10
+ VPAND Y6, Y9, Y9
+ VPAND Y6, Y10, Y10
+ VMOVDQU 3456(CX), Y7
+ VMOVDQU 3488(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y0, Y0
+ VMOVDQU 3520(CX), Y7
+ VMOVDQU 3552(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y1, Y1
+ VMOVDQU 3584(CX), Y7
+ VMOVDQU 3616(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y2, Y2
+ VMOVDQU 3648(CX), Y7
+ VMOVDQU 3680(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y3, Y3
+ VMOVDQU 3712(CX), Y7
+ VMOVDQU 3744(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y4, Y4
+ VMOVDQU 3776(CX), Y7
+ VMOVDQU 3808(CX), Y8
+ VPSHUFB Y9, Y7, Y7
+ VPSHUFB Y10, Y8, Y8
+ VPXOR Y7, Y8, Y7
+ VPXOR Y7, Y5, Y5
+
+ // Store 6 outputs
+ MOVQ (DX), R15
+ VMOVDQU Y0, (R15)(R14*1)
+ MOVQ 24(DX), R15
+ VMOVDQU Y1, (R15)(R14*1)
+ MOVQ 48(DX), R15
+ VMOVDQU Y2, (R15)(R14*1)
+ MOVQ 72(DX), R15
+ VMOVDQU Y3, (R15)(R14*1)
+ MOVQ 96(DX), R15
+ VMOVDQU Y4, (R15)(R14*1)
+ MOVQ 120(DX), R15
+ VMOVDQU Y5, (R15)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_10x6_loop
+ VZEROUPPER
+
+mulAvxTwo_10x6_end:
+ RET
+
+// func mulAvxTwo_10x7(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x7(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 152 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x7_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), R13
+ MOVQ 216(BX), BX
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X7
+ VPBROADCASTB X7, Y7
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_10x7_loop:
+ // Clear 7 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+
+ // Load and process 32 bytes from input 0 to 7 outputs
+ VMOVDQU (BP)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU (CX), Y8
+ VMOVDQU 32(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 64(CX), Y8
+ VMOVDQU 96(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 128(CX), Y8
+ VMOVDQU 160(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 192(CX), Y8
+ VMOVDQU 224(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 256(CX), Y8
+ VMOVDQU 288(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 320(CX), Y8
+ VMOVDQU 352(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 384(CX), Y8
+ VMOVDQU 416(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 1 to 7 outputs
+ VMOVDQU (SI)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 448(CX), Y8
+ VMOVDQU 480(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 512(CX), Y8
+ VMOVDQU 544(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 576(CX), Y8
+ VMOVDQU 608(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 640(CX), Y8
+ VMOVDQU 672(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 704(CX), Y8
+ VMOVDQU 736(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 768(CX), Y8
+ VMOVDQU 800(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 832(CX), Y8
+ VMOVDQU 864(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 2 to 7 outputs
+ VMOVDQU (DI)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 896(CX), Y8
+ VMOVDQU 928(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 960(CX), Y8
+ VMOVDQU 992(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1024(CX), Y8
+ VMOVDQU 1056(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1088(CX), Y8
+ VMOVDQU 1120(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1152(CX), Y8
+ VMOVDQU 1184(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1216(CX), Y8
+ VMOVDQU 1248(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1280(CX), Y8
+ VMOVDQU 1312(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 3 to 7 outputs
+ VMOVDQU (R8)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1344(CX), Y8
+ VMOVDQU 1376(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1408(CX), Y8
+ VMOVDQU 1440(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1472(CX), Y8
+ VMOVDQU 1504(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1536(CX), Y8
+ VMOVDQU 1568(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 1600(CX), Y8
+ VMOVDQU 1632(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 1664(CX), Y8
+ VMOVDQU 1696(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 1728(CX), Y8
+ VMOVDQU 1760(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 4 to 7 outputs
+ VMOVDQU (R9)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 1792(CX), Y8
+ VMOVDQU 1824(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 1856(CX), Y8
+ VMOVDQU 1888(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 1920(CX), Y8
+ VMOVDQU 1952(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 1984(CX), Y8
+ VMOVDQU 2016(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2048(CX), Y8
+ VMOVDQU 2080(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2112(CX), Y8
+ VMOVDQU 2144(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2176(CX), Y8
+ VMOVDQU 2208(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 5 to 7 outputs
+ VMOVDQU (R10)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2240(CX), Y8
+ VMOVDQU 2272(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2304(CX), Y8
+ VMOVDQU 2336(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2368(CX), Y8
+ VMOVDQU 2400(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2432(CX), Y8
+ VMOVDQU 2464(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2496(CX), Y8
+ VMOVDQU 2528(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 2560(CX), Y8
+ VMOVDQU 2592(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 2624(CX), Y8
+ VMOVDQU 2656(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 6 to 7 outputs
+ VMOVDQU (R11)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 2688(CX), Y8
+ VMOVDQU 2720(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 2752(CX), Y8
+ VMOVDQU 2784(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 2816(CX), Y8
+ VMOVDQU 2848(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 2880(CX), Y8
+ VMOVDQU 2912(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 2944(CX), Y8
+ VMOVDQU 2976(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3008(CX), Y8
+ VMOVDQU 3040(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3072(CX), Y8
+ VMOVDQU 3104(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 7 to 7 outputs
+ VMOVDQU (R12)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 3136(CX), Y8
+ VMOVDQU 3168(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 3200(CX), Y8
+ VMOVDQU 3232(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 3264(CX), Y8
+ VMOVDQU 3296(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 3328(CX), Y8
+ VMOVDQU 3360(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 3392(CX), Y8
+ VMOVDQU 3424(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3456(CX), Y8
+ VMOVDQU 3488(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3520(CX), Y8
+ VMOVDQU 3552(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 8 to 7 outputs
+ VMOVDQU (R13)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 3584(CX), Y8
+ VMOVDQU 3616(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 3648(CX), Y8
+ VMOVDQU 3680(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 3712(CX), Y8
+ VMOVDQU 3744(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 3776(CX), Y8
+ VMOVDQU 3808(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 3840(CX), Y8
+ VMOVDQU 3872(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 3904(CX), Y8
+ VMOVDQU 3936(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 3968(CX), Y8
+ VMOVDQU 4000(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Load and process 32 bytes from input 9 to 7 outputs
+ VMOVDQU (BX)(R14*1), Y10
+ VPSRLQ $0x04, Y10, Y11
+ VPAND Y7, Y10, Y10
+ VPAND Y7, Y11, Y11
+ VMOVDQU 4032(CX), Y8
+ VMOVDQU 4064(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y0, Y0
+ VMOVDQU 4096(CX), Y8
+ VMOVDQU 4128(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y1, Y1
+ VMOVDQU 4160(CX), Y8
+ VMOVDQU 4192(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y2, Y2
+ VMOVDQU 4224(CX), Y8
+ VMOVDQU 4256(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y3, Y3
+ VMOVDQU 4288(CX), Y8
+ VMOVDQU 4320(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y4, Y4
+ VMOVDQU 4352(CX), Y8
+ VMOVDQU 4384(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y5, Y5
+ VMOVDQU 4416(CX), Y8
+ VMOVDQU 4448(CX), Y9
+ VPSHUFB Y10, Y8, Y8
+ VPSHUFB Y11, Y9, Y9
+ VPXOR Y8, Y9, Y8
+ VPXOR Y8, Y6, Y6
+
+ // Store 7 outputs
+ MOVQ (DX), R15
+ VMOVDQU Y0, (R15)(R14*1)
+ MOVQ 24(DX), R15
+ VMOVDQU Y1, (R15)(R14*1)
+ MOVQ 48(DX), R15
+ VMOVDQU Y2, (R15)(R14*1)
+ MOVQ 72(DX), R15
+ VMOVDQU Y3, (R15)(R14*1)
+ MOVQ 96(DX), R15
+ VMOVDQU Y4, (R15)(R14*1)
+ MOVQ 120(DX), R15
+ VMOVDQU Y5, (R15)(R14*1)
+ MOVQ 144(DX), R15
+ VMOVDQU Y6, (R15)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_10x7_loop
+ VZEROUPPER
+
+mulAvxTwo_10x7_end:
+ RET
+
+// func mulAvxTwo_10x8(matrix []byte, in [][]byte, out [][]byte, start int, n int)
+// Requires: AVX, AVX2, SSE2
+TEXT ·mulAvxTwo_10x8(SB), $0-88
+ // Loading no tables to registers
+ // Full registers estimated 173 YMM used
+ MOVQ n+80(FP), AX
+ MOVQ matrix_base+0(FP), CX
+ SHRQ $0x05, AX
+ TESTQ AX, AX
+ JZ mulAvxTwo_10x8_end
+ MOVQ out_base+48(FP), DX
+ MOVQ in_base+24(FP), BX
+ MOVQ (BX), BP
+ MOVQ 24(BX), SI
+ MOVQ 48(BX), DI
+ MOVQ 72(BX), R8
+ MOVQ 96(BX), R9
+ MOVQ 120(BX), R10
+ MOVQ 144(BX), R11
+ MOVQ 168(BX), R12
+ MOVQ 192(BX), R13
+ MOVQ 216(BX), BX
+ MOVQ $0x0000000f, R14
+ MOVQ R14, X8
+ VPBROADCASTB X8, Y8
+ MOVQ start+72(FP), R14
+
+mulAvxTwo_10x8_loop:
+ // Clear 8 outputs
+ VPXOR Y0, Y0, Y0
+ VPXOR Y1, Y1, Y1
+ VPXOR Y2, Y2, Y2
+ VPXOR Y3, Y3, Y3
+ VPXOR Y4, Y4, Y4
+ VPXOR Y5, Y5, Y5
+ VPXOR Y6, Y6, Y6
+ VPXOR Y7, Y7, Y7
+
+ // Load and process 32 bytes from input 0 to 8 outputs
+ VMOVDQU (BP)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU (CX), Y9
+ VMOVDQU 32(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 64(CX), Y9
+ VMOVDQU 96(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 128(CX), Y9
+ VMOVDQU 160(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 192(CX), Y9
+ VMOVDQU 224(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 256(CX), Y9
+ VMOVDQU 288(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 320(CX), Y9
+ VMOVDQU 352(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 384(CX), Y9
+ VMOVDQU 416(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 448(CX), Y9
+ VMOVDQU 480(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 1 to 8 outputs
+ VMOVDQU (SI)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 512(CX), Y9
+ VMOVDQU 544(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 576(CX), Y9
+ VMOVDQU 608(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 640(CX), Y9
+ VMOVDQU 672(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 704(CX), Y9
+ VMOVDQU 736(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 768(CX), Y9
+ VMOVDQU 800(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 832(CX), Y9
+ VMOVDQU 864(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 896(CX), Y9
+ VMOVDQU 928(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 960(CX), Y9
+ VMOVDQU 992(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 2 to 8 outputs
+ VMOVDQU (DI)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1024(CX), Y9
+ VMOVDQU 1056(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1088(CX), Y9
+ VMOVDQU 1120(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1152(CX), Y9
+ VMOVDQU 1184(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1216(CX), Y9
+ VMOVDQU 1248(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1280(CX), Y9
+ VMOVDQU 1312(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1344(CX), Y9
+ VMOVDQU 1376(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1408(CX), Y9
+ VMOVDQU 1440(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1472(CX), Y9
+ VMOVDQU 1504(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 3 to 8 outputs
+ VMOVDQU (R8)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 1536(CX), Y9
+ VMOVDQU 1568(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 1600(CX), Y9
+ VMOVDQU 1632(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 1664(CX), Y9
+ VMOVDQU 1696(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 1728(CX), Y9
+ VMOVDQU 1760(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 1792(CX), Y9
+ VMOVDQU 1824(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 1856(CX), Y9
+ VMOVDQU 1888(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 1920(CX), Y9
+ VMOVDQU 1952(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 1984(CX), Y9
+ VMOVDQU 2016(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 4 to 8 outputs
+ VMOVDQU (R9)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2048(CX), Y9
+ VMOVDQU 2080(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2112(CX), Y9
+ VMOVDQU 2144(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2176(CX), Y9
+ VMOVDQU 2208(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2240(CX), Y9
+ VMOVDQU 2272(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2304(CX), Y9
+ VMOVDQU 2336(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2368(CX), Y9
+ VMOVDQU 2400(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2432(CX), Y9
+ VMOVDQU 2464(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 2496(CX), Y9
+ VMOVDQU 2528(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 5 to 8 outputs
+ VMOVDQU (R10)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 2560(CX), Y9
+ VMOVDQU 2592(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 2624(CX), Y9
+ VMOVDQU 2656(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 2688(CX), Y9
+ VMOVDQU 2720(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 2752(CX), Y9
+ VMOVDQU 2784(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 2816(CX), Y9
+ VMOVDQU 2848(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 2880(CX), Y9
+ VMOVDQU 2912(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 2944(CX), Y9
+ VMOVDQU 2976(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3008(CX), Y9
+ VMOVDQU 3040(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 6 to 8 outputs
+ VMOVDQU (R11)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 3072(CX), Y9
+ VMOVDQU 3104(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 3136(CX), Y9
+ VMOVDQU 3168(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 3200(CX), Y9
+ VMOVDQU 3232(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 3264(CX), Y9
+ VMOVDQU 3296(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 3328(CX), Y9
+ VMOVDQU 3360(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 3392(CX), Y9
+ VMOVDQU 3424(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 3456(CX), Y9
+ VMOVDQU 3488(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 3520(CX), Y9
+ VMOVDQU 3552(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 7 to 8 outputs
+ VMOVDQU (R12)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 3584(CX), Y9
+ VMOVDQU 3616(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 3648(CX), Y9
+ VMOVDQU 3680(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 3712(CX), Y9
+ VMOVDQU 3744(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 3776(CX), Y9
+ VMOVDQU 3808(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 3840(CX), Y9
+ VMOVDQU 3872(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 3904(CX), Y9
+ VMOVDQU 3936(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 3968(CX), Y9
+ VMOVDQU 4000(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 4032(CX), Y9
+ VMOVDQU 4064(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 8 to 8 outputs
+ VMOVDQU (R13)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 4096(CX), Y9
+ VMOVDQU 4128(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 4160(CX), Y9
+ VMOVDQU 4192(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 4224(CX), Y9
+ VMOVDQU 4256(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 4288(CX), Y9
+ VMOVDQU 4320(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 4352(CX), Y9
+ VMOVDQU 4384(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 4416(CX), Y9
+ VMOVDQU 4448(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 4480(CX), Y9
+ VMOVDQU 4512(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 4544(CX), Y9
+ VMOVDQU 4576(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Load and process 32 bytes from input 9 to 8 outputs
+ VMOVDQU (BX)(R14*1), Y11
+ VPSRLQ $0x04, Y11, Y12
+ VPAND Y8, Y11, Y11
+ VPAND Y8, Y12, Y12
+ VMOVDQU 4608(CX), Y9
+ VMOVDQU 4640(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y0, Y0
+ VMOVDQU 4672(CX), Y9
+ VMOVDQU 4704(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y1, Y1
+ VMOVDQU 4736(CX), Y9
+ VMOVDQU 4768(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y2, Y2
+ VMOVDQU 4800(CX), Y9
+ VMOVDQU 4832(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y3, Y3
+ VMOVDQU 4864(CX), Y9
+ VMOVDQU 4896(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y4, Y4
+ VMOVDQU 4928(CX), Y9
+ VMOVDQU 4960(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y5, Y5
+ VMOVDQU 4992(CX), Y9
+ VMOVDQU 5024(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y6, Y6
+ VMOVDQU 5056(CX), Y9
+ VMOVDQU 5088(CX), Y10
+ VPSHUFB Y11, Y9, Y9
+ VPSHUFB Y12, Y10, Y10
+ VPXOR Y9, Y10, Y9
+ VPXOR Y9, Y7, Y7
+
+ // Store 8 outputs
+ MOVQ (DX), R15
+ VMOVDQU Y0, (R15)(R14*1)
+ MOVQ 24(DX), R15
+ VMOVDQU Y1, (R15)(R14*1)
+ MOVQ 48(DX), R15
+ VMOVDQU Y2, (R15)(R14*1)
+ MOVQ 72(DX), R15
+ VMOVDQU Y3, (R15)(R14*1)
+ MOVQ 96(DX), R15
+ VMOVDQU Y4, (R15)(R14*1)
+ MOVQ 120(DX), R15
+ VMOVDQU Y5, (R15)(R14*1)
+ MOVQ 144(DX), R15
+ VMOVDQU Y6, (R15)(R14*1)
+ MOVQ 168(DX), R15
+ VMOVDQU Y7, (R15)(R14*1)
+
+ // Prepare for next loop
+ ADDQ $0x20, R14
+ DECQ AX
+ JNZ mulAvxTwo_10x8_loop
+ VZEROUPPER
+
+mulAvxTwo_10x8_end:
+ RET
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_gen_none.go b/vendor/github.com/klauspost/reedsolomon/galois_gen_none.go
new file mode 100644
index 0000000..b4917bc
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_gen_none.go
@@ -0,0 +1,11 @@
+//+build !amd64 noasm appengine gccgo nogen
+
+package reedsolomon
+
+const maxAvx2Inputs = 0
+const maxAvx2Outputs = 0
+const avx2CodeGen = false
+
+func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
+ panic("avx2 codegen not available")
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_gen_switch_amd64.go b/vendor/github.com/klauspost/reedsolomon/galois_gen_switch_amd64.go
new file mode 100644
index 0000000..0b49a1e
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_gen_switch_amd64.go
@@ -0,0 +1,293 @@
+// Code generated by command: go generate gen.go. DO NOT EDIT.
+
+// +build !appengine
+// +build !noasm
+// +build gc
+// +build !nogen
+
+package reedsolomon
+
+import "fmt"
+
+const avx2CodeGen = true
+const maxAvx2Inputs = 10
+const maxAvx2Outputs = 8
+
+func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
+ n := stop - start
+ n = (n >> 5) << 5
+
+ switch len(in) {
+ case 1:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_1x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_1x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_1x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_1x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_1x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_1x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_1x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_1x8(matrix, in, out, start, n)
+ return n
+ }
+ case 2:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_2x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_2x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_2x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_2x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_2x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_2x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_2x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_2x8(matrix, in, out, start, n)
+ return n
+ }
+ case 3:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_3x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_3x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_3x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_3x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_3x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_3x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_3x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_3x8(matrix, in, out, start, n)
+ return n
+ }
+ case 4:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_4x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_4x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_4x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_4x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_4x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_4x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_4x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_4x8(matrix, in, out, start, n)
+ return n
+ }
+ case 5:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_5x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_5x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_5x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_5x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_5x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_5x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_5x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_5x8(matrix, in, out, start, n)
+ return n
+ }
+ case 6:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_6x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_6x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_6x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_6x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_6x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_6x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_6x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_6x8(matrix, in, out, start, n)
+ return n
+ }
+ case 7:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_7x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_7x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_7x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_7x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_7x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_7x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_7x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_7x8(matrix, in, out, start, n)
+ return n
+ }
+ case 8:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_8x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_8x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_8x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_8x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_8x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_8x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_8x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_8x8(matrix, in, out, start, n)
+ return n
+ }
+ case 9:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_9x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_9x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_9x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_9x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_9x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_9x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_9x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_9x8(matrix, in, out, start, n)
+ return n
+ }
+ case 10:
+ switch len(out) {
+ case 1:
+ mulAvxTwo_10x1(matrix, in, out, start, n)
+ return n
+ case 2:
+ mulAvxTwo_10x2(matrix, in, out, start, n)
+ return n
+ case 3:
+ mulAvxTwo_10x3(matrix, in, out, start, n)
+ return n
+ case 4:
+ mulAvxTwo_10x4(matrix, in, out, start, n)
+ return n
+ case 5:
+ mulAvxTwo_10x5(matrix, in, out, start, n)
+ return n
+ case 6:
+ mulAvxTwo_10x6(matrix, in, out, start, n)
+ return n
+ case 7:
+ mulAvxTwo_10x7(matrix, in, out, start, n)
+ return n
+ case 8:
+ mulAvxTwo_10x8(matrix, in, out, start, n)
+ return n
+ }
+ }
+ panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_noasm.go b/vendor/github.com/klauspost/reedsolomon/galois_noasm.go
new file mode 100644
index 0000000..1d00e06
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_noasm.go
@@ -0,0 +1,44 @@
+//+build !amd64 noasm appengine gccgo
+//+build !arm64 noasm appengine gccgo
+//+build !ppc64le noasm appengine gccgo
+
+// Copyright 2015, Klaus Post, see LICENSE for details.
+
+package reedsolomon
+
+func galMulSlice(c byte, in, out []byte, o *options) {
+ out = out[:len(in)]
+ if c == 1 {
+ copy(out, in)
+ return
+ }
+ mt := mulTable[c][:256]
+ for n, input := range in {
+ out[n] = mt[input]
+ }
+}
+
+func galMulSliceXor(c byte, in, out []byte, o *options) {
+ out = out[:len(in)]
+ if c == 1 {
+ for n, input := range in {
+ out[n] ^= input
+ }
+ return
+ }
+ mt := mulTable[c][:256]
+ for n, input := range in {
+ out[n] ^= mt[input]
+ }
+}
+
+// slice galois add
+func sliceXor(in, out []byte, o *options) {
+ for n, input := range in {
+ out[n] ^= input
+ }
+}
+
+func init() {
+ defaultOptions.useAVX512 = false
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_notamd64.go b/vendor/github.com/klauspost/reedsolomon/galois_notamd64.go
new file mode 100644
index 0000000..bd15e3a
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_notamd64.go
@@ -0,0 +1,13 @@
+//+build !amd64 noasm appengine gccgo
+
+// Copyright 2020, Klaus Post, see LICENSE for details.
+
+package reedsolomon
+
+func (r *reedSolomon) codeSomeShardsAvx512(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
+ panic("codeSomeShardsAvx512 should not be called if built without asm")
+}
+
+func (r *reedSolomon) codeSomeShardsAvx512P(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
+ panic("codeSomeShardsAvx512P should not be called if built without asm")
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_ppc64le.go b/vendor/github.com/klauspost/reedsolomon/galois_ppc64le.go
new file mode 100644
index 0000000..70f93d6
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_ppc64le.go
@@ -0,0 +1,75 @@
+//+build !noasm
+//+build !appengine
+//+build !gccgo
+
+// Copyright 2015, Klaus Post, see LICENSE for details.
+// Copyright 2018, Minio, Inc.
+
+package reedsolomon
+
+//go:noescape
+func galMulPpc(low, high, in, out []byte)
+
+//go:noescape
+func galMulPpcXor(low, high, in, out []byte)
+
+// This is what the assembler routines do in blocks of 16 bytes:
+/*
+func galMulPpc(low, high, in, out []byte) {
+ for n, input := range in {
+ l := input & 0xf
+ h := input >> 4
+ out[n] = low[l] ^ high[h]
+ }
+}
+func galMulPpcXor(low, high, in, out []byte) {
+ for n, input := range in {
+ l := input & 0xf
+ h := input >> 4
+ out[n] ^= low[l] ^ high[h]
+ }
+}
+*/
+
+func galMulSlice(c byte, in, out []byte, o *options) {
+ if c == 1 {
+ copy(out, in)
+ return
+ }
+ done := (len(in) >> 4) << 4
+ if done > 0 {
+ galMulPpc(mulTableLow[c][:], mulTableHigh[c][:], in[:done], out)
+ }
+ remain := len(in) - done
+ if remain > 0 {
+ mt := mulTable[c][:256]
+ for i := done; i < len(in); i++ {
+ out[i] = mt[in[i]]
+ }
+ }
+}
+
+func galMulSliceXor(c byte, in, out []byte, o *options) {
+ if c == 1 {
+ sliceXor(in, out, o)
+ return
+ }
+ done := (len(in) >> 4) << 4
+ if done > 0 {
+ galMulPpcXor(mulTableLow[c][:], mulTableHigh[c][:], in[:done], out)
+ }
+ remain := len(in) - done
+ if remain > 0 {
+ mt := mulTable[c][:256]
+ for i := done; i < len(in); i++ {
+ out[i] ^= mt[in[i]]
+ }
+ }
+}
+
+// slice galois add
+func sliceXor(in, out []byte, o *options) {
+ for n, input := range in {
+ out[n] ^= input
+ }
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/galois_ppc64le.s b/vendor/github.com/klauspost/reedsolomon/galois_ppc64le.s
new file mode 100644
index 0000000..8838f0c
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/galois_ppc64le.s
@@ -0,0 +1,124 @@
+//+build !noasm !appengine !gccgo
+
+// Copyright 2015, Klaus Post, see LICENSE for details.
+// Copyright 2018, Minio, Inc.
+
+#include "textflag.h"
+
+#define LOW R3
+#define HIGH R4
+#define IN R5
+#define LEN R6
+#define OUT R7
+#define CONSTANTS R8
+#define OFFSET R9
+#define OFFSET1 R10
+#define OFFSET2 R11
+
+#define X6 VS34
+#define X6_ V2
+#define X7 VS35
+#define X7_ V3
+#define MSG VS36
+#define MSG_ V4
+#define MSG_HI VS37
+#define MSG_HI_ V5
+#define RESULT VS38
+#define RESULT_ V6
+#define ROTATE VS39
+#define ROTATE_ V7
+#define MASK VS40
+#define MASK_ V8
+#define FLIP VS41
+#define FLIP_ V9
+
+// func galMulPpc(low, high, in, out []byte)
+TEXT ·galMulPpc(SB), NOFRAME|NOSPLIT, $0-96
+ MOVD low+0(FP), LOW
+ MOVD high+24(FP), HIGH
+ MOVD in+48(FP), IN
+ MOVD in_len+56(FP), LEN
+ MOVD out+72(FP), OUT
+
+ MOVD $16, OFFSET1
+ MOVD $32, OFFSET2
+
+ MOVD $·constants(SB), CONSTANTS
+ LXVD2X (CONSTANTS)(R0), ROTATE
+ LXVD2X (CONSTANTS)(OFFSET1), MASK
+ LXVD2X (CONSTANTS)(OFFSET2), FLIP
+
+ LXVD2X (LOW)(R0), X6
+ LXVD2X (HIGH)(R0), X7
+ VPERM X6_, V31, FLIP_, X6_
+ VPERM X7_, V31, FLIP_, X7_
+
+ MOVD $0, OFFSET
+
+loop:
+ LXVD2X (IN)(OFFSET), MSG
+
+ VSRB MSG_, ROTATE_, MSG_HI_
+ VAND MSG_, MASK_, MSG_
+ VPERM X6_, V31, MSG_, MSG_
+ VPERM X7_, V31, MSG_HI_, MSG_HI_
+
+ VXOR MSG_, MSG_HI_, MSG_
+
+ STXVD2X MSG, (OUT)(OFFSET)
+
+ ADD $16, OFFSET, OFFSET
+ CMP LEN, OFFSET
+ BGT loop
+ RET
+
+// func galMulPpcXorlow, high, in, out []byte)
+TEXT ·galMulPpcXor(SB), NOFRAME|NOSPLIT, $0-96
+ MOVD low+0(FP), LOW
+ MOVD high+24(FP), HIGH
+ MOVD in+48(FP), IN
+ MOVD in_len+56(FP), LEN
+ MOVD out+72(FP), OUT
+
+ MOVD $16, OFFSET1
+ MOVD $32, OFFSET2
+
+ MOVD $·constants(SB), CONSTANTS
+ LXVD2X (CONSTANTS)(R0), ROTATE
+ LXVD2X (CONSTANTS)(OFFSET1), MASK
+ LXVD2X (CONSTANTS)(OFFSET2), FLIP
+
+ LXVD2X (LOW)(R0), X6
+ LXVD2X (HIGH)(R0), X7
+ VPERM X6_, V31, FLIP_, X6_
+ VPERM X7_, V31, FLIP_, X7_
+
+ MOVD $0, OFFSET
+
+loopXor:
+ LXVD2X (IN)(OFFSET), MSG
+ LXVD2X (OUT)(OFFSET), RESULT
+
+ VSRB MSG_, ROTATE_, MSG_HI_
+ VAND MSG_, MASK_, MSG_
+ VPERM X6_, V31, MSG_, MSG_
+ VPERM X7_, V31, MSG_HI_, MSG_HI_
+
+ VXOR MSG_, MSG_HI_, MSG_
+ VXOR MSG_, RESULT_, RESULT_
+
+ STXVD2X RESULT, (OUT)(OFFSET)
+
+ ADD $16, OFFSET, OFFSET
+ CMP LEN, OFFSET
+ BGT loopXor
+ RET
+
+DATA ·constants+0x0(SB)/8, $0x0404040404040404
+DATA ·constants+0x8(SB)/8, $0x0404040404040404
+DATA ·constants+0x10(SB)/8, $0x0f0f0f0f0f0f0f0f
+DATA ·constants+0x18(SB)/8, $0x0f0f0f0f0f0f0f0f
+DATA ·constants+0x20(SB)/8, $0x0706050403020100
+DATA ·constants+0x28(SB)/8, $0x0f0e0d0c0b0a0908
+
+GLOBL ·constants(SB), 8, $48
diff --git a/vendor/github.com/klauspost/reedsolomon/gen.go b/vendor/github.com/klauspost/reedsolomon/gen.go
new file mode 100644
index 0000000..6fc545c
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/gen.go
@@ -0,0 +1,249 @@
+//+build generate
+
+//go:generate go run gen.go -out galois_gen_amd64.s -stubs galois_gen_amd64.go
+//go:generate gofmt -w galois_gen_switch_amd64.go
+
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+
+ . "github.com/mmcloughlin/avo/build"
+ "github.com/mmcloughlin/avo/buildtags"
+ . "github.com/mmcloughlin/avo/operand"
+ "github.com/mmcloughlin/avo/reg"
+)
+
+// Technically we can do slightly bigger, but we stay reasonable.
+const inputMax = 10
+const outputMax = 8
+
+var switchDefs [inputMax][outputMax]string
+var switchDefsX [inputMax][outputMax]string
+
+const perLoopBits = 5
+const perLoop = 1 << perLoopBits
+
+func main() {
+ Constraint(buildtags.Not("appengine").ToConstraint())
+ Constraint(buildtags.Not("noasm").ToConstraint())
+ Constraint(buildtags.Not("nogen").ToConstraint())
+ Constraint(buildtags.Term("gc").ToConstraint())
+
+ for i := 1; i <= inputMax; i++ {
+ for j := 1; j <= outputMax; j++ {
+ //genMulAvx2(fmt.Sprintf("mulAvxTwoXor_%dx%d", i, j), i, j, true)
+ genMulAvx2(fmt.Sprintf("mulAvxTwo_%dx%d", i, j), i, j, false)
+ }
+ }
+ f, err := os.Create("galois_gen_switch_amd64.go")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ w := bufio.NewWriter(f)
+ defer w.Flush()
+ w.WriteString(`// Code generated by command: go generate ` + os.Getenv("GOFILE") + `. DO NOT EDIT.
+
+// +build !appengine
+// +build !noasm
+// +build gc
+// +build !nogen
+
+package reedsolomon
+
+import "fmt"
+
+`)
+
+ w.WriteString("const avx2CodeGen = true\n")
+ w.WriteString(fmt.Sprintf("const maxAvx2Inputs = %d\nconst maxAvx2Outputs = %d\n", inputMax, outputMax))
+ w.WriteString(`
+
+func galMulSlicesAvx2(matrix []byte, in, out [][]byte, start, stop int) int {
+ n := stop-start
+`)
+
+ w.WriteString(fmt.Sprintf("n = (n>>%d)<<%d\n\n", perLoopBits, perLoopBits))
+ w.WriteString(`switch len(in) {
+`)
+ for in, defs := range switchDefs[:] {
+ w.WriteString(fmt.Sprintf(" case %d:\n switch len(out) {\n", in+1))
+ for out, def := range defs[:] {
+ w.WriteString(fmt.Sprintf(" case %d:\n", out+1))
+ w.WriteString(def)
+ }
+ w.WriteString("}\n")
+ }
+ w.WriteString(`}
+ panic(fmt.Sprintf("unhandled size: %dx%d", len(in), len(out)))
+}
+`)
+ Generate()
+}
+
+func genMulAvx2(name string, inputs int, outputs int, xor bool) {
+ total := inputs * outputs
+
+ doc := []string{
+ fmt.Sprintf("%s takes %d inputs and produces %d outputs.", name, inputs, outputs),
+ }
+ if !xor {
+ doc = append(doc, "The output is initialized to 0.")
+ }
+
+ // Load shuffle masks on every use.
+ var loadNone bool
+ // Use registers for destination registers.
+ var regDst = true
+
+ // lo, hi, 1 in, 1 out, 2 tmp, 1 mask
+ est := total*2 + outputs + 5
+ if outputs == 1 {
+ // We don't need to keep a copy of the input if only 1 output.
+ est -= 2
+ }
+
+ if est > 16 {
+ loadNone = true
+ // We run out of GP registers first, now.
+ if inputs+outputs > 12 {
+ regDst = false
+ }
+ }
+
+ TEXT(name, 0, fmt.Sprintf("func(matrix []byte, in [][]byte, out [][]byte, start, n int)"))
+
+ // SWITCH DEFINITION:
+ s := fmt.Sprintf(" mulAvxTwo_%dx%d(matrix, in, out, start, n)\n", inputs, outputs)
+ s += fmt.Sprintf("\t\t\t\treturn n\n")
+ switchDefs[inputs-1][outputs-1] = s
+
+ if loadNone {
+ Comment("Loading no tables to registers")
+ } else {
+ // loadNone == false
+ Comment("Loading all tables to registers")
+ }
+
+ Doc(doc...)
+ Pragma("noescape")
+ Commentf("Full registers estimated %d YMM used", est)
+
+ length := Load(Param("n"), GP64())
+ matrixBase := GP64()
+ MOVQ(Param("matrix").Base().MustAddr(), matrixBase)
+ SHRQ(U8(perLoopBits), length)
+ TESTQ(length, length)
+ JZ(LabelRef(name + "_end"))
+
+ dst := make([]reg.VecVirtual, outputs)
+ dstPtr := make([]reg.GPVirtual, outputs)
+ outBase := Param("out").Base().MustAddr()
+ outSlicePtr := GP64()
+ MOVQ(outBase, outSlicePtr)
+ for i := range dst {
+ dst[i] = YMM()
+ if !regDst {
+ continue
+ }
+ ptr := GP64()
+ MOVQ(Mem{Base: outSlicePtr, Disp: i * 24}, ptr)
+ dstPtr[i] = ptr
+ }
+
+ inLo := make([]reg.VecVirtual, total)
+ inHi := make([]reg.VecVirtual, total)
+
+ for i := range inLo {
+ if loadNone {
+ break
+ }
+ tableLo := YMM()
+ tableHi := YMM()
+ VMOVDQU(Mem{Base: matrixBase, Disp: i * 64}, tableLo)
+ VMOVDQU(Mem{Base: matrixBase, Disp: i*64 + 32}, tableHi)
+ inLo[i] = tableLo
+ inHi[i] = tableHi
+ }
+
+ inPtrs := make([]reg.GPVirtual, inputs)
+ inSlicePtr := GP64()
+ MOVQ(Param("in").Base().MustAddr(), inSlicePtr)
+ for i := range inPtrs {
+ ptr := GP64()
+ MOVQ(Mem{Base: inSlicePtr, Disp: i * 24}, ptr)
+ inPtrs[i] = ptr
+ }
+
+ tmpMask := GP64()
+ MOVQ(U32(15), tmpMask)
+ lowMask := YMM()
+ MOVQ(tmpMask, lowMask.AsX())
+ VPBROADCASTB(lowMask.AsX(), lowMask)
+
+ offset := GP64()
+ MOVQ(Param("start").MustAddr(), offset)
+ Label(name + "_loop")
+ if xor {
+ Commentf("Load %d outputs", outputs)
+ } else {
+ Commentf("Clear %d outputs", outputs)
+ }
+ for i := range dst {
+ if xor {
+ if regDst {
+ VMOVDQU(Mem{Base: dstPtr[i], Index: offset, Scale: 1}, dst[i])
+ continue
+ }
+ ptr := GP64()
+ MOVQ(outBase, ptr)
+ VMOVDQU(Mem{Base: ptr, Index: offset, Scale: 1}, dst[i])
+ } else {
+ VPXOR(dst[i], dst[i], dst[i])
+ }
+ }
+
+ lookLow, lookHigh := YMM(), YMM()
+ inLow, inHigh := YMM(), YMM()
+ for i := range inPtrs {
+ Commentf("Load and process 32 bytes from input %d to %d outputs", i, outputs)
+ VMOVDQU(Mem{Base: inPtrs[i], Index: offset, Scale: 1}, inLow)
+ VPSRLQ(U8(4), inLow, inHigh)
+ VPAND(lowMask, inLow, inLow)
+ VPAND(lowMask, inHigh, inHigh)
+ for j := range dst {
+ if loadNone {
+ VMOVDQU(Mem{Base: matrixBase, Disp: 64 * (i*outputs + j)}, lookLow)
+ VMOVDQU(Mem{Base: matrixBase, Disp: 32 + 64*(i*outputs+j)}, lookHigh)
+ VPSHUFB(inLow, lookLow, lookLow)
+ VPSHUFB(inHigh, lookHigh, lookHigh)
+ } else {
+ VPSHUFB(inLow, inLo[i*outputs+j], lookLow)
+ VPSHUFB(inHigh, inHi[i*outputs+j], lookHigh)
+ }
+ VPXOR(lookLow, lookHigh, lookLow)
+ VPXOR(lookLow, dst[j], dst[j])
+ }
+ }
+ Commentf("Store %d outputs", outputs)
+ for i := range dst {
+ if regDst {
+ VMOVDQU(dst[i], Mem{Base: dstPtr[i], Index: offset, Scale: 1})
+ continue
+ }
+ ptr := GP64()
+ MOVQ(Mem{Base: outSlicePtr, Disp: i * 24}, ptr)
+ VMOVDQU(dst[i], Mem{Base: ptr, Index: offset, Scale: 1})
+ }
+ Comment("Prepare for next loop")
+ ADDQ(U8(perLoop), offset)
+ DECQ(length)
+ JNZ(LabelRef(name + "_loop"))
+ VZEROUPPER()
+
+ Label(name + "_end")
+ RET()
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/go.mod b/vendor/github.com/klauspost/reedsolomon/go.mod
new file mode 100644
index 0000000..a059d86
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/go.mod
@@ -0,0 +1,7 @@
+module github.com/klauspost/reedsolomon
+
+go 1.14
+
+require (
+ github.com/klauspost/cpuid v1.2.4
+)
diff --git a/vendor/github.com/klauspost/reedsolomon/go.sum b/vendor/github.com/klauspost/reedsolomon/go.sum
new file mode 100644
index 0000000..5a44d81
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/go.sum
@@ -0,0 +1,2 @@
+github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4=
+github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
diff --git a/vendor/github.com/klauspost/reedsolomon/inversion_tree.go b/vendor/github.com/klauspost/reedsolomon/inversion_tree.go
new file mode 100644
index 0000000..c9d8ab2
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/inversion_tree.go
@@ -0,0 +1,160 @@
+/**
+ * A thread-safe tree which caches inverted matrices.
+ *
+ * Copyright 2016, Peter Collins
+ */
+
+package reedsolomon
+
+import (
+ "errors"
+ "sync"
+)
+
+// The tree uses a Reader-Writer mutex to make it thread-safe
+// when accessing cached matrices and inserting new ones.
+type inversionTree struct {
+ mutex *sync.RWMutex
+ root inversionNode
+}
+
+type inversionNode struct {
+ matrix matrix
+ children []*inversionNode
+}
+
+// newInversionTree initializes a tree for storing inverted matrices.
+// Note that the root node is the identity matrix as it implies
+// there were no errors with the original data.
+func newInversionTree(dataShards, parityShards int) inversionTree {
+ identity, _ := identityMatrix(dataShards)
+ root := inversionNode{
+ matrix: identity,
+ children: make([]*inversionNode, dataShards+parityShards),
+ }
+ return inversionTree{
+ mutex: &sync.RWMutex{},
+ root: root,
+ }
+}
+
+// GetInvertedMatrix returns the cached inverted matrix or nil if it
+// is not found in the tree keyed on the indices of invalid rows.
+func (t inversionTree) GetInvertedMatrix(invalidIndices []int) matrix {
+ // Lock the tree for reading before accessing the tree.
+ t.mutex.RLock()
+ defer t.mutex.RUnlock()
+
+ // If no invalid indices were give we should return the root
+ // identity matrix.
+ if len(invalidIndices) == 0 {
+ return t.root.matrix
+ }
+
+ // Recursively search for the inverted matrix in the tree, passing in
+ // 0 as the parent index as we start at the root of the tree.
+ return t.root.getInvertedMatrix(invalidIndices, 0)
+}
+
+// errAlreadySet is returned if the root node matrix is overwritten
+var errAlreadySet = errors.New("the root node identity matrix is already set")
+
+// InsertInvertedMatrix inserts a new inverted matrix into the tree
+// keyed by the indices of invalid rows. The total number of shards
+// is required for creating the proper length lists of child nodes for
+// each node.
+func (t inversionTree) InsertInvertedMatrix(invalidIndices []int, matrix matrix, shards int) error {
+ // If no invalid indices were given then we are done because the
+ // root node is already set with the identity matrix.
+ if len(invalidIndices) == 0 {
+ return errAlreadySet
+ }
+
+ if !matrix.IsSquare() {
+ return errNotSquare
+ }
+
+ // Lock the tree for writing and reading before accessing the tree.
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ // Recursively create nodes for the inverted matrix in the tree until
+ // we reach the node to insert the matrix to. We start by passing in
+ // 0 as the parent index as we start at the root of the tree.
+ t.root.insertInvertedMatrix(invalidIndices, matrix, shards, 0)
+
+ return nil
+}
+
+func (n inversionNode) getInvertedMatrix(invalidIndices []int, parent int) matrix {
+ // Get the child node to search next from the list of children. The
+ // list of children starts relative to the parent index passed in
+ // because the indices of invalid rows is sorted (by default). As we
+ // search recursively, the first invalid index gets popped off the list,
+ // so when searching through the list of children, use that first invalid
+ // index to find the child node.
+ firstIndex := invalidIndices[0]
+ node := n.children[firstIndex-parent]
+
+ // If the child node doesn't exist in the list yet, fail fast by
+ // returning, so we can construct and insert the proper inverted matrix.
+ if node == nil {
+ return nil
+ }
+
+ // If there's more than one invalid index left in the list we should
+ // keep searching recursively.
+ if len(invalidIndices) > 1 {
+ // Search recursively on the child node by passing in the invalid indices
+ // with the first index popped off the front. Also the parent index to
+ // pass down is the first index plus one.
+ return node.getInvertedMatrix(invalidIndices[1:], firstIndex+1)
+ }
+ // If there aren't any more invalid indices to search, we've found our
+ // node. Return it, however keep in mind that the matrix could still be
+ // nil because intermediary nodes in the tree are created sometimes with
+ // their inversion matrices uninitialized.
+ return node.matrix
+}
+
+func (n inversionNode) insertInvertedMatrix(invalidIndices []int, matrix matrix, shards, parent int) {
+ // As above, get the child node to search next from the list of children.
+ // The list of children starts relative to the parent index passed in
+ // because the indices of invalid rows is sorted (by default). As we
+ // search recursively, the first invalid index gets popped off the list,
+ // so when searching through the list of children, use that first invalid
+ // index to find the child node.
+ firstIndex := invalidIndices[0]
+ node := n.children[firstIndex-parent]
+
+ // If the child node doesn't exist in the list yet, create a new
+ // node because we have the writer lock and add it to the list
+ // of children.
+ if node == nil {
+ // Make the length of the list of children equal to the number
+ // of shards minus the first invalid index because the list of
+ // invalid indices is sorted, so only this length of errors
+ // are possible in the tree.
+ node = &inversionNode{
+ children: make([]*inversionNode, shards-firstIndex),
+ }
+ // Insert the new node into the tree at the first index relative
+ // to the parent index that was given in this recursive call.
+ n.children[firstIndex-parent] = node
+ }
+
+ // If there's more than one invalid index left in the list we should
+ // keep searching recursively in order to find the node to add our
+ // matrix.
+ if len(invalidIndices) > 1 {
+ // As above, search recursively on the child node by passing in
+ // the invalid indices with the first index popped off the front.
+ // Also the total number of shards and parent index are passed down
+ // which is equal to the first index plus one.
+ node.insertInvertedMatrix(invalidIndices[1:], matrix, shards, firstIndex+1)
+ } else {
+ // If there aren't any more invalid indices to search, we've found our
+ // node. Cache the inverted matrix in this node.
+ node.matrix = matrix
+ }
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/matrix.go b/vendor/github.com/klauspost/reedsolomon/matrix.go
new file mode 100644
index 0000000..a6b9730
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/matrix.go
@@ -0,0 +1,279 @@
+/**
+ * Matrix Algebra over an 8-bit Galois Field
+ *
+ * Copyright 2015, Klaus Post
+ * Copyright 2015, Backblaze, Inc.
+ */
+
+package reedsolomon
+
+import (
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// byte[row][col]
+type matrix [][]byte
+
+// newMatrix returns a matrix of zeros.
+func newMatrix(rows, cols int) (matrix, error) {
+ if rows <= 0 {
+ return nil, errInvalidRowSize
+ }
+ if cols <= 0 {
+ return nil, errInvalidColSize
+ }
+
+ m := matrix(make([][]byte, rows))
+ for i := range m {
+ m[i] = make([]byte, cols)
+ }
+ return m, nil
+}
+
+// NewMatrixData initializes a matrix with the given row-major data.
+// Note that data is not copied from input.
+func newMatrixData(data [][]byte) (matrix, error) {
+ m := matrix(data)
+ err := m.Check()
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// IdentityMatrix returns an identity matrix of the given size.
+func identityMatrix(size int) (matrix, error) {
+ m, err := newMatrix(size, size)
+ if err != nil {
+ return nil, err
+ }
+ for i := range m {
+ m[i][i] = 1
+ }
+ return m, nil
+}
+
+// errInvalidRowSize will be returned if attempting to create a matrix with negative or zero row number.
+var errInvalidRowSize = errors.New("invalid row size")
+
+// errInvalidColSize will be returned if attempting to create a matrix with negative or zero column number.
+var errInvalidColSize = errors.New("invalid column size")
+
+// errColSizeMismatch is returned if the size of matrix columns mismatch.
+var errColSizeMismatch = errors.New("column size is not the same for all rows")
+
+func (m matrix) Check() error {
+ rows := len(m)
+ if rows <= 0 {
+ return errInvalidRowSize
+ }
+ cols := len(m[0])
+ if cols <= 0 {
+ return errInvalidColSize
+ }
+
+ for _, col := range m {
+ if len(col) != cols {
+ return errColSizeMismatch
+ }
+ }
+ return nil
+}
+
+// String returns a human-readable string of the matrix contents.
+//
+// Example: [[1, 2], [3, 4]]
+func (m matrix) String() string {
+ rowOut := make([]string, 0, len(m))
+ for _, row := range m {
+ colOut := make([]string, 0, len(row))
+ for _, col := range row {
+ colOut = append(colOut, strconv.Itoa(int(col)))
+ }
+ rowOut = append(rowOut, "["+strings.Join(colOut, ", ")+"]")
+ }
+ return "[" + strings.Join(rowOut, ", ") + "]"
+}
+
+// Multiply multiplies this matrix (the one on the left) by another
+// matrix (the one on the right) and returns a new matrix with the result.
+func (m matrix) Multiply(right matrix) (matrix, error) {
+ if len(m[0]) != len(right) {
+ return nil, fmt.Errorf("columns on left (%d) is different than rows on right (%d)", len(m[0]), len(right))
+ }
+ result, _ := newMatrix(len(m), len(right[0]))
+ for r, row := range result {
+ for c := range row {
+ var value byte
+ for i := range m[0] {
+ value ^= galMultiply(m[r][i], right[i][c])
+ }
+ result[r][c] = value
+ }
+ }
+ return result, nil
+}
+
+// Augment returns the concatenation of this matrix and the matrix on the right.
+func (m matrix) Augment(right matrix) (matrix, error) {
+ if len(m) != len(right) {
+ return nil, errMatrixSize
+ }
+
+ result, _ := newMatrix(len(m), len(m[0])+len(right[0]))
+ for r, row := range m {
+ for c := range row {
+ result[r][c] = m[r][c]
+ }
+ cols := len(m[0])
+ for c := range right[0] {
+ result[r][cols+c] = right[r][c]
+ }
+ }
+ return result, nil
+}
+
+// errMatrixSize is returned if matrix dimensions are doesn't match.
+var errMatrixSize = errors.New("matrix sizes do not match")
+
+func (m matrix) SameSize(n matrix) error {
+ if len(m) != len(n) {
+ return errMatrixSize
+ }
+ for i := range m {
+ if len(m[i]) != len(n[i]) {
+ return errMatrixSize
+ }
+ }
+ return nil
+}
+
+// SubMatrix returns a part of this matrix. Data is copied.
+func (m matrix) SubMatrix(rmin, cmin, rmax, cmax int) (matrix, error) {
+ result, err := newMatrix(rmax-rmin, cmax-cmin)
+ if err != nil {
+ return nil, err
+ }
+ // OPTME: If used heavily, use copy function to copy slice
+ for r := rmin; r < rmax; r++ {
+ for c := cmin; c < cmax; c++ {
+ result[r-rmin][c-cmin] = m[r][c]
+ }
+ }
+ return result, nil
+}
+
+// SwapRows Exchanges two rows in the matrix.
+func (m matrix) SwapRows(r1, r2 int) error {
+ if r1 < 0 || len(m) <= r1 || r2 < 0 || len(m) <= r2 {
+ return errInvalidRowSize
+ }
+ m[r2], m[r1] = m[r1], m[r2]
+ return nil
+}
+
+// IsSquare will return true if the matrix is square
+// and nil if the matrix is square
+func (m matrix) IsSquare() bool {
+ return len(m) == len(m[0])
+}
+
+// errSingular is returned if the matrix is singular and cannot be inversed
+var errSingular = errors.New("matrix is singular")
+
+// errNotSquare is returned if attempting to inverse a non-square matrix.
+var errNotSquare = errors.New("only square matrices can be inverted")
+
+// Invert returns the inverse of this matrix.
+// Returns ErrSingular when the matrix is singular and doesn't have an inverse.
+// The matrix must be square, otherwise ErrNotSquare is returned.
+func (m matrix) Invert() (matrix, error) {
+ if !m.IsSquare() {
+ return nil, errNotSquare
+ }
+
+ size := len(m)
+ work, _ := identityMatrix(size)
+ work, _ = m.Augment(work)
+
+ err := work.gaussianElimination()
+ if err != nil {
+ return nil, err
+ }
+
+ return work.SubMatrix(0, size, size, size*2)
+}
+
+func (m matrix) gaussianElimination() error {
+ rows := len(m)
+ columns := len(m[0])
+ // Clear out the part below the main diagonal and scale the main
+ // diagonal to be 1.
+ for r := 0; r < rows; r++ {
+ // If the element on the diagonal is 0, find a row below
+ // that has a non-zero and swap them.
+ if m[r][r] == 0 {
+ for rowBelow := r + 1; rowBelow < rows; rowBelow++ {
+ if m[rowBelow][r] != 0 {
+ m.SwapRows(r, rowBelow)
+ break
+ }
+ }
+ }
+ // If we couldn't find one, the matrix is singular.
+ if m[r][r] == 0 {
+ return errSingular
+ }
+ // Scale to 1.
+ if m[r][r] != 1 {
+ scale := galDivide(1, m[r][r])
+ for c := 0; c < columns; c++ {
+ m[r][c] = galMultiply(m[r][c], scale)
+ }
+ }
+ // Make everything below the 1 be a 0 by subtracting
+ // a multiple of it. (Subtraction and addition are
+ // both exclusive or in the Galois field.)
+ for rowBelow := r + 1; rowBelow < rows; rowBelow++ {
+ if m[rowBelow][r] != 0 {
+ scale := m[rowBelow][r]
+ for c := 0; c < columns; c++ {
+ m[rowBelow][c] ^= galMultiply(scale, m[r][c])
+ }
+ }
+ }
+ }
+
+ // Now clear the part above the main diagonal.
+ for d := 0; d < rows; d++ {
+ for rowAbove := 0; rowAbove < d; rowAbove++ {
+ if m[rowAbove][d] != 0 {
+ scale := m[rowAbove][d]
+ for c := 0; c < columns; c++ {
+ m[rowAbove][c] ^= galMultiply(scale, m[d][c])
+ }
+
+ }
+ }
+ }
+ return nil
+}
+
+// Create a Vandermonde matrix, which is guaranteed to have the
+// property that any subset of rows that forms a square matrix
+// is invertible.
+func vandermonde(rows, cols int) (matrix, error) {
+ result, err := newMatrix(rows, cols)
+ if err != nil {
+ return nil, err
+ }
+ for r, row := range result {
+ for c := range row {
+ result[r][c] = galExp(byte(r), c)
+ }
+ }
+ return result, nil
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/options.go b/vendor/github.com/klauspost/reedsolomon/options.go
new file mode 100644
index 0000000..b4adc2a
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/options.go
@@ -0,0 +1,175 @@
+package reedsolomon
+
+import (
+ "runtime"
+
+ "github.com/klauspost/cpuid"
+)
+
+// Option allows to override processing parameters.
+type Option func(*options)
+
+type options struct {
+ maxGoroutines int
+ minSplitSize int
+ shardSize int
+ perRound int
+
+ useAVX512, useAVX2, useSSSE3, useSSE2 bool
+ usePAR1Matrix bool
+ useCauchy bool
+ fastOneParity bool
+
+ // stream options
+ concReads bool
+ concWrites bool
+ streamBS int
+}
+
+var defaultOptions = options{
+ maxGoroutines: 384,
+ minSplitSize: -1,
+ fastOneParity: false,
+
+ // Detect CPU capabilities.
+ useSSSE3: cpuid.CPU.SSSE3(),
+ useSSE2: cpuid.CPU.SSE2(),
+ useAVX2: cpuid.CPU.AVX2(),
+ useAVX512: cpuid.CPU.AVX512F() && cpuid.CPU.AVX512BW(),
+}
+
+func init() {
+ if runtime.GOMAXPROCS(0) <= 1 {
+ defaultOptions.maxGoroutines = 1
+ }
+}
+
+// WithMaxGoroutines is the maximum number of goroutines number for encoding & decoding.
+// Jobs will be split into this many parts, unless each goroutine would have to process
+// less than minSplitSize bytes (set with WithMinSplitSize).
+// For the best speed, keep this well above the GOMAXPROCS number for more fine grained
+// scheduling.
+// If n <= 0, it is ignored.
+func WithMaxGoroutines(n int) Option {
+ return func(o *options) {
+ if n > 0 {
+ o.maxGoroutines = n
+ }
+ }
+}
+
+// WithAutoGoroutines will adjust the number of goroutines for optimal speed with a
+// specific shard size.
+// Send in the shard size you expect to send. Other shard sizes will work, but may not
+// run at the optimal speed.
+// Overwrites WithMaxGoroutines.
+// If shardSize <= 0, it is ignored.
+func WithAutoGoroutines(shardSize int) Option {
+ return func(o *options) {
+ o.shardSize = shardSize
+ }
+}
+
+// WithMinSplitSize is the minimum encoding size in bytes per goroutine.
+// By default this parameter is determined by CPU cache characteristics.
+// See WithMaxGoroutines on how jobs are split.
+// If n <= 0, it is ignored.
+func WithMinSplitSize(n int) Option {
+ return func(o *options) {
+ if n > 0 {
+ o.minSplitSize = n
+ }
+ }
+}
+
+// WithConcurrentStreams will enable concurrent reads and writes on the streams.
+// Default: Disabled, meaning only one stream will be read/written at the time.
+// Ignored if not used on a stream input.
+func WithConcurrentStreams(enabled bool) Option {
+ return func(o *options) {
+ o.concReads, o.concWrites = enabled, enabled
+ }
+}
+
+// WithConcurrentStreamReads will enable concurrent reads from the input streams.
+// Default: Disabled, meaning only one stream will be read at the time.
+// Ignored if not used on a stream input.
+func WithConcurrentStreamReads(enabled bool) Option {
+ return func(o *options) {
+ o.concReads = enabled
+ }
+}
+
+// WithConcurrentStreamWrites will enable concurrent writes to the the output streams.
+// Default: Disabled, meaning only one stream will be written at the time.
+// Ignored if not used on a stream input.
+func WithConcurrentStreamWrites(enabled bool) Option {
+ return func(o *options) {
+ o.concWrites = enabled
+ }
+}
+
+// WithStreamBlockSize allows to set a custom block size per round of reads/writes.
+// If not set, any shard size set with WithAutoGoroutines will be used.
+// If WithAutoGoroutines is also unset, 4MB will be used.
+// Ignored if not used on stream.
+func WithStreamBlockSize(n int) Option {
+ return func(o *options) {
+ o.streamBS = n
+ }
+}
+
+func withSSSE3(enabled bool) Option {
+ return func(o *options) {
+ o.useSSSE3 = enabled
+ }
+}
+
+func withAVX2(enabled bool) Option {
+ return func(o *options) {
+ o.useAVX2 = enabled
+ }
+}
+
+func withSSE2(enabled bool) Option {
+ return func(o *options) {
+ o.useSSE2 = enabled
+ }
+}
+
+func withAVX512(enabled bool) Option {
+ return func(o *options) {
+ o.useAVX512 = enabled
+ }
+}
+
+// WithPAR1Matrix causes the encoder to build the matrix how PARv1
+// does. Note that the method they use is buggy, and may lead to cases
+// where recovery is impossible, even if there are enough parity
+// shards.
+func WithPAR1Matrix() Option {
+ return func(o *options) {
+ o.usePAR1Matrix = true
+ o.useCauchy = false
+ }
+}
+
+// WithCauchyMatrix will make the encoder build a Cauchy style matrix.
+// The output of this is not compatible with the standard output.
+// A Cauchy matrix is faster to generate. This does not affect data throughput,
+// but will result in slightly faster start-up time.
+func WithCauchyMatrix() Option {
+ return func(o *options) {
+ o.useCauchy = true
+ o.usePAR1Matrix = false
+ }
+}
+
+// WithFastOneParityMatrix will switch the matrix to a simple xor
+// if there is only one parity shard.
+// The PAR1 matrix already has this property so it has little effect there.
+func WithFastOneParityMatrix() Option {
+ return func(o *options) {
+ o.fastOneParity = true
+ }
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/reedsolomon.go b/vendor/github.com/klauspost/reedsolomon/reedsolomon.go
new file mode 100644
index 0000000..13a35d2
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/reedsolomon.go
@@ -0,0 +1,1011 @@
+/**
+ * Reed-Solomon Coding over 8-bit values.
+ *
+ * Copyright 2015, Klaus Post
+ * Copyright 2015, Backblaze, Inc.
+ */
+
+// Package reedsolomon enables Erasure Coding in Go
+//
+// For usage and examples, see https://github.com/klauspost/reedsolomon
+//
+package reedsolomon
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "runtime"
+ "sync"
+
+ "github.com/klauspost/cpuid"
+)
+
+// Encoder is an interface to encode Reed-Salomon parity sets for your data.
+type Encoder interface {
+ // Encode parity for a set of data shards.
+ // Input is 'shards' containing data shards followed by parity shards.
+ // The number of shards must match the number given to New().
+ // Each shard is a byte array, and they must all be the same size.
+ // The parity shards will always be overwritten and the data shards
+ // will remain the same, so it is safe for you to read from the
+ // data shards while this is running.
+ Encode(shards [][]byte) error
+
+ // Verify returns true if the parity shards contain correct data.
+ // The data is the same format as Encode. No data is modified, so
+ // you are allowed to read from data while this is running.
+ Verify(shards [][]byte) (bool, error)
+
+ // Reconstruct will recreate the missing shards if possible.
+ //
+ // 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 the total number of 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.
+ //
+ // If there are too few shards to reconstruct the missing
+ // ones, ErrTooFewShards will be returned.
+ //
+ // The reconstructed shard set is complete, but integrity is not verified.
+ // Use the Verify function to check if data set is ok.
+ Reconstruct(shards [][]byte) error
+
+ // ReconstructData will recreate any missing data shards, if possible.
+ //
+ // 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.
+ // 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.
+ //
+ // If there are too few shards to reconstruct the missing
+ // ones, ErrTooFewShards will be returned.
+ //
+ // As the reconstructed shard set may contain missing parity shards,
+ // calling the Verify function is likely to fail.
+ ReconstructData(shards [][]byte) error
+
+ // Update parity is use for change a few data shards and update it's parity.
+ // Input 'newDatashards' containing data shards changed.
+ // Input 'shards' containing old data shards (if data shard not changed, it can be nil) and old parity shards.
+ // new parity shards will in shards[DataShards:]
+ // Update is very useful if DataShards much larger than ParityShards and changed data shards is few. It will
+ // faster than Encode and not need read all data shards to encode.
+ Update(shards [][]byte, newDatashards [][]byte) error
+
+ // Split a data slice into the number of shards given to the encoder,
+ // and create empty parity shards.
+ //
+ // The data will be split into equally sized shards.
+ // If the data size isn't dividable by the number of shards,
+ // the last shard will contain extra zeros.
+ //
+ // There must be at least 1 byte otherwise ErrShortData will be
+ // returned.
+ //
+ // The data will not be copied, except for the last shard, so you
+ // should not modify the data of the input slice afterwards.
+ Split(data []byte) ([][]byte, error)
+
+ // Join the shards and write the data segment to dst.
+ //
+ // Only the data shards are considered.
+ // You must supply the exact output size you want.
+ // If there are to few shards given, ErrTooFewShards will be returned.
+ // If the total data size is less than outSize, ErrShortData will be returned.
+ Join(dst io.Writer, shards [][]byte, outSize int) error
+}
+
+// reedSolomon contains a matrix for a specific
+// 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.
+ m matrix
+ tree inversionTree
+ parity [][]byte
+ o options
+ mPool sync.Pool
+}
+
+// ErrInvShardNum will be returned by New, if you attempt to create
+// an Encoder where either data or parity shards is zero or less.
+var ErrInvShardNum = errors.New("cannot create Encoder with zero or less data/parity shards")
+
+// ErrMaxShardNum will be returned by New, if you attempt to create an
+// Encoder where data and parity shards are bigger than the order of
+// GF(2^8).
+var ErrMaxShardNum = errors.New("cannot create Encoder with more than 256 data+parity shards")
+
+// buildMatrix creates the matrix to use for encoding, given the
+// number of data shards and the number of total shards.
+//
+// The top square of the matrix is guaranteed to be an identity
+// matrix, which means that the data shards are unchanged after
+// encoding.
+func buildMatrix(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
+ }
+
+ // Multiply by the inverse of the top square of the matrix.
+ // This will make the top square be the identity matrix, but
+ // preserve the property that any square subset of rows is
+ // invertible.
+ top, err := vm.SubMatrix(0, 0, dataShards, dataShards)
+ if err != nil {
+ return nil, err
+ }
+
+ topInv, err := top.Invert()
+ if err != nil {
+ return nil, err
+ }
+
+ return vm.Multiply(topInv)
+}
+
+// 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
+// to cases where recovery is impossible, even if there are enough
+// parity shards.
+//
+// The top square of the matrix is guaranteed to be an identity
+// matrix, which means that the data shards are unchanged after
+// encoding.
+func buildMatrixPAR1(dataShards, totalShards int) (matrix, error) {
+ result, err := newMatrix(totalShards, dataShards)
+ if err != nil {
+ return nil, err
+ }
+
+ for r, row := range result {
+ // The top portion of the matrix is the identity
+ // matrix, and the bottom is a transposed Vandermonde
+ // matrix starting at 1 instead of 0.
+ if r < dataShards {
+ result[r][r] = 1
+ } else {
+ for c := range row {
+ result[r][c] = galExp(byte(c+1), r-dataShards)
+ }
+ }
+ }
+ return result, nil
+}
+
+func buildMatrixCauchy(dataShards, totalShards int) (matrix, error) {
+ result, err := newMatrix(totalShards, dataShards)
+ if err != nil {
+ return nil, err
+ }
+
+ for r, row := range result {
+ // The top portion of the matrix is the identity
+ // matrix, and the bottom is a transposed Cauchy matrix.
+ if r < dataShards {
+ result[r][r] = 1
+ } else {
+ for c := range row {
+ result[r][c] = invTable[(byte(r ^ c))]
+ }
+ }
+ }
+ return result, nil
+}
+
+// buildXorMatrix can be used to build a matrix with pure XOR
+// operations if there is only one parity shard.
+func buildXorMatrix(dataShards, totalShards int) (matrix, error) {
+ if dataShards+1 != totalShards {
+ return nil, errors.New("internal error")
+ }
+ result, err := newMatrix(totalShards, dataShards)
+ if err != nil {
+ return nil, err
+ }
+
+ for r, row := range result {
+ // The top portion of the matrix is the identity
+ // matrix.
+ if r < dataShards {
+ result[r][r] = 1
+ } else {
+ // Set all values to 1 (XOR)
+ for c := range row {
+ result[r][c] = 1
+ }
+ }
+ }
+ return result, nil
+}
+
+// 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.
+// 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,
+ }
+
+ for _, opt := range opts {
+ opt(&r.o)
+ }
+ if dataShards <= 0 || parityShards <= 0 {
+ return nil, ErrInvShardNum
+ }
+
+ if dataShards+parityShards > 256 {
+ return nil, ErrMaxShardNum
+ }
+
+ var err error
+ switch {
+ case r.o.fastOneParity && parityShards == 1:
+ r.m, err = buildXorMatrix(dataShards, r.Shards)
+ case r.o.useCauchy:
+ r.m, err = buildMatrixCauchy(dataShards, r.Shards)
+ case r.o.usePAR1Matrix:
+ r.m, err = buildMatrixPAR1(dataShards, r.Shards)
+ default:
+ r.m, err = buildMatrix(dataShards, r.Shards)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // Calculate what we want per round
+ r.o.perRound = cpuid.CPU.Cache.L2
+ if r.o.perRound <= 0 {
+ // Set to 128K if undetectable.
+ r.o.perRound = 128 << 10
+ }
+
+ if cpuid.CPU.ThreadsPerCore > 1 && r.o.maxGoroutines > cpuid.CPU.PhysicalCores {
+ // If multiple threads per core, make sure they don't contend for cache.
+ r.o.perRound /= cpuid.CPU.ThreadsPerCore
+ }
+ // 1 input + parity must fit in cache, and we add one more to be safer.
+ r.o.perRound = r.o.perRound / (1 + parityShards)
+ // Align to 64 bytes.
+ r.o.perRound = ((r.o.perRound + 63) / 64) * 64
+
+ if r.o.minSplitSize <= 0 {
+ // Set minsplit as high as we can, but still have parity in L1.
+ cacheSize := cpuid.CPU.Cache.L1D
+ if cacheSize <= 0 {
+ cacheSize = 32 << 10
+ }
+
+ r.o.minSplitSize = cacheSize / (parityShards + 1)
+ // Min 1K
+ if r.o.minSplitSize < 1024 {
+ r.o.minSplitSize = 1024
+ }
+ }
+
+ if r.o.perRound < r.o.minSplitSize {
+ r.o.perRound = r.o.minSplitSize
+ }
+
+ if r.o.shardSize > 0 {
+ p := runtime.GOMAXPROCS(0)
+ if p == 1 || r.o.shardSize <= r.o.minSplitSize*2 {
+ // Not worth it.
+ r.o.maxGoroutines = 1
+ } else {
+ g := r.o.shardSize / r.o.perRound
+
+ // Overprovision by a factor of 2.
+ if g < p*2 && r.o.perRound > r.o.minSplitSize*2 {
+ g = p * 2
+ r.o.perRound /= 2
+ }
+
+ // Have g be multiple of p
+ g += p - 1
+ g -= g % p
+
+ r.o.maxGoroutines = g
+ }
+ }
+
+ // 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
+ // its inversion matrix because it implies there are no errors
+ // with the original data.
+ r.tree = newInversionTree(dataShards, parityShards)
+
+ r.parity = make([][]byte, parityShards)
+ for i := range r.parity {
+ r.parity[i] = r.m[dataShards+i]
+ }
+
+ if avx2CodeGen && r.o.useAVX2 {
+ r.mPool.New = func() interface{} {
+ return make([]byte, r.Shards*2*32)
+ }
+ }
+ return &r, err
+}
+
+// ErrTooFewShards is returned if too few shards where given to
+// Encode/Verify/Reconstruct/Update. It will also be returned from Reconstruct
+// if there were too few shards to reconstruct the missing data.
+var ErrTooFewShards = errors.New("too few shards given")
+
+// Encodes parity for a set of data shards.
+// An array 'shards' containing data shards followed by parity shards.
+// The number of shards must match the number given to New.
+// Each shard is a byte array, and they must all be the same size.
+// 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 {
+ return ErrTooFewShards
+ }
+
+ err := checkShards(shards, false)
+ if err != nil {
+ return err
+ }
+
+ // Get the slice of output buffers.
+ output := shards[r.DataShards:]
+
+ // Do the coding.
+ r.codeSomeShards(r.parity, shards[0:r.DataShards], output, r.ParityShards, len(shards[0]))
+ return nil
+}
+
+// ErrInvalidInput is returned if invalid input parameter of Update.
+var ErrInvalidInput = errors.New("invalid input")
+
+func (r *reedSolomon) Update(shards [][]byte, newDatashards [][]byte) error {
+ if len(shards) != r.Shards {
+ return ErrTooFewShards
+ }
+
+ if len(newDatashards) != r.DataShards {
+ return ErrTooFewShards
+ }
+
+ err := checkShards(shards, true)
+ if err != nil {
+ return err
+ }
+
+ err = checkShards(newDatashards, true)
+ if err != nil {
+ return err
+ }
+
+ for i := range newDatashards {
+ if newDatashards[i] != nil && shards[i] == nil {
+ return ErrInvalidInput
+ }
+ }
+ for _, p := range shards[r.DataShards:] {
+ if p == nil {
+ return ErrInvalidInput
+ }
+ }
+
+ shardSize := shardSize(shards)
+
+ // Get the slice of output buffers.
+ output := shards[r.DataShards:]
+
+ // Do the coding.
+ r.updateParityShards(r.parity, shards[0:r.DataShards], newDatashards[0:r.DataShards], output, r.ParityShards, shardSize)
+ return nil
+}
+
+func (r *reedSolomon) updateParityShards(matrixRows, oldinputs, newinputs, outputs [][]byte, outputCount, byteCount int) {
+ if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
+ r.updateParityShardsP(matrixRows, oldinputs, newinputs, outputs, outputCount, byteCount)
+ return
+ }
+
+ for c := 0; c < r.DataShards; c++ {
+ in := newinputs[c]
+ if in == nil {
+ continue
+ }
+ oldin := oldinputs[c]
+ // oldinputs data will be change
+ sliceXor(in, oldin, &r.o)
+ for iRow := 0; iRow < outputCount; iRow++ {
+ galMulSliceXor(matrixRows[iRow][c], oldin, outputs[iRow], &r.o)
+ }
+ }
+}
+
+func (r *reedSolomon) updateParityShardsP(matrixRows, oldinputs, newinputs, outputs [][]byte, outputCount, byteCount int) {
+ var wg sync.WaitGroup
+ do := byteCount / r.o.maxGoroutines
+ if do < r.o.minSplitSize {
+ do = r.o.minSplitSize
+ }
+ start := 0
+ for start < byteCount {
+ if start+do > byteCount {
+ do = byteCount - start
+ }
+ wg.Add(1)
+ go func(start, stop int) {
+ for c := 0; c < r.DataShards; c++ {
+ in := newinputs[c]
+ if in == nil {
+ continue
+ }
+ oldin := oldinputs[c]
+ // oldinputs data will be change
+ sliceXor(in[start:stop], oldin[start:stop], &r.o)
+ for iRow := 0; iRow < outputCount; iRow++ {
+ galMulSliceXor(matrixRows[iRow][c], oldin[start:stop], outputs[iRow][start:stop], &r.o)
+ }
+ }
+ wg.Done()
+ }(start, start+do)
+ start += do
+ }
+ wg.Wait()
+}
+
+// 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 {
+ return false, ErrTooFewShards
+ }
+ err := checkShards(shards, false)
+ if err != nil {
+ return false, err
+ }
+
+ // Slice of buffers being checked.
+ toCheck := shards[r.DataShards:]
+
+ // Do the checking.
+ return r.checkSomeShards(r.parity, shards[0:r.DataShards], toCheck, r.ParityShards, len(shards[0])), nil
+}
+
+// Multiplies a subset of rows from a coding matrix by a full set of
+// input shards to produce some output shards.
+// '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.
+// The number of outputs computed, and the
+// number of matrix rows used, is determined by
+// outputCount, which is the number of outputs to compute.
+func (r *reedSolomon) codeSomeShards(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
+ 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, outputCount, byteCount)
+ return
+ case r.o.useAVX512 && len(inputs) >= 4 && len(outputs) >= 2:
+ r.codeSomeShardsAvx512(matrixRows, inputs, outputs, outputCount, byteCount)
+ return
+ case r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize:
+ r.codeSomeShardsP(matrixRows, inputs, outputs, outputCount, byteCount)
+ return
+ }
+
+ // Process using no goroutines
+ start, end := 0, r.o.perRound
+ if end > len(inputs[0]) {
+ end = len(inputs[0])
+ }
+ if avx2CodeGen && r.o.useAVX2 && byteCount >= 32 && len(inputs) > 1 && len(outputs) > 1 && len(inputs) <= maxAvx2Inputs && len(outputs) <= maxAvx2Outputs {
+ m := genAvx2Matrix(matrixRows, len(inputs), len(outputs), r.mPool.Get().([]byte))
+ start += galMulSlicesAvx2(m, inputs, outputs, 0, byteCount)
+ r.mPool.Put(m)
+ end = len(inputs[0])
+ }
+
+ for start < len(inputs[0]) {
+ for c := 0; c < r.DataShards; c++ {
+ in := inputs[c][start:end]
+ for iRow := 0; iRow < outputCount; iRow++ {
+ if c == 0 {
+ galMulSlice(matrixRows[iRow][c], in, outputs[iRow][start:end], &r.o)
+ } else {
+ galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow][start:end], &r.o)
+ }
+ }
+ }
+ start = end
+ end += r.o.perRound
+ if end > len(inputs[0]) {
+ end = len(inputs[0])
+ }
+ }
+}
+
+// Perform the same as codeSomeShards, but split the workload into
+// several goroutines.
+func (r *reedSolomon) codeSomeShardsP(matrixRows, inputs, outputs [][]byte, outputCount, byteCount int) {
+ 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
+ var avx2Matrix []byte
+ if avx2CodeGen && r.o.useAVX2 && byteCount >= 32 && len(inputs) > 1 && len(outputs) > 1 && len(inputs) <= maxAvx2Inputs && len(outputs) <= maxAvx2Outputs {
+ avx2Matrix = genAvx2Matrix(matrixRows, len(inputs), len(outputs), r.mPool.Get().([]byte))
+ defer r.mPool.Put(avx2Matrix)
+ }
+ for start < byteCount {
+ if start+do > byteCount {
+ do = byteCount - start
+ }
+
+ wg.Add(1)
+ go func(start, stop int) {
+ if avx2CodeGen && r.o.useAVX2 && stop-start >= 32 && len(inputs) > 1 && len(outputs) > 1 && len(inputs) <= maxAvx2Inputs && len(outputs) <= maxAvx2Outputs {
+ start += galMulSlicesAvx2(avx2Matrix, inputs, outputs, start, stop)
+ }
+
+ lstart, lstop := start, start+r.o.perRound
+ if lstop > stop {
+ lstop = stop
+ }
+ for lstart < stop {
+ for c := 0; c < r.DataShards; c++ {
+ in := inputs[c][lstart:lstop]
+ for iRow := 0; iRow < outputCount; 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()
+ }(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.
+func (r *reedSolomon) checkSomeShards(matrixRows, inputs, toCheck [][]byte, outputCount, byteCount int) bool {
+ if r.o.maxGoroutines > 1 && byteCount > r.o.minSplitSize {
+ return r.checkSomeShardsP(matrixRows, inputs, toCheck, outputCount, byteCount)
+ }
+ outputs := make([][]byte, len(toCheck))
+ for i := range outputs {
+ outputs[i] = make([]byte, byteCount)
+ }
+ for c := 0; c < r.DataShards; c++ {
+ in := inputs[c]
+ for iRow := 0; iRow < outputCount; iRow++ {
+ galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow], &r.o)
+ }
+ }
+
+ for i, calc := range outputs {
+ if !bytes.Equal(calc, toCheck[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func (r *reedSolomon) checkSomeShardsP(matrixRows, inputs, toCheck [][]byte, outputCount, byteCount int) bool {
+ same := true
+ var mu sync.RWMutex // For above
+
+ 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(start, do int) {
+ defer wg.Done()
+ outputs := make([][]byte, len(toCheck))
+ for i := range outputs {
+ outputs[i] = make([]byte, do)
+ }
+ for c := 0; c < r.DataShards; c++ {
+ mu.RLock()
+ if !same {
+ mu.RUnlock()
+ return
+ }
+ mu.RUnlock()
+ in := inputs[c][start : start+do]
+ for iRow := 0; iRow < outputCount; iRow++ {
+ galMulSliceXor(matrixRows[iRow][c], in, outputs[iRow], &r.o)
+ }
+ }
+
+ for i, calc := range outputs {
+ if !bytes.Equal(calc, toCheck[i][start:start+do]) {
+ mu.Lock()
+ same = false
+ mu.Unlock()
+ return
+ }
+ }
+ }(start, do)
+ start += do
+ }
+ wg.Wait()
+ return same
+}
+
+// ErrShardNoData will be returned if there are no shards,
+// or if the length of all shards is zero.
+var ErrShardNoData = errors.New("no shard data")
+
+// ErrShardSize is returned if shard length isn't the same for all
+// shards.
+var ErrShardSize = errors.New("shard sizes do not match")
+
+// checkShards will check if shards are the same size
+// or 0, if allowed. An error is returned if this fails.
+// An error is also returned if all shards are size 0.
+func checkShards(shards [][]byte, nilok bool) error {
+ size := shardSize(shards)
+ if size == 0 {
+ return ErrShardNoData
+ }
+ for _, shard := range shards {
+ if len(shard) != size {
+ if len(shard) != 0 || !nilok {
+ return ErrShardSize
+ }
+ }
+ }
+ return nil
+}
+
+// shardSize return the size of a single shard.
+// The first non-zero size is returned,
+// or 0 if all shards are size 0.
+func shardSize(shards [][]byte) int {
+ for _, shard := range shards {
+ if len(shard) != 0 {
+ return len(shard)
+ }
+ }
+ return 0
+}
+
+// Reconstruct will recreate the missing shards, if possible.
+//
+// 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.
+// 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.
+//
+// If there are too few shards to reconstruct the missing
+// ones, ErrTooFewShards will be returned.
+//
+// The reconstructed shard set is complete, but integrity is not verified.
+// Use the Verify function to check if data set is ok.
+func (r *reedSolomon) Reconstruct(shards [][]byte) error {
+ return r.reconstruct(shards, false)
+}
+
+// ReconstructData will recreate any missing data shards, if possible.
+//
+// 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.
+// 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.
+//
+// If there are too few shards to reconstruct the missing
+// ones, ErrTooFewShards will be returned.
+//
+// As the reconstructed shard set may contain missing parity shards,
+// calling the Verify function is likely to fail.
+func (r *reedSolomon) ReconstructData(shards [][]byte) error {
+ return r.reconstruct(shards, true)
+}
+
+// reconstruct will recreate the missing data shards, and unless
+// dataOnly is true, also the missing parity shards
+//
+// The length of the array must be equal to Shards.
+// You indicate that a shard is missing by setting it to nil.
+//
+// If there are too few shards to reconstruct the missing
+// ones, ErrTooFewShards will be returned.
+func (r *reedSolomon) reconstruct(shards [][]byte, dataOnly bool) error {
+ if len(shards) != r.Shards {
+ return ErrTooFewShards
+ }
+ // Check arguments.
+ err := checkShards(shards, true)
+ if err != nil {
+ return err
+ }
+
+ shardSize := shardSize(shards)
+
+ // Quick check: are all of the shards present? If so, there's
+ // nothing to do.
+ numberPresent := 0
+ dataPresent := 0
+ for i := 0; i < r.Shards; i++ {
+ if len(shards[i]) != 0 {
+ numberPresent++
+ if i < r.DataShards {
+ dataPresent++
+ }
+ }
+ }
+ if numberPresent == r.Shards || dataOnly && dataPresent == r.DataShards {
+ // Cool. All of the shards data data. We don't
+ // need to do anything.
+ return nil
+ }
+
+ // More complete sanity check
+ if numberPresent < r.DataShards {
+ return ErrTooFewShards
+ }
+
+ // Pull out an array holding just the shards that
+ // correspond to the rows of the submatrix. These shards
+ // will be the input to the decoding process that re-creates
+ // the missing data shards.
+ //
+ // 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)
+ invalidIndices := make([]int, 0)
+ subMatrixRow := 0
+ for matrixRow := 0; matrixRow < r.Shards && subMatrixRow < r.DataShards; matrixRow++ {
+ if len(shards[matrixRow]) != 0 {
+ subShards[subMatrixRow] = shards[matrixRow]
+ validIndices[subMatrixRow] = matrixRow
+ subMatrixRow++
+ } else {
+ invalidIndices = append(invalidIndices, matrixRow)
+ }
+ }
+
+ // Attempt to get the cached inverted matrix out of the tree
+ // based on the indices of the invalid rows.
+ dataDecodeMatrix := r.tree.GetInvertedMatrix(invalidIndices)
+
+ // If the inverted matrix isn't cached in the tree yet we must
+ // construct it ourselves and insert it into the tree for the
+ // future. In this way the inversion tree is lazily loaded.
+ if dataDecodeMatrix == nil {
+ // Pull out the rows of the matrix that correspond to the
+ // 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)
+ for subMatrixRow, validIndex := range validIndices {
+ for c := 0; c < r.DataShards; c++ {
+ subMatrix[subMatrixRow][c] = r.m[validIndex][c]
+ }
+ }
+ // Invert the matrix, so we can go from the encoded shards
+ // back to the original data. Then pull out the row that
+ // generates the shard that we want to decode. Note that
+ // since this matrix maps back to the original data, it can
+ // be used to create a data shard, but not a parity shard.
+ dataDecodeMatrix, err = subMatrix.Invert()
+ if err != nil {
+ return err
+ }
+
+ // 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)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Re-create any data shards that were missing.
+ //
+ // 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)
+ outputCount := 0
+
+ for iShard := 0; iShard < r.DataShards; iShard++ {
+ if len(shards[iShard]) == 0 {
+ if cap(shards[iShard]) >= shardSize {
+ shards[iShard] = shards[iShard][0:shardSize]
+ } else {
+ shards[iShard] = make([]byte, shardSize)
+ }
+ outputs[outputCount] = shards[iShard]
+ matrixRows[outputCount] = dataDecodeMatrix[iShard]
+ outputCount++
+ }
+ }
+ r.codeSomeShards(matrixRows, subShards, outputs[:outputCount], outputCount, shardSize)
+
+ if dataOnly {
+ // Exit out early if we are only interested in the data shards
+ return nil
+ }
+
+ // Now that we have all of the data shards intact, we can
+ // compute any of the parity that is missing.
+ //
+ // The input to the coding is ALL of the data shards, including
+ // 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++ {
+ if len(shards[iShard]) == 0 {
+ if cap(shards[iShard]) >= shardSize {
+ shards[iShard] = shards[iShard][0:shardSize]
+ } else {
+ shards[iShard] = make([]byte, shardSize)
+ }
+ outputs[outputCount] = shards[iShard]
+ matrixRows[outputCount] = r.parity[iShard-r.DataShards]
+ outputCount++
+ }
+ }
+ r.codeSomeShards(matrixRows, shards[:r.DataShards], outputs[:outputCount], outputCount, shardSize)
+ return nil
+}
+
+// ErrShortData will be returned by Split(), if there isn't enough data
+// to fill the number of shards.
+var ErrShortData = errors.New("not enough data to fill the number of requested shards")
+
+// Split a data slice into the number of shards given to the encoder,
+// and create empty parity shards if necessary.
+//
+// The data will be split into equally sized shards.
+// If the data size isn't divisible by the number of shards,
+// the last shard will contain extra zeros.
+//
+// There must be at least 1 byte otherwise ErrShortData will be
+// returned.
+//
+// The data will not be copied, except for the last shard, so you
+// should not modify the data of the input slice afterwards.
+func (r *reedSolomon) Split(data []byte) ([][]byte, error) {
+ if len(data) == 0 {
+ return nil, ErrShortData
+ }
+ // Calculate number of bytes per data shard.
+ perShard := (len(data) + r.DataShards - 1) / r.DataShards
+
+ if cap(data) > len(data) {
+ data = data[:cap(data)]
+ }
+
+ // Only allocate memory if necessary
+ var padding []byte
+ if len(data) < (r.Shards * perShard) {
+ // 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]
+ }
+
+ // Split into equal-length shards.
+ dst := make([][]byte, r.Shards)
+ i := 0
+ for ; i < len(dst) && len(data) >= perShard; i++ {
+ dst[i] = data[:perShard:perShard]
+ data = data[perShard:]
+ }
+
+ for j := 0; i+j < len(dst); j++ {
+ dst[i+j] = padding[:perShard:perShard]
+ padding = padding[perShard:]
+ }
+
+ return dst, nil
+}
+
+// ErrReconstructRequired is returned if too few data shards are intact and a
+// reconstruction is required before you can successfully join the shards.
+var ErrReconstructRequired = errors.New("reconstruction required as one or more required data shards are nil")
+
+// Join the shards and write the data segment to dst.
+//
+// Only the data shards are considered.
+// You must supply the exact output size you want.
+//
+// If there are to few shards given, ErrTooFewShards will be returned.
+// If the total data size is less than outSize, ErrShortData will be returned.
+// 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 {
+ return ErrTooFewShards
+ }
+ shards = shards[:r.DataShards]
+
+ // Do we have enough data?
+ size := 0
+ for _, shard := range shards {
+ if shard == nil {
+ return ErrReconstructRequired
+ }
+ size += len(shard)
+
+ // Do we have enough data already?
+ if size >= outSize {
+ break
+ }
+ }
+ if size < outSize {
+ return ErrShortData
+ }
+
+ // Copy data to dst
+ write := outSize
+ for _, shard := range shards {
+ if write < len(shard) {
+ _, err := dst.Write(shard[:write])
+ return err
+ }
+ n, err := dst.Write(shard)
+ if err != nil {
+ return err
+ }
+ write -= n
+ }
+ return nil
+}
diff --git a/vendor/github.com/klauspost/reedsolomon/streaming.go b/vendor/github.com/klauspost/reedsolomon/streaming.go
new file mode 100644
index 0000000..d048ba0
--- /dev/null
+++ b/vendor/github.com/klauspost/reedsolomon/streaming.go
@@ -0,0 +1,603 @@
+/**
+ * Reed-Solomon Coding over 8-bit values.
+ *
+ * Copyright 2015, Klaus Post
+ * Copyright 2015, Backblaze, Inc.
+ */
+
+package reedsolomon
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "sync"
+)
+
+// StreamEncoder is an interface to encode Reed-Salomon parity sets for your data.
+// It provides a fully streaming interface, and processes data in blocks of up to 4MB.
+//
+// For small shard sizes, 10MB and below, it is recommended to use the in-memory interface,
+// since the streaming interface has a start up overhead.
+//
+// For all operations, no readers and writers should not assume any order/size of
+// individual reads/writes.
+//
+// For usage examples, see "stream-encoder.go" and "streamdecoder.go" in the examples
+// folder.
+type StreamEncoder interface {
+ // Encode parity shards for a set of data shards.
+ //
+ // Input is 'shards' containing readers for data shards followed by parity shards
+ // io.Writer.
+ //
+ // The number of shards must match the number given to NewStream().
+ //
+ // Each reader must supply the same number of bytes.
+ //
+ // The parity shards will be written to the writer.
+ // The number of bytes written will match the input size.
+ //
+ // If a data stream returns an error, a StreamReadError type error
+ // will be returned. If a parity writer returns an error, a
+ // StreamWriteError will be returned.
+ Encode(data []io.Reader, parity []io.Writer) error
+
+ // Verify returns true if the parity shards contain correct data.
+ //
+ // The number of shards must match the number total data+parity shards
+ // given to NewStream().
+ //
+ // Each reader must supply the same number of bytes.
+ // If a shard stream returns an error, a StreamReadError type error
+ // will be returned.
+ Verify(shards []io.Reader) (bool, error)
+
+ // Reconstruct will recreate the missing shards if possible.
+ //
+ // Given a list of valid shards (to read) and invalid shards (to write)
+ //
+ // You indicate that a shard is missing by setting it to nil in the 'valid'
+ // slice and at the same time setting a non-nil writer in "fill".
+ // An index cannot contain both non-nil 'valid' and 'fill' entry.
+ // If both are provided 'ErrReconstructMismatch' is returned.
+ //
+ // If there are too few shards to reconstruct the missing
+ // ones, ErrTooFewShards will be returned.
+ //
+ // The reconstructed shard set is complete, but integrity is not verified.
+ // Use the Verify function to check if data set is ok.
+ Reconstruct(valid []io.Reader, fill []io.Writer) error
+
+ // Split a an input stream into the number of shards given to the encoder.
+ //
+ // The data will be split into equally sized shards.
+ // If the data size isn't dividable by the number of shards,
+ // the last shard will contain extra zeros.
+ //
+ // You must supply the total size of your input.
+ // 'ErrShortData' will be returned if it is unable to retrieve the
+ // number of bytes indicated.
+ Split(data io.Reader, dst []io.Writer, size int64) (err error)
+
+ // Join the shards and write the data segment to dst.
+ //
+ // Only the data shards are considered.
+ //
+ // You must supply the exact output size you want.
+ // If there are to few shards given, ErrTooFewShards will be returned.
+ // If the total data size is less than outSize, ErrShortData will be returned.
+ Join(dst io.Writer, shards []io.Reader, outSize int64) error
+}
+
+// StreamReadError is returned when a read error is encountered
+// that relates to a supplied stream.
+// This will allow you to find out which reader has failed.
+type StreamReadError struct {
+ Err error // The error
+ Stream int // The stream number on which the error occurred
+}
+
+// Error returns the error as a string
+func (s StreamReadError) Error() string {
+ return fmt.Sprintf("error reading stream %d: %s", s.Stream, s.Err)
+}
+
+// String returns the error as a string
+func (s StreamReadError) String() string {
+ return s.Error()
+}
+
+// StreamWriteError is returned when a write error is encountered
+// that relates to a supplied stream. This will allow you to
+// find out which reader has failed.
+type StreamWriteError struct {
+ Err error // The error
+ Stream int // The stream number on which the error occurred
+}
+
+// Error returns the error as a string
+func (s StreamWriteError) Error() string {
+ return fmt.Sprintf("error writing stream %d: %s", s.Stream, s.Err)
+}
+
+// String returns the error as a string
+func (s StreamWriteError) String() string {
+ return s.Error()
+}
+
+// rsStream contains a matrix for a specific
+// distribution of datashards and parity shards.
+// Construct if using NewStream()
+type rsStream struct {
+ r *reedSolomon
+ o options
+
+ // Shard reader
+ readShards func(dst [][]byte, in []io.Reader) error
+ // Shard writer
+ writeShards func(out []io.Writer, in [][]byte) error
+
+ blockPool sync.Pool
+}
+
+// NewStream 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 data shards is 256.
+func NewStream(dataShards, parityShards int, o ...Option) (StreamEncoder, error) {
+ r := rsStream{o: defaultOptions}
+ for _, opt := range o {
+ opt(&r.o)
+ }
+ // Override block size if shard size is set.
+ if r.o.streamBS == 0 && r.o.shardSize > 0 {
+ r.o.streamBS = r.o.shardSize
+ }
+ if r.o.streamBS <= 0 {
+ r.o.streamBS = 4 << 20
+ }
+ if r.o.shardSize == 0 && r.o.maxGoroutines == defaultOptions.maxGoroutines {
+ o = append(o, WithAutoGoroutines(r.o.streamBS))
+ }
+
+ enc, err := New(dataShards, parityShards, o...)
+ if err != nil {
+ return nil, err
+ }
+ 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
+ }
+ r.readShards = readShards
+ r.writeShards = writeShards
+ if r.o.concReads {
+ r.readShards = cReadShards
+ }
+ if r.o.concWrites {
+ r.writeShards = cWriteShards
+ }
+
+ return &r, err
+}
+
+// NewStreamC creates a new encoder and initializes it to
+// the number of data shards and parity shards given.
+//
+// This functions as 'NewStream', but allows you to enable CONCURRENT reads and writes.
+func NewStreamC(dataShards, parityShards int, conReads, conWrites bool, o ...Option) (StreamEncoder, error) {
+ return NewStream(dataShards, parityShards, append(o, WithConcurrentStreamReads(conReads), WithConcurrentStreamWrites(conWrites))...)
+}
+
+func (r *rsStream) createSlice() [][]byte {
+ out := r.blockPool.Get().([][]byte)
+ for i := range out {
+ out[i] = out[i][:r.o.streamBS]
+ }
+ return out
+}
+
+// Encodes parity shards for a set of data shards.
+//
+// Input is 'shards' containing readers for data shards followed by parity shards
+// io.Writer.
+//
+// The number of shards must match the number given to NewStream().
+//
+// Each reader must supply the same number of bytes.
+//
+// The parity shards will be written to the writer.
+// The number of bytes written will match the input size.
+//
+// If a data stream returns an error, a StreamReadError type error
+// 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 {
+ return ErrTooFewShards
+ }
+
+ 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:]
+ read := 0
+
+ for {
+ err := r.readShards(in, data)
+ switch err {
+ case nil:
+ case io.EOF:
+ if read == 0 {
+ return ErrShardNoData
+ }
+ return nil
+ default:
+ return err
+ }
+ out = trimShards(out, shardSize(in))
+ read += shardSize(in)
+ err = r.r.Encode(all)
+ if err != nil {
+ return err
+ }
+ err = r.writeShards(parity, out)
+ if err != nil {
+ return err
+ }
+ }
+}
+
+// Trim the shards so they are all the same size
+func trimShards(in [][]byte, size int) [][]byte {
+ for i := range in {
+ if len(in[i]) != 0 {
+ in[i] = in[i][0:size]
+ }
+ if len(in[i]) < size {
+ in[i] = in[i][:0]
+ }
+ }
+ return in
+}
+
+func readShards(dst [][]byte, in []io.Reader) error {
+ if len(in) != len(dst) {
+ panic("internal error: in and dst size do not match")
+ }
+ size := -1
+ for i := range in {
+ if in[i] == nil {
+ dst[i] = dst[i][:0]
+ continue
+ }
+ n, err := io.ReadFull(in[i], dst[i])
+ // The error is EOF only if no bytes were read.
+ // If an EOF happens after reading some but not all the bytes,
+ // ReadFull returns ErrUnexpectedEOF.
+ switch err {
+ case io.ErrUnexpectedEOF, io.EOF:
+ if size < 0 {
+ size = n
+ } else if n != size {
+ // Shard sizes must match.
+ return ErrShardSize
+ }
+ dst[i] = dst[i][0:n]
+ case nil:
+ continue
+ default:
+ return StreamReadError{Err: err, Stream: i}
+ }
+ }
+ if size == 0 {
+ return io.EOF
+ }
+ return nil
+}
+
+func writeShards(out []io.Writer, in [][]byte) error {
+ if len(out) != len(in) {
+ panic("internal error: in and out size do not match")
+ }
+ for i := range in {
+ if out[i] == nil {
+ continue
+ }
+ n, err := out[i].Write(in[i])
+ if err != nil {
+ return StreamWriteError{Err: err, Stream: i}
+ }
+ //
+ if n != len(in[i]) {
+ return StreamWriteError{Err: io.ErrShortWrite, Stream: i}
+ }
+ }
+ return nil
+}
+
+type readResult struct {
+ n int
+ size int
+ err error
+}
+
+// cReadShards reads shards concurrently
+func cReadShards(dst [][]byte, in []io.Reader) error {
+ if len(in) != len(dst) {
+ panic("internal error: in and dst size do not match")
+ }
+ var wg sync.WaitGroup
+ wg.Add(len(in))
+ res := make(chan readResult, len(in))
+ for i := range in {
+ if in[i] == nil {
+ dst[i] = dst[i][:0]
+ wg.Done()
+ continue
+ }
+ go func(i int) {
+ defer wg.Done()
+ n, err := io.ReadFull(in[i], dst[i])
+ // The error is EOF only if no bytes were read.
+ // If an EOF happens after reading some but not all the bytes,
+ // ReadFull returns ErrUnexpectedEOF.
+ res <- readResult{size: n, err: err, n: i}
+
+ }(i)
+ }
+ wg.Wait()
+ close(res)
+ size := -1
+ for r := range res {
+ switch r.err {
+ case io.ErrUnexpectedEOF, io.EOF:
+ if size < 0 {
+ size = r.size
+ } else if r.size != size {
+ // Shard sizes must match.
+ return ErrShardSize
+ }
+ dst[r.n] = dst[r.n][0:r.size]
+ case nil:
+ default:
+ return StreamReadError{Err: r.err, Stream: r.n}
+ }
+ }
+ if size == 0 {
+ return io.EOF
+ }
+ return nil
+}
+
+// cWriteShards writes shards concurrently
+func cWriteShards(out []io.Writer, in [][]byte) error {
+ if len(out) != len(in) {
+ panic("internal error: in and out size do not match")
+ }
+ var errs = make(chan error, len(out))
+ var wg sync.WaitGroup
+ wg.Add(len(out))
+ for i := range in {
+ go func(i int) {
+ defer wg.Done()
+ if out[i] == nil {
+ errs <- nil
+ return
+ }
+ n, err := out[i].Write(in[i])
+ if err != nil {
+ errs <- StreamWriteError{Err: err, Stream: i}
+ return
+ }
+ if n != len(in[i]) {
+ errs <- StreamWriteError{Err: io.ErrShortWrite, Stream: i}
+ }
+ }(i)
+ }
+ wg.Wait()
+ close(errs)
+ for err := range errs {
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Verify returns true if the parity shards contain correct data.
+//
+// The number of shards must match the number total data+parity shards
+// given to NewStream().
+//
+// Each reader must supply the same number of bytes.
+// 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 {
+ return false, ErrTooFewShards
+ }
+
+ read := 0
+ all := r.createSlice()
+ defer r.blockPool.Put(all)
+ for {
+ err := r.readShards(all, shards)
+ if err == io.EOF {
+ if read == 0 {
+ return false, ErrShardNoData
+ }
+ return true, nil
+ }
+ if err != nil {
+ return false, err
+ }
+ read += shardSize(all)
+ ok, err := r.r.Verify(all)
+ if !ok || err != nil {
+ return ok, err
+ }
+ }
+}
+
+// ErrReconstructMismatch is returned by the StreamEncoder, if you supply
+// "valid" and "fill" streams on the same index.
+// Therefore it is impossible to see if you consider the shard valid
+// or would like to have it reconstructed.
+var ErrReconstructMismatch = errors.New("valid shards and fill shards are mutually exclusive")
+
+// Reconstruct will recreate the missing shards if possible.
+//
+// Given a list of valid shards (to read) and invalid shards (to write)
+//
+// You indicate that a shard is missing by setting it to nil in the 'valid'
+// slice and at the same time setting a non-nil writer in "fill".
+// An index cannot contain both non-nil 'valid' and 'fill' entry.
+//
+// If there are too few shards to reconstruct the missing
+// ones, ErrTooFewShards will be returned.
+//
+// The reconstructed shard set is complete when explicitly asked for all missing shards.
+// 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 {
+ return ErrTooFewShards
+ }
+ if len(fill) != r.r.Shards {
+ return ErrTooFewShards
+ }
+
+ all := r.createSlice()
+ defer r.blockPool.Put(all)
+ reconDataOnly := true
+ for i := range valid {
+ if valid[i] != nil && fill[i] != nil {
+ return ErrReconstructMismatch
+ }
+ if i >= r.r.DataShards && fill[i] != nil {
+ reconDataOnly = false
+ }
+ }
+
+ read := 0
+ for {
+ err := r.readShards(all, valid)
+ if err == io.EOF {
+ if read == 0 {
+ return ErrShardNoData
+ }
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ read += shardSize(all)
+ all = trimShards(all, shardSize(all))
+
+ if reconDataOnly {
+ err = r.r.ReconstructData(all) // just reconstruct missing data shards
+ } else {
+ err = r.r.Reconstruct(all) // reconstruct all missing shards
+ }
+ if err != nil {
+ return err
+ }
+ err = r.writeShards(fill, all)
+ if err != nil {
+ return err
+ }
+ }
+}
+
+// Join the shards and write the data segment to dst.
+//
+// Only the data shards are considered.
+//
+// You must supply the exact output size you want.
+// If there are to few shards given, ErrTooFewShards will be returned.
+// 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 {
+ return ErrTooFewShards
+ }
+
+ // Trim off parity shards if any
+ shards = shards[:r.r.DataShards]
+ for i := range shards {
+ if shards[i] == nil {
+ return StreamReadError{Err: ErrShardNoData, Stream: i}
+ }
+ }
+ // Join all shards
+ src := io.MultiReader(shards...)
+
+ // Copy data to dst
+ n, err := io.CopyN(dst, src, outSize)
+ if err == io.EOF {
+ return ErrShortData
+ }
+ if err != nil {
+ return err
+ }
+ if n != outSize {
+ return ErrShortData
+ }
+ return nil
+}
+
+// Split a an input stream into the number of shards given to the encoder.
+//
+// The data will be split into equally sized shards.
+// If the data size isn't dividable by the number of shards,
+// the last shard will contain extra zeros.
+//
+// You must supply the total size of your input.
+// 'ErrShortData' will be returned if it is unable to retrieve the
+// number of bytes indicated.
+func (r *rsStream) Split(data io.Reader, dst []io.Writer, size int64) error {
+ if size == 0 {
+ return ErrShortData
+ }
+ if len(dst) != r.r.DataShards {
+ return ErrInvShardNum
+ }
+
+ for i := range dst {
+ if dst[i] == nil {
+ return StreamWriteError{Err: ErrShardNoData, Stream: i}
+ }
+ }
+
+ // Calculate number of bytes per shard.
+ 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))
+
+ // Split into equal-length shards and copy.
+ for i := range dst {
+ n, err := io.CopyN(dst[i], data, perShard)
+ if err != io.EOF && err != nil {
+ return err
+ }
+ if n != perShard {
+ return ErrShortData
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/mmcloughlin/avo/LICENSE b/vendor/github.com/mmcloughlin/avo/LICENSE
new file mode 100644
index 0000000..c986d80
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2018, Michael McLoughlin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* 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.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+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.
diff --git a/vendor/github.com/mmcloughlin/avo/attr/attr.go b/vendor/github.com/mmcloughlin/avo/attr/attr.go
new file mode 100644
index 0000000..016e0a4
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/attr/attr.go
@@ -0,0 +1,102 @@
+// Package attr provides attributes for text and data sections.
+package attr
+
+import (
+ "fmt"
+ "math/bits"
+ "strings"
+)
+
+// Attribute represents TEXT or DATA flags.
+type Attribute uint16
+
+// Reference: https://github.com/golang/go/blob/aafe257390cc9048e8b5df898fabd79a9e0d4c39/src/runtime/textflag.h#L11-L37
+//
+// // Don't profile the marked routine. This flag is deprecated.
+// #define NOPROF 1
+// // It is ok for the linker to get multiple of these symbols. It will
+// // pick one of the duplicates to use.
+// #define DUPOK 2
+// // Don't insert stack check preamble.
+// #define NOSPLIT 4
+// // Put this data in a read-only section.
+// #define RODATA 8
+// // This data contains no pointers.
+// #define NOPTR 16
+// // This is a wrapper function and should not count as disabling 'recover'.
+// #define WRAPPER 32
+// // This function uses its incoming context register.
+// #define NEEDCTXT 64
+// // Allocate a word of thread local storage and store the offset from the
+// // thread local base to the thread local storage in this variable.
+// #define TLSBSS 256
+// // Do not insert instructions to allocate a stack frame for this function.
+// // Only valid on functions that declare a frame size of 0.
+// // TODO(mwhudson): only implemented for ppc64x at present.
+// #define NOFRAME 512
+// // Function can call reflect.Type.Method or reflect.Type.MethodByName.
+// #define REFLECTMETHOD 1024
+// // Function is the top of the call stack. Call stack unwinders should stop
+// // at this function.
+// #define TOPFRAME 2048
+//
+const (
+ NOPROF Attribute = 1 << iota
+ DUPOK
+ NOSPLIT
+ RODATA
+ NOPTR
+ WRAPPER
+ NEEDCTXT
+ _
+ TLSBSS
+ NOFRAME
+ REFLECTMETHOD
+ TOPFRAME
+)
+
+// Asm returns a representation of the attributes in assembly syntax. This may use macros from "textflags.h"; see ContainsTextFlags() to determine if this header is required.
+func (a Attribute) Asm() string {
+ parts, rest := a.split()
+ if len(parts) == 0 || rest != 0 {
+ parts = append(parts, fmt.Sprintf("%d", rest))
+ }
+ return strings.Join(parts, "|")
+}
+
+// ContainsTextFlags returns whether the Asm() representation requires macros in "textflags.h".
+func (a Attribute) ContainsTextFlags() bool {
+ flags, _ := a.split()
+ return len(flags) > 0
+}
+
+// split splits a into known flags and any remaining bits.
+func (a Attribute) split() ([]string, Attribute) {
+ var flags []string
+ var rest Attribute
+ for a != 0 {
+ i := uint(bits.TrailingZeros16(uint16(a)))
+ bit := Attribute(1) << i
+ if flag := attrname[bit]; flag != "" {
+ flags = append(flags, flag)
+ } else {
+ rest |= bit
+ }
+ a ^= bit
+ }
+ return flags, rest
+}
+
+var attrname = map[Attribute]string{
+ NOPROF: "NOPROF",
+ DUPOK: "DUPOK",
+ NOSPLIT: "NOSPLIT",
+ RODATA: "RODATA",
+ NOPTR: "NOPTR",
+ WRAPPER: "WRAPPER",
+ NEEDCTXT: "NEEDCTXT",
+ TLSBSS: "TLSBSS",
+ NOFRAME: "NOFRAME",
+ REFLECTMETHOD: "REFLECTMETHOD",
+ TOPFRAME: "TOPFRAME",
+}
diff --git a/vendor/github.com/mmcloughlin/avo/buildtags/buildtags.go b/vendor/github.com/mmcloughlin/avo/buildtags/buildtags.go
new file mode 100644
index 0000000..8fd61e1
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/buildtags/buildtags.go
@@ -0,0 +1,312 @@
+// Package buildtags provides types for representing and manipulating build constraints.
+//
+// In Go, build constraints are represented as comments in source code together with file naming conventions. For example
+//
+// // +build linux,386 darwin,!cgo
+// // +build !purego
+//
+// Any terms provided in the filename can be thought of as an implicit extra
+// constraint comment line. Collectively, these are referred to as
+// ``constraints''. Each line is a ``constraint''. Within each constraint the
+// space-separated terms are ``options'', and within that the comma-separated
+// items are ``terms'' which may be negated with at most one exclaimation mark.
+//
+// These represent a boolean formulae. The constraints are evaluated as the AND
+// of constraint lines; a constraint is evaluated as the OR of its options and
+// an option is evaluated as the AND of its terms. Overall build constraints are
+// a boolean formula that is an AND of ORs of ANDs.
+//
+// This level of complexity is rarely used in Go programs. Therefore this
+// package aims to provide access to all these layers of nesting if required,
+// but make it easy to forget about for basic use cases too.
+package buildtags
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+ "unicode"
+)
+
+// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/go/build/doc.go#L73-L92
+//
+// // A build constraint is evaluated as the OR of space-separated options;
+// // each option evaluates as the AND of its comma-separated terms;
+// // and each term is an alphanumeric word or, preceded by !, its negation.
+// // That is, the build constraint:
+// //
+// // // +build linux,386 darwin,!cgo
+// //
+// // corresponds to the boolean formula:
+// //
+// // (linux AND 386) OR (darwin AND (NOT cgo))
+// //
+// // A file may have multiple build constraints. The overall constraint is the AND
+// // of the individual constraints. That is, the build constraints:
+// //
+// // // +build linux darwin
+// // // +build 386
+// //
+// // corresponds to the boolean formula:
+// //
+// // (linux OR darwin) AND 386
+//
+
+// Interface represents a build constraint.
+type Interface interface {
+ ConstraintsConvertable
+ fmt.GoStringer
+ Evaluate(v map[string]bool) bool
+ Validate() error
+}
+
+// ConstraintsConvertable can be converted to a Constraints object.
+type ConstraintsConvertable interface {
+ ToConstraints() Constraints
+}
+
+// ConstraintConvertable can be converted to a Constraint.
+type ConstraintConvertable interface {
+ ToConstraint() Constraint
+}
+
+// OptionConvertable can be converted to an Option.
+type OptionConvertable interface {
+ ToOption() Option
+}
+
+// Constraints represents the AND of a list of Constraint lines.
+type Constraints []Constraint
+
+// And builds Constraints that will be true if all of its constraints are true.
+func And(cs ...ConstraintConvertable) Constraints {
+ constraints := Constraints{}
+ for _, c := range cs {
+ constraints = append(constraints, c.ToConstraint())
+ }
+ return constraints
+}
+
+// ToConstraints returns cs.
+func (cs Constraints) ToConstraints() Constraints { return cs }
+
+// Validate validates the constraints set.
+func (cs Constraints) Validate() error {
+ for _, c := range cs {
+ if err := c.Validate(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Evaluate the boolean formula represented by cs under the given assignment of
+// tag values. This is the AND of the values of the constituent Constraints.
+func (cs Constraints) Evaluate(v map[string]bool) bool {
+ r := true
+ for _, c := range cs {
+ r = r && c.Evaluate(v)
+ }
+ return r
+}
+
+// GoString represents Constraints as +build comment lines.
+func (cs Constraints) GoString() string {
+ s := ""
+ for _, c := range cs {
+ s += c.GoString()
+ }
+ return s
+}
+
+// Constraint represents the OR of a list of Options.
+type Constraint []Option
+
+// Any builds a Constraint that will be true if any of its options are true.
+func Any(opts ...OptionConvertable) Constraint {
+ c := Constraint{}
+ for _, opt := range opts {
+ c = append(c, opt.ToOption())
+ }
+ return c
+}
+
+// ParseConstraint parses a space-separated list of options.
+func ParseConstraint(expr string) (Constraint, error) {
+ c := Constraint{}
+ for _, field := range strings.Fields(expr) {
+ opt, err := ParseOption(field)
+ if err != nil {
+ return c, err
+ }
+ c = append(c, opt)
+ }
+ return c, nil
+}
+
+// ToConstraints returns the list of constraints containing just c.
+func (c Constraint) ToConstraints() Constraints { return Constraints{c} }
+
+// ToConstraint returns c.
+func (c Constraint) ToConstraint() Constraint { return c }
+
+// Validate validates the constraint.
+func (c Constraint) Validate() error {
+ for _, o := range c {
+ if err := o.Validate(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Evaluate the boolean formula represented by c under the given assignment of
+// tag values. This is the OR of the values of the constituent Options.
+func (c Constraint) Evaluate(v map[string]bool) bool {
+ r := false
+ for _, o := range c {
+ r = r || o.Evaluate(v)
+ }
+ return r
+}
+
+// GoString represents the Constraint as one +build comment line.
+func (c Constraint) GoString() string {
+ s := "// +build"
+ for _, o := range c {
+ s += " " + o.GoString()
+ }
+ return s + "\n"
+}
+
+// Option represents the AND of a list of Terms.
+type Option []Term
+
+// Opt builds an Option from the list of Terms.
+func Opt(terms ...Term) Option {
+ return Option(terms)
+}
+
+// ParseOption parses a comma-separated list of terms.
+func ParseOption(expr string) (Option, error) {
+ opt := Option{}
+ for _, t := range strings.Split(expr, ",") {
+ opt = append(opt, Term(t))
+ }
+ return opt, opt.Validate()
+}
+
+// ToConstraints returns Constraints containing just this option.
+func (o Option) ToConstraints() Constraints { return o.ToConstraint().ToConstraints() }
+
+// ToConstraint returns a Constraint containing just this option.
+func (o Option) ToConstraint() Constraint { return Constraint{o} }
+
+// ToOption returns o.
+func (o Option) ToOption() Option { return o }
+
+// Validate validates o.
+func (o Option) Validate() error {
+ for _, t := range o {
+ if err := t.Validate(); err != nil {
+ return fmt.Errorf("invalid term \"%s\": %s", t, err)
+ }
+ }
+ return nil
+}
+
+// Evaluate the boolean formula represented by o under the given assignment of
+// tag values. This is the AND of the values of the constituent Terms.
+func (o Option) Evaluate(v map[string]bool) bool {
+ r := true
+ for _, t := range o {
+ r = r && t.Evaluate(v)
+ }
+ return r
+}
+
+// GoString represents the Option as a comma-separated list of terms.
+func (o Option) GoString() string {
+ var ts []string
+ for _, t := range o {
+ ts = append(ts, t.GoString())
+ }
+ return strings.Join(ts, ",")
+}
+
+// Term is an atomic term in a build constraint: an identifier or its negation.
+type Term string
+
+// Not returns a term for the negation of ident.
+func Not(ident string) Term {
+ return Term("!" + ident)
+}
+
+// ToConstraints returns Constraints containing just this term.
+func (t Term) ToConstraints() Constraints { return t.ToOption().ToConstraints() }
+
+// ToConstraint returns a Constraint containing just this term.
+func (t Term) ToConstraint() Constraint { return t.ToOption().ToConstraint() }
+
+// ToOption returns an Option containing just this term.
+func (t Term) ToOption() Option { return Option{t} }
+
+// IsNegated reports whether t is the negation of an identifier.
+func (t Term) IsNegated() bool { return strings.HasPrefix(string(t), "!") }
+
+// Name returns the identifier for this term.
+func (t Term) Name() string {
+ return strings.TrimPrefix(string(t), "!")
+}
+
+// Validate the term.
+func (t Term) Validate() error {
+ // Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L110-L112
+ //
+ // if strings.HasPrefix(name, "!!") { // bad syntax, reject always
+ // return false
+ // }
+ //
+ if strings.HasPrefix(string(t), "!!") {
+ return errors.New("at most one '!' allowed")
+ }
+
+ if len(t.Name()) == 0 {
+ return errors.New("empty tag name")
+ }
+
+ // Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L121-L127
+ //
+ // // Tags must be letters, digits, underscores or dots.
+ // // Unlike in Go identifiers, all digits are fine (e.g., "386").
+ // for _, c := range name {
+ // if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+ // return false
+ // }
+ // }
+ //
+ for _, c := range t.Name() {
+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+ return fmt.Errorf("character '%c' disallowed in tags", c)
+ }
+ }
+
+ return nil
+}
+
+// Evaluate the term under the given set of identifier values.
+func (t Term) Evaluate(v map[string]bool) bool {
+ return (t.Validate() == nil) && (v[t.Name()] == !t.IsNegated())
+}
+
+// GoString returns t.
+func (t Term) GoString() string { return string(t) }
+
+// SetTags builds a set where the given list of identifiers are true.
+func SetTags(idents ...string) map[string]bool {
+ v := map[string]bool{}
+ for _, ident := range idents {
+ v[ident] = true
+ }
+ return v
+}
diff --git a/vendor/github.com/mmcloughlin/avo/gotypes/components.go b/vendor/github.com/mmcloughlin/avo/gotypes/components.go
new file mode 100644
index 0000000..2206afa
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/gotypes/components.go
@@ -0,0 +1,253 @@
+package gotypes
+
+import (
+ "errors"
+ "fmt"
+ "go/token"
+ "go/types"
+ "strconv"
+
+ "github.com/mmcloughlin/avo/reg"
+
+ "github.com/mmcloughlin/avo/operand"
+)
+
+// Sizes provides type sizes used by the standard Go compiler on amd64.
+var Sizes = types.SizesFor("gc", "amd64")
+
+// Basic represents a primitive/basic type at a given memory address.
+type Basic struct {
+ Addr operand.Mem
+ Type *types.Basic
+}
+
+// Component provides access to sub-components of a Go type.
+type Component interface {
+ // When the component has no further sub-components, Resolve will return a
+ // reference to the components type and memory address. If there was an error
+ // during any previous calls to Component methods, they will be returned at
+ // resolution time.
+ Resolve() (*Basic, error)
+
+ Dereference(r reg.Register) Component // dereference a pointer
+ Base() Component // base pointer of a string or slice
+ Len() Component // length of a string or slice
+ Cap() Component // capacity of a slice
+ Real() Component // real part of a complex value
+ Imag() Component // imaginary part of a complex value
+ Index(int) Component // index into an array
+ Field(string) Component // access a struct field
+}
+
+// componenterr is an error that also provides a null implementation of the
+// Component interface. This enables us to return an error from Component
+// methods whilst also allowing method chaining to continue.
+type componenterr string
+
+func errorf(format string, args ...interface{}) Component {
+ return componenterr(fmt.Sprintf(format, args...))
+}
+
+func (c componenterr) Error() string { return string(c) }
+func (c componenterr) Resolve() (*Basic, error) { return nil, c }
+func (c componenterr) Dereference(r reg.Register) Component { return c }
+func (c componenterr) Base() Component { return c }
+func (c componenterr) Len() Component { return c }
+func (c componenterr) Cap() Component { return c }
+func (c componenterr) Real() Component { return c }
+func (c componenterr) Imag() Component { return c }
+func (c componenterr) Index(int) Component { return c }
+func (c componenterr) Field(string) Component { return c }
+
+type component struct {
+ typ types.Type
+ addr operand.Mem
+}
+
+// NewComponent builds a component for the named type at the given address.
+func NewComponent(t types.Type, addr operand.Mem) Component {
+ return &component{
+ typ: t,
+ addr: addr,
+ }
+}
+
+func (c *component) Resolve() (*Basic, error) {
+ b := toprimitive(c.typ)
+ if b == nil {
+ return nil, errors.New("component is not primitive")
+ }
+ return &Basic{
+ Addr: c.addr,
+ Type: b,
+ }, nil
+}
+
+func (c *component) Dereference(r reg.Register) Component {
+ p, ok := c.typ.Underlying().(*types.Pointer)
+ if !ok {
+ return errorf("not pointer type")
+ }
+ return NewComponent(p.Elem(), operand.Mem{Base: r})
+}
+
+// Reference: https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/reflect/value.go#L1800-L1804
+//
+// type SliceHeader struct {
+// Data uintptr
+// Len int
+// Cap int
+// }
+//
+var slicehdroffsets = Sizes.Offsetsof([]*types.Var{
+ types.NewField(token.NoPos, nil, "Data", types.Typ[types.Uintptr], false),
+ types.NewField(token.NoPos, nil, "Len", types.Typ[types.Int], false),
+ types.NewField(token.NoPos, nil, "Cap", types.Typ[types.Int], false),
+})
+
+func (c *component) Base() Component {
+ if !isslice(c.typ) && !isstring(c.typ) {
+ return errorf("only slices and strings have base pointers")
+ }
+ return c.sub("_base", int(slicehdroffsets[0]), types.Typ[types.Uintptr])
+}
+
+func (c *component) Len() Component {
+ if !isslice(c.typ) && !isstring(c.typ) {
+ return errorf("only slices and strings have length fields")
+ }
+ return c.sub("_len", int(slicehdroffsets[1]), types.Typ[types.Int])
+}
+
+func (c *component) Cap() Component {
+ if !isslice(c.typ) {
+ return errorf("only slices have capacity fields")
+ }
+ return c.sub("_cap", int(slicehdroffsets[2]), types.Typ[types.Int])
+}
+
+func (c *component) Real() Component {
+ if !iscomplex(c.typ) {
+ return errorf("only complex types have real values")
+ }
+ f := complextofloat(c.typ)
+ return c.sub("_real", 0, f)
+}
+
+func (c *component) Imag() Component {
+ if !iscomplex(c.typ) {
+ return errorf("only complex types have imaginary values")
+ }
+ f := complextofloat(c.typ)
+ return c.sub("_imag", int(Sizes.Sizeof(f)), f)
+}
+
+func (c *component) Index(i int) Component {
+ a, ok := c.typ.Underlying().(*types.Array)
+ if !ok {
+ return errorf("not array type")
+ }
+ if int64(i) >= a.Len() {
+ return errorf("array index out of bounds")
+ }
+ // Reference: https://github.com/golang/tools/blob/bcd4e47d02889ebbc25c9f4bf3d27e4124b0bf9d/go/analysis/passes/asmdecl/asmdecl.go#L482-L494
+ //
+ // case asmArray:
+ // tu := t.Underlying().(*types.Array)
+ // elem := tu.Elem()
+ // // Calculate offset of each element array.
+ // fields := []*types.Var{
+ // types.NewVar(token.NoPos, nil, "fake0", elem),
+ // types.NewVar(token.NoPos, nil, "fake1", elem),
+ // }
+ // offsets := arch.sizes.Offsetsof(fields)
+ // elemoff := int(offsets[1])
+ // for i := 0; i < int(tu.Len()); i++ {
+ // cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
+ // }
+ //
+ elem := a.Elem()
+ elemsize := int(Sizes.Sizeof(types.NewArray(elem, 2)) - Sizes.Sizeof(types.NewArray(elem, 1)))
+ return c.sub("_"+strconv.Itoa(i), i*elemsize, elem)
+}
+
+func (c *component) Field(n string) Component {
+ s, ok := c.typ.Underlying().(*types.Struct)
+ if !ok {
+ return errorf("not struct type")
+ }
+ // Reference: https://github.com/golang/tools/blob/13ba8ad772dfbf0f451b5dd0679e9c5605afc05d/go/analysis/passes/asmdecl/asmdecl.go#L471-L480
+ //
+ // case asmStruct:
+ // tu := t.Underlying().(*types.Struct)
+ // fields := make([]*types.Var, tu.NumFields())
+ // for i := 0; i < tu.NumFields(); i++ {
+ // fields[i] = tu.Field(i)
+ // }
+ // offsets := arch.sizes.Offsetsof(fields)
+ // for i, f := range fields {
+ // cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
+ // }
+ //
+ fields := make([]*types.Var, s.NumFields())
+ for i := 0; i < s.NumFields(); i++ {
+ fields[i] = s.Field(i)
+ }
+ offsets := Sizes.Offsetsof(fields)
+ for i, f := range fields {
+ if f.Name() == n {
+ return c.sub("_"+n, int(offsets[i]), f.Type())
+ }
+ }
+ return errorf("struct does not have field '%s'", n)
+}
+
+func (c *component) sub(suffix string, offset int, t types.Type) *component {
+ s := *c
+ if s.addr.Symbol.Name != "" {
+ s.addr.Symbol.Name += suffix
+ }
+ s.addr = s.addr.Offset(offset)
+ s.typ = t
+ return &s
+}
+
+func isslice(t types.Type) bool {
+ _, ok := t.Underlying().(*types.Slice)
+ return ok
+}
+
+func isstring(t types.Type) bool {
+ b, ok := t.Underlying().(*types.Basic)
+ return ok && b.Kind() == types.String
+}
+
+func iscomplex(t types.Type) bool {
+ b, ok := t.Underlying().(*types.Basic)
+ return ok && (b.Info()&types.IsComplex) != 0
+}
+
+func complextofloat(t types.Type) types.Type {
+ switch Sizes.Sizeof(t) {
+ case 16:
+ return types.Typ[types.Float64]
+ case 8:
+ return types.Typ[types.Float32]
+ }
+ panic("bad")
+}
+
+// toprimitive determines whether t is primitive (cannot be reduced into
+// components). If it is, it returns the basic type for t, otherwise returns
+// nil.
+func toprimitive(t types.Type) *types.Basic {
+ switch b := t.(type) {
+ case *types.Basic:
+ if (b.Info() & (types.IsString | types.IsComplex)) == 0 {
+ return b
+ }
+ case *types.Pointer:
+ return types.Typ[types.Uintptr]
+ }
+ return nil
+}
diff --git a/vendor/github.com/mmcloughlin/avo/gotypes/doc.go b/vendor/github.com/mmcloughlin/avo/gotypes/doc.go
new file mode 100644
index 0000000..fa8f078
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/gotypes/doc.go
@@ -0,0 +1,2 @@
+// Package gotypes provides helpers for interacting with Go types within avo functions.
+package gotypes
diff --git a/vendor/github.com/mmcloughlin/avo/gotypes/signature.go b/vendor/github.com/mmcloughlin/avo/gotypes/signature.go
new file mode 100644
index 0000000..e000020
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/gotypes/signature.go
@@ -0,0 +1,177 @@
+package gotypes
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/token"
+ "go/types"
+ "strconv"
+
+ "github.com/mmcloughlin/avo/operand"
+)
+
+// Signature represents a Go function signature.
+type Signature struct {
+ pkg *types.Package
+ sig *types.Signature
+ params *Tuple
+ results *Tuple
+}
+
+// NewSignature constructs a Signature.
+func NewSignature(pkg *types.Package, sig *types.Signature) *Signature {
+ s := &Signature{
+ pkg: pkg,
+ sig: sig,
+ }
+ s.init()
+ return s
+}
+
+// NewSignatureVoid builds the void signature "func()".
+func NewSignatureVoid() *Signature {
+ return NewSignature(nil, types.NewSignature(nil, nil, nil, false))
+}
+
+// LookupSignature returns the signature of the named function in the provided package.
+func LookupSignature(pkg *types.Package, name string) (*Signature, error) {
+ scope := pkg.Scope()
+ obj := scope.Lookup(name)
+ if obj == nil {
+ return nil, fmt.Errorf("could not find function \"%s\"", name)
+ }
+ s, ok := obj.Type().(*types.Signature)
+ if !ok {
+ return nil, fmt.Errorf("object \"%s\" does not have signature type", name)
+ }
+ return NewSignature(pkg, s), nil
+}
+
+// ParseSignature builds a Signature by parsing a Go function type expression.
+// The function type must reference builtin types only; see
+// ParseSignatureInPackage if custom types are required.
+func ParseSignature(expr string) (*Signature, error) {
+ return ParseSignatureInPackage(nil, expr)
+}
+
+// ParseSignatureInPackage builds a Signature by parsing a Go function type
+// expression. The expression may reference types in the provided package.
+func ParseSignatureInPackage(pkg *types.Package, expr string) (*Signature, error) {
+ tv, err := types.Eval(token.NewFileSet(), pkg, token.NoPos, expr)
+ if err != nil {
+ return nil, err
+ }
+ if tv.Value != nil {
+ return nil, errors.New("signature expression should have nil value")
+ }
+ s, ok := tv.Type.(*types.Signature)
+ if !ok {
+ return nil, errors.New("provided type is not a function signature")
+ }
+ return NewSignature(pkg, s), nil
+}
+
+// Params returns the function signature argument types.
+func (s *Signature) Params() *Tuple { return s.params }
+
+// Results returns the function return types.
+func (s *Signature) Results() *Tuple { return s.results }
+
+// Bytes returns the total size of the function arguments and return values.
+func (s *Signature) Bytes() int { return s.Params().Bytes() + s.Results().Bytes() }
+
+// String writes Signature as a string. This does not include the "func" keyword.
+func (s *Signature) String() string {
+ var buf bytes.Buffer
+ types.WriteSignature(&buf, s.sig, func(pkg *types.Package) string {
+ if pkg == s.pkg {
+ return ""
+ }
+ return pkg.Name()
+ })
+ return buf.String()
+}
+
+func (s *Signature) init() {
+ p := s.sig.Params()
+ r := s.sig.Results()
+
+ // Compute parameter offsets.
+ vs := tuplevars(p)
+ vs = append(vs, types.NewParam(token.NoPos, nil, "sentinel", types.Typ[types.Uint64]))
+ paramsoffsets := Sizes.Offsetsof(vs)
+ paramssize := paramsoffsets[p.Len()]
+ s.params = newTuple(p, paramsoffsets, paramssize, "arg")
+
+ // Result offsets.
+ vs = tuplevars(r)
+ resultsoffsets := Sizes.Offsetsof(vs)
+ var resultssize int64
+ if n := len(vs); n > 0 {
+ resultssize = resultsoffsets[n-1] + Sizes.Sizeof(vs[n-1].Type())
+ }
+ for i := range resultsoffsets {
+ resultsoffsets[i] += paramssize
+ }
+ s.results = newTuple(r, resultsoffsets, resultssize, "ret")
+}
+
+// Tuple represents a tuple of variables, such as function arguments or results.
+type Tuple struct {
+ components []Component
+ byname map[string]Component
+ size int
+}
+
+func newTuple(t *types.Tuple, offsets []int64, size int64, defaultprefix string) *Tuple {
+ tuple := &Tuple{
+ byname: map[string]Component{},
+ size: int(size),
+ }
+ for i := 0; i < t.Len(); i++ {
+ v := t.At(i)
+ name := v.Name()
+ if name == "" {
+ name = defaultprefix
+ if i > 0 {
+ name += strconv.Itoa(i)
+ }
+ }
+ addr := operand.NewParamAddr(name, int(offsets[i]))
+ c := NewComponent(v.Type(), addr)
+ tuple.components = append(tuple.components, c)
+ if v.Name() != "" {
+ tuple.byname[v.Name()] = c
+ }
+ }
+ return tuple
+}
+
+// Lookup returns the variable with the given name.
+func (t *Tuple) Lookup(name string) Component {
+ e := t.byname[name]
+ if e == nil {
+ return errorf("unknown variable \"%s\"", name)
+ }
+ return e
+}
+
+// At returns the variable at index i.
+func (t *Tuple) At(i int) Component {
+ if i >= len(t.components) {
+ return errorf("index out of range")
+ }
+ return t.components[i]
+}
+
+// Bytes returns the size of the Tuple. This may include additional padding.
+func (t *Tuple) Bytes() int { return t.size }
+
+func tuplevars(t *types.Tuple) []*types.Var {
+ vs := make([]*types.Var, t.Len())
+ for i := 0; i < t.Len(); i++ {
+ vs[i] = t.At(i)
+ }
+ return vs
+}
diff --git a/vendor/github.com/mmcloughlin/avo/internal/prnt/printer.go b/vendor/github.com/mmcloughlin/avo/internal/prnt/printer.go
new file mode 100644
index 0000000..410895c
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/internal/prnt/printer.go
@@ -0,0 +1,60 @@
+// Package prnt provides common functionality for code generators.
+package prnt
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+// Generator provides convenience methods for code generators. In particular it
+// provides fmt-like methods which print to an internal buffer. It also allows
+// any errors to be stored so they can be checked at the end, rather than having
+// error checks obscuring the code generation.
+type Generator struct {
+ buf bytes.Buffer
+ err error
+}
+
+// Raw provides direct access to the underlying output stream.
+func (g *Generator) Raw() io.Writer {
+ return &g.buf
+}
+
+// Printf prints to the internal buffer.
+func (g *Generator) Printf(format string, args ...interface{}) {
+ if g.err != nil {
+ return
+ }
+ _, err := fmt.Fprintf(&g.buf, format, args...)
+ g.AddError(err)
+}
+
+// NL prints a new line.
+func (g *Generator) NL() {
+ g.Printf("\n")
+}
+
+// Comment writes comment lines prefixed with "// ".
+func (g *Generator) Comment(lines ...string) {
+ for _, line := range lines {
+ line = strings.TrimSpace("// " + line)
+ g.Printf("%s\n", line)
+ }
+}
+
+// AddError records an error in code generation. The first non-nil error will
+// prevent printing operations from writing anything else, and the error will be
+// returned from Result().
+func (g *Generator) AddError(err error) {
+ if err != nil && g.err == nil {
+ g.err = err
+ }
+}
+
+// Result returns the printed bytes. If any error was recorded with AddError
+// during code generation, the first such error will be returned here.
+func (g *Generator) Result() ([]byte, error) {
+ return g.buf.Bytes(), g.err
+}
diff --git a/vendor/github.com/mmcloughlin/avo/internal/stack/stack.go b/vendor/github.com/mmcloughlin/avo/internal/stack/stack.go
new file mode 100644
index 0000000..1d327d9
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/internal/stack/stack.go
@@ -0,0 +1,73 @@
+// Package stack provides helpers for querying the callstack.
+package stack
+
+import (
+ "path"
+ "runtime"
+ "strings"
+)
+
+// Frames returns at most max callstack Frames, starting with its caller and
+// skipping skip Frames.
+func Frames(skip, max int) []runtime.Frame {
+ pc := make([]uintptr, max)
+ n := runtime.Callers(skip+2, pc)
+ if n == 0 {
+ return nil
+ }
+ pc = pc[:n]
+ frames := runtime.CallersFrames(pc)
+ var fs []runtime.Frame
+ for {
+ f, more := frames.Next()
+ fs = append(fs, f)
+ if !more {
+ break
+ }
+ }
+ return fs
+}
+
+// Match returns the first stack frame for which the predicate function returns
+// true. Returns nil if no match is found. Starts matching after skip frames,
+// starting with its caller.
+func Match(skip int, predicate func(runtime.Frame) bool) *runtime.Frame {
+ i, n := skip+1, 16
+ for {
+ fs := Frames(i, n)
+ for j, f := range fs {
+ if predicate(f) {
+ return &fs[j]
+ }
+ }
+ if len(fs) < n {
+ break
+ }
+ i += n
+ }
+ return nil
+}
+
+// Main returns the main() function Frame.
+func Main() *runtime.Frame {
+ return Match(1, func(f runtime.Frame) bool {
+ return f.Function == "main.main"
+ })
+}
+
+// ExternalCaller returns the first frame outside the callers package.
+func ExternalCaller() *runtime.Frame {
+ var first *runtime.Frame
+ return Match(1, func(f runtime.Frame) bool {
+ if first == nil {
+ first = &f
+ }
+ return pkg(first.Function) != pkg(f.Function)
+ })
+}
+
+func pkg(ident string) string {
+ dir, name := path.Split(ident)
+ parts := strings.Split(name, ".")
+ return dir + parts[0]
+}
diff --git a/vendor/github.com/mmcloughlin/avo/ir/doc.go b/vendor/github.com/mmcloughlin/avo/ir/doc.go
new file mode 100644
index 0000000..de02f46
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/ir/doc.go
@@ -0,0 +1,2 @@
+// Package ir provides the intermediate representation of avo programs.
+package ir
diff --git a/vendor/github.com/mmcloughlin/avo/ir/ir.go b/vendor/github.com/mmcloughlin/avo/ir/ir.go
new file mode 100644
index 0000000..6fb9216
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/ir/ir.go
@@ -0,0 +1,355 @@
+package ir
+
+import (
+ "errors"
+
+ "github.com/mmcloughlin/avo/attr"
+ "github.com/mmcloughlin/avo/buildtags"
+ "github.com/mmcloughlin/avo/gotypes"
+ "github.com/mmcloughlin/avo/operand"
+ "github.com/mmcloughlin/avo/reg"
+)
+
+// Node is a part of a Function.
+type Node interface {
+ node()
+}
+
+// Label within a function.
+type Label string
+
+func (l Label) node() {}
+
+// Comment represents a multi-line comment.
+type Comment struct {
+ Lines []string
+}
+
+func (c *Comment) node() {}
+
+// NewComment builds a Comment consisting of the provided lines.
+func NewComment(lines ...string) *Comment {
+ return &Comment{
+ Lines: lines,
+ }
+}
+
+// Instruction is a single instruction in a function.
+type Instruction struct {
+ Opcode string
+ Operands []operand.Op
+
+ Inputs []operand.Op
+ Outputs []operand.Op
+
+ IsTerminal bool
+ IsBranch bool
+ IsConditional bool
+ CancellingInputs bool
+
+ // ISA is the list of required instruction set extensions.
+ ISA []string
+
+ // CFG.
+ Pred []*Instruction
+ Succ []*Instruction
+
+ // LiveIn/LiveOut are sets of live register IDs pre/post execution.
+ LiveIn reg.MaskSet
+ LiveOut reg.MaskSet
+}
+
+func (i *Instruction) node() {}
+
+// IsUnconditionalBranch reports whether i is an unconditional branch.
+func (i Instruction) IsUnconditionalBranch() bool {
+ return i.IsBranch && !i.IsConditional
+}
+
+// TargetLabel returns the label referenced by this instruction. Returns nil if
+// no label is referenced.
+func (i Instruction) TargetLabel() *Label {
+ if !i.IsBranch {
+ return nil
+ }
+ if len(i.Operands) == 0 {
+ return nil
+ }
+ if ref, ok := i.Operands[0].(operand.LabelRef); ok {
+ lbl := Label(ref)
+ return &lbl
+ }
+ return nil
+}
+
+// Registers returns all registers involved in the instruction.
+func (i Instruction) Registers() []reg.Register {
+ var rs []reg.Register
+ for _, op := range i.Operands {
+ rs = append(rs, operand.Registers(op)...)
+ }
+ return rs
+}
+
+// InputRegisters returns all registers read by this instruction.
+func (i Instruction) InputRegisters() []reg.Register {
+ var rs []reg.Register
+ for _, op := range i.Inputs {
+ rs = append(rs, operand.Registers(op)...)
+ }
+ if i.CancellingInputs && rs[0] == rs[1] {
+ rs = []reg.Register{}
+ }
+ for _, op := range i.Outputs {
+ if operand.IsMem(op) {
+ rs = append(rs, operand.Registers(op)...)
+ }
+ }
+ return rs
+}
+
+// OutputRegisters returns all registers written by this instruction.
+func (i Instruction) OutputRegisters() []reg.Register {
+ var rs []reg.Register
+ for _, op := range i.Outputs {
+ if r, ok := op.(reg.Register); ok {
+ rs = append(rs, r)
+ }
+ }
+ return rs
+}
+
+// Section is a part of a file.
+type Section interface {
+ section()
+}
+
+// File represents an assembly file.
+type File struct {
+ Constraints buildtags.Constraints
+ Includes []string
+ Sections []Section
+}
+
+// NewFile initializes an empty file.
+func NewFile() *File {
+ return &File{}
+}
+
+// AddSection appends a Section to the file.
+func (f *File) AddSection(s Section) {
+ f.Sections = append(f.Sections, s)
+}
+
+// Functions returns all functions in the file.
+func (f *File) Functions() []*Function {
+ var fns []*Function
+ for _, s := range f.Sections {
+ if fn, ok := s.(*Function); ok {
+ fns = append(fns, fn)
+ }
+ }
+ return fns
+}
+
+// Pragma represents a function compiler directive.
+type Pragma struct {
+ Directive string
+ Arguments []string
+}
+
+// Function represents an assembly function.
+type Function struct {
+ Name string
+ Attributes attr.Attribute
+ Pragmas []Pragma
+ Doc []string
+ Signature *gotypes.Signature
+ LocalSize int
+
+ Nodes []Node
+
+ // LabelTarget maps from label name to the following instruction.
+ LabelTarget map[Label]*Instruction
+
+ // Register allocation.
+ Allocation reg.Allocation
+
+ // ISA is the list of required instruction set extensions.
+ ISA []string
+}
+
+func (f *Function) section() {}
+
+// NewFunction builds an empty function of the given name.
+func NewFunction(name string) *Function {
+ return &Function{
+ Name: name,
+ Signature: gotypes.NewSignatureVoid(),
+ }
+}
+
+// AddPragma adds a pragma to this function.
+func (f *Function) AddPragma(directive string, args ...string) {
+ f.Pragmas = append(f.Pragmas, Pragma{
+ Directive: directive,
+ Arguments: args,
+ })
+}
+
+// SetSignature sets the function signature.
+func (f *Function) SetSignature(s *gotypes.Signature) {
+ f.Signature = s
+}
+
+// AllocLocal allocates size bytes in this function's stack.
+// Returns a reference to the base pointer for the newly allocated region.
+func (f *Function) AllocLocal(size int) operand.Mem {
+ ptr := operand.NewStackAddr(f.LocalSize)
+ f.LocalSize += size
+ return ptr
+}
+
+// AddInstruction appends an instruction to f.
+func (f *Function) AddInstruction(i *Instruction) {
+ f.AddNode(i)
+}
+
+// AddLabel appends a label to f.
+func (f *Function) AddLabel(l Label) {
+ f.AddNode(l)
+}
+
+// AddComment adds comment lines to f.
+func (f *Function) AddComment(lines ...string) {
+ f.AddNode(NewComment(lines...))
+}
+
+// AddNode appends a Node to f.
+func (f *Function) AddNode(n Node) {
+ f.Nodes = append(f.Nodes, n)
+}
+
+// Instructions returns just the list of instruction nodes.
+func (f *Function) Instructions() []*Instruction {
+ var is []*Instruction
+ for _, n := range f.Nodes {
+ i, ok := n.(*Instruction)
+ if ok {
+ is = append(is, i)
+ }
+ }
+ return is
+}
+
+// Labels returns just the list of label nodes.
+func (f *Function) Labels() []Label {
+ var lbls []Label
+ for _, n := range f.Nodes {
+ lbl, ok := n.(Label)
+ if ok {
+ lbls = append(lbls, lbl)
+ }
+ }
+ return lbls
+}
+
+// Stub returns the Go function declaration.
+func (f *Function) Stub() string {
+ return "func " + f.Name + f.Signature.String()
+}
+
+// FrameBytes returns the size of the stack frame in bytes.
+func (f *Function) FrameBytes() int {
+ return f.LocalSize
+}
+
+// ArgumentBytes returns the size of the arguments in bytes.
+func (f *Function) ArgumentBytes() int {
+ return f.Signature.Bytes()
+}
+
+// Datum represents a data element at a particular offset of a data section.
+type Datum struct {
+ Offset int
+ Value operand.Constant
+}
+
+// NewDatum builds a Datum from the given constant.
+func NewDatum(offset int, v operand.Constant) Datum {
+ return Datum{
+ Offset: offset,
+ Value: v,
+ }
+}
+
+// Interval returns the range of bytes this datum will occupy within its section.
+func (d Datum) Interval() (int, int) {
+ return d.Offset, d.Offset + d.Value.Bytes()
+}
+
+// Overlaps returns true
+func (d Datum) Overlaps(other Datum) bool {
+ s, e := d.Interval()
+ so, eo := other.Interval()
+ return !(eo <= s || e <= so)
+}
+
+// Global represents a DATA section.
+type Global struct {
+ Symbol operand.Symbol
+ Attributes attr.Attribute
+ Data []Datum
+ Size int
+}
+
+// NewGlobal constructs an empty DATA section.
+func NewGlobal(sym operand.Symbol) *Global {
+ return &Global{
+ Symbol: sym,
+ }
+}
+
+// NewStaticGlobal is a convenience for building a static DATA section.
+func NewStaticGlobal(name string) *Global {
+ return NewGlobal(operand.NewStaticSymbol(name))
+}
+
+func (g *Global) section() {}
+
+// Base returns a pointer to the start of the data section.
+func (g *Global) Base() operand.Mem {
+ return operand.NewDataAddr(g.Symbol, 0)
+}
+
+// Grow ensures that the data section has at least the given size.
+func (g *Global) Grow(size int) {
+ if g.Size < size {
+ g.Size = size
+ }
+}
+
+// AddDatum adds d to this data section, growing it if necessary. Errors if the datum overlaps with existing data.
+func (g *Global) AddDatum(d Datum) error {
+ for _, other := range g.Data {
+ if d.Overlaps(other) {
+ return errors.New("overlaps existing datum")
+ }
+ }
+ g.add(d)
+ return nil
+}
+
+// Append the constant to the end of the data section.
+func (g *Global) Append(v operand.Constant) {
+ g.add(Datum{
+ Offset: g.Size,
+ Value: v,
+ })
+}
+
+func (g *Global) add(d Datum) {
+ _, end := d.Interval()
+ g.Grow(end)
+ g.Data = append(g.Data, d)
+}
diff --git a/vendor/github.com/mmcloughlin/avo/operand/checks.go b/vendor/github.com/mmcloughlin/avo/operand/checks.go
new file mode 100644
index 0000000..2585479
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/operand/checks.go
@@ -0,0 +1,247 @@
+package operand
+
+import "github.com/mmcloughlin/avo/reg"
+
+// Pure type assertion checks:
+
+// IsRegister returns whether op has type reg.Register.
+func IsRegister(op Op) bool { _, ok := op.(reg.Register); return ok }
+
+// IsMem returns whether op has type Mem.
+func IsMem(op Op) bool { _, ok := op.(Mem); return ok }
+
+// IsRel returns whether op has type Rel.
+func IsRel(op Op) bool { _, ok := op.(Rel); return ok }
+
+// Checks corresponding to specific operand types in the Intel Manual:
+
+// Is1 returns true if op is the immediate constant 1.
+func Is1(op Op) bool {
+ i, ok := op.(U8)
+ return ok && i == 1
+}
+
+// Is3 returns true if op is the immediate constant 3.
+func Is3(op Op) bool {
+ i, ok := op.(U8)
+ return ok && i == 3
+}
+
+// IsIMM2U returns true if op is a 2-bit unsigned immediate (less than 4).
+func IsIMM2U(op Op) bool {
+ i, ok := op.(U8)
+ return ok && i < 4
+}
+
+// IsIMM8 returns true is op is an 8-bit immediate.
+func IsIMM8(op Op) bool {
+ _, ok := op.(U8)
+ return ok
+}
+
+// IsIMM16 returns true is op is a 16-bit immediate.
+func IsIMM16(op Op) bool {
+ _, ok := op.(U16)
+ return ok
+}
+
+// IsIMM32 returns true is op is a 32-bit immediate.
+func IsIMM32(op Op) bool {
+ _, ok := op.(U32)
+ return ok
+}
+
+// IsIMM64 returns true is op is a 64-bit immediate.
+func IsIMM64(op Op) bool {
+ _, ok := op.(U64)
+ return ok
+}
+
+// IsAL returns true if op is the AL register.
+func IsAL(op Op) bool {
+ return op == reg.AL
+}
+
+// IsCL returns true if op is the CL register.
+func IsCL(op Op) bool {
+ return op == reg.CL
+}
+
+// IsAX returns true if op is the 16-bit AX register.
+func IsAX(op Op) bool {
+ return op == reg.AX
+}
+
+// IsEAX returns true if op is the 32-bit EAX register.
+func IsEAX(op Op) bool {
+ return op == reg.EAX
+}
+
+// IsRAX returns true if op is the 64-bit RAX register.
+func IsRAX(op Op) bool {
+ return op == reg.RAX
+}
+
+// IsR8 returns true if op is an 8-bit general-purpose register.
+func IsR8(op Op) bool {
+ return IsGP(op, 1)
+}
+
+// IsR16 returns true if op is a 16-bit general-purpose register.
+func IsR16(op Op) bool {
+ return IsGP(op, 2)
+}
+
+// IsR32 returns true if op is a 32-bit general-purpose register.
+func IsR32(op Op) bool {
+ return IsGP(op, 4)
+}
+
+// IsR64 returns true if op is a 64-bit general-purpose register.
+func IsR64(op Op) bool {
+ return IsGP(op, 8)
+}
+
+// IsPseudo returns true if op is a pseudo register.
+func IsPseudo(op Op) bool {
+ return IsRegisterKind(op, reg.KindPseudo)
+}
+
+// IsGP returns true if op is a general-purpose register of size n bytes.
+func IsGP(op Op, n uint) bool {
+ return IsRegisterKindSize(op, reg.KindGP, n)
+}
+
+// IsXMM0 returns true if op is the X0 register.
+func IsXMM0(op Op) bool {
+ return op == reg.X0
+}
+
+// IsXMM returns true if op is a 128-bit XMM register.
+func IsXMM(op Op) bool {
+ return IsRegisterKindSize(op, reg.KindVector, 16)
+}
+
+// IsYMM returns true if op is a 256-bit YMM register.
+func IsYMM(op Op) bool {
+ return IsRegisterKindSize(op, reg.KindVector, 32)
+}
+
+// IsRegisterKindSize returns true if op is a register of the given kind and size in bytes.
+func IsRegisterKindSize(op Op, k reg.Kind, n uint) bool {
+ r, ok := op.(reg.Register)
+ return ok && r.Kind() == k && r.Size() == n
+}
+
+// IsRegisterKind returns true if op is a register of the given kind.
+func IsRegisterKind(op Op, k reg.Kind) bool {
+ r, ok := op.(reg.Register)
+ return ok && r.Kind() == k
+}
+
+// IsM returns true if op is a 16-, 32- or 64-bit memory operand.
+func IsM(op Op) bool {
+ // TODO(mbm): confirm "m" check is defined correctly
+ // Intel manual: "A 16-, 32- or 64-bit operand in memory."
+ return IsM16(op) || IsM32(op) || IsM64(op)
+}
+
+// IsM8 returns true if op is an 8-bit memory operand.
+func IsM8(op Op) bool {
+ // TODO(mbm): confirm "m8" check is defined correctly
+ // Intel manual: "A byte operand in memory, usually expressed as a variable or
+ // array name, but pointed to by the DS:(E)SI or ES:(E)DI registers. In 64-bit
+ // mode, it is pointed to by the RSI or RDI registers."
+ return IsMSize(op, 1)
+}
+
+// IsM16 returns true if op is a 16-bit memory operand.
+func IsM16(op Op) bool {
+ return IsMSize(op, 2)
+}
+
+// IsM32 returns true if op is a 16-bit memory operand.
+func IsM32(op Op) bool {
+ return IsMSize(op, 4)
+}
+
+// IsM64 returns true if op is a 64-bit memory operand.
+func IsM64(op Op) bool {
+ return IsMSize(op, 8)
+}
+
+// IsMSize returns true if op is a memory operand using general-purpose address
+// registers of the given size in bytes.
+func IsMSize(op Op, n uint) bool {
+ // TODO(mbm): should memory operands have a size attribute as well?
+ // TODO(mbm): m8,m16,m32,m64 checks do not actually check size
+ m, ok := op.(Mem)
+ return ok && IsMReg(m.Base) && (m.Index == nil || IsMReg(m.Index))
+}
+
+// IsMReg returns true if op is a register that can be used in a memory operand.
+func IsMReg(op Op) bool {
+ return IsPseudo(op) || IsRegisterKind(op, reg.KindGP)
+}
+
+// IsM128 returns true if op is a 128-bit memory operand.
+func IsM128(op Op) bool {
+ // TODO(mbm): should "m128" be the same as "m64"?
+ return IsM64(op)
+}
+
+// IsM256 returns true if op is a 256-bit memory operand.
+func IsM256(op Op) bool {
+ // TODO(mbm): should "m256" be the same as "m64"?
+ return IsM64(op)
+}
+
+// IsVM32X returns true if op is a vector memory operand with 32-bit XMM index.
+func IsVM32X(op Op) bool {
+ return IsVmx(op)
+}
+
+// IsVM64X returns true if op is a vector memory operand with 64-bit XMM index.
+func IsVM64X(op Op) bool {
+ return IsVmx(op)
+}
+
+// IsVmx returns true if op is a vector memory operand with XMM index.
+func IsVmx(op Op) bool {
+ return isvm(op, IsXMM)
+}
+
+// IsVM32Y returns true if op is a vector memory operand with 32-bit YMM index.
+func IsVM32Y(op Op) bool {
+ return IsVmy(op)
+}
+
+// IsVM64Y returns true if op is a vector memory operand with 64-bit YMM index.
+func IsVM64Y(op Op) bool {
+ return IsVmy(op)
+}
+
+// IsVmy returns true if op is a vector memory operand with YMM index.
+func IsVmy(op Op) bool {
+ return isvm(op, IsYMM)
+}
+
+func isvm(op Op, idx func(Op) bool) bool {
+ m, ok := op.(Mem)
+ return ok && IsR64(m.Base) && idx(m.Index)
+}
+
+// IsREL8 returns true if op is an 8-bit offset relative to instruction pointer.
+func IsREL8(op Op) bool {
+ r, ok := op.(Rel)
+ return ok && r == Rel(int8(r))
+}
+
+// IsREL32 returns true if op is an offset relative to instruction pointer, or a
+// label reference.
+func IsREL32(op Op) bool {
+ // TODO(mbm): should labels be considered separately?
+ _, rel := op.(Rel)
+ _, label := op.(LabelRef)
+ return rel || label
+}
diff --git a/vendor/github.com/mmcloughlin/avo/operand/const.go b/vendor/github.com/mmcloughlin/avo/operand/const.go
new file mode 100644
index 0000000..b2c6a6f
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/operand/const.go
@@ -0,0 +1,36 @@
+package operand
+
+import "fmt"
+
+// Constant represents a constant literal.
+type Constant interface {
+ Op
+ Bytes() int
+ constant()
+}
+
+//go:generate go run make_const.go -output zconst.go
+
+// String is a string constant.
+type String string
+
+// Asm returns an assembly syntax representation of the string s.
+func (s String) Asm() string { return fmt.Sprintf("$%q", s) }
+
+// Bytes returns the length of s.
+func (s String) Bytes() int { return len(s) }
+
+func (s String) constant() {}
+
+// Imm returns an unsigned integer constant with size guessed from x.
+func Imm(x uint64) Constant {
+ switch {
+ case uint64(uint8(x)) == x:
+ return U8(x)
+ case uint64(uint16(x)) == x:
+ return U16(x)
+ case uint64(uint32(x)) == x:
+ return U32(x)
+ }
+ return U64(x)
+}
diff --git a/vendor/github.com/mmcloughlin/avo/operand/doc.go b/vendor/github.com/mmcloughlin/avo/operand/doc.go
new file mode 100644
index 0000000..51c44df
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/operand/doc.go
@@ -0,0 +1,2 @@
+// Package operand provides types for instruction operands.
+package operand
diff --git a/vendor/github.com/mmcloughlin/avo/operand/types.go b/vendor/github.com/mmcloughlin/avo/operand/types.go
new file mode 100644
index 0000000..878425e
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/operand/types.go
@@ -0,0 +1,151 @@
+package operand
+
+import (
+ "fmt"
+
+ "github.com/mmcloughlin/avo/reg"
+)
+
+// Op is an operand.
+type Op interface {
+ Asm() string
+}
+
+// Symbol represents a symbol name.
+type Symbol struct {
+ Name string
+ Static bool // only visible in current source file
+}
+
+// NewStaticSymbol builds a static Symbol. Static symbols are only visible in the current source file.
+func NewStaticSymbol(name string) Symbol {
+ return Symbol{Name: name, Static: true}
+}
+
+func (s Symbol) String() string {
+ n := s.Name
+ if s.Static {
+ n += "<>"
+ }
+ return n
+}
+
+// Mem represents a memory reference.
+type Mem struct {
+ Symbol Symbol
+ Disp int
+ Base reg.Register
+ Index reg.Register
+ Scale uint8
+}
+
+// NewParamAddr is a convenience to build a Mem operand pointing to a function
+// parameter, which is a named offset from the frame pointer pseudo register.
+func NewParamAddr(name string, offset int) Mem {
+ return Mem{
+ Symbol: Symbol{
+ Name: name,
+ Static: false,
+ },
+ Disp: offset,
+ Base: reg.FramePointer,
+ }
+}
+
+// NewStackAddr returns a memory reference relative to the stack pointer.
+func NewStackAddr(offset int) Mem {
+ return Mem{
+ Disp: offset,
+ Base: reg.StackPointer,
+ }
+}
+
+// NewDataAddr returns a memory reference relative to the named data symbol.
+func NewDataAddr(sym Symbol, offset int) Mem {
+ return Mem{
+ Symbol: sym,
+ Disp: offset,
+ Base: reg.StaticBase,
+ }
+}
+
+// Offset returns a reference to m plus idx bytes.
+func (m Mem) Offset(idx int) Mem {
+ a := m
+ a.Disp += idx
+ return a
+}
+
+// Idx returns a new memory reference with (Index, Scale) set to (r, s).
+func (m Mem) Idx(r reg.Register, s uint8) Mem {
+ a := m
+ a.Index = r
+ a.Scale = s
+ return a
+}
+
+// Asm returns an assembly syntax representation of m.
+func (m Mem) Asm() string {
+ a := m.Symbol.String()
+ if a != "" {
+ a += fmt.Sprintf("%+d", m.Disp)
+ } else if m.Disp != 0 {
+ a += fmt.Sprintf("%d", m.Disp)
+ }
+ if m.Base != nil {
+ a += fmt.Sprintf("(%s)", m.Base.Asm())
+ }
+ if m.Index != nil && m.Scale != 0 {
+ a += fmt.Sprintf("(%s*%d)", m.Index.Asm(), m.Scale)
+ }
+ return a
+}
+
+// Rel is an offset relative to the instruction pointer.
+type Rel int32
+
+// Asm returns an assembly syntax representation of r.
+func (r Rel) Asm() string {
+ return fmt.Sprintf(".%+d", r)
+}
+
+// LabelRef is a reference to a label.
+type LabelRef string
+
+// Asm returns an assembly syntax representation of l.
+func (l LabelRef) Asm() string {
+ return string(l)
+}
+
+// Registers returns the list of all operands involved in the given operand.
+func Registers(op Op) []reg.Register {
+ switch op := op.(type) {
+ case reg.Register:
+ return []reg.Register{op}
+ case Mem:
+ var r []reg.Register
+ if op.Base != nil {
+ r = append(r, op.Base)
+ }
+ if op.Index != nil {
+ r = append(r, op.Index)
+ }
+ return r
+ case Constant, Rel, LabelRef:
+ return nil
+ }
+ panic("unknown operand type")
+}
+
+// ApplyAllocation returns an operand with allocated registers replaced. Registers missing from the allocation are left alone.
+func ApplyAllocation(op Op, a reg.Allocation) Op {
+ switch op := op.(type) {
+ case reg.Register:
+ return a.LookupRegisterDefault(op)
+ case Mem:
+ op.Base = a.LookupRegisterDefault(op.Base)
+ op.Index = a.LookupRegisterDefault(op.Index)
+ return op
+ }
+ return op
+}
diff --git a/vendor/github.com/mmcloughlin/avo/operand/zconst.go b/vendor/github.com/mmcloughlin/avo/operand/zconst.go
new file mode 100644
index 0000000..324b4a9
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/operand/zconst.go
@@ -0,0 +1,75 @@
+// Code generated by make_const.go. DO NOT EDIT.
+
+package operand
+
+import "fmt"
+
+// I8 is a 8-bit signed integer constant.
+type I8 int8
+
+func (i I8) Asm() string { return fmt.Sprintf("$%+d", i) }
+func (i I8) Bytes() int { return 1 }
+func (i I8) constant() {}
+
+// U8 is a 8-bit unsigned integer constant.
+type U8 uint8
+
+func (u U8) Asm() string { return fmt.Sprintf("$%#02x", u) }
+func (u U8) Bytes() int { return 1 }
+func (u U8) constant() {}
+
+// I16 is a 16-bit signed integer constant.
+type I16 int16
+
+func (i I16) Asm() string { return fmt.Sprintf("$%+d", i) }
+func (i I16) Bytes() int { return 2 }
+func (i I16) constant() {}
+
+// U16 is a 16-bit unsigned integer constant.
+type U16 uint16
+
+func (u U16) Asm() string { return fmt.Sprintf("$%#04x", u) }
+func (u U16) Bytes() int { return 2 }
+func (u U16) constant() {}
+
+// F32 is a 32-bit floating point constant.
+type F32 float32
+
+func (f F32) Asm() string { return fmt.Sprintf("$(%#v)", f) }
+func (f F32) Bytes() int { return 4 }
+func (f F32) constant() {}
+
+// I32 is a 32-bit signed integer constant.
+type I32 int32
+
+func (i I32) Asm() string { return fmt.Sprintf("$%+d", i) }
+func (i I32) Bytes() int { return 4 }
+func (i I32) constant() {}
+
+// U32 is a 32-bit unsigned integer constant.
+type U32 uint32
+
+func (u U32) Asm() string { return fmt.Sprintf("$%#08x", u) }
+func (u U32) Bytes() int { return 4 }
+func (u U32) constant() {}
+
+// F64 is a 64-bit floating point constant.
+type F64 float64
+
+func (f F64) Asm() string { return fmt.Sprintf("$(%#v)", f) }
+func (f F64) Bytes() int { return 8 }
+func (f F64) constant() {}
+
+// I64 is a 64-bit signed integer constant.
+type I64 int64
+
+func (i I64) Asm() string { return fmt.Sprintf("$%+d", i) }
+func (i I64) Bytes() int { return 8 }
+func (i I64) constant() {}
+
+// U64 is a 64-bit unsigned integer constant.
+type U64 uint64
+
+func (u U64) Asm() string { return fmt.Sprintf("$%#016x", u) }
+func (u U64) Bytes() int { return 8 }
+func (u U64) constant() {}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/alloc.go b/vendor/github.com/mmcloughlin/avo/pass/alloc.go
new file mode 100644
index 0000000..fc7773a
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/alloc.go
@@ -0,0 +1,190 @@
+package pass
+
+import (
+ "errors"
+ "math"
+ "sort"
+
+ "github.com/mmcloughlin/avo/reg"
+)
+
+// edge is an edge of the interference graph, indicating that registers X and Y
+// must be in non-conflicting registers.
+type edge struct {
+ X, Y reg.ID
+}
+
+// Allocator is a graph-coloring register allocator.
+type Allocator struct {
+ registers []reg.ID
+ allocation reg.Allocation
+ edges []*edge
+ possible map[reg.ID][]reg.ID
+}
+
+// NewAllocator builds an allocator for the given physical registers.
+func NewAllocator(rs []reg.Physical) (*Allocator, error) {
+ // Set of IDs, excluding restricted registers.
+ idset := map[reg.ID]bool{}
+ for _, r := range rs {
+ if (r.Info() & reg.Restricted) != 0 {
+ continue
+ }
+ idset[r.ID()] = true
+ }
+
+ if len(idset) == 0 {
+ return nil, errors.New("no allocatable registers")
+ }
+
+ // Produce slice of unique register IDs.
+ var ids []reg.ID
+ for id := range idset {
+ ids = append(ids, id)
+ }
+ sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
+
+ return &Allocator{
+ registers: ids,
+ allocation: reg.NewEmptyAllocation(),
+ possible: map[reg.ID][]reg.ID{},
+ }, nil
+}
+
+// NewAllocatorForKind builds an allocator for the given kind of registers.
+func NewAllocatorForKind(k reg.Kind) (*Allocator, error) {
+ f := reg.FamilyOfKind(k)
+ if f == nil {
+ return nil, errors.New("unknown register family")
+ }
+ return NewAllocator(f.Registers())
+}
+
+// AddInterferenceSet records that r interferes with every register in s. Convenience wrapper around AddInterference.
+func (a *Allocator) AddInterferenceSet(r reg.Register, s reg.MaskSet) {
+ for id, mask := range s {
+ if (r.Mask() & mask) != 0 {
+ a.AddInterference(r.ID(), id)
+ }
+ }
+}
+
+// AddInterference records that x and y must be assigned to non-conflicting physical registers.
+func (a *Allocator) AddInterference(x, y reg.ID) {
+ a.Add(x)
+ a.Add(y)
+ a.edges = append(a.edges, &edge{X: x, Y: y})
+}
+
+// Add adds a register to be allocated. Does nothing if the register has already been added.
+func (a *Allocator) Add(v reg.ID) {
+ if !v.IsVirtual() {
+ return
+ }
+ if _, found := a.possible[v]; found {
+ return
+ }
+ a.possible[v] = a.possibleregisters(v)
+}
+
+// Allocate allocates physical registers.
+func (a *Allocator) Allocate() (reg.Allocation, error) {
+ for {
+ if err := a.update(); err != nil {
+ return nil, err
+ }
+
+ if a.remaining() == 0 {
+ break
+ }
+
+ v := a.mostrestricted()
+ if err := a.alloc(v); err != nil {
+ return nil, err
+ }
+ }
+ return a.allocation, nil
+}
+
+// update possible allocations based on edges.
+func (a *Allocator) update() error {
+ var rem []*edge
+ for _, e := range a.edges {
+ x := a.allocation.LookupDefault(e.X)
+ y := a.allocation.LookupDefault(e.Y)
+ switch {
+ case x.IsVirtual() && y.IsVirtual():
+ rem = append(rem, e)
+ continue
+ case x.IsPhysical() && y.IsPhysical():
+ if x == y {
+ return errors.New("impossible register allocation")
+ }
+ case x.IsPhysical() && y.IsVirtual():
+ a.discardconflicting(y, x)
+ case x.IsVirtual() && y.IsPhysical():
+ a.discardconflicting(x, y)
+ default:
+ panic("unreachable")
+ }
+ }
+ a.edges = rem
+
+ return nil
+}
+
+// mostrestricted returns the virtual register with the least possibilities.
+func (a *Allocator) mostrestricted() reg.ID {
+ n := int(math.MaxInt32)
+ var v reg.ID
+ for w, p := range a.possible {
+ // On a tie, choose the smallest ID in numeric order. This avoids
+ // non-deterministic allocations due to map iteration order.
+ if len(p) < n || (len(p) == n && w < v) {
+ n = len(p)
+ v = w
+ }
+ }
+ return v
+}
+
+// discardconflicting removes registers from vs possible list that conflict with p.
+func (a *Allocator) discardconflicting(v, p reg.ID) {
+ a.possible[v] = filterregisters(a.possible[v], func(r reg.ID) bool {
+ return r != p
+ })
+}
+
+// alloc attempts to allocate a register to v.
+func (a *Allocator) alloc(v reg.ID) error {
+ ps := a.possible[v]
+ if len(ps) == 0 {
+ return errors.New("failed to allocate registers")
+ }
+ p := ps[0]
+ a.allocation[v] = p
+ delete(a.possible, v)
+ return nil
+}
+
+// remaining returns the number of unallocated registers.
+func (a *Allocator) remaining() int {
+ return len(a.possible)
+}
+
+// possibleregisters returns all allocate-able registers for the given virtual.
+func (a *Allocator) possibleregisters(v reg.ID) []reg.ID {
+ return filterregisters(a.registers, func(r reg.ID) bool {
+ return v.Kind() == r.Kind()
+ })
+}
+
+func filterregisters(in []reg.ID, predicate func(reg.ID) bool) []reg.ID {
+ var rs []reg.ID
+ for _, r := range in {
+ if predicate(r) {
+ rs = append(rs, r)
+ }
+ }
+ return rs
+}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/cfg.go b/vendor/github.com/mmcloughlin/avo/pass/cfg.go
new file mode 100644
index 0000000..d5f6ea4
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/cfg.go
@@ -0,0 +1,81 @@
+package pass
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/mmcloughlin/avo/ir"
+)
+
+// LabelTarget populates the LabelTarget of the given function. This maps from
+// label name to the following instruction.
+func LabelTarget(fn *ir.Function) error {
+ target := map[ir.Label]*ir.Instruction{}
+ var pending []ir.Label
+ for _, node := range fn.Nodes {
+ switch n := node.(type) {
+ case ir.Label:
+ if _, found := target[n]; found {
+ return fmt.Errorf("duplicate label \"%s\"", n)
+ }
+ pending = append(pending, n)
+ case *ir.Instruction:
+ for _, label := range pending {
+ target[label] = n
+ }
+ pending = nil
+ }
+ }
+ if len(pending) != 0 {
+ return errors.New("function ends with label")
+ }
+ fn.LabelTarget = target
+ return nil
+}
+
+// CFG constructs the call-flow-graph for the function.
+func CFG(fn *ir.Function) error {
+ is := fn.Instructions()
+ n := len(is)
+
+ // Populate successors.
+ for i := 0; i < n; i++ {
+ cur := is[i]
+ var nxt *ir.Instruction
+ if i+1 < n {
+ nxt = is[i+1]
+ }
+
+ // If it's a branch, locate the target.
+ if cur.IsBranch {
+ lbl := cur.TargetLabel()
+ if lbl == nil {
+ return errors.New("no label for branch instruction")
+ }
+ target, found := fn.LabelTarget[*lbl]
+ if !found {
+ return fmt.Errorf("unknown label %q", *lbl)
+ }
+ cur.Succ = append(cur.Succ, target)
+ }
+
+ // Otherwise, could continue to the following instruction.
+ switch {
+ case cur.IsTerminal:
+ case cur.IsUnconditionalBranch():
+ default:
+ cur.Succ = append(cur.Succ, nxt)
+ }
+ }
+
+ // Populate predecessors.
+ for _, i := range is {
+ for _, s := range i.Succ {
+ if s != nil {
+ s.Pred = append(s.Pred, i)
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/cleanup.go b/vendor/github.com/mmcloughlin/avo/pass/cleanup.go
new file mode 100644
index 0000000..d91250f
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/cleanup.go
@@ -0,0 +1,123 @@
+package pass
+
+import (
+ "github.com/mmcloughlin/avo/ir"
+ "github.com/mmcloughlin/avo/operand"
+)
+
+// PruneJumpToFollowingLabel removes jump instructions that target an
+// immediately following label.
+func PruneJumpToFollowingLabel(fn *ir.Function) error {
+ for i := 0; i+1 < len(fn.Nodes); i++ {
+ node := fn.Nodes[i]
+ next := fn.Nodes[i+1]
+
+ // This node is an unconditional jump.
+ inst, ok := node.(*ir.Instruction)
+ if !ok || !inst.IsBranch || inst.IsConditional {
+ continue
+ }
+
+ target := inst.TargetLabel()
+ if target == nil {
+ continue
+ }
+
+ // And the jump target is the immediately following node.
+ lbl, ok := next.(ir.Label)
+ if !ok || lbl != *target {
+ continue
+ }
+
+ // Then the jump is unnecessary and can be removed.
+ fn.Nodes = deletenode(fn.Nodes, i)
+ i--
+ }
+
+ return nil
+}
+
+// PruneDanglingLabels removes labels that are not referenced by any branches.
+func PruneDanglingLabels(fn *ir.Function) error {
+ // Count label references.
+ count := map[ir.Label]int{}
+ for _, n := range fn.Nodes {
+ i, ok := n.(*ir.Instruction)
+ if !ok || !i.IsBranch {
+ continue
+ }
+
+ target := i.TargetLabel()
+ if target == nil {
+ continue
+ }
+
+ count[*target]++
+ }
+
+ // Look for labels with no references.
+ for i := 0; i < len(fn.Nodes); i++ {
+ node := fn.Nodes[i]
+ lbl, ok := node.(ir.Label)
+ if !ok {
+ continue
+ }
+
+ if count[lbl] == 0 {
+ fn.Nodes = deletenode(fn.Nodes, i)
+ i--
+ }
+ }
+
+ return nil
+}
+
+// PruneSelfMoves removes move instructions from one register to itself.
+func PruneSelfMoves(fn *ir.Function) error {
+ return removeinstructions(fn, func(i *ir.Instruction) bool {
+ switch i.Opcode {
+ case "MOVB", "MOVW", "MOVL", "MOVQ":
+ default:
+ return false
+ }
+
+ return operand.IsRegister(i.Operands[0]) && operand.IsRegister(i.Operands[1]) && i.Operands[0] == i.Operands[1]
+ })
+}
+
+// removeinstructions deletes instructions from the given function which match predicate.
+func removeinstructions(fn *ir.Function, predicate func(*ir.Instruction) bool) error {
+ // Removal of instructions has the potential to invalidate CFG structures.
+ // Clear them to prevent accidental use of stale structures after this pass.
+ invalidatecfg(fn)
+
+ for i := 0; i < len(fn.Nodes); i++ {
+ n := fn.Nodes[i]
+
+ inst, ok := n.(*ir.Instruction)
+ if !ok || !predicate(inst) {
+ continue
+ }
+
+ fn.Nodes = deletenode(fn.Nodes, i)
+ }
+
+ return nil
+}
+
+// deletenode deletes node i from nodes and returns the resulting slice.
+func deletenode(nodes []ir.Node, i int) []ir.Node {
+ n := len(nodes)
+ copy(nodes[i:], nodes[i+1:])
+ nodes[n-1] = nil
+ return nodes[:n-1]
+}
+
+// invalidatecfg clears CFG structures.
+func invalidatecfg(fn *ir.Function) {
+ fn.LabelTarget = nil
+ for _, i := range fn.Instructions() {
+ i.Pred = nil
+ i.Succ = nil
+ }
+}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/isa.go b/vendor/github.com/mmcloughlin/avo/pass/isa.go
new file mode 100644
index 0000000..951834d
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/isa.go
@@ -0,0 +1,31 @@
+package pass
+
+import (
+ "sort"
+
+ "github.com/mmcloughlin/avo/ir"
+)
+
+// RequiredISAExtensions determines ISA extensions required for the given
+// function. Populates the ISA field.
+func RequiredISAExtensions(fn *ir.Function) error {
+ // Collect ISA set.
+ set := map[string]bool{}
+ for _, i := range fn.Instructions() {
+ for _, isa := range i.ISA {
+ set[isa] = true
+ }
+ }
+
+ if len(set) == 0 {
+ return nil
+ }
+
+ // Populate the function's ISA field with the unique sorted list.
+ for isa := range set {
+ fn.ISA = append(fn.ISA, isa)
+ }
+ sort.Strings(fn.ISA)
+
+ return nil
+}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/pass.go b/vendor/github.com/mmcloughlin/avo/pass/pass.go
new file mode 100644
index 0000000..62f37b1
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/pass.go
@@ -0,0 +1,100 @@
+// Package pass implements processing passes on avo Files.
+package pass
+
+import (
+ "io"
+
+ "github.com/mmcloughlin/avo/ir"
+ "github.com/mmcloughlin/avo/printer"
+)
+
+// Compile pass compiles an avo file. Upon successful completion the avo file
+// may be printed to Go assembly.
+var Compile = Concat(
+ Verify,
+ FunctionPass(PruneJumpToFollowingLabel),
+ FunctionPass(PruneDanglingLabels),
+ FunctionPass(LabelTarget),
+ FunctionPass(CFG),
+ InstructionPass(ZeroExtend32BitOutputs),
+ FunctionPass(Liveness),
+ FunctionPass(AllocateRegisters),
+ FunctionPass(BindRegisters),
+ FunctionPass(VerifyAllocation),
+ Func(IncludeTextFlagHeader),
+ FunctionPass(PruneSelfMoves),
+ FunctionPass(RequiredISAExtensions),
+)
+
+// Interface for a processing pass.
+type Interface interface {
+ Execute(*ir.File) error
+}
+
+// Func adapts a function to the pass Interface.
+type Func func(*ir.File) error
+
+// Execute calls p.
+func (p Func) Execute(f *ir.File) error {
+ return p(f)
+}
+
+// FunctionPass is a convenience for implementing a full file pass with a
+// function that operates on each avo Function independently.
+type FunctionPass func(*ir.Function) error
+
+// Execute calls p on every function in the file. Exits on the first error.
+func (p FunctionPass) Execute(f *ir.File) error {
+ for _, fn := range f.Functions() {
+ if err := p(fn); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// InstructionPass is a convenience for implementing a full file pass with a
+// function that operates on each Instruction independently.
+type InstructionPass func(*ir.Instruction) error
+
+// Execute calls p on every instruction in the file. Exits on the first error.
+func (p InstructionPass) Execute(f *ir.File) error {
+ for _, fn := range f.Functions() {
+ for _, i := range fn.Instructions() {
+ if err := p(i); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// Concat returns a pass that executes the given passes in order, stopping on the first error.
+func Concat(passes ...Interface) Interface {
+ return Func(func(f *ir.File) error {
+ for _, p := range passes {
+ if err := p.Execute(f); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+}
+
+// Output pass prints a file.
+type Output struct {
+ Writer io.WriteCloser
+ Printer printer.Printer
+}
+
+// Execute prints f with the configured Printer and writes output to Writer.
+func (o *Output) Execute(f *ir.File) error {
+ b, err := o.Printer.Print(f)
+ if err != nil {
+ return err
+ }
+ if _, err = o.Writer.Write(b); err != nil {
+ return err
+ }
+ return o.Writer.Close()
+}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/reg.go b/vendor/github.com/mmcloughlin/avo/pass/reg.go
new file mode 100644
index 0000000..79147b0
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/reg.go
@@ -0,0 +1,139 @@
+package pass
+
+import (
+ "errors"
+
+ "github.com/mmcloughlin/avo/ir"
+ "github.com/mmcloughlin/avo/operand"
+ "github.com/mmcloughlin/avo/reg"
+)
+
+// ZeroExtend32BitOutputs applies the rule that "32-bit operands generate a
+// 32-bit result, zero-extended to a 64-bit result in the destination
+// general-purpose register" (Intel Software Developer’s Manual, Volume 1,
+// 3.4.1.1).
+func ZeroExtend32BitOutputs(i *ir.Instruction) error {
+ for j, op := range i.Outputs {
+ if !operand.IsR32(op) {
+ continue
+ }
+ r, ok := op.(reg.GP)
+ if !ok {
+ panic("r32 operand should satisfy reg.GP")
+ }
+ i.Outputs[j] = r.As64()
+ }
+ return nil
+}
+
+// Liveness computes register liveness.
+func Liveness(fn *ir.Function) error {
+ // Note this implementation is initially naive so as to be "obviously correct".
+ // There are a well-known optimizations we can apply if necessary.
+
+ is := fn.Instructions()
+
+ // Process instructions in reverse: poor approximation to topological sort.
+ // TODO(mbm): process instructions in topological sort order
+ for l, r := 0, len(is)-1; l < r; l, r = l+1, r-1 {
+ is[l], is[r] = is[r], is[l]
+ }
+
+ // Initialize.
+ for _, i := range is {
+ i.LiveIn = reg.NewMaskSetFromRegisters(i.InputRegisters())
+ i.LiveOut = reg.NewEmptyMaskSet()
+ }
+
+ // Iterative dataflow analysis.
+ for {
+ changes := false
+
+ for _, i := range is {
+ // out[n] = UNION[s IN succ[n]] in[s]
+ for _, s := range i.Succ {
+ if s == nil {
+ continue
+ }
+ changes = i.LiveOut.Update(s.LiveIn) || changes
+ }
+
+ // in[n] = use[n] UNION (out[n] - def[n])
+ def := reg.NewMaskSetFromRegisters(i.OutputRegisters())
+ changes = i.LiveIn.Update(i.LiveOut.Difference(def)) || changes
+ }
+
+ if !changes {
+ break
+ }
+ }
+
+ return nil
+}
+
+// AllocateRegisters performs register allocation.
+func AllocateRegisters(fn *ir.Function) error {
+ // Populate allocators (one per kind).
+ as := map[reg.Kind]*Allocator{}
+ for _, i := range fn.Instructions() {
+ for _, r := range i.Registers() {
+ k := r.Kind()
+ if _, found := as[k]; !found {
+ a, err := NewAllocatorForKind(k)
+ if err != nil {
+ return err
+ }
+ as[k] = a
+ }
+ as[k].Add(r.ID())
+ }
+ }
+
+ // Record register interferences.
+ for _, i := range fn.Instructions() {
+ for _, d := range i.OutputRegisters() {
+ k := d.Kind()
+ out := i.LiveOut.OfKind(k)
+ out.DiscardRegister(d)
+ as[k].AddInterferenceSet(d, out)
+ }
+ }
+
+ // Execute register allocation.
+ fn.Allocation = reg.NewEmptyAllocation()
+ for _, a := range as {
+ al, err := a.Allocate()
+ if err != nil {
+ return err
+ }
+ if err := fn.Allocation.Merge(al); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// BindRegisters applies the result of register allocation, replacing all virtual registers with their assigned physical registers.
+func BindRegisters(fn *ir.Function) error {
+ for _, i := range fn.Instructions() {
+ for idx := range i.Operands {
+ i.Operands[idx] = operand.ApplyAllocation(i.Operands[idx], fn.Allocation)
+ }
+ }
+ return nil
+}
+
+// VerifyAllocation performs sanity checks following register allocation.
+func VerifyAllocation(fn *ir.Function) error {
+ // All registers should be physical.
+ for _, i := range fn.Instructions() {
+ for _, r := range i.Registers() {
+ if reg.ToPhysical(r) == nil {
+ return errors.New("non physical register found")
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/textflag.go b/vendor/github.com/mmcloughlin/avo/pass/textflag.go
new file mode 100644
index 0000000..35a848b
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/textflag.go
@@ -0,0 +1,42 @@
+package pass
+
+import (
+ "github.com/mmcloughlin/avo/attr"
+ "github.com/mmcloughlin/avo/ir"
+)
+
+// IncludeTextFlagHeader includes textflag.h if necessary.
+func IncludeTextFlagHeader(f *ir.File) error {
+ const textflagheader = "textflag.h"
+
+ // Check if we already have it.
+ for _, path := range f.Includes {
+ if path == textflagheader {
+ return nil
+ }
+ }
+
+ // Add it if necessary.
+ if requirestextflags(f) {
+ f.Includes = append(f.Includes, textflagheader)
+ }
+
+ return nil
+}
+
+// requirestextflags returns whether the file uses flags in the textflags.h header.
+func requirestextflags(f *ir.File) bool {
+ for _, s := range f.Sections {
+ var a attr.Attribute
+ switch s := s.(type) {
+ case *ir.Function:
+ a = s.Attributes
+ case *ir.Global:
+ a = s.Attributes
+ }
+ if a.ContainsTextFlags() {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/mmcloughlin/avo/pass/verify.go b/vendor/github.com/mmcloughlin/avo/pass/verify.go
new file mode 100644
index 0000000..1e7b368
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/pass/verify.go
@@ -0,0 +1,32 @@
+package pass
+
+import (
+ "errors"
+
+ "github.com/mmcloughlin/avo/ir"
+ "github.com/mmcloughlin/avo/operand"
+)
+
+// Verify pass validates an avo file.
+var Verify = Concat(
+ InstructionPass(VerifyMemOperands),
+)
+
+// VerifyMemOperands checks the instruction's memory operands.
+func VerifyMemOperands(i *ir.Instruction) error {
+ for _, op := range i.Operands {
+ m, ok := op.(operand.Mem)
+ if !ok {
+ continue
+ }
+
+ if m.Base == nil {
+ return errors.New("bad memory operand: missing base register")
+ }
+
+ if m.Index != nil && m.Scale == 0 {
+ return errors.New("bad memory operand: index register with scale 0")
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/mmcloughlin/avo/printer/goasm.go b/vendor/github.com/mmcloughlin/avo/printer/goasm.go
new file mode 100644
index 0000000..0d8a12c
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/printer/goasm.go
@@ -0,0 +1,186 @@
+package printer
+
+import (
+ "strconv"
+ "strings"
+
+ "github.com/mmcloughlin/avo/internal/prnt"
+ "github.com/mmcloughlin/avo/ir"
+ "github.com/mmcloughlin/avo/operand"
+)
+
+// dot is the pesky unicode dot used in Go assembly.
+const dot = "\u00b7"
+
+type goasm struct {
+ cfg Config
+ prnt.Generator
+
+ instructions []*ir.Instruction
+ clear bool
+}
+
+// NewGoAsm constructs a printer for writing Go assembly files.
+func NewGoAsm(cfg Config) Printer {
+ return &goasm{cfg: cfg}
+}
+
+func (p *goasm) Print(f *ir.File) ([]byte, error) {
+ p.header(f)
+ for _, s := range f.Sections {
+ switch s := s.(type) {
+ case *ir.Function:
+ p.function(s)
+ case *ir.Global:
+ p.global(s)
+ default:
+ panic("unknown section type")
+ }
+ }
+ return p.Result()
+}
+
+func (p *goasm) header(f *ir.File) {
+ p.Comment(p.cfg.GeneratedWarning())
+
+ if len(f.Constraints) > 0 {
+ p.NL()
+ p.Printf(f.Constraints.GoString())
+ }
+
+ if len(f.Includes) > 0 {
+ p.NL()
+ p.includes(f.Includes)
+ }
+}
+
+func (p *goasm) includes(paths []string) {
+ for _, path := range paths {
+ p.Printf("#include \"%s\"\n", path)
+ }
+}
+
+func (p *goasm) function(f *ir.Function) {
+ p.NL()
+ p.Comment(f.Stub())
+
+ if len(f.ISA) > 0 {
+ p.Comment("Requires: " + strings.Join(f.ISA, ", "))
+ }
+
+ // Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L166-L176
+ //
+ // if p.As == ATEXT {
+ // // If there are attributes, print them. Otherwise, skip the comma.
+ // // In short, print one of these two:
+ // // TEXT foo(SB), DUPOK|NOSPLIT, $0
+ // // TEXT foo(SB), $0
+ // s := p.From.Sym.Attribute.TextAttrString()
+ // if s != "" {
+ // fmt.Fprintf(&buf, "%s%s", sep, s)
+ // sep = ", "
+ // }
+ // }
+ //
+ p.Printf("TEXT %s%s(SB)", dot, f.Name)
+ if f.Attributes != 0 {
+ p.Printf(", %s", f.Attributes.Asm())
+ }
+ p.Printf(", %s\n", textsize(f))
+
+ p.clear = true
+ for _, node := range f.Nodes {
+ switch n := node.(type) {
+ case *ir.Instruction:
+ p.instruction(n)
+ if n.IsTerminal || n.IsUnconditionalBranch() {
+ p.flush()
+ }
+ case ir.Label:
+ p.flush()
+ p.ensureclear()
+ p.Printf("%s:\n", n)
+ case *ir.Comment:
+ p.flush()
+ p.ensureclear()
+ for _, line := range n.Lines {
+ p.Printf("\t// %s\n", line)
+ }
+ default:
+ panic("unexpected node type")
+ }
+ }
+ p.flush()
+}
+
+func (p *goasm) instruction(i *ir.Instruction) {
+ p.instructions = append(p.instructions, i)
+ p.clear = false
+}
+
+func (p *goasm) flush() {
+ if len(p.instructions) == 0 {
+ return
+ }
+
+ // Determine instruction width. Instructions with no operands are not
+ // considered in this calculation.
+ width := 0
+ for _, i := range p.instructions {
+ if len(i.Operands) > 0 && len(i.Opcode) > width {
+ width = len(i.Opcode)
+ }
+ }
+
+ // Output instruction block.
+ for _, i := range p.instructions {
+ if len(i.Operands) > 0 {
+ p.Printf("\t%-*s%s\n", width+1, i.Opcode, joinOperands(i.Operands))
+ } else {
+ p.Printf("\t%s\n", i.Opcode)
+ }
+ }
+
+ p.instructions = nil
+}
+
+func (p *goasm) ensureclear() {
+ if !p.clear {
+ p.NL()
+ p.clear = true
+ }
+}
+
+func (p *goasm) global(g *ir.Global) {
+ p.NL()
+ for _, d := range g.Data {
+ a := operand.NewDataAddr(g.Symbol, d.Offset)
+ p.Printf("DATA %s/%d, %s\n", a.Asm(), d.Value.Bytes(), d.Value.Asm())
+ }
+ p.Printf("GLOBL %s(SB), %s, $%d\n", g.Symbol, g.Attributes.Asm(), g.Size)
+}
+
+func textsize(f *ir.Function) string {
+ // Reference: https://github.com/golang/go/blob/b115207baf6c2decc3820ada4574ef4e5ad940ec/src/cmd/internal/obj/util.go#L260-L265
+ //
+ // case TYPE_TEXTSIZE:
+ // if a.Val.(int32) == objabi.ArgsSizeUnknown {
+ // str = fmt.Sprintf("$%d", a.Offset)
+ // } else {
+ // str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
+ // }
+ //
+ s := "$" + strconv.Itoa(f.FrameBytes())
+ if argsize := f.ArgumentBytes(); argsize > 0 {
+ return s + "-" + strconv.Itoa(argsize)
+ }
+ return s
+}
+
+func joinOperands(operands []operand.Op) string {
+ asm := make([]string, len(operands))
+ for i, op := range operands {
+ asm[i] = op.Asm()
+ }
+ return strings.Join(asm, ", ")
+}
diff --git a/vendor/github.com/mmcloughlin/avo/printer/printer.go b/vendor/github.com/mmcloughlin/avo/printer/printer.go
new file mode 100644
index 0000000..b562c74
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/printer/printer.go
@@ -0,0 +1,98 @@
+// Package printer implements printing of avo files in various formats.
+package printer
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/mmcloughlin/avo/internal/stack"
+ "github.com/mmcloughlin/avo/ir"
+)
+
+// Printer can produce output for an avo File.
+type Printer interface {
+ Print(*ir.File) ([]byte, error)
+}
+
+// Builder can construct a printer.
+type Builder func(Config) Printer
+
+// Config represents general printing configuration.
+type Config struct {
+ // Command-line arguments passed to the generator. If provided, this will be
+ // included in a code generation warning.
+ Argv []string
+
+ // Name of the code generator.
+ Name string
+
+ // Name of Go package the generated code will belong to.
+ Pkg string
+}
+
+// NewDefaultConfig produces a config with Name "avo".
+// The package name is guessed from the current directory.
+func NewDefaultConfig() Config {
+ return Config{
+ Name: "avo",
+ Pkg: pkg(),
+ }
+}
+
+// NewArgvConfig constructs a Config from os.Args.
+// The package name is guessed from the current directory.
+func NewArgvConfig() Config {
+ return Config{
+ Argv: os.Args,
+ Pkg: pkg(),
+ }
+}
+
+// NewGoRunConfig produces a Config for a generator that's expected to be
+// executed via "go run ...".
+func NewGoRunConfig() Config {
+ path := mainfile()
+ if path == "" {
+ return NewDefaultConfig()
+ }
+ argv := []string{"go", "run", filepath.Base(path)}
+ if len(os.Args) > 1 {
+ argv = append(argv, os.Args[1:]...)
+ }
+ return Config{
+ Argv: argv,
+ Pkg: pkg(),
+ }
+}
+
+// GeneratedBy returns a description of the code generator.
+func (c Config) GeneratedBy() string {
+ if c.Argv == nil {
+ return c.Name
+ }
+ return fmt.Sprintf("command: %s", strings.Join(c.Argv, " "))
+}
+
+// GeneratedWarning returns text for a code generation warning. Conforms to https://golang.org/s/generatedcode.
+func (c Config) GeneratedWarning() string {
+ return fmt.Sprintf("Code generated by %s. DO NOT EDIT.", c.GeneratedBy())
+}
+
+// mainfile attempts to determine the file path of the main function by
+// inspecting the stack. Returns empty string on failure.
+func mainfile() string {
+ if m := stack.Main(); m != nil {
+ return m.File
+ }
+ return ""
+}
+
+// pkg guesses the name of the package from the working directory.
+func pkg() string {
+ if cwd, err := os.Getwd(); err == nil {
+ return filepath.Base(cwd)
+ }
+ return ""
+}
diff --git a/vendor/github.com/mmcloughlin/avo/printer/stubs.go b/vendor/github.com/mmcloughlin/avo/printer/stubs.go
new file mode 100644
index 0000000..171bc62
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/printer/stubs.go
@@ -0,0 +1,45 @@
+package printer
+
+import (
+ "github.com/mmcloughlin/avo/internal/prnt"
+ "github.com/mmcloughlin/avo/ir"
+)
+
+type stubs struct {
+ cfg Config
+ prnt.Generator
+}
+
+// NewStubs constructs a printer for writing stub function declarations.
+func NewStubs(cfg Config) Printer {
+ return &stubs{cfg: cfg}
+}
+
+func (s *stubs) Print(f *ir.File) ([]byte, error) {
+ s.Comment(s.cfg.GeneratedWarning())
+
+ if len(f.Constraints) > 0 {
+ s.NL()
+ s.Printf(f.Constraints.GoString())
+ }
+
+ s.NL()
+ s.Printf("package %s\n", s.cfg.Pkg)
+ for _, fn := range f.Functions() {
+ s.NL()
+ s.Comment(fn.Doc...)
+ for _, pragma := range fn.Pragmas {
+ s.pragma(pragma)
+ }
+ s.Printf("%s\n", fn.Stub())
+ }
+ return s.Result()
+}
+
+func (s *stubs) pragma(p ir.Pragma) {
+ s.Printf("//go:%s", p.Directive)
+ for _, arg := range p.Arguments {
+ s.Printf(" %s", arg)
+ }
+ s.NL()
+}
diff --git a/vendor/github.com/mmcloughlin/avo/reg/collection.go b/vendor/github.com/mmcloughlin/avo/reg/collection.go
new file mode 100644
index 0000000..d35c3a0
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/reg/collection.go
@@ -0,0 +1,54 @@
+package reg
+
+// Collection represents a collection of virtual registers. This is primarily
+// useful for allocating virtual registers with distinct IDs.
+type Collection struct {
+ idx map[Kind]Index
+}
+
+// NewCollection builds an empty register collection.
+func NewCollection() *Collection {
+ return &Collection{
+ idx: map[Kind]Index{},
+ }
+}
+
+// VirtualRegister allocates and returns a new virtual register of the given kind and width.
+func (c *Collection) VirtualRegister(k Kind, s Spec) Virtual {
+ idx := c.idx[k]
+ c.idx[k]++
+ return NewVirtual(idx, k, s)
+}
+
+// GP8L allocates and returns a general-purpose 8-bit register (low byte).
+func (c *Collection) GP8L() GPVirtual { return c.GP(S8L) }
+
+// GP8H allocates and returns a general-purpose 8-bit register (high byte).
+func (c *Collection) GP8H() GPVirtual { return c.GP(S8H) }
+
+// GP8 allocates and returns a general-purpose 8-bit register (low byte).
+func (c *Collection) GP8() GPVirtual { return c.GP8L() }
+
+// GP16 allocates and returns a general-purpose 16-bit register.
+func (c *Collection) GP16() GPVirtual { return c.GP(S16) }
+
+// GP32 allocates and returns a general-purpose 32-bit register.
+func (c *Collection) GP32() GPVirtual { return c.GP(S32) }
+
+// GP64 allocates and returns a general-purpose 64-bit register.
+func (c *Collection) GP64() GPVirtual { return c.GP(S64) }
+
+// GP allocates and returns a general-purpose register of the given width.
+func (c *Collection) GP(s Spec) GPVirtual { return newgpv(c.VirtualRegister(KindGP, s)) }
+
+// XMM allocates and returns a 128-bit vector register.
+func (c *Collection) XMM() VecVirtual { return c.Vec(S128) }
+
+// YMM allocates and returns a 256-bit vector register.
+func (c *Collection) YMM() VecVirtual { return c.Vec(S256) }
+
+// ZMM allocates and returns a 512-bit vector register.
+func (c *Collection) ZMM() VecVirtual { return c.Vec(S512) }
+
+// Vec allocates and returns a vector register of the given width.
+func (c *Collection) Vec(s Spec) VecVirtual { return newvecv(c.VirtualRegister(KindVector, s)) }
diff --git a/vendor/github.com/mmcloughlin/avo/reg/doc.go b/vendor/github.com/mmcloughlin/avo/reg/doc.go
new file mode 100644
index 0000000..1c0aee3
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/reg/doc.go
@@ -0,0 +1,2 @@
+// Package reg provides types for physical and virtual registers, and definitions of x86-64 register families.
+package reg
diff --git a/vendor/github.com/mmcloughlin/avo/reg/set.go b/vendor/github.com/mmcloughlin/avo/reg/set.go
new file mode 100644
index 0000000..2cf8814
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/reg/set.go
@@ -0,0 +1,112 @@
+package reg
+
+// MaskSet maps register IDs to masks.
+type MaskSet map[ID]uint16
+
+// NewEmptyMaskSet builds an empty register mask set.
+func NewEmptyMaskSet() MaskSet {
+ return MaskSet{}
+}
+
+// NewMaskSetFromRegisters forms a mask set from the given register list.
+func NewMaskSetFromRegisters(rs []Register) MaskSet {
+ s := NewEmptyMaskSet()
+ for _, r := range rs {
+ s.AddRegister(r)
+ }
+ return s
+}
+
+// Clone returns a copy of s.
+func (s MaskSet) Clone() MaskSet {
+ c := NewEmptyMaskSet()
+ for id, mask := range s {
+ c.Add(id, mask)
+ }
+ return c
+}
+
+// Add mask to the given register ID.
+// Reports whether this made any change to the set.
+func (s MaskSet) Add(id ID, mask uint16) bool {
+ if (s[id] & mask) == mask {
+ return false
+ }
+ s[id] |= mask
+ return true
+}
+
+// AddRegister is a convenience for adding the register's (ID, mask) to the set.
+// Reports whether this made any change to the set.
+func (s MaskSet) AddRegister(r Register) bool {
+ return s.Add(r.ID(), r.Mask())
+}
+
+// Discard clears masked bits from register ID.
+// Reports whether this made any change to the set.
+func (s MaskSet) Discard(id ID, mask uint16) bool {
+ if curr, found := s[id]; !found || (curr&mask) == 0 {
+ return false
+ }
+ s[id] &^= mask
+ if s[id] == 0 {
+ delete(s, id)
+ }
+ return true
+}
+
+// DiscardRegister is a convenience for discarding the register's (ID, mask) from the set.
+// Reports whether this made any change to the set.
+func (s MaskSet) DiscardRegister(r Register) bool {
+ return s.Discard(r.ID(), r.Mask())
+}
+
+// Update adds masks in t to s.
+// Reports whether this made any change to the set.
+func (s MaskSet) Update(t MaskSet) bool {
+ change := false
+ for id, mask := range t {
+ change = s.Add(id, mask) || change
+ }
+ return change
+}
+
+// Difference returns the set of registers in s but not t.
+func (s MaskSet) Difference(t MaskSet) MaskSet {
+ d := s.Clone()
+ d.DifferenceUpdate(t)
+ return d
+}
+
+// DifferenceUpdate removes every element of t from s.
+func (s MaskSet) DifferenceUpdate(t MaskSet) bool {
+ change := false
+ for id, mask := range t {
+ change = s.Discard(id, mask) || change
+ }
+ return change
+}
+
+// Equals returns true if s and t contain the same masks.
+func (s MaskSet) Equals(t MaskSet) bool {
+ if len(s) != len(t) {
+ return false
+ }
+ for id, mask := range s {
+ if _, found := t[id]; !found || mask != t[id] {
+ return false
+ }
+ }
+ return true
+}
+
+// OfKind returns the set of elements of s with kind k.
+func (s MaskSet) OfKind(k Kind) MaskSet {
+ t := NewEmptyMaskSet()
+ for id, mask := range s {
+ if id.Kind() == k {
+ t.Add(id, mask)
+ }
+ }
+ return t
+}
diff --git a/vendor/github.com/mmcloughlin/avo/reg/types.go b/vendor/github.com/mmcloughlin/avo/reg/types.go
new file mode 100644
index 0000000..9f69e91
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/reg/types.go
@@ -0,0 +1,304 @@
+package reg
+
+import (
+ "errors"
+ "fmt"
+)
+
+// Kind is a class of registers.
+type Kind uint8
+
+// Index of a register within a kind.
+type Index uint16
+
+// Family is a collection of Physical registers of a common kind.
+type Family struct {
+ Kind Kind
+ registers []Physical
+}
+
+// define builds a register and adds it to the Family.
+func (f *Family) define(s Spec, idx Index, name string, flags ...Info) Physical {
+ r := newregister(f, s, idx, name, flags...)
+ f.add(r)
+ return r
+}
+
+// add r to the family.
+func (f *Family) add(r Physical) {
+ if r.Kind() != f.Kind {
+ panic("bad kind")
+ }
+ f.registers = append(f.registers, r)
+}
+
+// Virtual returns a virtual register from this family's kind.
+func (f *Family) Virtual(idx Index, s Spec) Virtual {
+ return NewVirtual(idx, f.Kind, s)
+}
+
+// Registers returns the registers in this family.
+func (f *Family) Registers() []Physical {
+ return append([]Physical(nil), f.registers...)
+}
+
+// Lookup returns the register with given physical index and spec. Returns nil if no such register exists.
+func (f *Family) Lookup(idx Index, s Spec) Physical {
+ for _, r := range f.registers {
+ if r.PhysicalIndex() == idx && r.Mask() == s.Mask() {
+ return r
+ }
+ }
+ return nil
+}
+
+// ID is a register identifier.
+type ID uint32
+
+// newid builds a new register ID from the virtual flag v, kind and index.
+func newid(v uint8, kind Kind, idx Index) ID {
+ return ID(v) | (ID(kind) << 8) | (ID(idx) << 16)
+}
+
+// IsVirtual reports whether this is an ID for a virtual register.
+func (id ID) IsVirtual() bool { return (id & 1) == 1 }
+
+// IsPhysical reports whether this is an ID for a physical register.
+func (id ID) IsPhysical() bool { return !id.IsVirtual() }
+
+// Kind extracts the kind from the register ID.
+func (id ID) Kind() Kind { return Kind(id >> 8) }
+
+// Index extracts the index from the register ID.
+func (id ID) Index() Index { return Index(id >> 16) }
+
+// Register represents a virtual or physical register.
+type Register interface {
+ ID() ID
+ Kind() Kind
+ Size() uint
+ Mask() uint16
+ Asm() string
+ as(Spec) Register
+ spec() Spec
+ register()
+}
+
+// Equal reports whether a and b are equal registers.
+func Equal(a, b Register) bool {
+ return (a.ID() == b.ID()) && (a.Mask() == b.Mask())
+}
+
+// Virtual is a register of a given type and size, not yet allocated to a physical register.
+type Virtual interface {
+ VirtualIndex() Index
+ Register
+}
+
+// ToVirtual converts r to Virtual if possible, otherwise returns nil.
+func ToVirtual(r Register) Virtual {
+ if v, ok := r.(Virtual); ok {
+ return v
+ }
+ return nil
+}
+
+type virtual struct {
+ idx Index
+ kind Kind
+ Spec
+}
+
+// NewVirtual builds a Virtual register.
+func NewVirtual(idx Index, k Kind, s Spec) Virtual {
+ return virtual{
+ idx: idx,
+ kind: k,
+ Spec: s,
+ }
+}
+
+func (v virtual) ID() ID { return newid(1, v.kind, v.idx) }
+func (v virtual) VirtualIndex() Index { return v.idx }
+func (v virtual) Kind() Kind { return v.kind }
+
+func (v virtual) Asm() string {
+ // TODO(mbm): decide on virtual register syntax
+ return fmt.Sprintf("<virtual:%v:%v:%v>", v.idx, v.Kind(), v.Size())
+}
+
+func (v virtual) as(s Spec) Register {
+ return virtual{
+ idx: v.idx,
+ kind: v.kind,
+ Spec: s,
+ }
+}
+
+func (v virtual) spec() Spec { return v.Spec }
+func (v virtual) register() {}
+
+// Info is a bitmask of register properties.
+type Info uint8
+
+// Defined register Info flags.
+const (
+ None Info = 0
+ Restricted Info = 1 << iota
+)
+
+// Physical is a concrete register.
+type Physical interface {
+ PhysicalIndex() Index
+ Info() Info
+ Register
+}
+
+// ToPhysical converts r to Physical if possible, otherwise returns nil.
+func ToPhysical(r Register) Physical {
+ if p, ok := r.(Physical); ok {
+ return p
+ }
+ return nil
+}
+
+// register implements Physical.
+type register struct {
+ family *Family
+ idx Index
+ name string
+ info Info
+ Spec
+}
+
+func newregister(f *Family, s Spec, idx Index, name string, flags ...Info) register {
+ r := register{
+ family: f,
+ idx: idx,
+ name: name,
+ info: None,
+ Spec: s,
+ }
+ for _, flag := range flags {
+ r.info |= flag
+ }
+ return r
+}
+
+func (r register) ID() ID { return newid(0, r.Kind(), r.idx) }
+func (r register) PhysicalIndex() Index { return r.idx }
+func (r register) Kind() Kind { return r.family.Kind }
+func (r register) Asm() string { return r.name }
+func (r register) Info() Info { return r.info }
+
+func (r register) as(s Spec) Register {
+ return r.family.Lookup(r.PhysicalIndex(), s)
+}
+
+func (r register) spec() Spec { return r.Spec }
+func (r register) register() {}
+
+// Spec defines the size of a register as well as the bit ranges it occupies in
+// an underlying physical register.
+type Spec uint16
+
+// Spec values required for x86-64.
+const (
+ S0 Spec = 0x0 // zero value reserved for pseudo registers
+ S8L Spec = 0x1
+ S8H Spec = 0x2
+ S8 = S8L
+ S16 Spec = 0x3
+ S32 Spec = 0x7
+ S64 Spec = 0xf
+ S128 Spec = 0x1f
+ S256 Spec = 0x3f
+ S512 Spec = 0x7f
+)
+
+// Mask returns a mask representing which bytes of an underlying register are
+// used by this register. This is almost always the low bytes, except for the
+// case of the high-byte registers. If bit n of the mask is set, this means
+// bytes 2^(n-1) to 2^n-1 are used.
+func (s Spec) Mask() uint16 {
+ return uint16(s)
+}
+
+// Size returns the register width in bytes.
+func (s Spec) Size() uint {
+ x := uint(s)
+ return (x >> 1) + (x & 1)
+}
+
+// LookupPhysical returns the physical register with the given parameters, or nil if not found.
+func LookupPhysical(k Kind, idx Index, s Spec) Physical {
+ f := FamilyOfKind(k)
+ if f == nil {
+ return nil
+ }
+ return f.Lookup(idx, s)
+}
+
+// LookupID returns the physical register with the given id and spec, or nil if not found.
+func LookupID(id ID, s Spec) Physical {
+ if id.IsVirtual() {
+ return nil
+ }
+ return LookupPhysical(id.Kind(), id.Index(), s)
+}
+
+// Allocation records a register allocation.
+type Allocation map[ID]ID
+
+// NewEmptyAllocation builds an empty register allocation.
+func NewEmptyAllocation() Allocation {
+ return Allocation{}
+}
+
+// Merge allocations from b into a. Errors if there is disagreement on a common
+// register.
+func (a Allocation) Merge(b Allocation) error {
+ for id, p := range b {
+ if alt, found := a[id]; found && alt != p {
+ return errors.New("disagreement on overlapping register")
+ }
+ a[id] = p
+ }
+ return nil
+}
+
+// LookupDefault returns the register ID assigned by this allocation, returning
+// id if none is found.
+func (a Allocation) LookupDefault(id ID) ID {
+ if _, found := a[id]; found {
+ return a[id]
+ }
+ return id
+}
+
+// LookupRegister the allocation for register r, or return nil if there is none.
+func (a Allocation) LookupRegister(r Register) Physical {
+ // Return immediately if it is already a physical register.
+ if p := ToPhysical(r); p != nil {
+ return p
+ }
+
+ // Lookup an allocation for this virtual ID.
+ id, found := a[r.ID()]
+ if !found {
+ return nil
+ }
+
+ return LookupID(id, r.spec())
+}
+
+// LookupRegisterDefault returns the register assigned to r, or r itself if there is none.
+func (a Allocation) LookupRegisterDefault(r Register) Register {
+ if r == nil {
+ return nil
+ }
+ if p := a.LookupRegister(r); p != nil {
+ return p
+ }
+ return r
+}
diff --git a/vendor/github.com/mmcloughlin/avo/reg/x86.go b/vendor/github.com/mmcloughlin/avo/reg/x86.go
new file mode 100644
index 0000000..a1ec94c
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/reg/x86.go
@@ -0,0 +1,331 @@
+package reg
+
+// Register kinds.
+const (
+ KindPseudo Kind = iota
+ KindGP
+ KindVector
+)
+
+// Declare register families.
+var (
+ Pseudo = &Family{Kind: KindPseudo}
+ GeneralPurpose = &Family{Kind: KindGP}
+ Vector = &Family{Kind: KindVector}
+
+ Families = []*Family{
+ Pseudo,
+ GeneralPurpose,
+ Vector,
+ }
+)
+
+var familiesByKind = map[Kind]*Family{}
+
+func init() {
+ for _, f := range Families {
+ familiesByKind[f.Kind] = f
+ }
+}
+
+// FamilyOfKind returns the Family of registers of the given kind, or nil if not found.
+func FamilyOfKind(k Kind) *Family {
+ return familiesByKind[k]
+}
+
+// Pseudo registers.
+var (
+ FramePointer = Pseudo.define(S0, 0, "FP")
+ ProgramCounter = Pseudo.define(S0, 0, "PC")
+ StaticBase = Pseudo.define(S0, 0, "SB")
+ StackPointer = Pseudo.define(S0, 0, "SP")
+)
+
+// GP provides additional methods for general purpose registers.
+type GP interface {
+ As8() Register
+ As8L() Register
+ As8H() Register
+ As16() Register
+ As32() Register
+ As64() Register
+}
+
+// GPPhysical is a general-purpose physical register.
+type GPPhysical interface {
+ Physical
+ GP
+}
+
+type gpp struct {
+ Physical
+}
+
+func newgpp(r Physical) GPPhysical { return gpp{Physical: r} }
+
+func (p gpp) As8() Register { return newgpp(p.as(S8).(Physical)) }
+func (p gpp) As8L() Register { return newgpp(p.as(S8L).(Physical)) }
+func (p gpp) As8H() Register { return newgpp(p.as(S8H).(Physical)) }
+func (p gpp) As16() Register { return newgpp(p.as(S16).(Physical)) }
+func (p gpp) As32() Register { return newgpp(p.as(S32).(Physical)) }
+func (p gpp) As64() Register { return newgpp(p.as(S64).(Physical)) }
+
+// GPVirtual is a general-purpose virtual register.
+type GPVirtual interface {
+ Virtual
+ GP
+}
+
+type gpv struct {
+ Virtual
+}
+
+func newgpv(v Virtual) GPVirtual { return gpv{Virtual: v} }
+
+func (v gpv) As8() Register { return newgpv(v.as(S8).(Virtual)) }
+func (v gpv) As8L() Register { return newgpv(v.as(S8L).(Virtual)) }
+func (v gpv) As8H() Register { return newgpv(v.as(S8H).(Virtual)) }
+func (v gpv) As16() Register { return newgpv(v.as(S16).(Virtual)) }
+func (v gpv) As32() Register { return newgpv(v.as(S32).(Virtual)) }
+func (v gpv) As64() Register { return newgpv(v.as(S64).(Virtual)) }
+
+func gp(s Spec, id Index, name string, flags ...Info) GPPhysical {
+ r := newgpp(newregister(GeneralPurpose, s, id, name, flags...))
+ GeneralPurpose.add(r)
+ return r
+}
+
+// General purpose registers.
+var (
+ // Low byte
+ AL = gp(S8L, 0, "AL")
+ CL = gp(S8L, 1, "CL")
+ DL = gp(S8L, 2, "DL")
+ BL = gp(S8L, 3, "BL")
+
+ // High byte
+ AH = gp(S8H, 0, "AH")
+ CH = gp(S8H, 1, "CH")
+ DH = gp(S8H, 2, "DH")
+ BH = gp(S8H, 3, "BH")
+
+ // 8-bit
+ SPB = gp(S8, 4, "SP", Restricted)
+ BPB = gp(S8, 5, "BP")
+ SIB = gp(S8, 6, "SI")
+ DIB = gp(S8, 7, "DI")
+ R8B = gp(S8, 8, "R8")
+ R9B = gp(S8, 9, "R9")
+ R10B = gp(S8, 10, "R10")
+ R11B = gp(S8, 11, "R11")
+ R12B = gp(S8, 12, "R12")
+ R13B = gp(S8, 13, "R13")
+ R14B = gp(S8, 14, "R14")
+ R15B = gp(S8, 15, "R15")
+
+ // 16-bit
+ AX = gp(S16, 0, "AX")
+ CX = gp(S16, 1, "CX")
+ DX = gp(S16, 2, "DX")
+ BX = gp(S16, 3, "BX")
+ SP = gp(S16, 4, "SP", Restricted)
+ BP = gp(S16, 5, "BP")
+ SI = gp(S16, 6, "SI")
+ DI = gp(S16, 7, "DI")
+ R8W = gp(S16, 8, "R8")
+ R9W = gp(S16, 9, "R9")
+ R10W = gp(S16, 10, "R10")
+ R11W = gp(S16, 11, "R11")
+ R12W = gp(S16, 12, "R12")
+ R13W = gp(S16, 13, "R13")
+ R14W = gp(S16, 14, "R14")
+ R15W = gp(S16, 15, "R15")
+
+ // 32-bit
+ EAX = gp(S32, 0, "AX")
+ ECX = gp(S32, 1, "CX")
+ EDX = gp(S32, 2, "DX")
+ EBX = gp(S32, 3, "BX")
+ ESP = gp(S32, 4, "SP", Restricted)
+ EBP = gp(S32, 5, "BP")
+ ESI = gp(S32, 6, "SI")
+ EDI = gp(S32, 7, "DI")
+ R8L = gp(S32, 8, "R8")
+ R9L = gp(S32, 9, "R9")
+ R10L = gp(S32, 10, "R10")
+ R11L = gp(S32, 11, "R11")
+ R12L = gp(S32, 12, "R12")
+ R13L = gp(S32, 13, "R13")
+ R14L = gp(S32, 14, "R14")
+ R15L = gp(S32, 15, "R15")
+
+ // 64-bit
+ RAX = gp(S64, 0, "AX")
+ RCX = gp(S64, 1, "CX")
+ RDX = gp(S64, 2, "DX")
+ RBX = gp(S64, 3, "BX")
+ RSP = gp(S64, 4, "SP", Restricted)
+ RBP = gp(S64, 5, "BP")
+ RSI = gp(S64, 6, "SI")
+ RDI = gp(S64, 7, "DI")
+ R8 = gp(S64, 8, "R8")
+ R9 = gp(S64, 9, "R9")
+ R10 = gp(S64, 10, "R10")
+ R11 = gp(S64, 11, "R11")
+ R12 = gp(S64, 12, "R12")
+ R13 = gp(S64, 13, "R13")
+ R14 = gp(S64, 14, "R14")
+ R15 = gp(S64, 15, "R15")
+)
+
+// Vec provides methods for vector registers.
+type Vec interface {
+ AsX() Register
+ AsY() Register
+ AsZ() Register
+}
+
+// VecPhysical is a physical vector register.
+type VecPhysical interface {
+ Physical
+ Vec
+}
+
+type vecp struct {
+ Physical
+ Vec
+}
+
+func newvecp(r Physical) VecPhysical { return vecp{Physical: r} }
+
+func (p vecp) AsX() Register { return newvecp(p.as(S128).(Physical)) }
+func (p vecp) AsY() Register { return newvecp(p.as(S256).(Physical)) }
+func (p vecp) AsZ() Register { return newvecp(p.as(S512).(Physical)) }
+
+// VecVirtual is a virtual vector register.
+type VecVirtual interface {
+ Virtual
+ Vec
+}
+
+type vecv struct {
+ Virtual
+ Vec
+}
+
+func newvecv(v Virtual) VecVirtual { return vecv{Virtual: v} }
+
+func (v vecv) AsX() Register { return newvecv(v.as(S128).(Virtual)) }
+func (v vecv) AsY() Register { return newvecv(v.as(S256).(Virtual)) }
+func (v vecv) AsZ() Register { return newvecv(v.as(S512).(Virtual)) }
+
+func vec(s Spec, id Index, name string, flags ...Info) VecPhysical {
+ r := newvecp(newregister(Vector, s, id, name, flags...))
+ Vector.add(r)
+ return r
+}
+
+// Vector registers.
+var (
+ // 128-bit
+ X0 = vec(S128, 0, "X0")
+ X1 = vec(S128, 1, "X1")
+ X2 = vec(S128, 2, "X2")
+ X3 = vec(S128, 3, "X3")
+ X4 = vec(S128, 4, "X4")
+ X5 = vec(S128, 5, "X5")
+ X6 = vec(S128, 6, "X6")
+ X7 = vec(S128, 7, "X7")
+ X8 = vec(S128, 8, "X8")
+ X9 = vec(S128, 9, "X9")
+ X10 = vec(S128, 10, "X10")
+ X11 = vec(S128, 11, "X11")
+ X12 = vec(S128, 12, "X12")
+ X13 = vec(S128, 13, "X13")
+ X14 = vec(S128, 14, "X14")
+ X15 = vec(S128, 15, "X15")
+ X16 = vec(S128, 16, "X16")
+ X17 = vec(S128, 17, "X17")
+ X18 = vec(S128, 18, "X18")
+ X19 = vec(S128, 19, "X19")
+ X20 = vec(S128, 20, "X20")
+ X21 = vec(S128, 21, "X21")
+ X22 = vec(S128, 22, "X22")
+ X23 = vec(S128, 23, "X23")
+ X24 = vec(S128, 24, "X24")
+ X25 = vec(S128, 25, "X25")
+ X26 = vec(S128, 26, "X26")
+ X27 = vec(S128, 27, "X27")
+ X28 = vec(S128, 28, "X28")
+ X29 = vec(S128, 29, "X29")
+ X30 = vec(S128, 30, "X30")
+ X31 = vec(S128, 31, "X31")
+
+ // 256-bit
+ Y0 = vec(S256, 0, "Y0")
+ Y1 = vec(S256, 1, "Y1")
+ Y2 = vec(S256, 2, "Y2")
+ Y3 = vec(S256, 3, "Y3")
+ Y4 = vec(S256, 4, "Y4")
+ Y5 = vec(S256, 5, "Y5")
+ Y6 = vec(S256, 6, "Y6")
+ Y7 = vec(S256, 7, "Y7")
+ Y8 = vec(S256, 8, "Y8")
+ Y9 = vec(S256, 9, "Y9")
+ Y10 = vec(S256, 10, "Y10")
+ Y11 = vec(S256, 11, "Y11")
+ Y12 = vec(S256, 12, "Y12")
+ Y13 = vec(S256, 13, "Y13")
+ Y14 = vec(S256, 14, "Y14")
+ Y15 = vec(S256, 15, "Y15")
+ Y16 = vec(S256, 16, "Y16")
+ Y17 = vec(S256, 17, "Y17")
+ Y18 = vec(S256, 18, "Y18")
+ Y19 = vec(S256, 19, "Y19")
+ Y20 = vec(S256, 20, "Y20")
+ Y21 = vec(S256, 21, "Y21")
+ Y22 = vec(S256, 22, "Y22")
+ Y23 = vec(S256, 23, "Y23")
+ Y24 = vec(S256, 24, "Y24")
+ Y25 = vec(S256, 25, "Y25")
+ Y26 = vec(S256, 26, "Y26")
+ Y27 = vec(S256, 27, "Y27")
+ Y28 = vec(S256, 28, "Y28")
+ Y29 = vec(S256, 29, "Y29")
+ Y30 = vec(S256, 30, "Y30")
+ Y31 = vec(S256, 31, "Y31")
+
+ // 512-bit
+ Z0 = vec(S512, 0, "Z0")
+ Z1 = vec(S512, 1, "Z1")
+ Z2 = vec(S512, 2, "Z2")
+ Z3 = vec(S512, 3, "Z3")
+ Z4 = vec(S512, 4, "Z4")
+ Z5 = vec(S512, 5, "Z5")
+ Z6 = vec(S512, 6, "Z6")
+ Z7 = vec(S512, 7, "Z7")
+ Z8 = vec(S512, 8, "Z8")
+ Z9 = vec(S512, 9, "Z9")
+ Z10 = vec(S512, 10, "Z10")
+ Z11 = vec(S512, 11, "Z11")
+ Z12 = vec(S512, 12, "Z12")
+ Z13 = vec(S512, 13, "Z13")
+ Z14 = vec(S512, 14, "Z14")
+ Z15 = vec(S512, 15, "Z15")
+ Z16 = vec(S512, 16, "Z16")
+ Z17 = vec(S512, 17, "Z17")
+ Z18 = vec(S512, 18, "Z18")
+ Z19 = vec(S512, 19, "Z19")
+ Z20 = vec(S512, 20, "Z20")
+ Z21 = vec(S512, 21, "Z21")
+ Z22 = vec(S512, 22, "Z22")
+ Z23 = vec(S512, 23, "Z23")
+ Z24 = vec(S512, 24, "Z24")
+ Z25 = vec(S512, 25, "Z25")
+ Z26 = vec(S512, 26, "Z26")
+ Z27 = vec(S512, 27, "Z27")
+ Z28 = vec(S512, 28, "Z28")
+ Z29 = vec(S512, 29, "Z29")
+ Z30 = vec(S512, 30, "Z30")
+ Z31 = vec(S512, 31, "Z31")
+)
diff --git a/vendor/github.com/mmcloughlin/avo/src/src.go b/vendor/github.com/mmcloughlin/avo/src/src.go
new file mode 100644
index 0000000..3a47886
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/src/src.go
@@ -0,0 +1,62 @@
+// Package src provides types for working with source files.
+package src
+
+import (
+ "os"
+ "path/filepath"
+ "runtime"
+ "strconv"
+)
+
+// Position represents a position in a source file.
+type Position struct {
+ Filename string
+ Line int // 1-up
+}
+
+// FramePosition returns the Position of the given stack frame.
+func FramePosition(f runtime.Frame) Position {
+ return Position{
+ Filename: f.File,
+ Line: f.Line,
+ }
+}
+
+// IsValid reports whether the position is valid: Line must be positive, but
+// Filename may be empty.
+func (p Position) IsValid() bool {
+ return p.Line > 0
+}
+
+// String represents Position as a string.
+func (p Position) String() string {
+ if !p.IsValid() {
+ return "-"
+ }
+ var s string
+ if p.Filename != "" {
+ s += p.Filename + ":"
+ }
+ s += strconv.Itoa(p.Line)
+ return s
+}
+
+// Rel returns Position relative to basepath. If the given filename cannot be
+// expressed relative to basepath the position will be returned unchanged.
+func (p Position) Rel(basepath string) Position {
+ q := p
+ if rel, err := filepath.Rel(basepath, q.Filename); err == nil {
+ q.Filename = rel
+ }
+ return q
+}
+
+// Relwd returns Position relative to the current working directory. Returns p
+// unchanged if the working directory cannot be determined, or the filename
+// cannot be expressed relative to the working directory.
+func (p Position) Relwd() Position {
+ if wd, err := os.Getwd(); err == nil {
+ return p.Rel(wd)
+ }
+ return p
+}
diff --git a/vendor/github.com/mmcloughlin/avo/x86/doc.go b/vendor/github.com/mmcloughlin/avo/x86/doc.go
new file mode 100644
index 0000000..6e4c8ee
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/x86/doc.go
@@ -0,0 +1,2 @@
+// Package x86 provides constructors for all x86-64 instructions.
+package x86
diff --git a/vendor/github.com/mmcloughlin/avo/x86/gen.go b/vendor/github.com/mmcloughlin/avo/x86/gen.go
new file mode 100644
index 0000000..25d15fa
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/x86/gen.go
@@ -0,0 +1,4 @@
+package x86
+
+//go:generate avogen -output zctors.go ctors
+//go:generate avogen -output zctors_test.go ctorstest
diff --git a/vendor/github.com/mmcloughlin/avo/x86/zctors.go b/vendor/github.com/mmcloughlin/avo/x86/zctors.go
new file mode 100644
index 0000000..447c0a1
--- /dev/null
+++ b/vendor/github.com/mmcloughlin/avo/x86/zctors.go
@@ -0,0 +1,34629 @@
+// Code generated by command: avogen -output zctors.go ctors. DO NOT EDIT.
+
+package x86
+
+import (
+ "errors"
+
+ intrep "github.com/mmcloughlin/avo/ir"
+ "github.com/mmcloughlin/avo/operand"
+ "github.com/mmcloughlin/avo/reg"
+)
+
+// ADCB: Add with Carry.
+//
+// Forms:
+//
+// ADCB imm8 al
+// ADCB imm8 r8
+// ADCB r8 r8
+// ADCB m8 r8
+// ADCB imm8 m8
+// ADCB r8 m8
+func ADCB(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ADCB: bad operands")
+}
+
+// ADCL: Add with Carry.
+//
+// Forms:
+//
+// ADCL imm32 eax
+// ADCL imm8 r32
+// ADCL imm32 r32
+// ADCL r32 r32
+// ADCL m32 r32
+// ADCL imm8 m32
+// ADCL imm32 m32
+// ADCL r32 m32
+func ADCL(imr, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADCL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ }
+ return nil, errors.New("ADCL: bad operands")
+}
+
+// ADCQ: Add with Carry.
+//
+// Forms:
+//
+// ADCQ imm32 rax
+// ADCQ imm8 r64
+// ADCQ imm32 r64
+// ADCQ r64 r64
+// ADCQ m64 r64
+// ADCQ imm8 m64
+// ADCQ imm32 m64
+// ADCQ r64 m64
+func ADCQ(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADCQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ADCQ: bad operands")
+}
+
+// ADCW: Add with Carry.
+//
+// Forms:
+//
+// ADCW imm16 ax
+// ADCW imm8 r16
+// ADCW imm16 r16
+// ADCW r16 r16
+// ADCW m16 r16
+// ADCW imm8 m16
+// ADCW imm16 m16
+// ADCW r16 m16
+func ADCW(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADCW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ADCW: bad operands")
+}
+
+// ADCXL: Unsigned Integer Addition of Two Operands with Carry Flag.
+//
+// Forms:
+//
+// ADCXL r32 r32
+// ADCXL m32 r32
+func ADCXL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "ADCXL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "ADCXL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ }
+ return nil, errors.New("ADCXL: bad operands")
+}
+
+// ADCXQ: Unsigned Integer Addition of Two Operands with Carry Flag.
+//
+// Forms:
+//
+// ADCXQ r64 r64
+// ADCXQ m64 r64
+func ADCXQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "ADCXQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "ADCXQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ }
+ return nil, errors.New("ADCXQ: bad operands")
+}
+
+// ADDB: Add.
+//
+// Forms:
+//
+// ADDB imm8 al
+// ADDB imm8 r8
+// ADDB r8 r8
+// ADDB m8 r8
+// ADDB imm8 m8
+// ADDB r8 m8
+func ADDB(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ADDB: bad operands")
+}
+
+// ADDL: Add.
+//
+// Forms:
+//
+// ADDL imm32 eax
+// ADDL imm8 r32
+// ADDL imm32 r32
+// ADDL r32 r32
+// ADDL m32 r32
+// ADDL imm8 m32
+// ADDL imm32 m32
+// ADDL r32 m32
+func ADDL(imr, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ADDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ }
+ return nil, errors.New("ADDL: bad operands")
+}
+
+// ADDPD: Add Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ADDPD xmm xmm
+// ADDPD m128 xmm
+func ADDPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("ADDPD: bad operands")
+}
+
+// ADDPS: Add Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ADDPS xmm xmm
+// ADDPS m128 xmm
+func ADDPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("ADDPS: bad operands")
+}
+
+// ADDQ: Add.
+//
+// Forms:
+//
+// ADDQ imm32 rax
+// ADDQ imm8 r64
+// ADDQ imm32 r64
+// ADDQ r64 r64
+// ADDQ m64 r64
+// ADDQ imm8 m64
+// ADDQ imm32 m64
+// ADDQ r64 m64
+func ADDQ(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ADDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ADDQ: bad operands")
+}
+
+// ADDSD: Add Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ADDSD xmm xmm
+// ADDSD m64 xmm
+func ADDSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("ADDSD: bad operands")
+}
+
+// ADDSS: Add Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ADDSS xmm xmm
+// ADDSS m32 xmm
+func ADDSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("ADDSS: bad operands")
+}
+
+// ADDSUBPD: Packed Double-FP Add/Subtract.
+//
+// Forms:
+//
+// ADDSUBPD xmm xmm
+// ADDSUBPD m128 xmm
+func ADDSUBPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSUBPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSUBPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("ADDSUBPD: bad operands")
+}
+
+// ADDSUBPS: Packed Single-FP Add/Subtract.
+//
+// Forms:
+//
+// ADDSUBPS xmm xmm
+// ADDSUBPS m128 xmm
+func ADDSUBPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSUBPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ADDSUBPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("ADDSUBPS: bad operands")
+}
+
+// ADDW: Add.
+//
+// Forms:
+//
+// ADDW imm16 ax
+// ADDW imm8 r16
+// ADDW imm16 r16
+// ADDW r16 r16
+// ADDW m16 r16
+// ADDW imm8 m16
+// ADDW imm16 m16
+// ADDW r16 m16
+func ADDW(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ADDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ADDW: bad operands")
+}
+
+// ADOXL: Unsigned Integer Addition of Two Operands with Overflow Flag.
+//
+// Forms:
+//
+// ADOXL r32 r32
+// ADOXL m32 r32
+func ADOXL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "ADOXL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "ADOXL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ }
+ return nil, errors.New("ADOXL: bad operands")
+}
+
+// ADOXQ: Unsigned Integer Addition of Two Operands with Overflow Flag.
+//
+// Forms:
+//
+// ADOXQ r64 r64
+// ADOXQ m64 r64
+func ADOXQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "ADOXQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "ADOXQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"ADX"},
+ }, nil
+ }
+ return nil, errors.New("ADOXQ: bad operands")
+}
+
+// AESDEC: Perform One Round of an AES Decryption Flow.
+//
+// Forms:
+//
+// AESDEC xmm xmm
+// AESDEC m128 xmm
+func AESDEC(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESDEC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESDEC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ }
+ return nil, errors.New("AESDEC: bad operands")
+}
+
+// AESDECLAST: Perform Last Round of an AES Decryption Flow.
+//
+// Forms:
+//
+// AESDECLAST xmm xmm
+// AESDECLAST m128 xmm
+func AESDECLAST(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESDECLAST",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESDECLAST",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ }
+ return nil, errors.New("AESDECLAST: bad operands")
+}
+
+// AESENC: Perform One Round of an AES Encryption Flow.
+//
+// Forms:
+//
+// AESENC xmm xmm
+// AESENC m128 xmm
+func AESENC(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESENC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESENC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ }
+ return nil, errors.New("AESENC: bad operands")
+}
+
+// AESENCLAST: Perform Last Round of an AES Encryption Flow.
+//
+// Forms:
+//
+// AESENCLAST xmm xmm
+// AESENCLAST m128 xmm
+func AESENCLAST(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESENCLAST",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESENCLAST",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ }
+ return nil, errors.New("AESENCLAST: bad operands")
+}
+
+// AESIMC: Perform the AES InvMixColumn Transformation.
+//
+// Forms:
+//
+// AESIMC xmm xmm
+// AESIMC m128 xmm
+func AESIMC(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESIMC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESIMC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ }
+ return nil, errors.New("AESIMC: bad operands")
+}
+
+// AESKEYGENASSIST: AES Round Key Generation Assist.
+//
+// Forms:
+//
+// AESKEYGENASSIST imm8 xmm xmm
+// AESKEYGENASSIST imm8 m128 xmm
+func AESKEYGENASSIST(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESKEYGENASSIST",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "AESKEYGENASSIST",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AES"},
+ }, nil
+ }
+ return nil, errors.New("AESKEYGENASSIST: bad operands")
+}
+
+// ANDB: Logical AND.
+//
+// Forms:
+//
+// ANDB imm8 al
+// ANDB imm8 r8
+// ANDB r8 r8
+// ANDB m8 r8
+// ANDB imm8 m8
+// ANDB r8 m8
+func ANDB(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ANDB: bad operands")
+}
+
+// ANDL: Logical AND.
+//
+// Forms:
+//
+// ANDL imm32 eax
+// ANDL imm8 r32
+// ANDL imm32 r32
+// ANDL r32 r32
+// ANDL m32 r32
+// ANDL imm8 m32
+// ANDL imm32 m32
+// ANDL r32 m32
+func ANDL(imr, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ANDL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ }
+ return nil, errors.New("ANDL: bad operands")
+}
+
+// ANDNL: Logical AND NOT.
+//
+// Forms:
+//
+// ANDNL r32 r32 r32
+// ANDNL m32 r32 r32
+func ANDNL(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "ANDNL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "ANDNL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("ANDNL: bad operands")
+}
+
+// ANDNPD: Bitwise Logical AND NOT of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ANDNPD xmm xmm
+// ANDNPD m128 xmm
+func ANDNPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDNPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDNPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("ANDNPD: bad operands")
+}
+
+// ANDNPS: Bitwise Logical AND NOT of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ANDNPS xmm xmm
+// ANDNPS m128 xmm
+func ANDNPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDNPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDNPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("ANDNPS: bad operands")
+}
+
+// ANDNQ: Logical AND NOT.
+//
+// Forms:
+//
+// ANDNQ r64 r64 r64
+// ANDNQ m64 r64 r64
+func ANDNQ(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "ANDNQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "ANDNQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("ANDNQ: bad operands")
+}
+
+// ANDPD: Bitwise Logical AND of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ANDPD xmm xmm
+// ANDPD m128 xmm
+func ANDPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("ANDPD: bad operands")
+}
+
+// ANDPS: Bitwise Logical AND of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ANDPS xmm xmm
+// ANDPS m128 xmm
+func ANDPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ANDPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("ANDPS: bad operands")
+}
+
+// ANDQ: Logical AND.
+//
+// Forms:
+//
+// ANDQ imm32 rax
+// ANDQ imm8 r64
+// ANDQ imm32 r64
+// ANDQ r64 r64
+// ANDQ m64 r64
+// ANDQ imm8 m64
+// ANDQ imm32 m64
+// ANDQ r64 m64
+func ANDQ(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ANDQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ANDQ: bad operands")
+}
+
+// ANDW: Logical AND.
+//
+// Forms:
+//
+// ANDW imm16 ax
+// ANDW imm8 r16
+// ANDW imm16 r16
+// ANDW r16 r16
+// ANDW m16 r16
+// ANDW imm8 m16
+// ANDW imm16 m16
+// ANDW r16 m16
+func ANDW(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ANDW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ANDW: bad operands")
+}
+
+// BEXTRL: Bit Field Extract.
+//
+// Forms:
+//
+// BEXTRL r32 r32 r32
+// BEXTRL r32 m32 r32
+func BEXTRL(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsR32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "BEXTRL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsR32(r) && operand.IsM32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "BEXTRL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BEXTRL: bad operands")
+}
+
+// BEXTRQ: Bit Field Extract.
+//
+// Forms:
+//
+// BEXTRQ r64 r64 r64
+// BEXTRQ r64 m64 r64
+func BEXTRQ(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsR64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "BEXTRQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsR64(r) && operand.IsM64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "BEXTRQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BEXTRQ: bad operands")
+}
+
+// BLENDPD: Blend Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// BLENDPD imm8 xmm xmm
+// BLENDPD imm8 m128 xmm
+func BLENDPD(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "BLENDPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "BLENDPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("BLENDPD: bad operands")
+}
+
+// BLENDPS: Blend Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// BLENDPS imm8 xmm xmm
+// BLENDPS imm8 m128 xmm
+func BLENDPS(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "BLENDPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "BLENDPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("BLENDPS: bad operands")
+}
+
+// BLENDVPD: Variable Blend Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// BLENDVPD xmm0 xmm xmm
+// BLENDVPD xmm0 m128 xmm
+func BLENDVPD(x, mx, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM0(x) && operand.IsXMM(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "BLENDVPD",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsXMM0(x) && operand.IsM128(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "BLENDVPD",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("BLENDVPD: bad operands")
+}
+
+// BLENDVPS: Variable Blend Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// BLENDVPS xmm0 xmm xmm
+// BLENDVPS xmm0 m128 xmm
+func BLENDVPS(x, mx, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM0(x) && operand.IsXMM(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "BLENDVPS",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsXMM0(x) && operand.IsM128(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "BLENDVPS",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("BLENDVPS: bad operands")
+}
+
+// BLSIL: Isolate Lowest Set Bit.
+//
+// Forms:
+//
+// BLSIL r32 r32
+// BLSIL m32 r32
+func BLSIL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BLSIL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BLSIL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BLSIL: bad operands")
+}
+
+// BLSIQ: Isolate Lowest Set Bit.
+//
+// Forms:
+//
+// BLSIQ r64 r64
+// BLSIQ m64 r64
+func BLSIQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BLSIQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BLSIQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BLSIQ: bad operands")
+}
+
+// BLSMSKL: Mask From Lowest Set Bit.
+//
+// Forms:
+//
+// BLSMSKL r32 r32
+// BLSMSKL m32 r32
+func BLSMSKL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BLSMSKL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BLSMSKL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BLSMSKL: bad operands")
+}
+
+// BLSMSKQ: Mask From Lowest Set Bit.
+//
+// Forms:
+//
+// BLSMSKQ r64 r64
+// BLSMSKQ m64 r64
+func BLSMSKQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BLSMSKQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BLSMSKQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BLSMSKQ: bad operands")
+}
+
+// BLSRL: Reset Lowest Set Bit.
+//
+// Forms:
+//
+// BLSRL r32 r32
+// BLSRL m32 r32
+func BLSRL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BLSRL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BLSRL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BLSRL: bad operands")
+}
+
+// BLSRQ: Reset Lowest Set Bit.
+//
+// Forms:
+//
+// BLSRQ r64 r64
+// BLSRQ m64 r64
+func BLSRQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BLSRQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BLSRQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("BLSRQ: bad operands")
+}
+
+// BSFL: Bit Scan Forward.
+//
+// Forms:
+//
+// BSFL r32 r32
+// BSFL m32 r32
+func BSFL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BSFL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BSFL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSFL: bad operands")
+}
+
+// BSFQ: Bit Scan Forward.
+//
+// Forms:
+//
+// BSFQ r64 r64
+// BSFQ m64 r64
+func BSFQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BSFQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BSFQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSFQ: bad operands")
+}
+
+// BSFW: Bit Scan Forward.
+//
+// Forms:
+//
+// BSFW r16 r16
+// BSFW m16 r16
+func BSFW(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "BSFW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "BSFW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSFW: bad operands")
+}
+
+// BSRL: Bit Scan Reverse.
+//
+// Forms:
+//
+// BSRL r32 r32
+// BSRL m32 r32
+func BSRL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BSRL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BSRL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSRL: bad operands")
+}
+
+// BSRQ: Bit Scan Reverse.
+//
+// Forms:
+//
+// BSRQ r64 r64
+// BSRQ m64 r64
+func BSRQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BSRQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BSRQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSRQ: bad operands")
+}
+
+// BSRW: Bit Scan Reverse.
+//
+// Forms:
+//
+// BSRW r16 r16
+// BSRW m16 r16
+func BSRW(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "BSRW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "BSRW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSRW: bad operands")
+}
+
+// BSWAPL: Byte Swap.
+//
+// Forms:
+//
+// BSWAPL r32
+func BSWAPL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "BSWAPL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSWAPL: bad operands")
+}
+
+// BSWAPQ: Byte Swap.
+//
+// Forms:
+//
+// BSWAPQ r64
+func BSWAPQ(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "BSWAPQ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{r},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("BSWAPQ: bad operands")
+}
+
+// BTCL: Bit Test and Complement.
+//
+// Forms:
+//
+// BTCL imm8 r32
+// BTCL r32 r32
+// BTCL imm8 m32
+// BTCL r32 m32
+func BTCL(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTCL: bad operands")
+}
+
+// BTCQ: Bit Test and Complement.
+//
+// Forms:
+//
+// BTCQ imm8 r64
+// BTCQ r64 r64
+// BTCQ imm8 m64
+// BTCQ r64 m64
+func BTCQ(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTCQ: bad operands")
+}
+
+// BTCW: Bit Test and Complement.
+//
+// Forms:
+//
+// BTCW imm8 r16
+// BTCW r16 r16
+// BTCW imm8 m16
+// BTCW r16 m16
+func BTCW(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTCW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTCW: bad operands")
+}
+
+// BTL: Bit Test.
+//
+// Forms:
+//
+// BTL imm8 r32
+// BTL r32 r32
+// BTL imm8 m32
+// BTL r32 m32
+func BTL(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR32(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR32(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("BTL: bad operands")
+}
+
+// BTQ: Bit Test.
+//
+// Forms:
+//
+// BTQ imm8 r64
+// BTQ r64 r64
+// BTQ imm8 m64
+// BTQ r64 m64
+func BTQ(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("BTQ: bad operands")
+}
+
+// BTRL: Bit Test and Reset.
+//
+// Forms:
+//
+// BTRL imm8 r32
+// BTRL r32 r32
+// BTRL imm8 m32
+// BTRL r32 m32
+func BTRL(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTRL: bad operands")
+}
+
+// BTRQ: Bit Test and Reset.
+//
+// Forms:
+//
+// BTRQ imm8 r64
+// BTRQ r64 r64
+// BTRQ imm8 m64
+// BTRQ r64 m64
+func BTRQ(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTRQ: bad operands")
+}
+
+// BTRW: Bit Test and Reset.
+//
+// Forms:
+//
+// BTRW imm8 r16
+// BTRW r16 r16
+// BTRW imm8 m16
+// BTRW r16 m16
+func BTRW(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTRW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTRW: bad operands")
+}
+
+// BTSL: Bit Test and Set.
+//
+// Forms:
+//
+// BTSL imm8 r32
+// BTSL r32 r32
+// BTSL imm8 m32
+// BTSL r32 m32
+func BTSL(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(ir) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(ir) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSL",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTSL: bad operands")
+}
+
+// BTSQ: Bit Test and Set.
+//
+// Forms:
+//
+// BTSQ imm8 r64
+// BTSQ r64 r64
+// BTSQ imm8 m64
+// BTSQ r64 m64
+func BTSQ(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTSQ: bad operands")
+}
+
+// BTSW: Bit Test and Set.
+//
+// Forms:
+//
+// BTSW imm8 r16
+// BTSW r16 r16
+// BTSW imm8 m16
+// BTSW r16 m16
+func BTSW(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTSW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("BTSW: bad operands")
+}
+
+// BTW: Bit Test.
+//
+// Forms:
+//
+// BTW imm8 r16
+// BTW r16 r16
+// BTW imm8 m16
+// BTW r16 m16
+func BTW(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR16(ir) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR16(ir) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "BTW",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("BTW: bad operands")
+}
+
+// BZHIL: Zero High Bits Starting with Specified Bit Position.
+//
+// Forms:
+//
+// BZHIL r32 r32 r32
+// BZHIL r32 m32 r32
+func BZHIL(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsR32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "BZHIL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR32(r) && operand.IsM32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "BZHIL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("BZHIL: bad operands")
+}
+
+// BZHIQ: Zero High Bits Starting with Specified Bit Position.
+//
+// Forms:
+//
+// BZHIQ r64 r64 r64
+// BZHIQ r64 m64 r64
+func BZHIQ(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsR64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "BZHIQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR64(r) && operand.IsM64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "BZHIQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("BZHIQ: bad operands")
+}
+
+// CALL: Call Procedure.
+//
+// Forms:
+//
+// CALL rel32
+func CALL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "CALL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("CALL: bad operands")
+}
+
+// CBW: Convert Byte to Word.
+//
+// Forms:
+//
+// CBW
+func CBW() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CBW",
+ Operands: nil,
+ Inputs: []operand.Op{reg.AL},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+}
+
+// CDQ: Convert Doubleword to Quadword.
+//
+// Forms:
+//
+// CDQ
+func CDQ() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CDQ",
+ Operands: nil,
+ Inputs: []operand.Op{reg.EAX},
+ Outputs: []operand.Op{reg.EDX},
+ }, nil
+}
+
+// CDQE: Convert Doubleword to Quadword.
+//
+// Forms:
+//
+// CDQE
+func CDQE() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CDQE",
+ Operands: nil,
+ Inputs: []operand.Op{reg.EAX},
+ Outputs: []operand.Op{reg.RAX},
+ }, nil
+}
+
+// CLC: Clear Carry Flag.
+//
+// Forms:
+//
+// CLC
+func CLC() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CLC",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// CLD: Clear Direction Flag.
+//
+// Forms:
+//
+// CLD
+func CLD() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CLD",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// CLFLUSH: Flush Cache Line.
+//
+// Forms:
+//
+// CLFLUSH m8
+func CLFLUSH(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM8(m):
+ return &intrep.Instruction{
+ Opcode: "CLFLUSH",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"CLFLUSH"},
+ }, nil
+ }
+ return nil, errors.New("CLFLUSH: bad operands")
+}
+
+// CLFLUSHOPT: Flush Cache Line Optimized.
+//
+// Forms:
+//
+// CLFLUSHOPT m8
+func CLFLUSHOPT(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM8(m):
+ return &intrep.Instruction{
+ Opcode: "CLFLUSHOPT",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"CLFLUSHOPT"},
+ }, nil
+ }
+ return nil, errors.New("CLFLUSHOPT: bad operands")
+}
+
+// CMC: Complement Carry Flag.
+//
+// Forms:
+//
+// CMC
+func CMC() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CMC",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// CMOVLCC: Move if above or equal (CF == 0).
+//
+// Forms:
+//
+// CMOVLCC r32 r32
+// CMOVLCC m32 r32
+func CMOVLCC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLCC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLCC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLCC: bad operands")
+}
+
+// CMOVLCS: Move if below (CF == 1).
+//
+// Forms:
+//
+// CMOVLCS r32 r32
+// CMOVLCS m32 r32
+func CMOVLCS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLCS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLCS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLCS: bad operands")
+}
+
+// CMOVLEQ: Move if equal (ZF == 1).
+//
+// Forms:
+//
+// CMOVLEQ r32 r32
+// CMOVLEQ m32 r32
+func CMOVLEQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLEQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLEQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLEQ: bad operands")
+}
+
+// CMOVLGE: Move if greater or equal (SF == OF).
+//
+// Forms:
+//
+// CMOVLGE r32 r32
+// CMOVLGE m32 r32
+func CMOVLGE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLGE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLGE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLGE: bad operands")
+}
+
+// CMOVLGT: Move if greater (ZF == 0 and SF == OF).
+//
+// Forms:
+//
+// CMOVLGT r32 r32
+// CMOVLGT m32 r32
+func CMOVLGT(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLGT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLGT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLGT: bad operands")
+}
+
+// CMOVLHI: Move if above (CF == 0 and ZF == 0).
+//
+// Forms:
+//
+// CMOVLHI r32 r32
+// CMOVLHI m32 r32
+func CMOVLHI(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLHI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLHI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLHI: bad operands")
+}
+
+// CMOVLLE: Move if less or equal (ZF == 1 or SF != OF).
+//
+// Forms:
+//
+// CMOVLLE r32 r32
+// CMOVLLE m32 r32
+func CMOVLLE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLLE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLLE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLLE: bad operands")
+}
+
+// CMOVLLS: Move if below or equal (CF == 1 or ZF == 1).
+//
+// Forms:
+//
+// CMOVLLS r32 r32
+// CMOVLLS m32 r32
+func CMOVLLS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLLS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLLS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLLS: bad operands")
+}
+
+// CMOVLLT: Move if less (SF != OF).
+//
+// Forms:
+//
+// CMOVLLT r32 r32
+// CMOVLLT m32 r32
+func CMOVLLT(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLLT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLLT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLLT: bad operands")
+}
+
+// CMOVLMI: Move if sign (SF == 1).
+//
+// Forms:
+//
+// CMOVLMI r32 r32
+// CMOVLMI m32 r32
+func CMOVLMI(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLMI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLMI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLMI: bad operands")
+}
+
+// CMOVLNE: Move if not equal (ZF == 0).
+//
+// Forms:
+//
+// CMOVLNE r32 r32
+// CMOVLNE m32 r32
+func CMOVLNE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLNE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLNE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLNE: bad operands")
+}
+
+// CMOVLOC: Move if not overflow (OF == 0).
+//
+// Forms:
+//
+// CMOVLOC r32 r32
+// CMOVLOC m32 r32
+func CMOVLOC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLOC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLOC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLOC: bad operands")
+}
+
+// CMOVLOS: Move if overflow (OF == 1).
+//
+// Forms:
+//
+// CMOVLOS r32 r32
+// CMOVLOS m32 r32
+func CMOVLOS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLOS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLOS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLOS: bad operands")
+}
+
+// CMOVLPC: Move if not parity (PF == 0).
+//
+// Forms:
+//
+// CMOVLPC r32 r32
+// CMOVLPC m32 r32
+func CMOVLPC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLPC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLPC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLPC: bad operands")
+}
+
+// CMOVLPL: Move if not sign (SF == 0).
+//
+// Forms:
+//
+// CMOVLPL r32 r32
+// CMOVLPL m32 r32
+func CMOVLPL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLPL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLPL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLPL: bad operands")
+}
+
+// CMOVLPS: Move if parity (PF == 1).
+//
+// Forms:
+//
+// CMOVLPS r32 r32
+// CMOVLPS m32 r32
+func CMOVLPS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLPS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVLPS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVLPS: bad operands")
+}
+
+// CMOVQCC: Move if above or equal (CF == 0).
+//
+// Forms:
+//
+// CMOVQCC r64 r64
+// CMOVQCC m64 r64
+func CMOVQCC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQCC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQCC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQCC: bad operands")
+}
+
+// CMOVQCS: Move if below (CF == 1).
+//
+// Forms:
+//
+// CMOVQCS r64 r64
+// CMOVQCS m64 r64
+func CMOVQCS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQCS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQCS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQCS: bad operands")
+}
+
+// CMOVQEQ: Move if equal (ZF == 1).
+//
+// Forms:
+//
+// CMOVQEQ r64 r64
+// CMOVQEQ m64 r64
+func CMOVQEQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQEQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQEQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQEQ: bad operands")
+}
+
+// CMOVQGE: Move if greater or equal (SF == OF).
+//
+// Forms:
+//
+// CMOVQGE r64 r64
+// CMOVQGE m64 r64
+func CMOVQGE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQGE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQGE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQGE: bad operands")
+}
+
+// CMOVQGT: Move if greater (ZF == 0 and SF == OF).
+//
+// Forms:
+//
+// CMOVQGT r64 r64
+// CMOVQGT m64 r64
+func CMOVQGT(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQGT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQGT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQGT: bad operands")
+}
+
+// CMOVQHI: Move if above (CF == 0 and ZF == 0).
+//
+// Forms:
+//
+// CMOVQHI r64 r64
+// CMOVQHI m64 r64
+func CMOVQHI(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQHI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQHI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQHI: bad operands")
+}
+
+// CMOVQLE: Move if less or equal (ZF == 1 or SF != OF).
+//
+// Forms:
+//
+// CMOVQLE r64 r64
+// CMOVQLE m64 r64
+func CMOVQLE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQLE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQLE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQLE: bad operands")
+}
+
+// CMOVQLS: Move if below or equal (CF == 1 or ZF == 1).
+//
+// Forms:
+//
+// CMOVQLS r64 r64
+// CMOVQLS m64 r64
+func CMOVQLS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQLS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQLS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQLS: bad operands")
+}
+
+// CMOVQLT: Move if less (SF != OF).
+//
+// Forms:
+//
+// CMOVQLT r64 r64
+// CMOVQLT m64 r64
+func CMOVQLT(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQLT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQLT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQLT: bad operands")
+}
+
+// CMOVQMI: Move if sign (SF == 1).
+//
+// Forms:
+//
+// CMOVQMI r64 r64
+// CMOVQMI m64 r64
+func CMOVQMI(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQMI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQMI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQMI: bad operands")
+}
+
+// CMOVQNE: Move if not equal (ZF == 0).
+//
+// Forms:
+//
+// CMOVQNE r64 r64
+// CMOVQNE m64 r64
+func CMOVQNE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQNE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQNE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQNE: bad operands")
+}
+
+// CMOVQOC: Move if not overflow (OF == 0).
+//
+// Forms:
+//
+// CMOVQOC r64 r64
+// CMOVQOC m64 r64
+func CMOVQOC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQOC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQOC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQOC: bad operands")
+}
+
+// CMOVQOS: Move if overflow (OF == 1).
+//
+// Forms:
+//
+// CMOVQOS r64 r64
+// CMOVQOS m64 r64
+func CMOVQOS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQOS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQOS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQOS: bad operands")
+}
+
+// CMOVQPC: Move if not parity (PF == 0).
+//
+// Forms:
+//
+// CMOVQPC r64 r64
+// CMOVQPC m64 r64
+func CMOVQPC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQPC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQPC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQPC: bad operands")
+}
+
+// CMOVQPL: Move if not sign (SF == 0).
+//
+// Forms:
+//
+// CMOVQPL r64 r64
+// CMOVQPL m64 r64
+func CMOVQPL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQPL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQPL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQPL: bad operands")
+}
+
+// CMOVQPS: Move if parity (PF == 1).
+//
+// Forms:
+//
+// CMOVQPS r64 r64
+// CMOVQPS m64 r64
+func CMOVQPS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQPS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVQPS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVQPS: bad operands")
+}
+
+// CMOVWCC: Move if above or equal (CF == 0).
+//
+// Forms:
+//
+// CMOVWCC r16 r16
+// CMOVWCC m16 r16
+func CMOVWCC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWCC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWCC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWCC: bad operands")
+}
+
+// CMOVWCS: Move if below (CF == 1).
+//
+// Forms:
+//
+// CMOVWCS r16 r16
+// CMOVWCS m16 r16
+func CMOVWCS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWCS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWCS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWCS: bad operands")
+}
+
+// CMOVWEQ: Move if equal (ZF == 1).
+//
+// Forms:
+//
+// CMOVWEQ r16 r16
+// CMOVWEQ m16 r16
+func CMOVWEQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWEQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWEQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWEQ: bad operands")
+}
+
+// CMOVWGE: Move if greater or equal (SF == OF).
+//
+// Forms:
+//
+// CMOVWGE r16 r16
+// CMOVWGE m16 r16
+func CMOVWGE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWGE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWGE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWGE: bad operands")
+}
+
+// CMOVWGT: Move if greater (ZF == 0 and SF == OF).
+//
+// Forms:
+//
+// CMOVWGT r16 r16
+// CMOVWGT m16 r16
+func CMOVWGT(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWGT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWGT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWGT: bad operands")
+}
+
+// CMOVWHI: Move if above (CF == 0 and ZF == 0).
+//
+// Forms:
+//
+// CMOVWHI r16 r16
+// CMOVWHI m16 r16
+func CMOVWHI(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWHI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWHI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWHI: bad operands")
+}
+
+// CMOVWLE: Move if less or equal (ZF == 1 or SF != OF).
+//
+// Forms:
+//
+// CMOVWLE r16 r16
+// CMOVWLE m16 r16
+func CMOVWLE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWLE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWLE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWLE: bad operands")
+}
+
+// CMOVWLS: Move if below or equal (CF == 1 or ZF == 1).
+//
+// Forms:
+//
+// CMOVWLS r16 r16
+// CMOVWLS m16 r16
+func CMOVWLS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWLS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWLS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWLS: bad operands")
+}
+
+// CMOVWLT: Move if less (SF != OF).
+//
+// Forms:
+//
+// CMOVWLT r16 r16
+// CMOVWLT m16 r16
+func CMOVWLT(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWLT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWLT",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWLT: bad operands")
+}
+
+// CMOVWMI: Move if sign (SF == 1).
+//
+// Forms:
+//
+// CMOVWMI r16 r16
+// CMOVWMI m16 r16
+func CMOVWMI(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWMI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWMI",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWMI: bad operands")
+}
+
+// CMOVWNE: Move if not equal (ZF == 0).
+//
+// Forms:
+//
+// CMOVWNE r16 r16
+// CMOVWNE m16 r16
+func CMOVWNE(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWNE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWNE",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWNE: bad operands")
+}
+
+// CMOVWOC: Move if not overflow (OF == 0).
+//
+// Forms:
+//
+// CMOVWOC r16 r16
+// CMOVWOC m16 r16
+func CMOVWOC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWOC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWOC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWOC: bad operands")
+}
+
+// CMOVWOS: Move if overflow (OF == 1).
+//
+// Forms:
+//
+// CMOVWOS r16 r16
+// CMOVWOS m16 r16
+func CMOVWOS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWOS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWOS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWOS: bad operands")
+}
+
+// CMOVWPC: Move if not parity (PF == 0).
+//
+// Forms:
+//
+// CMOVWPC r16 r16
+// CMOVWPC m16 r16
+func CMOVWPC(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWPC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWPC",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWPC: bad operands")
+}
+
+// CMOVWPL: Move if not sign (SF == 0).
+//
+// Forms:
+//
+// CMOVWPL r16 r16
+// CMOVWPL m16 r16
+func CMOVWPL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWPL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWPL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWPL: bad operands")
+}
+
+// CMOVWPS: Move if parity (PF == 1).
+//
+// Forms:
+//
+// CMOVWPS r16 r16
+// CMOVWPS m16 r16
+func CMOVWPS(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWPS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "CMOVWPS",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"CMOV"},
+ }, nil
+ }
+ return nil, errors.New("CMOVWPS: bad operands")
+}
+
+// CMPB: Compare Two Operands.
+//
+// Forms:
+//
+// CMPB al imm8
+// CMPB r8 imm8
+// CMPB r8 r8
+// CMPB r8 m8
+// CMPB m8 imm8
+// CMPB m8 r8
+func CMPB(amr, imr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsAL(amr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPB",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR8(amr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPB",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR8(amr) && operand.IsR8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPB",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr, imr},
+ Outputs: []operand.Op{},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsR8(amr) && operand.IsM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPB",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM8(amr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPB",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM8(amr) && operand.IsR8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPB",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("CMPB: bad operands")
+}
+
+// CMPL: Compare Two Operands.
+//
+// Forms:
+//
+// CMPL eax imm32
+// CMPL r32 imm8
+// CMPL r32 imm32
+// CMPL r32 r32
+// CMPL r32 m32
+// CMPL m32 imm8
+// CMPL m32 imm32
+// CMPL m32 r32
+func CMPL(emr, imr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsEAX(emr) && operand.IsIMM32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR32(emr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR32(emr) && operand.IsIMM32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR32(emr) && operand.IsR32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr, imr},
+ Outputs: []operand.Op{},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsR32(emr) && operand.IsM32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM32(emr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM32(emr) && operand.IsIMM32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM32(emr) && operand.IsR32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPL",
+ Operands: []operand.Op{emr, imr},
+ Inputs: []operand.Op{emr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("CMPL: bad operands")
+}
+
+// CMPPD: Compare Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// CMPPD xmm xmm imm8
+// CMPPD m128 xmm imm8
+func CMPPD(mx, x, i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPPD",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPPD",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CMPPD: bad operands")
+}
+
+// CMPPS: Compare Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// CMPPS xmm xmm imm8
+// CMPPS m128 xmm imm8
+func CMPPS(mx, x, i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPPS",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPPS",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("CMPPS: bad operands")
+}
+
+// CMPQ: Compare Two Operands.
+//
+// Forms:
+//
+// CMPQ rax imm32
+// CMPQ r64 imm8
+// CMPQ r64 imm32
+// CMPQ r64 r64
+// CMPQ r64 m64
+// CMPQ m64 imm8
+// CMPQ m64 imm32
+// CMPQ m64 r64
+func CMPQ(mr, imr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsRAX(mr) && operand.IsIMM32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(mr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(mr) && operand.IsIMM32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(mr) && operand.IsR64(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr, imr},
+ Outputs: []operand.Op{},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsR64(mr) && operand.IsM64(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM64(mr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM64(mr) && operand.IsIMM32(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPQ",
+ Operands: []operand.Op{mr, imr},
+ Inputs: []operand.Op{mr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("CMPQ: bad operands")
+}
+
+// CMPSD: Compare Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// CMPSD xmm xmm imm8
+// CMPSD m64 xmm imm8
+func CMPSD(mx, x, i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPSD",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPSD",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CMPSD: bad operands")
+}
+
+// CMPSS: Compare Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// CMPSS xmm xmm imm8
+// CMPSS m32 xmm imm8
+func CMPSS(mx, x, i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPSS",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "CMPSS",
+ Operands: []operand.Op{mx, x, i},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("CMPSS: bad operands")
+}
+
+// CMPW: Compare Two Operands.
+//
+// Forms:
+//
+// CMPW ax imm16
+// CMPW r16 imm8
+// CMPW r16 imm16
+// CMPW r16 r16
+// CMPW r16 m16
+// CMPW m16 imm8
+// CMPW m16 imm16
+// CMPW m16 r16
+func CMPW(amr, imr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsAX(amr) && operand.IsIMM16(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR16(amr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR16(amr) && operand.IsIMM16(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR16(amr) && operand.IsR16(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr, imr},
+ Outputs: []operand.Op{},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsR16(amr) && operand.IsM16(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM16(amr) && operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM16(amr) && operand.IsIMM16(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM16(amr) && operand.IsR16(imr):
+ return &intrep.Instruction{
+ Opcode: "CMPW",
+ Operands: []operand.Op{amr, imr},
+ Inputs: []operand.Op{amr, imr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("CMPW: bad operands")
+}
+
+// CMPXCHG16B: Compare and Exchange 16 Bytes.
+//
+// Forms:
+//
+// CMPXCHG16B m128
+func CMPXCHG16B(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHG16B",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m, reg.RAX, reg.RBX, reg.RCX, reg.RDX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ }
+ return nil, errors.New("CMPXCHG16B: bad operands")
+}
+
+// CMPXCHG8B: Compare and Exchange 8 Bytes.
+//
+// Forms:
+//
+// CMPXCHG8B m64
+func CMPXCHG8B(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM64(m):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHG8B",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m, reg.EAX, reg.EBX, reg.ECX, reg.EDX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ }
+ return nil, errors.New("CMPXCHG8B: bad operands")
+}
+
+// CMPXCHGB: Compare and Exchange.
+//
+// Forms:
+//
+// CMPXCHGB r8 r8
+// CMPXCHGB r8 m8
+func CMPXCHGB(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(r) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGB",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR8(r) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGB",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("CMPXCHGB: bad operands")
+}
+
+// CMPXCHGL: Compare and Exchange.
+//
+// Forms:
+//
+// CMPXCHGL r32 r32
+// CMPXCHGL r32 m32
+func CMPXCHGL(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGL",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(r) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGL",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("CMPXCHGL: bad operands")
+}
+
+// CMPXCHGQ: Compare and Exchange.
+//
+// Forms:
+//
+// CMPXCHGQ r64 r64
+// CMPXCHGQ r64 m64
+func CMPXCHGQ(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGQ",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(r) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGQ",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("CMPXCHGQ: bad operands")
+}
+
+// CMPXCHGW: Compare and Exchange.
+//
+// Forms:
+//
+// CMPXCHGW r16 r16
+// CMPXCHGW r16 m16
+func CMPXCHGW(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(r) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGW",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(r) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "CMPXCHGW",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("CMPXCHGW: bad operands")
+}
+
+// COMISD: Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// COMISD xmm xmm
+// COMISD m64 xmm
+func COMISD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "COMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "COMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("COMISD: bad operands")
+}
+
+// COMISS: Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// COMISS xmm xmm
+// COMISS m32 xmm
+func COMISS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "COMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "COMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("COMISS: bad operands")
+}
+
+// CPUID: CPU Identification.
+//
+// Forms:
+//
+// CPUID
+func CPUID() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CPUID",
+ Operands: nil,
+ Inputs: []operand.Op{reg.EAX, reg.ECX},
+ Outputs: []operand.Op{reg.EAX, reg.EBX, reg.ECX, reg.EDX},
+ ISA: []string{"CPUID"},
+ }, nil
+}
+
+// CQO: Convert Quadword to Octaword.
+//
+// Forms:
+//
+// CQO
+func CQO() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CQO",
+ Operands: nil,
+ Inputs: []operand.Op{reg.RAX},
+ Outputs: []operand.Op{reg.RDX},
+ }, nil
+}
+
+// CRC32B: Accumulate CRC32 Value.
+//
+// Forms:
+//
+// CRC32B r8 r32
+// CRC32B m8 r32
+// CRC32B r8 r64
+// CRC32B m8 r64
+func CRC32B(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32B",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32B",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsR8(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32B",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32B",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("CRC32B: bad operands")
+}
+
+// CRC32L: Accumulate CRC32 Value.
+//
+// Forms:
+//
+// CRC32L r32 r32
+// CRC32L m32 r32
+func CRC32L(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32L",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32L",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("CRC32L: bad operands")
+}
+
+// CRC32Q: Accumulate CRC32 Value.
+//
+// Forms:
+//
+// CRC32Q r64 r64
+// CRC32Q m64 r64
+func CRC32Q(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32Q",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32Q",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("CRC32Q: bad operands")
+}
+
+// CRC32W: Accumulate CRC32 Value.
+//
+// Forms:
+//
+// CRC32W r16 r32
+// CRC32W m16 r32
+func CRC32W(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32W",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CRC32W",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("CRC32W: bad operands")
+}
+
+// CVTPD2PL: Convert Packed Double-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// CVTPD2PL xmm xmm
+// CVTPD2PL m128 xmm
+func CVTPD2PL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPD2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPD2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTPD2PL: bad operands")
+}
+
+// CVTPD2PS: Convert Packed Double-Precision FP Values to Packed Single-Precision FP Values.
+//
+// Forms:
+//
+// CVTPD2PS xmm xmm
+// CVTPD2PS m128 xmm
+func CVTPD2PS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPD2PS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPD2PS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTPD2PS: bad operands")
+}
+
+// CVTPL2PD: Convert Packed Dword Integers to Packed Double-Precision FP Values.
+//
+// Forms:
+//
+// CVTPL2PD xmm xmm
+// CVTPL2PD m64 xmm
+func CVTPL2PD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPL2PD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPL2PD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTPL2PD: bad operands")
+}
+
+// CVTPL2PS: Convert Packed Dword Integers to Packed Single-Precision FP Values.
+//
+// Forms:
+//
+// CVTPL2PS xmm xmm
+// CVTPL2PS m128 xmm
+func CVTPL2PS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPL2PS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPL2PS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTPL2PS: bad operands")
+}
+
+// CVTPS2PD: Convert Packed Single-Precision FP Values to Packed Double-Precision FP Values.
+//
+// Forms:
+//
+// CVTPS2PD xmm xmm
+// CVTPS2PD m64 xmm
+func CVTPS2PD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPS2PD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPS2PD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTPS2PD: bad operands")
+}
+
+// CVTPS2PL: Convert Packed Single-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// CVTPS2PL xmm xmm
+// CVTPS2PL m128 xmm
+func CVTPS2PL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPS2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTPS2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTPS2PL: bad operands")
+}
+
+// CVTSD2SL: Convert Scalar Double-Precision FP Value to Integer.
+//
+// Forms:
+//
+// CVTSD2SL xmm r32
+// CVTSD2SL m64 r32
+// CVTSD2SL xmm r64
+// CVTSD2SL m64 r64
+func CVTSD2SL(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSD2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSD2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSD2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSD2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTSD2SL: bad operands")
+}
+
+// CVTSD2SS: Convert Scalar Double-Precision FP Value to Scalar Single-Precision FP Value.
+//
+// Forms:
+//
+// CVTSD2SS xmm xmm
+// CVTSD2SS m64 xmm
+func CVTSD2SS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSD2SS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSD2SS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTSD2SS: bad operands")
+}
+
+// CVTSL2SD: Convert Dword Integer to Scalar Double-Precision FP Value.
+//
+// Forms:
+//
+// CVTSL2SD r32 xmm
+// CVTSL2SD m32 xmm
+func CVTSL2SD(mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSL2SD",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSL2SD",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTSL2SD: bad operands")
+}
+
+// CVTSL2SS: Convert Dword Integer to Scalar Single-Precision FP Value.
+//
+// Forms:
+//
+// CVTSL2SS r32 xmm
+// CVTSL2SS m32 xmm
+func CVTSL2SS(mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSL2SS",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSL2SS",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("CVTSL2SS: bad operands")
+}
+
+// CVTSQ2SD: Convert Dword Integer to Scalar Double-Precision FP Value.
+//
+// Forms:
+//
+// CVTSQ2SD r64 xmm
+// CVTSQ2SD m64 xmm
+func CVTSQ2SD(mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSQ2SD",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSQ2SD",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTSQ2SD: bad operands")
+}
+
+// CVTSQ2SS: Convert Dword Integer to Scalar Single-Precision FP Value.
+//
+// Forms:
+//
+// CVTSQ2SS r64 xmm
+// CVTSQ2SS m64 xmm
+func CVTSQ2SS(mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSQ2SS",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSQ2SS",
+ Operands: []operand.Op{mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("CVTSQ2SS: bad operands")
+}
+
+// CVTSS2SD: Convert Scalar Single-Precision FP Value to Scalar Double-Precision FP Value.
+//
+// Forms:
+//
+// CVTSS2SD xmm xmm
+// CVTSS2SD m32 xmm
+func CVTSS2SD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSS2SD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTSS2SD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTSS2SD: bad operands")
+}
+
+// CVTSS2SL: Convert Scalar Single-Precision FP Value to Dword Integer.
+//
+// Forms:
+//
+// CVTSS2SL xmm r32
+// CVTSS2SL m32 r32
+// CVTSS2SL xmm r64
+// CVTSS2SL m32 r64
+func CVTSS2SL(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("CVTSS2SL: bad operands")
+}
+
+// CVTTPD2PL: Convert with Truncation Packed Double-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// CVTTPD2PL xmm xmm
+// CVTTPD2PL m128 xmm
+func CVTTPD2PL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTTPD2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTTPD2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTTPD2PL: bad operands")
+}
+
+// CVTTPS2PL: Convert with Truncation Packed Single-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// CVTTPS2PL xmm xmm
+// CVTTPS2PL m128 xmm
+func CVTTPS2PL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTTPS2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "CVTTPS2PL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTTPS2PL: bad operands")
+}
+
+// CVTTSD2SL: Convert with Truncation Scalar Double-Precision FP Value to Signed Integer.
+//
+// Forms:
+//
+// CVTTSD2SL xmm r32
+// CVTTSD2SL m64 r32
+func CVTTSD2SL(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSD2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSD2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTTSD2SL: bad operands")
+}
+
+// CVTTSD2SQ: Convert with Truncation Scalar Double-Precision FP Value to Signed Integer.
+//
+// Forms:
+//
+// CVTTSD2SQ xmm r64
+// CVTTSD2SQ m64 r64
+func CVTTSD2SQ(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSD2SQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSD2SQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("CVTTSD2SQ: bad operands")
+}
+
+// CVTTSS2SL: Convert with Truncation Scalar Single-Precision FP Value to Dword Integer.
+//
+// Forms:
+//
+// CVTTSS2SL xmm r32
+// CVTTSS2SL m32 r32
+// CVTTSS2SL xmm r64
+// CVTTSS2SL m32 r64
+func CVTTSS2SL(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "CVTTSS2SL",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("CVTTSS2SL: bad operands")
+}
+
+// CWD: Convert Word to Doubleword.
+//
+// Forms:
+//
+// CWD
+func CWD() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CWD",
+ Operands: nil,
+ Inputs: []operand.Op{reg.AX},
+ Outputs: []operand.Op{reg.DX},
+ }, nil
+}
+
+// CWDE: Convert Word to Doubleword.
+//
+// Forms:
+//
+// CWDE
+func CWDE() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "CWDE",
+ Operands: nil,
+ Inputs: []operand.Op{reg.AX},
+ Outputs: []operand.Op{reg.EAX},
+ }, nil
+}
+
+// DECB: Decrement by 1.
+//
+// Forms:
+//
+// DECB r8
+// DECB m8
+func DECB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "DECB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "DECB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("DECB: bad operands")
+}
+
+// DECL: Decrement by 1.
+//
+// Forms:
+//
+// DECL r32
+// DECL m32
+func DECL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "DECL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "DECL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("DECL: bad operands")
+}
+
+// DECQ: Decrement by 1.
+//
+// Forms:
+//
+// DECQ r64
+// DECQ m64
+func DECQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "DECQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "DECQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("DECQ: bad operands")
+}
+
+// DECW: Decrement by 1.
+//
+// Forms:
+//
+// DECW r16
+// DECW m16
+func DECW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "DECW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "DECW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("DECW: bad operands")
+}
+
+// DIVB: Unsigned Divide.
+//
+// Forms:
+//
+// DIVB r8
+// DIVB m8
+func DIVB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ }
+ return nil, errors.New("DIVB: bad operands")
+}
+
+// DIVL: Unsigned Divide.
+//
+// Forms:
+//
+// DIVL r32
+// DIVL m32
+func DIVL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ case operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ }
+ return nil, errors.New("DIVL: bad operands")
+}
+
+// DIVPD: Divide Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// DIVPD xmm xmm
+// DIVPD m128 xmm
+func DIVPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("DIVPD: bad operands")
+}
+
+// DIVPS: Divide Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// DIVPS xmm xmm
+// DIVPS m128 xmm
+func DIVPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("DIVPS: bad operands")
+}
+
+// DIVQ: Unsigned Divide.
+//
+// Forms:
+//
+// DIVQ r64
+// DIVQ m64
+func DIVQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.RAX, reg.RDX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.RAX, reg.RDX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ }
+ return nil, errors.New("DIVQ: bad operands")
+}
+
+// DIVSD: Divide Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// DIVSD xmm xmm
+// DIVSD m64 xmm
+func DIVSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("DIVSD: bad operands")
+}
+
+// DIVSS: Divide Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// DIVSS xmm xmm
+// DIVSS m32 xmm
+func DIVSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DIVSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("DIVSS: bad operands")
+}
+
+// DIVW: Unsigned Divide.
+//
+// Forms:
+//
+// DIVW r16
+// DIVW m16
+func DIVW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX, reg.DX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "DIVW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX, reg.DX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ }
+ return nil, errors.New("DIVW: bad operands")
+}
+
+// DPPD: Dot Product of Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// DPPD imm8 xmm xmm
+// DPPD imm8 m128 xmm
+func DPPD(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DPPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DPPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("DPPD: bad operands")
+}
+
+// DPPS: Dot Product of Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// DPPS imm8 xmm xmm
+// DPPS imm8 m128 xmm
+func DPPS(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DPPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "DPPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("DPPS: bad operands")
+}
+
+// EXTRACTPS: Extract Packed Single Precision Floating-Point Value.
+//
+// Forms:
+//
+// EXTRACTPS imm2u xmm r32
+// EXTRACTPS imm2u xmm m32
+func EXTRACTPS(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM2U(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "EXTRACTPS",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM2U(i) && operand.IsXMM(x) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "EXTRACTPS",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("EXTRACTPS: bad operands")
+}
+
+// HADDPD: Packed Double-FP Horizontal Add.
+//
+// Forms:
+//
+// HADDPD xmm xmm
+// HADDPD m128 xmm
+func HADDPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HADDPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HADDPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("HADDPD: bad operands")
+}
+
+// HADDPS: Packed Single-FP Horizontal Add.
+//
+// Forms:
+//
+// HADDPS xmm xmm
+// HADDPS m128 xmm
+func HADDPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HADDPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HADDPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("HADDPS: bad operands")
+}
+
+// HSUBPD: Packed Double-FP Horizontal Subtract.
+//
+// Forms:
+//
+// HSUBPD xmm xmm
+// HSUBPD m128 xmm
+func HSUBPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HSUBPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HSUBPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("HSUBPD: bad operands")
+}
+
+// HSUBPS: Packed Single-FP Horizontal Subtract.
+//
+// Forms:
+//
+// HSUBPS xmm xmm
+// HSUBPS m128 xmm
+func HSUBPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HSUBPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "HSUBPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("HSUBPS: bad operands")
+}
+
+// IDIVB: Signed Divide.
+//
+// Forms:
+//
+// IDIVB r8
+// IDIVB m8
+func IDIVB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ }
+ return nil, errors.New("IDIVB: bad operands")
+}
+
+// IDIVL: Signed Divide.
+//
+// Forms:
+//
+// IDIVL r32
+// IDIVL m32
+func IDIVL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ case operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ }
+ return nil, errors.New("IDIVL: bad operands")
+}
+
+// IDIVQ: Signed Divide.
+//
+// Forms:
+//
+// IDIVQ r64
+// IDIVQ m64
+func IDIVQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.RAX, reg.RDX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.RAX, reg.RDX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ }
+ return nil, errors.New("IDIVQ: bad operands")
+}
+
+// IDIVW: Signed Divide.
+//
+// Forms:
+//
+// IDIVW r16
+// IDIVW m16
+func IDIVW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX, reg.DX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "IDIVW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX, reg.DX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ }
+ return nil, errors.New("IDIVW: bad operands")
+}
+
+// IMUL3L: Signed Multiply.
+//
+// Forms:
+//
+// IMUL3L imm8 r32 r32
+// IMUL3L imm32 r32 r32
+// IMUL3L imm8 m32 r32
+// IMUL3L imm32 m32 r32
+func IMUL3L(i, mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3L",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM32(i) && operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3L",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3L",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM32(i) && operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3L",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("IMUL3L: bad operands")
+}
+
+// IMUL3Q: Signed Multiply.
+//
+// Forms:
+//
+// IMUL3Q imm8 r64 r64
+// IMUL3Q imm32 r64 r64
+// IMUL3Q imm8 m64 r64
+// IMUL3Q imm32 m64 r64
+func IMUL3Q(i, mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3Q",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM32(i) && operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3Q",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3Q",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM32(i) && operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3Q",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("IMUL3Q: bad operands")
+}
+
+// IMUL3W: Signed Multiply.
+//
+// Forms:
+//
+// IMUL3W imm8 r16 r16
+// IMUL3W imm16 r16 r16
+// IMUL3W imm8 m16 r16
+// IMUL3W imm16 m16 r16
+func IMUL3W(i, mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3W",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM16(i) && operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3W",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3W",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsIMM16(i) && operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "IMUL3W",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("IMUL3W: bad operands")
+}
+
+// IMULB: Signed Multiply.
+//
+// Forms:
+//
+// IMULB r8
+// IMULB m8
+func IMULB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "IMULB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AL},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "IMULB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AL},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ }
+ return nil, errors.New("IMULB: bad operands")
+}
+
+// IMULL: Signed Multiply.
+//
+// Forms:
+//
+// IMULL r32
+// IMULL m32
+// IMULL r32 r32
+// IMULL m32 r32
+func IMULL(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 1 && operand.IsR32(ops[0]):
+ return &intrep.Instruction{
+ Opcode: "IMULL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], reg.EAX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ case len(ops) == 1 && operand.IsM32(ops[0]):
+ return &intrep.Instruction{
+ Opcode: "IMULL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], reg.EAX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ case len(ops) == 2 && operand.IsR32(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "IMULL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsM32(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "IMULL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ }
+ return nil, errors.New("IMULL: bad operands")
+}
+
+// IMULQ: Signed Multiply.
+//
+// Forms:
+//
+// IMULQ r64
+// IMULQ m64
+// IMULQ r64 r64
+// IMULQ m64 r64
+func IMULQ(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 1 && operand.IsR64(ops[0]):
+ return &intrep.Instruction{
+ Opcode: "IMULQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], reg.RAX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ case len(ops) == 1 && operand.IsM64(ops[0]):
+ return &intrep.Instruction{
+ Opcode: "IMULQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], reg.RAX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ case len(ops) == 2 && operand.IsR64(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "IMULQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsM64(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "IMULQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ }
+ return nil, errors.New("IMULQ: bad operands")
+}
+
+// IMULW: Signed Multiply.
+//
+// Forms:
+//
+// IMULW r16
+// IMULW m16
+// IMULW r16 r16
+// IMULW m16 r16
+func IMULW(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 1 && operand.IsR16(ops[0]):
+ return &intrep.Instruction{
+ Opcode: "IMULW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], reg.AX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ case len(ops) == 1 && operand.IsM16(ops[0]):
+ return &intrep.Instruction{
+ Opcode: "IMULW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], reg.AX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ case len(ops) == 2 && operand.IsR16(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "IMULW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsM16(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "IMULW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ }
+ return nil, errors.New("IMULW: bad operands")
+}
+
+// INCB: Increment by 1.
+//
+// Forms:
+//
+// INCB r8
+// INCB m8
+func INCB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "INCB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "INCB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("INCB: bad operands")
+}
+
+// INCL: Increment by 1.
+//
+// Forms:
+//
+// INCL r32
+// INCL m32
+func INCL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "INCL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "INCL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("INCL: bad operands")
+}
+
+// INCQ: Increment by 1.
+//
+// Forms:
+//
+// INCQ r64
+// INCQ m64
+func INCQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "INCQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "INCQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("INCQ: bad operands")
+}
+
+// INCW: Increment by 1.
+//
+// Forms:
+//
+// INCW r16
+// INCW m16
+func INCW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "INCW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "INCW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("INCW: bad operands")
+}
+
+// INSERTPS: Insert Packed Single Precision Floating-Point Value.
+//
+// Forms:
+//
+// INSERTPS imm8 xmm xmm
+// INSERTPS imm8 m32 xmm
+func INSERTPS(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "INSERTPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "INSERTPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("INSERTPS: bad operands")
+}
+
+// INT: Call to Interrupt Procedure.
+//
+// Forms:
+//
+// INT 3
+// INT imm8
+func INT(i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is3(i):
+ return &intrep.Instruction{
+ Opcode: "INT",
+ Operands: []operand.Op{i},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM8(i):
+ return &intrep.Instruction{
+ Opcode: "INT",
+ Operands: []operand.Op{i},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("INT: bad operands")
+}
+
+// JA: Jump if above (CF == 0 and ZF == 0).
+//
+// Forms:
+//
+// JA rel8
+// JA rel32
+func JA(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JA",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JA",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JA: bad operands")
+}
+
+// JAE: Jump if above or equal (CF == 0).
+//
+// Forms:
+//
+// JAE rel8
+// JAE rel32
+func JAE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JAE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JAE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JAE: bad operands")
+}
+
+// JB: Jump if below (CF == 1).
+//
+// Forms:
+//
+// JB rel8
+// JB rel32
+func JB(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JB",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JB",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JB: bad operands")
+}
+
+// JBE: Jump if below or equal (CF == 1 or ZF == 1).
+//
+// Forms:
+//
+// JBE rel8
+// JBE rel32
+func JBE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JBE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JBE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JBE: bad operands")
+}
+
+// JC: Jump if below (CF == 1).
+//
+// Forms:
+//
+// JC rel8
+// JC rel32
+func JC(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JC: bad operands")
+}
+
+// JCC: Jump if above or equal (CF == 0).
+//
+// Forms:
+//
+// JCC rel8
+// JCC rel32
+func JCC(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JCC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JCC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JCC: bad operands")
+}
+
+// JCS: Jump if below (CF == 1).
+//
+// Forms:
+//
+// JCS rel8
+// JCS rel32
+func JCS(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JCS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JCS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JCS: bad operands")
+}
+
+// JCXZL: Jump if ECX register is 0.
+//
+// Forms:
+//
+// JCXZL rel8
+func JCXZL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JCXZL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{reg.ECX},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JCXZL: bad operands")
+}
+
+// JCXZQ: Jump if RCX register is 0.
+//
+// Forms:
+//
+// JCXZQ rel8
+func JCXZQ(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JCXZQ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{reg.RCX},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JCXZQ: bad operands")
+}
+
+// JE: Jump if equal (ZF == 1).
+//
+// Forms:
+//
+// JE rel8
+// JE rel32
+func JE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JE: bad operands")
+}
+
+// JEQ: Jump if equal (ZF == 1).
+//
+// Forms:
+//
+// JEQ rel8
+// JEQ rel32
+func JEQ(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JEQ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JEQ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JEQ: bad operands")
+}
+
+// JG: Jump if greater (ZF == 0 and SF == OF).
+//
+// Forms:
+//
+// JG rel8
+// JG rel32
+func JG(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JG",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JG",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JG: bad operands")
+}
+
+// JGE: Jump if greater or equal (SF == OF).
+//
+// Forms:
+//
+// JGE rel8
+// JGE rel32
+func JGE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JGE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JGE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JGE: bad operands")
+}
+
+// JGT: Jump if greater (ZF == 0 and SF == OF).
+//
+// Forms:
+//
+// JGT rel8
+// JGT rel32
+func JGT(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JGT",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JGT",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JGT: bad operands")
+}
+
+// JHI: Jump if above (CF == 0 and ZF == 0).
+//
+// Forms:
+//
+// JHI rel8
+// JHI rel32
+func JHI(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JHI",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JHI",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JHI: bad operands")
+}
+
+// JHS: Jump if above or equal (CF == 0).
+//
+// Forms:
+//
+// JHS rel8
+// JHS rel32
+func JHS(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JHS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JHS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JHS: bad operands")
+}
+
+// JL: Jump if less (SF != OF).
+//
+// Forms:
+//
+// JL rel8
+// JL rel32
+func JL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JL: bad operands")
+}
+
+// JLE: Jump if less or equal (ZF == 1 or SF != OF).
+//
+// Forms:
+//
+// JLE rel8
+// JLE rel32
+func JLE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JLE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JLE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JLE: bad operands")
+}
+
+// JLO: Jump if below (CF == 1).
+//
+// Forms:
+//
+// JLO rel8
+// JLO rel32
+func JLO(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JLO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JLO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JLO: bad operands")
+}
+
+// JLS: Jump if below or equal (CF == 1 or ZF == 1).
+//
+// Forms:
+//
+// JLS rel8
+// JLS rel32
+func JLS(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JLS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JLS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JLS: bad operands")
+}
+
+// JLT: Jump if less (SF != OF).
+//
+// Forms:
+//
+// JLT rel8
+// JLT rel32
+func JLT(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JLT",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JLT",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JLT: bad operands")
+}
+
+// JMI: Jump if sign (SF == 1).
+//
+// Forms:
+//
+// JMI rel8
+// JMI rel32
+func JMI(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JMI",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JMI",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JMI: bad operands")
+}
+
+// JMP: Jump Unconditionally.
+//
+// Forms:
+//
+// JMP rel8
+// JMP rel32
+// JMP r64
+// JMP m64
+func JMP(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(mr):
+ return &intrep.Instruction{
+ Opcode: "JMP",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: false,
+ }, nil
+ case operand.IsREL32(mr):
+ return &intrep.Instruction{
+ Opcode: "JMP",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: false,
+ }, nil
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "JMP",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: false,
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "JMP",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: false,
+ }, nil
+ }
+ return nil, errors.New("JMP: bad operands")
+}
+
+// JNA: Jump if below or equal (CF == 1 or ZF == 1).
+//
+// Forms:
+//
+// JNA rel8
+// JNA rel32
+func JNA(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNA",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNA",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNA: bad operands")
+}
+
+// JNAE: Jump if below (CF == 1).
+//
+// Forms:
+//
+// JNAE rel8
+// JNAE rel32
+func JNAE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNAE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNAE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNAE: bad operands")
+}
+
+// JNB: Jump if above or equal (CF == 0).
+//
+// Forms:
+//
+// JNB rel8
+// JNB rel32
+func JNB(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNB",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNB",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNB: bad operands")
+}
+
+// JNBE: Jump if above (CF == 0 and ZF == 0).
+//
+// Forms:
+//
+// JNBE rel8
+// JNBE rel32
+func JNBE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNBE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNBE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNBE: bad operands")
+}
+
+// JNC: Jump if above or equal (CF == 0).
+//
+// Forms:
+//
+// JNC rel8
+// JNC rel32
+func JNC(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNC: bad operands")
+}
+
+// JNE: Jump if not equal (ZF == 0).
+//
+// Forms:
+//
+// JNE rel8
+// JNE rel32
+func JNE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNE: bad operands")
+}
+
+// JNG: Jump if less or equal (ZF == 1 or SF != OF).
+//
+// Forms:
+//
+// JNG rel8
+// JNG rel32
+func JNG(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNG",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNG",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNG: bad operands")
+}
+
+// JNGE: Jump if less (SF != OF).
+//
+// Forms:
+//
+// JNGE rel8
+// JNGE rel32
+func JNGE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNGE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNGE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNGE: bad operands")
+}
+
+// JNL: Jump if greater or equal (SF == OF).
+//
+// Forms:
+//
+// JNL rel8
+// JNL rel32
+func JNL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNL: bad operands")
+}
+
+// JNLE: Jump if greater (ZF == 0 and SF == OF).
+//
+// Forms:
+//
+// JNLE rel8
+// JNLE rel32
+func JNLE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNLE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNLE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNLE: bad operands")
+}
+
+// JNO: Jump if not overflow (OF == 0).
+//
+// Forms:
+//
+// JNO rel8
+// JNO rel32
+func JNO(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNO: bad operands")
+}
+
+// JNP: Jump if not parity (PF == 0).
+//
+// Forms:
+//
+// JNP rel8
+// JNP rel32
+func JNP(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNP",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNP",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNP: bad operands")
+}
+
+// JNS: Jump if not sign (SF == 0).
+//
+// Forms:
+//
+// JNS rel8
+// JNS rel32
+func JNS(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNS: bad operands")
+}
+
+// JNZ: Jump if not equal (ZF == 0).
+//
+// Forms:
+//
+// JNZ rel8
+// JNZ rel32
+func JNZ(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JNZ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JNZ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JNZ: bad operands")
+}
+
+// JO: Jump if overflow (OF == 1).
+//
+// Forms:
+//
+// JO rel8
+// JO rel32
+func JO(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JO: bad operands")
+}
+
+// JOC: Jump if not overflow (OF == 0).
+//
+// Forms:
+//
+// JOC rel8
+// JOC rel32
+func JOC(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JOC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JOC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JOC: bad operands")
+}
+
+// JOS: Jump if overflow (OF == 1).
+//
+// Forms:
+//
+// JOS rel8
+// JOS rel32
+func JOS(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JOS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JOS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JOS: bad operands")
+}
+
+// JP: Jump if parity (PF == 1).
+//
+// Forms:
+//
+// JP rel8
+// JP rel32
+func JP(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JP",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JP",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JP: bad operands")
+}
+
+// JPC: Jump if not parity (PF == 0).
+//
+// Forms:
+//
+// JPC rel8
+// JPC rel32
+func JPC(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JPC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JPC",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JPC: bad operands")
+}
+
+// JPE: Jump if parity (PF == 1).
+//
+// Forms:
+//
+// JPE rel8
+// JPE rel32
+func JPE(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JPE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JPE",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JPE: bad operands")
+}
+
+// JPL: Jump if not sign (SF == 0).
+//
+// Forms:
+//
+// JPL rel8
+// JPL rel32
+func JPL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JPL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JPL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JPL: bad operands")
+}
+
+// JPO: Jump if not parity (PF == 0).
+//
+// Forms:
+//
+// JPO rel8
+// JPO rel32
+func JPO(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JPO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JPO",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JPO: bad operands")
+}
+
+// JPS: Jump if parity (PF == 1).
+//
+// Forms:
+//
+// JPS rel8
+// JPS rel32
+func JPS(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JPS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JPS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JPS: bad operands")
+}
+
+// JS: Jump if sign (SF == 1).
+//
+// Forms:
+//
+// JS rel8
+// JS rel32
+func JS(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JS",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JS: bad operands")
+}
+
+// JZ: Jump if equal (ZF == 1).
+//
+// Forms:
+//
+// JZ rel8
+// JZ rel32
+func JZ(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsREL8(r):
+ return &intrep.Instruction{
+ Opcode: "JZ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ case operand.IsREL32(r):
+ return &intrep.Instruction{
+ Opcode: "JZ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsBranch: true,
+ IsConditional: true,
+ }, nil
+ }
+ return nil, errors.New("JZ: bad operands")
+}
+
+// LDDQU: Load Unaligned Integer 128 Bits.
+//
+// Forms:
+//
+// LDDQU m128 xmm
+func LDDQU(m, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(m) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "LDDQU",
+ Operands: []operand.Op{m, x},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("LDDQU: bad operands")
+}
+
+// LDMXCSR: Load MXCSR Register.
+//
+// Forms:
+//
+// LDMXCSR m32
+func LDMXCSR(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM32(m):
+ return &intrep.Instruction{
+ Opcode: "LDMXCSR",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("LDMXCSR: bad operands")
+}
+
+// LEAL: Load Effective Address.
+//
+// Forms:
+//
+// LEAL m r32
+func LEAL(m, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM(m) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "LEAL",
+ Operands: []operand.Op{m, r},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("LEAL: bad operands")
+}
+
+// LEAQ: Load Effective Address.
+//
+// Forms:
+//
+// LEAQ m r64
+func LEAQ(m, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM(m) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "LEAQ",
+ Operands: []operand.Op{m, r},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("LEAQ: bad operands")
+}
+
+// LEAW: Load Effective Address.
+//
+// Forms:
+//
+// LEAW m r16
+func LEAW(m, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM(m) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "LEAW",
+ Operands: []operand.Op{m, r},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("LEAW: bad operands")
+}
+
+// LFENCE: Load Fence.
+//
+// Forms:
+//
+// LFENCE
+func LFENCE() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "LFENCE",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+}
+
+// LZCNTL: Count the Number of Leading Zero Bits.
+//
+// Forms:
+//
+// LZCNTL r32 r32
+// LZCNTL m32 r32
+func LZCNTL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "LZCNTL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"LZCNT"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "LZCNTL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"LZCNT"},
+ }, nil
+ }
+ return nil, errors.New("LZCNTL: bad operands")
+}
+
+// LZCNTQ: Count the Number of Leading Zero Bits.
+//
+// Forms:
+//
+// LZCNTQ r64 r64
+// LZCNTQ m64 r64
+func LZCNTQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "LZCNTQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"LZCNT"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "LZCNTQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"LZCNT"},
+ }, nil
+ }
+ return nil, errors.New("LZCNTQ: bad operands")
+}
+
+// LZCNTW: Count the Number of Leading Zero Bits.
+//
+// Forms:
+//
+// LZCNTW r16 r16
+// LZCNTW m16 r16
+func LZCNTW(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "LZCNTW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"LZCNT"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "LZCNTW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"LZCNT"},
+ }, nil
+ }
+ return nil, errors.New("LZCNTW: bad operands")
+}
+
+// MASKMOVDQU: Store Selected Bytes of Double Quadword.
+//
+// Forms:
+//
+// MASKMOVDQU xmm xmm
+func MASKMOVDQU(x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "MASKMOVDQU",
+ Operands: []operand.Op{x, x1},
+ Inputs: []operand.Op{x, x1, reg.RDI},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MASKMOVDQU: bad operands")
+}
+
+// MASKMOVOU: Store Selected Bytes of Double Quadword.
+//
+// Forms:
+//
+// MASKMOVOU xmm xmm
+func MASKMOVOU(x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "MASKMOVOU",
+ Operands: []operand.Op{x, x1},
+ Inputs: []operand.Op{x, x1, reg.RDI},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MASKMOVOU: bad operands")
+}
+
+// MAXPD: Return Maximum Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MAXPD xmm xmm
+// MAXPD m128 xmm
+func MAXPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MAXPD: bad operands")
+}
+
+// MAXPS: Return Maximum Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MAXPS xmm xmm
+// MAXPS m128 xmm
+func MAXPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MAXPS: bad operands")
+}
+
+// MAXSD: Return Maximum Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// MAXSD xmm xmm
+// MAXSD m64 xmm
+func MAXSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MAXSD: bad operands")
+}
+
+// MAXSS: Return Maximum Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// MAXSS xmm xmm
+// MAXSS m32 xmm
+func MAXSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MAXSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MAXSS: bad operands")
+}
+
+// MFENCE: Memory Fence.
+//
+// Forms:
+//
+// MFENCE
+func MFENCE() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "MFENCE",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+}
+
+// MINPD: Return Minimum Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MINPD xmm xmm
+// MINPD m128 xmm
+func MINPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MINPD: bad operands")
+}
+
+// MINPS: Return Minimum Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MINPS xmm xmm
+// MINPS m128 xmm
+func MINPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MINPS: bad operands")
+}
+
+// MINSD: Return Minimum Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// MINSD xmm xmm
+// MINSD m64 xmm
+func MINSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MINSD: bad operands")
+}
+
+// MINSS: Return Minimum Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// MINSS xmm xmm
+// MINSS m32 xmm
+func MINSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MINSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MINSS: bad operands")
+}
+
+// MONITOR: Monitor a Linear Address Range.
+//
+// Forms:
+//
+// MONITOR
+func MONITOR() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "MONITOR",
+ Operands: nil,
+ Inputs: []operand.Op{reg.RAX, reg.ECX, reg.EDX},
+ Outputs: []operand.Op{},
+ ISA: []string{"MONITOR"},
+ }, nil
+}
+
+// MOVAPD: Move Aligned Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MOVAPD xmm xmm
+// MOVAPD m128 xmm
+// MOVAPD xmm m128
+func MOVAPD(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVAPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVAPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM128(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVAPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVAPD: bad operands")
+}
+
+// MOVAPS: Move Aligned Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MOVAPS xmm xmm
+// MOVAPS m128 xmm
+// MOVAPS xmm m128
+func MOVAPS(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVAPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVAPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM128(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVAPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVAPS: bad operands")
+}
+
+// MOVB: Move.
+//
+// Forms:
+//
+// MOVB imm8 r8
+// MOVB r8 r8
+// MOVB m8 r8
+// MOVB imm8 m8
+// MOVB r8 m8
+func MOVB(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVB",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVB",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVB",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVB",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVB",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("MOVB: bad operands")
+}
+
+// MOVBELL: Move Data After Swapping Bytes.
+//
+// Forms:
+//
+// MOVBELL m32 r32
+// MOVBELL r32 m32
+func MOVBELL(mr, mr1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM32(mr) && operand.IsR32(mr1):
+ return &intrep.Instruction{
+ Opcode: "MOVBELL",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr1},
+ ISA: []string{"MOVBE"},
+ }, nil
+ case operand.IsR32(mr) && operand.IsM32(mr1):
+ return &intrep.Instruction{
+ Opcode: "MOVBELL",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr1},
+ ISA: []string{"MOVBE"},
+ }, nil
+ }
+ return nil, errors.New("MOVBELL: bad operands")
+}
+
+// MOVBEQQ: Move Data After Swapping Bytes.
+//
+// Forms:
+//
+// MOVBEQQ m64 r64
+// MOVBEQQ r64 m64
+func MOVBEQQ(mr, mr1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM64(mr) && operand.IsR64(mr1):
+ return &intrep.Instruction{
+ Opcode: "MOVBEQQ",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr1},
+ ISA: []string{"MOVBE"},
+ }, nil
+ case operand.IsR64(mr) && operand.IsM64(mr1):
+ return &intrep.Instruction{
+ Opcode: "MOVBEQQ",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr1},
+ ISA: []string{"MOVBE"},
+ }, nil
+ }
+ return nil, errors.New("MOVBEQQ: bad operands")
+}
+
+// MOVBEWW: Move Data After Swapping Bytes.
+//
+// Forms:
+//
+// MOVBEWW m16 r16
+// MOVBEWW r16 m16
+func MOVBEWW(mr, mr1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM16(mr) && operand.IsR16(mr1):
+ return &intrep.Instruction{
+ Opcode: "MOVBEWW",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr1},
+ ISA: []string{"MOVBE"},
+ }, nil
+ case operand.IsR16(mr) && operand.IsM16(mr1):
+ return &intrep.Instruction{
+ Opcode: "MOVBEWW",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr1},
+ ISA: []string{"MOVBE"},
+ }, nil
+ }
+ return nil, errors.New("MOVBEWW: bad operands")
+}
+
+// MOVBLSX: Move with Sign-Extension.
+//
+// Forms:
+//
+// MOVBLSX r8 r32
+// MOVBLSX m8 r32
+func MOVBLSX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBLSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBLSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVBLSX: bad operands")
+}
+
+// MOVBLZX: Move with Zero-Extend.
+//
+// Forms:
+//
+// MOVBLZX r8 r32
+// MOVBLZX m8 r32
+func MOVBLZX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBLZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBLZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVBLZX: bad operands")
+}
+
+// MOVBQSX: Move with Sign-Extension.
+//
+// Forms:
+//
+// MOVBQSX r8 r64
+// MOVBQSX m8 r64
+func MOVBQSX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBQSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBQSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVBQSX: bad operands")
+}
+
+// MOVBQZX: Move with Zero-Extend.
+//
+// Forms:
+//
+// MOVBQZX r8 r64
+// MOVBQZX m8 r64
+func MOVBQZX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBQZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBQZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVBQZX: bad operands")
+}
+
+// MOVBWSX: Move with Sign-Extension.
+//
+// Forms:
+//
+// MOVBWSX r8 r16
+// MOVBWSX m8 r16
+func MOVBWSX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBWSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBWSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVBWSX: bad operands")
+}
+
+// MOVBWZX: Move with Zero-Extend.
+//
+// Forms:
+//
+// MOVBWZX r8 r16
+// MOVBWZX m8 r16
+func MOVBWZX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBWZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "MOVBWZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVBWZX: bad operands")
+}
+
+// MOVD: Move.
+//
+// Forms:
+//
+// MOVD imm32 r64
+// MOVD imm64 r64
+// MOVD r64 r64
+// MOVD m64 r64
+// MOVD imm32 m64
+// MOVD r64 m64
+// MOVD xmm r64
+// MOVD r64 xmm
+// MOVD xmm xmm
+// MOVD m64 xmm
+// MOVD xmm m64
+// MOVD xmm r32
+// MOVD r32 xmm
+// MOVD m32 xmm
+// MOVD xmm m32
+func MOVD(imrx, mrx operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsIMM64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsM64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsIMM32(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsR32(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsR32(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM32(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsM32(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVD",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVD: bad operands")
+}
+
+// MOVDDUP: Move One Double-FP and Duplicate.
+//
+// Forms:
+//
+// MOVDDUP xmm xmm
+// MOVDDUP m64 xmm
+func MOVDDUP(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MOVDDUP",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MOVDDUP",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("MOVDDUP: bad operands")
+}
+
+// MOVDQ2Q: Move.
+//
+// Forms:
+//
+// MOVDQ2Q imm32 r64
+// MOVDQ2Q imm64 r64
+// MOVDQ2Q r64 r64
+// MOVDQ2Q m64 r64
+// MOVDQ2Q imm32 m64
+// MOVDQ2Q r64 m64
+// MOVDQ2Q xmm r64
+// MOVDQ2Q r64 xmm
+// MOVDQ2Q xmm xmm
+// MOVDQ2Q m64 xmm
+// MOVDQ2Q xmm m64
+// MOVDQ2Q xmm r32
+// MOVDQ2Q r32 xmm
+// MOVDQ2Q m32 xmm
+// MOVDQ2Q xmm m32
+func MOVDQ2Q(imrx, mrx operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsIMM64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsM64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsIMM32(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsR32(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsR32(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM32(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsM32(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVDQ2Q",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVDQ2Q: bad operands")
+}
+
+// MOVHLPS: Move Packed Single-Precision Floating-Point Values High to Low.
+//
+// Forms:
+//
+// MOVHLPS xmm xmm
+func MOVHLPS(x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "MOVHLPS",
+ Operands: []operand.Op{x, x1},
+ Inputs: []operand.Op{x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVHLPS: bad operands")
+}
+
+// MOVHPD: Move High Packed Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// MOVHPD m64 xmm
+// MOVHPD xmm m64
+func MOVHPD(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM64(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVHPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx, mx1},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM64(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVHPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVHPD: bad operands")
+}
+
+// MOVHPS: Move High Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MOVHPS m64 xmm
+// MOVHPS xmm m64
+func MOVHPS(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM64(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVHPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx, mx1},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM64(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVHPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVHPS: bad operands")
+}
+
+// MOVL: Move.
+//
+// Forms:
+//
+// MOVL imm32 r32
+// MOVL r32 r32
+// MOVL m32 r32
+// MOVL imm32 m32
+// MOVL r32 m32
+func MOVL(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVL",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVL",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVL",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVL",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVL",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("MOVL: bad operands")
+}
+
+// MOVLHPS: Move Packed Single-Precision Floating-Point Values Low to High.
+//
+// Forms:
+//
+// MOVLHPS xmm xmm
+func MOVLHPS(x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "MOVLHPS",
+ Operands: []operand.Op{x, x1},
+ Inputs: []operand.Op{x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVLHPS: bad operands")
+}
+
+// MOVLPD: Move Low Packed Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// MOVLPD m64 xmm
+// MOVLPD xmm m64
+func MOVLPD(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM64(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVLPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx, mx1},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM64(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVLPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVLPD: bad operands")
+}
+
+// MOVLPS: Move Low Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MOVLPS m64 xmm
+// MOVLPS xmm m64
+func MOVLPS(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM64(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVLPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx, mx1},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM64(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVLPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVLPS: bad operands")
+}
+
+// MOVLQSX: Move Doubleword to Quadword with Sign-Extension.
+//
+// Forms:
+//
+// MOVLQSX r32 r64
+// MOVLQSX m32 r64
+func MOVLQSX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVLQSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVLQSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVLQSX: bad operands")
+}
+
+// MOVLQZX: Move with Zero-Extend.
+//
+// Forms:
+//
+// MOVLQZX m32 r64
+func MOVLQZX(m, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM32(m) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVLQZX",
+ Operands: []operand.Op{m, r},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVLQZX: bad operands")
+}
+
+// MOVMSKPD: Extract Packed Double-Precision Floating-Point Sign Mask.
+//
+// Forms:
+//
+// MOVMSKPD xmm r32
+func MOVMSKPD(x, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVMSKPD",
+ Operands: []operand.Op{x, r},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVMSKPD: bad operands")
+}
+
+// MOVMSKPS: Extract Packed Single-Precision Floating-Point Sign Mask.
+//
+// Forms:
+//
+// MOVMSKPS xmm r32
+func MOVMSKPS(x, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVMSKPS",
+ Operands: []operand.Op{x, r},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVMSKPS: bad operands")
+}
+
+// MOVNTDQ: Store Double Quadword Using Non-Temporal Hint.
+//
+// Forms:
+//
+// MOVNTDQ xmm m128
+func MOVNTDQ(x, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "MOVNTDQ",
+ Operands: []operand.Op{x, m},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{m},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVNTDQ: bad operands")
+}
+
+// MOVNTDQA: Load Double Quadword Non-Temporal Aligned Hint.
+//
+// Forms:
+//
+// MOVNTDQA m128 xmm
+func MOVNTDQA(m, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(m) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MOVNTDQA",
+ Operands: []operand.Op{m, x},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("MOVNTDQA: bad operands")
+}
+
+// MOVNTIL: Store Doubleword Using Non-Temporal Hint.
+//
+// Forms:
+//
+// MOVNTIL r32 m32
+func MOVNTIL(r, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsM32(m):
+ return &intrep.Instruction{
+ Opcode: "MOVNTIL",
+ Operands: []operand.Op{r, m},
+ Inputs: []operand.Op{r},
+ Outputs: []operand.Op{m},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVNTIL: bad operands")
+}
+
+// MOVNTIQ: Store Doubleword Using Non-Temporal Hint.
+//
+// Forms:
+//
+// MOVNTIQ r64 m64
+func MOVNTIQ(r, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsM64(m):
+ return &intrep.Instruction{
+ Opcode: "MOVNTIQ",
+ Operands: []operand.Op{r, m},
+ Inputs: []operand.Op{r},
+ Outputs: []operand.Op{m},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVNTIQ: bad operands")
+}
+
+// MOVNTO: Store Double Quadword Using Non-Temporal Hint.
+//
+// Forms:
+//
+// MOVNTO xmm m128
+func MOVNTO(x, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "MOVNTO",
+ Operands: []operand.Op{x, m},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{m},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVNTO: bad operands")
+}
+
+// MOVNTPD: Store Packed Double-Precision Floating-Point Values Using Non-Temporal Hint.
+//
+// Forms:
+//
+// MOVNTPD xmm m128
+func MOVNTPD(x, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "MOVNTPD",
+ Operands: []operand.Op{x, m},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{m},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVNTPD: bad operands")
+}
+
+// MOVNTPS: Store Packed Single-Precision Floating-Point Values Using Non-Temporal Hint.
+//
+// Forms:
+//
+// MOVNTPS xmm m128
+func MOVNTPS(x, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "MOVNTPS",
+ Operands: []operand.Op{x, m},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{m},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVNTPS: bad operands")
+}
+
+// MOVO: Move Aligned Double Quadword.
+//
+// Forms:
+//
+// MOVO xmm xmm
+// MOVO m128 xmm
+// MOVO xmm m128
+func MOVO(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVO",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVO",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM128(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVO",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVO: bad operands")
+}
+
+// MOVOA: Move Aligned Double Quadword.
+//
+// Forms:
+//
+// MOVOA xmm xmm
+// MOVOA m128 xmm
+// MOVOA xmm m128
+func MOVOA(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVOA",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVOA",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM128(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVOA",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVOA: bad operands")
+}
+
+// MOVOU: Move Unaligned Double Quadword.
+//
+// Forms:
+//
+// MOVOU xmm xmm
+// MOVOU m128 xmm
+// MOVOU xmm m128
+func MOVOU(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVOU",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVOU",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM128(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVOU",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVOU: bad operands")
+}
+
+// MOVQ: Move.
+//
+// Forms:
+//
+// MOVQ imm32 r64
+// MOVQ imm64 r64
+// MOVQ r64 r64
+// MOVQ m64 r64
+// MOVQ imm32 m64
+// MOVQ r64 m64
+// MOVQ xmm r64
+// MOVQ r64 xmm
+// MOVQ xmm xmm
+// MOVQ m64 xmm
+// MOVQ xmm m64
+// MOVQ xmm r32
+// MOVQ r32 xmm
+// MOVQ m32 xmm
+// MOVQ xmm m32
+func MOVQ(imrx, mrx operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsIMM64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsM64(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsIMM32(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsR64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsR64(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsM64(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsR32(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsR32(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM32(imrx) && operand.IsXMM(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imrx) && operand.IsM32(mrx):
+ return &intrep.Instruction{
+ Opcode: "MOVQ",
+ Operands: []operand.Op{imrx, mrx},
+ Inputs: []operand.Op{imrx},
+ Outputs: []operand.Op{mrx},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVQ: bad operands")
+}
+
+// MOVSD: Move Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// MOVSD xmm xmm
+// MOVSD m64 xmm
+// MOVSD xmm m64
+func MOVSD(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVSD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx, mx1},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVSD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM64(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVSD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVSD: bad operands")
+}
+
+// MOVSHDUP: Move Packed Single-FP High and Duplicate.
+//
+// Forms:
+//
+// MOVSHDUP xmm xmm
+// MOVSHDUP m128 xmm
+func MOVSHDUP(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MOVSHDUP",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MOVSHDUP",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("MOVSHDUP: bad operands")
+}
+
+// MOVSLDUP: Move Packed Single-FP Low and Duplicate.
+//
+// Forms:
+//
+// MOVSLDUP xmm xmm
+// MOVSLDUP m128 xmm
+func MOVSLDUP(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MOVSLDUP",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MOVSLDUP",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE3"},
+ }, nil
+ }
+ return nil, errors.New("MOVSLDUP: bad operands")
+}
+
+// MOVSS: Move Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MOVSS xmm xmm
+// MOVSS m32 xmm
+// MOVSS xmm m32
+func MOVSS(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVSS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx, mx1},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVSS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM32(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVSS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVSS: bad operands")
+}
+
+// MOVUPD: Move Unaligned Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MOVUPD xmm xmm
+// MOVUPD m128 xmm
+// MOVUPD xmm m128
+func MOVUPD(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVUPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVUPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM128(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVUPD",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MOVUPD: bad operands")
+}
+
+// MOVUPS: Move Unaligned Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MOVUPS xmm xmm
+// MOVUPS m128 xmm
+// MOVUPS xmm m128
+func MOVUPS(mx, mx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVUPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVUPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsM128(mx1):
+ return &intrep.Instruction{
+ Opcode: "MOVUPS",
+ Operands: []operand.Op{mx, mx1},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{mx1},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MOVUPS: bad operands")
+}
+
+// MOVW: Move.
+//
+// Forms:
+//
+// MOVW imm16 r16
+// MOVW r16 r16
+// MOVW m16 r16
+// MOVW imm16 m16
+// MOVW r16 m16
+func MOVW(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVW",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVW",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVW",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVW",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "MOVW",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("MOVW: bad operands")
+}
+
+// MOVWLSX: Move with Sign-Extension.
+//
+// Forms:
+//
+// MOVWLSX r16 r32
+// MOVWLSX m16 r32
+func MOVWLSX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWLSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWLSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVWLSX: bad operands")
+}
+
+// MOVWLZX: Move with Zero-Extend.
+//
+// Forms:
+//
+// MOVWLZX r16 r32
+// MOVWLZX m16 r32
+func MOVWLZX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWLZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWLZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVWLZX: bad operands")
+}
+
+// MOVWQSX: Move with Sign-Extension.
+//
+// Forms:
+//
+// MOVWQSX r16 r64
+// MOVWQSX m16 r64
+func MOVWQSX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWQSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWQSX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVWQSX: bad operands")
+}
+
+// MOVWQZX: Move with Zero-Extend.
+//
+// Forms:
+//
+// MOVWQZX r16 r64
+// MOVWQZX m16 r64
+func MOVWQZX(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWQZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "MOVWQZX",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ }, nil
+ }
+ return nil, errors.New("MOVWQZX: bad operands")
+}
+
+// MPSADBW: Compute Multiple Packed Sums of Absolute Difference.
+//
+// Forms:
+//
+// MPSADBW imm8 xmm xmm
+// MPSADBW imm8 m128 xmm
+func MPSADBW(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MPSADBW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MPSADBW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("MPSADBW: bad operands")
+}
+
+// MULB: Unsigned Multiply.
+//
+// Forms:
+//
+// MULB r8
+// MULB m8
+func MULB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "MULB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AL},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "MULB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AL},
+ Outputs: []operand.Op{reg.AX},
+ }, nil
+ }
+ return nil, errors.New("MULB: bad operands")
+}
+
+// MULL: Unsigned Multiply.
+//
+// Forms:
+//
+// MULL r32
+// MULL m32
+func MULL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "MULL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.EAX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ case operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "MULL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.EAX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+ }
+ return nil, errors.New("MULL: bad operands")
+}
+
+// MULPD: Multiply Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MULPD xmm xmm
+// MULPD m128 xmm
+func MULPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MULPD: bad operands")
+}
+
+// MULPS: Multiply Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MULPS xmm xmm
+// MULPS m128 xmm
+func MULPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MULPS: bad operands")
+}
+
+// MULQ: Unsigned Multiply.
+//
+// Forms:
+//
+// MULQ r64
+// MULQ m64
+func MULQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "MULQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.RAX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "MULQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.RAX},
+ Outputs: []operand.Op{reg.RAX, reg.RDX},
+ }, nil
+ }
+ return nil, errors.New("MULQ: bad operands")
+}
+
+// MULSD: Multiply Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MULSD xmm xmm
+// MULSD m64 xmm
+func MULSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("MULSD: bad operands")
+}
+
+// MULSS: Multiply Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// MULSS xmm xmm
+// MULSS m32 xmm
+func MULSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "MULSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("MULSS: bad operands")
+}
+
+// MULW: Unsigned Multiply.
+//
+// Forms:
+//
+// MULW r16
+// MULW m16
+func MULW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "MULW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "MULW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr, reg.AX},
+ Outputs: []operand.Op{reg.AX, reg.DX},
+ }, nil
+ }
+ return nil, errors.New("MULW: bad operands")
+}
+
+// MULXL: Unsigned Multiply Without Affecting Flags.
+//
+// Forms:
+//
+// MULXL r32 r32 r32
+// MULXL m32 r32 r32
+func MULXL(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "MULXL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, reg.EDX},
+ Outputs: []operand.Op{r, r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "MULXL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, reg.EDX},
+ Outputs: []operand.Op{r, r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("MULXL: bad operands")
+}
+
+// MULXQ: Unsigned Multiply Without Affecting Flags.
+//
+// Forms:
+//
+// MULXQ r64 r64 r64
+// MULXQ m64 r64 r64
+func MULXQ(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "MULXQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, reg.RDX},
+ Outputs: []operand.Op{r, r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "MULXQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, reg.RDX},
+ Outputs: []operand.Op{r, r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("MULXQ: bad operands")
+}
+
+// MWAIT: Monitor Wait.
+//
+// Forms:
+//
+// MWAIT
+func MWAIT() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "MWAIT",
+ Operands: nil,
+ Inputs: []operand.Op{reg.EAX, reg.ECX},
+ Outputs: []operand.Op{},
+ ISA: []string{"MONITOR"},
+ }, nil
+}
+
+// NEGB: Two's Complement Negation.
+//
+// Forms:
+//
+// NEGB r8
+// NEGB m8
+func NEGB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NEGB: bad operands")
+}
+
+// NEGL: Two's Complement Negation.
+//
+// Forms:
+//
+// NEGL r32
+// NEGL m32
+func NEGL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NEGL: bad operands")
+}
+
+// NEGQ: Two's Complement Negation.
+//
+// Forms:
+//
+// NEGQ r64
+// NEGQ m64
+func NEGQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NEGQ: bad operands")
+}
+
+// NEGW: Two's Complement Negation.
+//
+// Forms:
+//
+// NEGW r16
+// NEGW m16
+func NEGW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "NEGW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NEGW: bad operands")
+}
+
+// NOP: No Operation.
+//
+// Forms:
+//
+// NOP
+func NOP() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "NOP",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// NOTB: One's Complement Negation.
+//
+// Forms:
+//
+// NOTB r8
+// NOTB m8
+func NOTB(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTB",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NOTB: bad operands")
+}
+
+// NOTL: One's Complement Negation.
+//
+// Forms:
+//
+// NOTL r32
+// NOTL m32
+func NOTL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NOTL: bad operands")
+}
+
+// NOTQ: One's Complement Negation.
+//
+// Forms:
+//
+// NOTQ r64
+// NOTQ m64
+func NOTQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NOTQ: bad operands")
+}
+
+// NOTW: One's Complement Negation.
+//
+// Forms:
+//
+// NOTW r16
+// NOTW m16
+func NOTW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "NOTW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("NOTW: bad operands")
+}
+
+// ORB: Logical Inclusive OR.
+//
+// Forms:
+//
+// ORB imm8 al
+// ORB imm8 r8
+// ORB r8 r8
+// ORB m8 r8
+// ORB imm8 m8
+// ORB r8 m8
+func ORB(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "ORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "ORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "ORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ORB: bad operands")
+}
+
+// ORL: Logical Inclusive OR.
+//
+// Forms:
+//
+// ORL imm32 eax
+// ORL imm8 r32
+// ORL imm32 r32
+// ORL r32 r32
+// ORL m32 r32
+// ORL imm8 m32
+// ORL imm32 m32
+// ORL r32 m32
+func ORL(imr, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "ORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ }
+ return nil, errors.New("ORL: bad operands")
+}
+
+// ORPD: Bitwise Logical OR of Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ORPD xmm xmm
+// ORPD m128 xmm
+func ORPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ORPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ORPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("ORPD: bad operands")
+}
+
+// ORPS: Bitwise Logical OR of Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// ORPS xmm xmm
+// ORPS m128 xmm
+func ORPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ORPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ORPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("ORPS: bad operands")
+}
+
+// ORQ: Logical Inclusive OR.
+//
+// Forms:
+//
+// ORQ imm32 rax
+// ORQ imm8 r64
+// ORQ imm32 r64
+// ORQ r64 r64
+// ORQ m64 r64
+// ORQ imm8 m64
+// ORQ imm32 m64
+// ORQ r64 m64
+func ORQ(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ORQ: bad operands")
+}
+
+// ORW: Logical Inclusive OR.
+//
+// Forms:
+//
+// ORW imm16 ax
+// ORW imm8 r16
+// ORW imm16 r16
+// ORW r16 r16
+// ORW m16 r16
+// ORW imm8 m16
+// ORW imm16 m16
+// ORW r16 m16
+func ORW(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "ORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("ORW: bad operands")
+}
+
+// PABSB: Packed Absolute Value of Byte Integers.
+//
+// Forms:
+//
+// PABSB xmm xmm
+// PABSB m128 xmm
+func PABSB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PABSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PABSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PABSB: bad operands")
+}
+
+// PABSD: Packed Absolute Value of Doubleword Integers.
+//
+// Forms:
+//
+// PABSD xmm xmm
+// PABSD m128 xmm
+func PABSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PABSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PABSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PABSD: bad operands")
+}
+
+// PABSW: Packed Absolute Value of Word Integers.
+//
+// Forms:
+//
+// PABSW xmm xmm
+// PABSW m128 xmm
+func PABSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PABSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PABSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PABSW: bad operands")
+}
+
+// PACKSSLW: Pack Doublewords into Words with Signed Saturation.
+//
+// Forms:
+//
+// PACKSSLW xmm xmm
+// PACKSSLW m128 xmm
+func PACKSSLW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKSSLW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKSSLW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PACKSSLW: bad operands")
+}
+
+// PACKSSWB: Pack Words into Bytes with Signed Saturation.
+//
+// Forms:
+//
+// PACKSSWB xmm xmm
+// PACKSSWB m128 xmm
+func PACKSSWB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKSSWB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKSSWB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PACKSSWB: bad operands")
+}
+
+// PACKUSDW: Pack Doublewords into Words with Unsigned Saturation.
+//
+// Forms:
+//
+// PACKUSDW xmm xmm
+// PACKUSDW m128 xmm
+func PACKUSDW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKUSDW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKUSDW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PACKUSDW: bad operands")
+}
+
+// PACKUSWB: Pack Words into Bytes with Unsigned Saturation.
+//
+// Forms:
+//
+// PACKUSWB xmm xmm
+// PACKUSWB m128 xmm
+func PACKUSWB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKUSWB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PACKUSWB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PACKUSWB: bad operands")
+}
+
+// PADDB: Add Packed Byte Integers.
+//
+// Forms:
+//
+// PADDB xmm xmm
+// PADDB m128 xmm
+func PADDB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDB: bad operands")
+}
+
+// PADDD: Add Packed Doubleword Integers.
+//
+// Forms:
+//
+// PADDD xmm xmm
+// PADDD m128 xmm
+func PADDD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDD: bad operands")
+}
+
+// PADDL: Add Packed Doubleword Integers.
+//
+// Forms:
+//
+// PADDL xmm xmm
+// PADDL m128 xmm
+func PADDL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDL: bad operands")
+}
+
+// PADDQ: Add Packed Quadword Integers.
+//
+// Forms:
+//
+// PADDQ xmm xmm
+// PADDQ m128 xmm
+func PADDQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDQ: bad operands")
+}
+
+// PADDSB: Add Packed Signed Byte Integers with Signed Saturation.
+//
+// Forms:
+//
+// PADDSB xmm xmm
+// PADDSB m128 xmm
+func PADDSB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDSB: bad operands")
+}
+
+// PADDSW: Add Packed Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// PADDSW xmm xmm
+// PADDSW m128 xmm
+func PADDSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDSW: bad operands")
+}
+
+// PADDUSB: Add Packed Unsigned Byte Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// PADDUSB xmm xmm
+// PADDUSB m128 xmm
+func PADDUSB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDUSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDUSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDUSB: bad operands")
+}
+
+// PADDUSW: Add Packed Unsigned Word Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// PADDUSW xmm xmm
+// PADDUSW m128 xmm
+func PADDUSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDUSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDUSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDUSW: bad operands")
+}
+
+// PADDW: Add Packed Word Integers.
+//
+// Forms:
+//
+// PADDW xmm xmm
+// PADDW m128 xmm
+func PADDW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PADDW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PADDW: bad operands")
+}
+
+// PALIGNR: Packed Align Right.
+//
+// Forms:
+//
+// PALIGNR imm8 xmm xmm
+// PALIGNR imm8 m128 xmm
+func PALIGNR(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PALIGNR",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PALIGNR",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PALIGNR: bad operands")
+}
+
+// PAND: Packed Bitwise Logical AND.
+//
+// Forms:
+//
+// PAND xmm xmm
+// PAND m128 xmm
+func PAND(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PAND",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PAND",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PAND: bad operands")
+}
+
+// PANDN: Packed Bitwise Logical AND NOT.
+//
+// Forms:
+//
+// PANDN xmm xmm
+// PANDN m128 xmm
+func PANDN(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PANDN",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PANDN",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PANDN: bad operands")
+}
+
+// PAUSE: Spin Loop Hint.
+//
+// Forms:
+//
+// PAUSE
+func PAUSE() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "PAUSE",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// PAVGB: Average Packed Byte Integers.
+//
+// Forms:
+//
+// PAVGB xmm xmm
+// PAVGB m128 xmm
+func PAVGB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PAVGB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PAVGB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PAVGB: bad operands")
+}
+
+// PAVGW: Average Packed Word Integers.
+//
+// Forms:
+//
+// PAVGW xmm xmm
+// PAVGW m128 xmm
+func PAVGW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PAVGW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PAVGW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PAVGW: bad operands")
+}
+
+// PBLENDVB: Variable Blend Packed Bytes.
+//
+// Forms:
+//
+// PBLENDVB xmm0 xmm xmm
+// PBLENDVB xmm0 m128 xmm
+func PBLENDVB(x, mx, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM0(x) && operand.IsXMM(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "PBLENDVB",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsXMM0(x) && operand.IsM128(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "PBLENDVB",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PBLENDVB: bad operands")
+}
+
+// PBLENDW: Blend Packed Words.
+//
+// Forms:
+//
+// PBLENDW imm8 xmm xmm
+// PBLENDW imm8 m128 xmm
+func PBLENDW(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PBLENDW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PBLENDW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PBLENDW: bad operands")
+}
+
+// PCLMULQDQ: Carry-Less Quadword Multiplication.
+//
+// Forms:
+//
+// PCLMULQDQ imm8 xmm xmm
+// PCLMULQDQ imm8 m128 xmm
+func PCLMULQDQ(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCLMULQDQ",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"PCLMULQDQ"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCLMULQDQ",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"PCLMULQDQ"},
+ }, nil
+ }
+ return nil, errors.New("PCLMULQDQ: bad operands")
+}
+
+// PCMPEQB: Compare Packed Byte Data for Equality.
+//
+// Forms:
+//
+// PCMPEQB xmm xmm
+// PCMPEQB m128 xmm
+func PCMPEQB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPEQB: bad operands")
+}
+
+// PCMPEQL: Compare Packed Doubleword Data for Equality.
+//
+// Forms:
+//
+// PCMPEQL xmm xmm
+// PCMPEQL m128 xmm
+func PCMPEQL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPEQL: bad operands")
+}
+
+// PCMPEQQ: Compare Packed Quadword Data for Equality.
+//
+// Forms:
+//
+// PCMPEQQ xmm xmm
+// PCMPEQQ m128 xmm
+func PCMPEQQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PCMPEQQ: bad operands")
+}
+
+// PCMPEQW: Compare Packed Word Data for Equality.
+//
+// Forms:
+//
+// PCMPEQW xmm xmm
+// PCMPEQW m128 xmm
+func PCMPEQW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPEQW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPEQW: bad operands")
+}
+
+// PCMPESTRI: Packed Compare Explicit Length Strings, Return Index.
+//
+// Forms:
+//
+// PCMPESTRI imm8 xmm xmm
+// PCMPESTRI imm8 m128 xmm
+func PCMPESTRI(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPESTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPESTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPESTRI: bad operands")
+}
+
+// PCMPESTRM: Packed Compare Explicit Length Strings, Return Mask.
+//
+// Forms:
+//
+// PCMPESTRM imm8 xmm xmm
+// PCMPESTRM imm8 m128 xmm
+func PCMPESTRM(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPESTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPESTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPESTRM: bad operands")
+}
+
+// PCMPGTB: Compare Packed Signed Byte Integers for Greater Than.
+//
+// Forms:
+//
+// PCMPGTB xmm xmm
+// PCMPGTB m128 xmm
+func PCMPGTB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPGTB: bad operands")
+}
+
+// PCMPGTL: Compare Packed Signed Doubleword Integers for Greater Than.
+//
+// Forms:
+//
+// PCMPGTL xmm xmm
+// PCMPGTL m128 xmm
+func PCMPGTL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPGTL: bad operands")
+}
+
+// PCMPGTQ: Compare Packed Data for Greater Than.
+//
+// Forms:
+//
+// PCMPGTQ xmm xmm
+// PCMPGTQ m128 xmm
+func PCMPGTQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPGTQ: bad operands")
+}
+
+// PCMPGTW: Compare Packed Signed Word Integers for Greater Than.
+//
+// Forms:
+//
+// PCMPGTW xmm xmm
+// PCMPGTW m128 xmm
+func PCMPGTW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPGTW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPGTW: bad operands")
+}
+
+// PCMPISTRI: Packed Compare Implicit Length Strings, Return Index.
+//
+// Forms:
+//
+// PCMPISTRI imm8 xmm xmm
+// PCMPISTRI imm8 m128 xmm
+func PCMPISTRI(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPISTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPISTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPISTRI: bad operands")
+}
+
+// PCMPISTRM: Packed Compare Implicit Length Strings, Return Mask.
+//
+// Forms:
+//
+// PCMPISTRM imm8 xmm xmm
+// PCMPISTRM imm8 m128 xmm
+func PCMPISTRM(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPISTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PCMPISTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"SSE4.2"},
+ }, nil
+ }
+ return nil, errors.New("PCMPISTRM: bad operands")
+}
+
+// PDEPL: Parallel Bits Deposit.
+//
+// Forms:
+//
+// PDEPL r32 r32 r32
+// PDEPL m32 r32 r32
+func PDEPL(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "PDEPL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "PDEPL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("PDEPL: bad operands")
+}
+
+// PDEPQ: Parallel Bits Deposit.
+//
+// Forms:
+//
+// PDEPQ r64 r64 r64
+// PDEPQ m64 r64 r64
+func PDEPQ(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "PDEPQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "PDEPQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("PDEPQ: bad operands")
+}
+
+// PEXTL: Parallel Bits Extract.
+//
+// Forms:
+//
+// PEXTL r32 r32 r32
+// PEXTL m32 r32 r32
+func PEXTL(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "PEXTL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "PEXTL",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("PEXTL: bad operands")
+}
+
+// PEXTQ: Parallel Bits Extract.
+//
+// Forms:
+//
+// PEXTQ r64 r64 r64
+// PEXTQ m64 r64 r64
+func PEXTQ(mr, r, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "PEXTQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "PEXTQ",
+ Operands: []operand.Op{mr, r, r1},
+ Inputs: []operand.Op{mr, r},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("PEXTQ: bad operands")
+}
+
+// PEXTRB: Extract Byte.
+//
+// Forms:
+//
+// PEXTRB imm8 xmm r32
+// PEXTRB imm8 xmm m8
+func PEXTRB(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRB",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRB",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PEXTRB: bad operands")
+}
+
+// PEXTRD: Extract Doubleword.
+//
+// Forms:
+//
+// PEXTRD imm8 xmm r32
+// PEXTRD imm8 xmm m32
+func PEXTRD(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRD",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRD",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PEXTRD: bad operands")
+}
+
+// PEXTRQ: Extract Quadword.
+//
+// Forms:
+//
+// PEXTRQ imm8 xmm r64
+// PEXTRQ imm8 xmm m64
+func PEXTRQ(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRQ",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRQ",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PEXTRQ: bad operands")
+}
+
+// PEXTRW: Extract Word.
+//
+// Forms:
+//
+// PEXTRW imm8 xmm r32
+// PEXTRW imm8 xmm m16
+func PEXTRW(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRW",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "PEXTRW",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PEXTRW: bad operands")
+}
+
+// PHADDD: Packed Horizontal Add Doubleword Integer.
+//
+// Forms:
+//
+// PHADDD xmm xmm
+// PHADDD m128 xmm
+func PHADDD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHADDD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHADDD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PHADDD: bad operands")
+}
+
+// PHADDSW: Packed Horizontal Add Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// PHADDSW xmm xmm
+// PHADDSW m128 xmm
+func PHADDSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHADDSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHADDSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PHADDSW: bad operands")
+}
+
+// PHADDW: Packed Horizontal Add Word Integers.
+//
+// Forms:
+//
+// PHADDW xmm xmm
+// PHADDW m128 xmm
+func PHADDW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHADDW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHADDW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PHADDW: bad operands")
+}
+
+// PHMINPOSUW: Packed Horizontal Minimum of Unsigned Word Integers.
+//
+// Forms:
+//
+// PHMINPOSUW xmm xmm
+// PHMINPOSUW m128 xmm
+func PHMINPOSUW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHMINPOSUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHMINPOSUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PHMINPOSUW: bad operands")
+}
+
+// PHSUBD: Packed Horizontal Subtract Doubleword Integers.
+//
+// Forms:
+//
+// PHSUBD xmm xmm
+// PHSUBD m128 xmm
+func PHSUBD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHSUBD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHSUBD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PHSUBD: bad operands")
+}
+
+// PHSUBSW: Packed Horizontal Subtract Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// PHSUBSW xmm xmm
+// PHSUBSW m128 xmm
+func PHSUBSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHSUBSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHSUBSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PHSUBSW: bad operands")
+}
+
+// PHSUBW: Packed Horizontal Subtract Word Integers.
+//
+// Forms:
+//
+// PHSUBW xmm xmm
+// PHSUBW m128 xmm
+func PHSUBW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHSUBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PHSUBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PHSUBW: bad operands")
+}
+
+// PINSRB: Insert Byte.
+//
+// Forms:
+//
+// PINSRB imm8 r32 xmm
+// PINSRB imm8 m8 xmm
+func PINSRB(i, mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRB",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM8(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRB",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PINSRB: bad operands")
+}
+
+// PINSRD: Insert Doubleword.
+//
+// Forms:
+//
+// PINSRD imm8 r32 xmm
+// PINSRD imm8 m32 xmm
+func PINSRD(i, mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRD",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRD",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PINSRD: bad operands")
+}
+
+// PINSRQ: Insert Quadword.
+//
+// Forms:
+//
+// PINSRQ imm8 r64 xmm
+// PINSRQ imm8 m64 xmm
+func PINSRQ(i, mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR64(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRQ",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM64(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRQ",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PINSRQ: bad operands")
+}
+
+// PINSRW: Insert Word.
+//
+// Forms:
+//
+// PINSRW imm8 r32 xmm
+// PINSRW imm8 m16 xmm
+func PINSRW(i, mr, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRW",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM16(mr) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PINSRW",
+ Operands: []operand.Op{i, mr, x},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PINSRW: bad operands")
+}
+
+// PMADDUBSW: Multiply and Add Packed Signed and Unsigned Byte Integers.
+//
+// Forms:
+//
+// PMADDUBSW xmm xmm
+// PMADDUBSW m128 xmm
+func PMADDUBSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMADDUBSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMADDUBSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PMADDUBSW: bad operands")
+}
+
+// PMADDWL: Multiply and Add Packed Signed Word Integers.
+//
+// Forms:
+//
+// PMADDWL xmm xmm
+// PMADDWL m128 xmm
+func PMADDWL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMADDWL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMADDWL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMADDWL: bad operands")
+}
+
+// PMAXSB: Maximum of Packed Signed Byte Integers.
+//
+// Forms:
+//
+// PMAXSB xmm xmm
+// PMAXSB m128 xmm
+func PMAXSB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMAXSB: bad operands")
+}
+
+// PMAXSD: Maximum of Packed Signed Doubleword Integers.
+//
+// Forms:
+//
+// PMAXSD xmm xmm
+// PMAXSD m128 xmm
+func PMAXSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMAXSD: bad operands")
+}
+
+// PMAXSW: Maximum of Packed Signed Word Integers.
+//
+// Forms:
+//
+// PMAXSW xmm xmm
+// PMAXSW m128 xmm
+func PMAXSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMAXSW: bad operands")
+}
+
+// PMAXUB: Maximum of Packed Unsigned Byte Integers.
+//
+// Forms:
+//
+// PMAXUB xmm xmm
+// PMAXUB m128 xmm
+func PMAXUB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXUB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXUB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMAXUB: bad operands")
+}
+
+// PMAXUD: Maximum of Packed Unsigned Doubleword Integers.
+//
+// Forms:
+//
+// PMAXUD xmm xmm
+// PMAXUD m128 xmm
+func PMAXUD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXUD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXUD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMAXUD: bad operands")
+}
+
+// PMAXUW: Maximum of Packed Unsigned Word Integers.
+//
+// Forms:
+//
+// PMAXUW xmm xmm
+// PMAXUW m128 xmm
+func PMAXUW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMAXUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMAXUW: bad operands")
+}
+
+// PMINSB: Minimum of Packed Signed Byte Integers.
+//
+// Forms:
+//
+// PMINSB xmm xmm
+// PMINSB m128 xmm
+func PMINSB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMINSB: bad operands")
+}
+
+// PMINSD: Minimum of Packed Signed Doubleword Integers.
+//
+// Forms:
+//
+// PMINSD xmm xmm
+// PMINSD m128 xmm
+func PMINSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMINSD: bad operands")
+}
+
+// PMINSW: Minimum of Packed Signed Word Integers.
+//
+// Forms:
+//
+// PMINSW xmm xmm
+// PMINSW m128 xmm
+func PMINSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMINSW: bad operands")
+}
+
+// PMINUB: Minimum of Packed Unsigned Byte Integers.
+//
+// Forms:
+//
+// PMINUB xmm xmm
+// PMINUB m128 xmm
+func PMINUB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINUB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINUB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMINUB: bad operands")
+}
+
+// PMINUD: Minimum of Packed Unsigned Doubleword Integers.
+//
+// Forms:
+//
+// PMINUD xmm xmm
+// PMINUD m128 xmm
+func PMINUD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINUD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINUD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMINUD: bad operands")
+}
+
+// PMINUW: Minimum of Packed Unsigned Word Integers.
+//
+// Forms:
+//
+// PMINUW xmm xmm
+// PMINUW m128 xmm
+func PMINUW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMINUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMINUW: bad operands")
+}
+
+// PMOVMSKB: Move Byte Mask.
+//
+// Forms:
+//
+// PMOVMSKB xmm r32
+func PMOVMSKB(x, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "PMOVMSKB",
+ Operands: []operand.Op{x, r},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{r},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMOVMSKB: bad operands")
+}
+
+// PMOVSXBD: Move Packed Byte Integers to Doubleword Integers with Sign Extension.
+//
+// Forms:
+//
+// PMOVSXBD xmm xmm
+// PMOVSXBD m32 xmm
+func PMOVSXBD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXBD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXBD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVSXBD: bad operands")
+}
+
+// PMOVSXBQ: Move Packed Byte Integers to Quadword Integers with Sign Extension.
+//
+// Forms:
+//
+// PMOVSXBQ xmm xmm
+// PMOVSXBQ m16 xmm
+func PMOVSXBQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXBQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM16(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXBQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVSXBQ: bad operands")
+}
+
+// PMOVSXBW: Move Packed Byte Integers to Word Integers with Sign Extension.
+//
+// Forms:
+//
+// PMOVSXBW xmm xmm
+// PMOVSXBW m64 xmm
+func PMOVSXBW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVSXBW: bad operands")
+}
+
+// PMOVSXDQ: Move Packed Doubleword Integers to Quadword Integers with Sign Extension.
+//
+// Forms:
+//
+// PMOVSXDQ xmm xmm
+// PMOVSXDQ m64 xmm
+func PMOVSXDQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVSXDQ: bad operands")
+}
+
+// PMOVSXWD: Move Packed Word Integers to Doubleword Integers with Sign Extension.
+//
+// Forms:
+//
+// PMOVSXWD xmm xmm
+// PMOVSXWD m64 xmm
+func PMOVSXWD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXWD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXWD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVSXWD: bad operands")
+}
+
+// PMOVSXWQ: Move Packed Word Integers to Quadword Integers with Sign Extension.
+//
+// Forms:
+//
+// PMOVSXWQ xmm xmm
+// PMOVSXWQ m32 xmm
+func PMOVSXWQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXWQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVSXWQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVSXWQ: bad operands")
+}
+
+// PMOVZXBD: Move Packed Byte Integers to Doubleword Integers with Zero Extension.
+//
+// Forms:
+//
+// PMOVZXBD xmm xmm
+// PMOVZXBD m32 xmm
+func PMOVZXBD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXBD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXBD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVZXBD: bad operands")
+}
+
+// PMOVZXBQ: Move Packed Byte Integers to Quadword Integers with Zero Extension.
+//
+// Forms:
+//
+// PMOVZXBQ xmm xmm
+// PMOVZXBQ m16 xmm
+func PMOVZXBQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXBQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM16(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXBQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVZXBQ: bad operands")
+}
+
+// PMOVZXBW: Move Packed Byte Integers to Word Integers with Zero Extension.
+//
+// Forms:
+//
+// PMOVZXBW xmm xmm
+// PMOVZXBW m64 xmm
+func PMOVZXBW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVZXBW: bad operands")
+}
+
+// PMOVZXDQ: Move Packed Doubleword Integers to Quadword Integers with Zero Extension.
+//
+// Forms:
+//
+// PMOVZXDQ xmm xmm
+// PMOVZXDQ m64 xmm
+func PMOVZXDQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVZXDQ: bad operands")
+}
+
+// PMOVZXWD: Move Packed Word Integers to Doubleword Integers with Zero Extension.
+//
+// Forms:
+//
+// PMOVZXWD xmm xmm
+// PMOVZXWD m64 xmm
+func PMOVZXWD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXWD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXWD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVZXWD: bad operands")
+}
+
+// PMOVZXWQ: Move Packed Word Integers to Quadword Integers with Zero Extension.
+//
+// Forms:
+//
+// PMOVZXWQ xmm xmm
+// PMOVZXWQ m32 xmm
+func PMOVZXWQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXWQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMOVZXWQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMOVZXWQ: bad operands")
+}
+
+// PMULDQ: Multiply Packed Signed Doubleword Integers and Store Quadword Result.
+//
+// Forms:
+//
+// PMULDQ xmm xmm
+// PMULDQ m128 xmm
+func PMULDQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMULDQ: bad operands")
+}
+
+// PMULHRSW: Packed Multiply Signed Word Integers and Store High Result with Round and Scale.
+//
+// Forms:
+//
+// PMULHRSW xmm xmm
+// PMULHRSW m128 xmm
+func PMULHRSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULHRSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULHRSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PMULHRSW: bad operands")
+}
+
+// PMULHUW: Multiply Packed Unsigned Word Integers and Store High Result.
+//
+// Forms:
+//
+// PMULHUW xmm xmm
+// PMULHUW m128 xmm
+func PMULHUW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULHUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULHUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMULHUW: bad operands")
+}
+
+// PMULHW: Multiply Packed Signed Word Integers and Store High Result.
+//
+// Forms:
+//
+// PMULHW xmm xmm
+// PMULHW m128 xmm
+func PMULHW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULHW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULHW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMULHW: bad operands")
+}
+
+// PMULLD: Multiply Packed Signed Doubleword Integers and Store Low Result.
+//
+// Forms:
+//
+// PMULLD xmm xmm
+// PMULLD m128 xmm
+func PMULLD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULLD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULLD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PMULLD: bad operands")
+}
+
+// PMULLW: Multiply Packed Signed Word Integers and Store Low Result.
+//
+// Forms:
+//
+// PMULLW xmm xmm
+// PMULLW m128 xmm
+func PMULLW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULLW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULLW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMULLW: bad operands")
+}
+
+// PMULULQ: Multiply Packed Unsigned Doubleword Integers.
+//
+// Forms:
+//
+// PMULULQ xmm xmm
+// PMULULQ m128 xmm
+func PMULULQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULULQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PMULULQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PMULULQ: bad operands")
+}
+
+// POPCNTL: Count of Number of Bits Set to 1.
+//
+// Forms:
+//
+// POPCNTL r32 r32
+// POPCNTL m32 r32
+func POPCNTL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "POPCNTL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"POPCNT"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "POPCNTL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"POPCNT"},
+ }, nil
+ }
+ return nil, errors.New("POPCNTL: bad operands")
+}
+
+// POPCNTQ: Count of Number of Bits Set to 1.
+//
+// Forms:
+//
+// POPCNTQ r64 r64
+// POPCNTQ m64 r64
+func POPCNTQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "POPCNTQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"POPCNT"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "POPCNTQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"POPCNT"},
+ }, nil
+ }
+ return nil, errors.New("POPCNTQ: bad operands")
+}
+
+// POPCNTW: Count of Number of Bits Set to 1.
+//
+// Forms:
+//
+// POPCNTW r16 r16
+// POPCNTW m16 r16
+func POPCNTW(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "POPCNTW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"POPCNT"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "POPCNTW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"POPCNT"},
+ }, nil
+ }
+ return nil, errors.New("POPCNTW: bad operands")
+}
+
+// POPQ: Pop a Value from the Stack.
+//
+// Forms:
+//
+// POPQ r64
+// POPQ m64
+func POPQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "POPQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "POPQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("POPQ: bad operands")
+}
+
+// POPW: Pop a Value from the Stack.
+//
+// Forms:
+//
+// POPW r16
+// POPW m16
+func POPW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "POPW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "POPW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("POPW: bad operands")
+}
+
+// POR: Packed Bitwise Logical OR.
+//
+// Forms:
+//
+// POR xmm xmm
+// POR m128 xmm
+func POR(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "POR",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "POR",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("POR: bad operands")
+}
+
+// PREFETCHNTA: Prefetch Data Into Caches using NTA Hint.
+//
+// Forms:
+//
+// PREFETCHNTA m8
+func PREFETCHNTA(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM8(m):
+ return &intrep.Instruction{
+ Opcode: "PREFETCHNTA",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"MMX+"},
+ }, nil
+ }
+ return nil, errors.New("PREFETCHNTA: bad operands")
+}
+
+// PREFETCHT0: Prefetch Data Into Caches using T0 Hint.
+//
+// Forms:
+//
+// PREFETCHT0 m8
+func PREFETCHT0(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM8(m):
+ return &intrep.Instruction{
+ Opcode: "PREFETCHT0",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"MMX+"},
+ }, nil
+ }
+ return nil, errors.New("PREFETCHT0: bad operands")
+}
+
+// PREFETCHT1: Prefetch Data Into Caches using T1 Hint.
+//
+// Forms:
+//
+// PREFETCHT1 m8
+func PREFETCHT1(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM8(m):
+ return &intrep.Instruction{
+ Opcode: "PREFETCHT1",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"MMX+"},
+ }, nil
+ }
+ return nil, errors.New("PREFETCHT1: bad operands")
+}
+
+// PREFETCHT2: Prefetch Data Into Caches using T2 Hint.
+//
+// Forms:
+//
+// PREFETCHT2 m8
+func PREFETCHT2(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM8(m):
+ return &intrep.Instruction{
+ Opcode: "PREFETCHT2",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"MMX+"},
+ }, nil
+ }
+ return nil, errors.New("PREFETCHT2: bad operands")
+}
+
+// PSADBW: Compute Sum of Absolute Differences.
+//
+// Forms:
+//
+// PSADBW xmm xmm
+// PSADBW m128 xmm
+func PSADBW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSADBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSADBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSADBW: bad operands")
+}
+
+// PSHUFB: Packed Shuffle Bytes.
+//
+// Forms:
+//
+// PSHUFB xmm xmm
+// PSHUFB m128 xmm
+func PSHUFB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PSHUFB: bad operands")
+}
+
+// PSHUFD: Shuffle Packed Doublewords.
+//
+// Forms:
+//
+// PSHUFD imm8 xmm xmm
+// PSHUFD imm8 m128 xmm
+func PSHUFD(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSHUFD: bad operands")
+}
+
+// PSHUFHW: Shuffle Packed High Words.
+//
+// Forms:
+//
+// PSHUFHW imm8 xmm xmm
+// PSHUFHW imm8 m128 xmm
+func PSHUFHW(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFHW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFHW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSHUFHW: bad operands")
+}
+
+// PSHUFL: Shuffle Packed Doublewords.
+//
+// Forms:
+//
+// PSHUFL imm8 xmm xmm
+// PSHUFL imm8 m128 xmm
+func PSHUFL(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFL",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFL",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSHUFL: bad operands")
+}
+
+// PSHUFLW: Shuffle Packed Low Words.
+//
+// Forms:
+//
+// PSHUFLW imm8 xmm xmm
+// PSHUFLW imm8 m128 xmm
+func PSHUFLW(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFLW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSHUFLW",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSHUFLW: bad operands")
+}
+
+// PSIGNB: Packed Sign of Byte Integers.
+//
+// Forms:
+//
+// PSIGNB xmm xmm
+// PSIGNB m128 xmm
+func PSIGNB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSIGNB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSIGNB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PSIGNB: bad operands")
+}
+
+// PSIGND: Packed Sign of Doubleword Integers.
+//
+// Forms:
+//
+// PSIGND xmm xmm
+// PSIGND m128 xmm
+func PSIGND(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSIGND",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSIGND",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PSIGND: bad operands")
+}
+
+// PSIGNW: Packed Sign of Word Integers.
+//
+// Forms:
+//
+// PSIGNW xmm xmm
+// PSIGNW m128 xmm
+func PSIGNW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSIGNW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSIGNW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSSE3"},
+ }, nil
+ }
+ return nil, errors.New("PSIGNW: bad operands")
+}
+
+// PSLLDQ: Shift Packed Double Quadword Left Logical.
+//
+// Forms:
+//
+// PSLLDQ imm8 xmm
+func PSLLDQ(i, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLDQ",
+ Operands: []operand.Op{i, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSLLDQ: bad operands")
+}
+
+// PSLLL: Shift Packed Doubleword Data Left Logical.
+//
+// Forms:
+//
+// PSLLL imm8 xmm
+// PSLLL xmm xmm
+// PSLLL m128 xmm
+func PSLLL(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSLLL: bad operands")
+}
+
+// PSLLO: Shift Packed Double Quadword Left Logical.
+//
+// Forms:
+//
+// PSLLO imm8 xmm
+func PSLLO(i, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLO",
+ Operands: []operand.Op{i, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSLLO: bad operands")
+}
+
+// PSLLQ: Shift Packed Quadword Data Left Logical.
+//
+// Forms:
+//
+// PSLLQ imm8 xmm
+// PSLLQ xmm xmm
+// PSLLQ m128 xmm
+func PSLLQ(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLQ",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLQ",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLQ",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSLLQ: bad operands")
+}
+
+// PSLLW: Shift Packed Word Data Left Logical.
+//
+// Forms:
+//
+// PSLLW imm8 xmm
+// PSLLW xmm xmm
+// PSLLW m128 xmm
+func PSLLW(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSLLW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSLLW: bad operands")
+}
+
+// PSRAL: Shift Packed Doubleword Data Right Arithmetic.
+//
+// Forms:
+//
+// PSRAL imm8 xmm
+// PSRAL xmm xmm
+// PSRAL m128 xmm
+func PSRAL(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRAL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRAL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRAL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSRAL: bad operands")
+}
+
+// PSRAW: Shift Packed Word Data Right Arithmetic.
+//
+// Forms:
+//
+// PSRAW imm8 xmm
+// PSRAW xmm xmm
+// PSRAW m128 xmm
+func PSRAW(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRAW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRAW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRAW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSRAW: bad operands")
+}
+
+// PSRLDQ: Shift Packed Double Quadword Right Logical.
+//
+// Forms:
+//
+// PSRLDQ imm8 xmm
+func PSRLDQ(i, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLDQ",
+ Operands: []operand.Op{i, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSRLDQ: bad operands")
+}
+
+// PSRLL: Shift Packed Doubleword Data Right Logical.
+//
+// Forms:
+//
+// PSRLL imm8 xmm
+// PSRLL xmm xmm
+// PSRLL m128 xmm
+func PSRLL(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLL",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSRLL: bad operands")
+}
+
+// PSRLO: Shift Packed Double Quadword Right Logical.
+//
+// Forms:
+//
+// PSRLO imm8 xmm
+func PSRLO(i, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLO",
+ Operands: []operand.Op{i, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSRLO: bad operands")
+}
+
+// PSRLQ: Shift Packed Quadword Data Right Logical.
+//
+// Forms:
+//
+// PSRLQ imm8 xmm
+// PSRLQ xmm xmm
+// PSRLQ m128 xmm
+func PSRLQ(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLQ",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLQ",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLQ",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSRLQ: bad operands")
+}
+
+// PSRLW: Shift Packed Word Data Right Logical.
+//
+// Forms:
+//
+// PSRLW imm8 xmm
+// PSRLW xmm xmm
+// PSRLW m128 xmm
+func PSRLW(imx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSRLW",
+ Operands: []operand.Op{imx, x},
+ Inputs: []operand.Op{imx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSRLW: bad operands")
+}
+
+// PSUBB: Subtract Packed Byte Integers.
+//
+// Forms:
+//
+// PSUBB xmm xmm
+// PSUBB m128 xmm
+func PSUBB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBB: bad operands")
+}
+
+// PSUBL: Subtract Packed Doubleword Integers.
+//
+// Forms:
+//
+// PSUBL xmm xmm
+// PSUBL m128 xmm
+func PSUBL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBL: bad operands")
+}
+
+// PSUBQ: Subtract Packed Quadword Integers.
+//
+// Forms:
+//
+// PSUBQ xmm xmm
+// PSUBQ m128 xmm
+func PSUBQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBQ: bad operands")
+}
+
+// PSUBSB: Subtract Packed Signed Byte Integers with Signed Saturation.
+//
+// Forms:
+//
+// PSUBSB xmm xmm
+// PSUBSB m128 xmm
+func PSUBSB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBSB: bad operands")
+}
+
+// PSUBSW: Subtract Packed Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// PSUBSW xmm xmm
+// PSUBSW m128 xmm
+func PSUBSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBSW: bad operands")
+}
+
+// PSUBUSB: Subtract Packed Unsigned Byte Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// PSUBUSB xmm xmm
+// PSUBUSB m128 xmm
+func PSUBUSB(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBUSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBUSB",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBUSB: bad operands")
+}
+
+// PSUBUSW: Subtract Packed Unsigned Word Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// PSUBUSW xmm xmm
+// PSUBUSW m128 xmm
+func PSUBUSW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBUSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBUSW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBUSW: bad operands")
+}
+
+// PSUBW: Subtract Packed Word Integers.
+//
+// Forms:
+//
+// PSUBW xmm xmm
+// PSUBW m128 xmm
+func PSUBW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PSUBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PSUBW: bad operands")
+}
+
+// PTEST: Packed Logical Compare.
+//
+// Forms:
+//
+// PTEST xmm xmm
+// PTEST m128 xmm
+func PTEST(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PTEST",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PTEST",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("PTEST: bad operands")
+}
+
+// PUNPCKHBW: Unpack and Interleave High-Order Bytes into Words.
+//
+// Forms:
+//
+// PUNPCKHBW xmm xmm
+// PUNPCKHBW m128 xmm
+func PUNPCKHBW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKHBW: bad operands")
+}
+
+// PUNPCKHLQ: Unpack and Interleave High-Order Doublewords into Quadwords.
+//
+// Forms:
+//
+// PUNPCKHLQ xmm xmm
+// PUNPCKHLQ m128 xmm
+func PUNPCKHLQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHLQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHLQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKHLQ: bad operands")
+}
+
+// PUNPCKHQDQ: Unpack and Interleave High-Order Quadwords into Double Quadwords.
+//
+// Forms:
+//
+// PUNPCKHQDQ xmm xmm
+// PUNPCKHQDQ m128 xmm
+func PUNPCKHQDQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHQDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHQDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKHQDQ: bad operands")
+}
+
+// PUNPCKHWL: Unpack and Interleave High-Order Words into Doublewords.
+//
+// Forms:
+//
+// PUNPCKHWL xmm xmm
+// PUNPCKHWL m128 xmm
+func PUNPCKHWL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHWL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKHWL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKHWL: bad operands")
+}
+
+// PUNPCKLBW: Unpack and Interleave Low-Order Bytes into Words.
+//
+// Forms:
+//
+// PUNPCKLBW xmm xmm
+// PUNPCKLBW m128 xmm
+func PUNPCKLBW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLBW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKLBW: bad operands")
+}
+
+// PUNPCKLLQ: Unpack and Interleave Low-Order Doublewords into Quadwords.
+//
+// Forms:
+//
+// PUNPCKLLQ xmm xmm
+// PUNPCKLLQ m128 xmm
+func PUNPCKLLQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLLQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLLQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKLLQ: bad operands")
+}
+
+// PUNPCKLQDQ: Unpack and Interleave Low-Order Quadwords into Double Quadwords.
+//
+// Forms:
+//
+// PUNPCKLQDQ xmm xmm
+// PUNPCKLQDQ m128 xmm
+func PUNPCKLQDQ(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLQDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLQDQ",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKLQDQ: bad operands")
+}
+
+// PUNPCKLWL: Unpack and Interleave Low-Order Words into Doublewords.
+//
+// Forms:
+//
+// PUNPCKLWL xmm xmm
+// PUNPCKLWL m128 xmm
+func PUNPCKLWL(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLWL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PUNPCKLWL",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PUNPCKLWL: bad operands")
+}
+
+// PUSHQ: Push Value Onto the Stack.
+//
+// Forms:
+//
+// PUSHQ imm8
+// PUSHQ imm32
+// PUSHQ r64
+// PUSHQ m64
+func PUSHQ(imr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr):
+ return &intrep.Instruction{
+ Opcode: "PUSHQ",
+ Operands: []operand.Op{imr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM32(imr):
+ return &intrep.Instruction{
+ Opcode: "PUSHQ",
+ Operands: []operand.Op{imr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(imr):
+ return &intrep.Instruction{
+ Opcode: "PUSHQ",
+ Operands: []operand.Op{imr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM64(imr):
+ return &intrep.Instruction{
+ Opcode: "PUSHQ",
+ Operands: []operand.Op{imr},
+ Inputs: []operand.Op{imr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("PUSHQ: bad operands")
+}
+
+// PUSHW: Push Value Onto the Stack.
+//
+// Forms:
+//
+// PUSHW r16
+// PUSHW m16
+func PUSHW(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "PUSHW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "PUSHW",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("PUSHW: bad operands")
+}
+
+// PXOR: Packed Bitwise Logical Exclusive OR.
+//
+// Forms:
+//
+// PXOR xmm xmm
+// PXOR m128 xmm
+func PXOR(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PXOR",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "PXOR",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("PXOR: bad operands")
+}
+
+// RCLB: Rotate Left through Carry Flag.
+//
+// Forms:
+//
+// RCLB 1 r8
+// RCLB imm8 r8
+// RCLB cl r8
+// RCLB 1 m8
+// RCLB imm8 m8
+// RCLB cl m8
+func RCLB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCLB: bad operands")
+}
+
+// RCLL: Rotate Left through Carry Flag.
+//
+// Forms:
+//
+// RCLL 1 r32
+// RCLL imm8 r32
+// RCLL cl r32
+// RCLL 1 m32
+// RCLL imm8 m32
+// RCLL cl m32
+func RCLL(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCLL: bad operands")
+}
+
+// RCLQ: Rotate Left through Carry Flag.
+//
+// Forms:
+//
+// RCLQ 1 r64
+// RCLQ imm8 r64
+// RCLQ cl r64
+// RCLQ 1 m64
+// RCLQ imm8 m64
+// RCLQ cl m64
+func RCLQ(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCLQ: bad operands")
+}
+
+// RCLW: Rotate Left through Carry Flag.
+//
+// Forms:
+//
+// RCLW 1 r16
+// RCLW imm8 r16
+// RCLW cl r16
+// RCLW 1 m16
+// RCLW imm8 m16
+// RCLW cl m16
+func RCLW(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCLW: bad operands")
+}
+
+// RCPPS: Compute Approximate Reciprocals of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// RCPPS xmm xmm
+// RCPPS m128 xmm
+func RCPPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RCPPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RCPPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("RCPPS: bad operands")
+}
+
+// RCPSS: Compute Approximate Reciprocal of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// RCPSS xmm xmm
+// RCPSS m32 xmm
+func RCPSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RCPSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RCPSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("RCPSS: bad operands")
+}
+
+// RCRB: Rotate Right through Carry Flag.
+//
+// Forms:
+//
+// RCRB 1 r8
+// RCRB imm8 r8
+// RCRB cl r8
+// RCRB 1 m8
+// RCRB imm8 m8
+// RCRB cl m8
+func RCRB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCRB: bad operands")
+}
+
+// RCRL: Rotate Right through Carry Flag.
+//
+// Forms:
+//
+// RCRL 1 r32
+// RCRL imm8 r32
+// RCRL cl r32
+// RCRL 1 m32
+// RCRL imm8 m32
+// RCRL cl m32
+func RCRL(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCRL: bad operands")
+}
+
+// RCRQ: Rotate Right through Carry Flag.
+//
+// Forms:
+//
+// RCRQ 1 r64
+// RCRQ imm8 r64
+// RCRQ cl r64
+// RCRQ 1 m64
+// RCRQ imm8 m64
+// RCRQ cl m64
+func RCRQ(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCRQ: bad operands")
+}
+
+// RCRW: Rotate Right through Carry Flag.
+//
+// Forms:
+//
+// RCRW 1 r16
+// RCRW imm8 r16
+// RCRW cl r16
+// RCRW 1 m16
+// RCRW imm8 m16
+// RCRW cl m16
+func RCRW(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RCRW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RCRW: bad operands")
+}
+
+// RDRANDL: Read Random Number.
+//
+// Forms:
+//
+// RDRANDL r32
+func RDRANDL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "RDRANDL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{r},
+ ISA: []string{"RDRAND"},
+ }, nil
+ }
+ return nil, errors.New("RDRANDL: bad operands")
+}
+
+// RDRANDQ: Read Random Number.
+//
+// Forms:
+//
+// RDRANDQ r64
+func RDRANDQ(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "RDRANDQ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{r},
+ ISA: []string{"RDRAND"},
+ }, nil
+ }
+ return nil, errors.New("RDRANDQ: bad operands")
+}
+
+// RDRANDW: Read Random Number.
+//
+// Forms:
+//
+// RDRANDW r16
+func RDRANDW(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "RDRANDW",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{r},
+ ISA: []string{"RDRAND"},
+ }, nil
+ }
+ return nil, errors.New("RDRANDW: bad operands")
+}
+
+// RDSEEDL: Read Random SEED.
+//
+// Forms:
+//
+// RDSEEDL r32
+func RDSEEDL(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "RDSEEDL",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{r},
+ ISA: []string{"RDSEED"},
+ }, nil
+ }
+ return nil, errors.New("RDSEEDL: bad operands")
+}
+
+// RDSEEDQ: Read Random SEED.
+//
+// Forms:
+//
+// RDSEEDQ r64
+func RDSEEDQ(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "RDSEEDQ",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{r},
+ ISA: []string{"RDSEED"},
+ }, nil
+ }
+ return nil, errors.New("RDSEEDQ: bad operands")
+}
+
+// RDSEEDW: Read Random SEED.
+//
+// Forms:
+//
+// RDSEEDW r16
+func RDSEEDW(r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "RDSEEDW",
+ Operands: []operand.Op{r},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{r},
+ ISA: []string{"RDSEED"},
+ }, nil
+ }
+ return nil, errors.New("RDSEEDW: bad operands")
+}
+
+// RDTSC: Read Time-Stamp Counter.
+//
+// Forms:
+//
+// RDTSC
+func RDTSC() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "RDTSC",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ ISA: []string{"RDTSC"},
+ }, nil
+}
+
+// RDTSCP: Read Time-Stamp Counter and Processor ID.
+//
+// Forms:
+//
+// RDTSCP
+func RDTSCP() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "RDTSCP",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{reg.EAX, reg.ECX, reg.EDX},
+ ISA: []string{"RDTSCP"},
+ }, nil
+}
+
+// RET: Return from Procedure.
+//
+// Forms:
+//
+// RET
+func RET() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "RET",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ IsTerminal: true,
+ }, nil
+}
+
+// RETFL: Return from Procedure.
+//
+// Forms:
+//
+// RETFL imm16
+func RETFL(i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(i):
+ return &intrep.Instruction{
+ Opcode: "RETFL",
+ Operands: []operand.Op{i},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("RETFL: bad operands")
+}
+
+// RETFQ: Return from Procedure.
+//
+// Forms:
+//
+// RETFQ imm16
+func RETFQ(i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(i):
+ return &intrep.Instruction{
+ Opcode: "RETFQ",
+ Operands: []operand.Op{i},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("RETFQ: bad operands")
+}
+
+// RETFW: Return from Procedure.
+//
+// Forms:
+//
+// RETFW imm16
+func RETFW(i operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(i):
+ return &intrep.Instruction{
+ Opcode: "RETFW",
+ Operands: []operand.Op{i},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("RETFW: bad operands")
+}
+
+// ROLB: Rotate Left.
+//
+// Forms:
+//
+// ROLB 1 r8
+// ROLB imm8 r8
+// ROLB cl r8
+// ROLB 1 m8
+// ROLB imm8 m8
+// ROLB cl m8
+func ROLB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ROLB: bad operands")
+}
+
+// ROLL: Rotate Left.
+//
+// Forms:
+//
+// ROLL 1 r32
+// ROLL imm8 r32
+// ROLL cl r32
+// ROLL 1 m32
+// ROLL imm8 m32
+// ROLL cl m32
+func ROLL(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ROLL: bad operands")
+}
+
+// ROLQ: Rotate Left.
+//
+// Forms:
+//
+// ROLQ 1 r64
+// ROLQ imm8 r64
+// ROLQ cl r64
+// ROLQ 1 m64
+// ROLQ imm8 m64
+// ROLQ cl m64
+func ROLQ(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ROLQ: bad operands")
+}
+
+// ROLW: Rotate Left.
+//
+// Forms:
+//
+// ROLW 1 r16
+// ROLW imm8 r16
+// ROLW cl r16
+// ROLW 1 m16
+// ROLW imm8 m16
+// ROLW cl m16
+func ROLW(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "ROLW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("ROLW: bad operands")
+}
+
+// RORB: Rotate Right.
+//
+// Forms:
+//
+// RORB 1 r8
+// RORB imm8 r8
+// RORB cl r8
+// RORB 1 m8
+// RORB imm8 m8
+// RORB cl m8
+func RORB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RORB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RORB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "RORB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RORB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RORB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "RORB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RORB: bad operands")
+}
+
+// RORL: Rotate Right.
+//
+// Forms:
+//
+// RORL 1 r32
+// RORL imm8 r32
+// RORL cl r32
+// RORL 1 m32
+// RORL imm8 m32
+// RORL cl m32
+func RORL(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RORL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RORL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "RORL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RORL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RORL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "RORL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RORL: bad operands")
+}
+
+// RORQ: Rotate Right.
+//
+// Forms:
+//
+// RORQ 1 r64
+// RORQ imm8 r64
+// RORQ cl r64
+// RORQ 1 m64
+// RORQ imm8 m64
+// RORQ cl m64
+func RORQ(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RORQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RORQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "RORQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RORQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RORQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "RORQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RORQ: bad operands")
+}
+
+// RORW: Rotate Right.
+//
+// Forms:
+//
+// RORW 1 r16
+// RORW imm8 r16
+// RORW cl r16
+// RORW 1 m16
+// RORW imm8 m16
+// RORW cl m16
+func RORW(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RORW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RORW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "RORW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RORW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RORW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "RORW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("RORW: bad operands")
+}
+
+// RORXL: Rotate Right Logical Without Affecting Flags.
+//
+// Forms:
+//
+// RORXL imm8 r32 r32
+// RORXL imm8 m32 r32
+func RORXL(i, mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "RORXL",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "RORXL",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("RORXL: bad operands")
+}
+
+// RORXQ: Rotate Right Logical Without Affecting Flags.
+//
+// Forms:
+//
+// RORXQ imm8 r64 r64
+// RORXQ imm8 m64 r64
+func RORXQ(i, mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "RORXQ",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "RORXQ",
+ Operands: []operand.Op{i, mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("RORXQ: bad operands")
+}
+
+// ROUNDPD: Round Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// ROUNDPD imm8 xmm xmm
+// ROUNDPD imm8 m128 xmm
+func ROUNDPD(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("ROUNDPD: bad operands")
+}
+
+// ROUNDPS: Round Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// ROUNDPS imm8 xmm xmm
+// ROUNDPS imm8 m128 xmm
+func ROUNDPS(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("ROUNDPS: bad operands")
+}
+
+// ROUNDSD: Round Scalar Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// ROUNDSD imm8 xmm xmm
+// ROUNDSD imm8 m64 xmm
+func ROUNDSD(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDSD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDSD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("ROUNDSD: bad operands")
+}
+
+// ROUNDSS: Round Scalar Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// ROUNDSS imm8 xmm xmm
+// ROUNDSS imm8 m32 xmm
+func ROUNDSS(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDSS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "ROUNDSS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE4.1"},
+ }, nil
+ }
+ return nil, errors.New("ROUNDSS: bad operands")
+}
+
+// RSQRTPS: Compute Reciprocals of Square Roots of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// RSQRTPS xmm xmm
+// RSQRTPS m128 xmm
+func RSQRTPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RSQRTPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RSQRTPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("RSQRTPS: bad operands")
+}
+
+// RSQRTSS: Compute Reciprocal of Square Root of Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// RSQRTSS xmm xmm
+// RSQRTSS m32 xmm
+func RSQRTSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RSQRTSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "RSQRTSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("RSQRTSS: bad operands")
+}
+
+// SALB: Arithmetic Shift Left.
+//
+// Forms:
+//
+// SALB 1 r8
+// SALB imm8 r8
+// SALB cl r8
+// SALB 1 m8
+// SALB imm8 m8
+// SALB cl m8
+func SALB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SALB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SALB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SALB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SALB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SALB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SALB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SALB: bad operands")
+}
+
+// SALL: Arithmetic Shift Left.
+//
+// Forms:
+//
+// SALL 1 r32
+// SALL imm8 r32
+// SALL cl r32
+// SALL 1 m32
+// SALL imm8 m32
+// SALL cl m32
+func SALL(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "SALL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "SALL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "SALL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "SALL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "SALL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "SALL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SALL: bad operands")
+}
+
+// SALQ: Arithmetic Shift Left.
+//
+// Forms:
+//
+// SALQ 1 r64
+// SALQ imm8 r64
+// SALQ cl r64
+// SALQ 1 m64
+// SALQ imm8 m64
+// SALQ cl m64
+func SALQ(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SALQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SALQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SALQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SALQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SALQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SALQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SALQ: bad operands")
+}
+
+// SALW: Arithmetic Shift Left.
+//
+// Forms:
+//
+// SALW 1 r16
+// SALW imm8 r16
+// SALW cl r16
+// SALW 1 m16
+// SALW imm8 m16
+// SALW cl m16
+func SALW(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "SALW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "SALW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "SALW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "SALW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "SALW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "SALW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SALW: bad operands")
+}
+
+// SARB: Arithmetic Shift Right.
+//
+// Forms:
+//
+// SARB 1 r8
+// SARB imm8 r8
+// SARB cl r8
+// SARB 1 m8
+// SARB imm8 m8
+// SARB cl m8
+func SARB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SARB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SARB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SARB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SARB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SARB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SARB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SARB: bad operands")
+}
+
+// SARL: Arithmetic Shift Right.
+//
+// Forms:
+//
+// SARL 1 r32
+// SARL imm8 r32
+// SARL cl r32
+// SARL 1 m32
+// SARL imm8 m32
+// SARL cl m32
+func SARL(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "SARL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "SARL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "SARL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "SARL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "SARL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "SARL",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SARL: bad operands")
+}
+
+// SARQ: Arithmetic Shift Right.
+//
+// Forms:
+//
+// SARQ 1 r64
+// SARQ imm8 r64
+// SARQ cl r64
+// SARQ 1 m64
+// SARQ imm8 m64
+// SARQ cl m64
+func SARQ(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SARQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SARQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SARQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SARQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SARQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SARQ",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SARQ: bad operands")
+}
+
+// SARW: Arithmetic Shift Right.
+//
+// Forms:
+//
+// SARW 1 r16
+// SARW imm8 r16
+// SARW cl r16
+// SARW 1 m16
+// SARW imm8 m16
+// SARW cl m16
+func SARW(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "SARW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "SARW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "SARW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "SARW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "SARW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "SARW",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SARW: bad operands")
+}
+
+// SARXL: Arithmetic Shift Right Without Affecting Flags.
+//
+// Forms:
+//
+// SARXL r32 r32 r32
+// SARXL r32 m32 r32
+func SARXL(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsR32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "SARXL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR32(r) && operand.IsM32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "SARXL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("SARXL: bad operands")
+}
+
+// SARXQ: Arithmetic Shift Right Without Affecting Flags.
+//
+// Forms:
+//
+// SARXQ r64 r64 r64
+// SARXQ r64 m64 r64
+func SARXQ(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsR64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "SARXQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR64(r) && operand.IsM64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "SARXQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("SARXQ: bad operands")
+}
+
+// SBBB: Subtract with Borrow.
+//
+// Forms:
+//
+// SBBB imm8 al
+// SBBB imm8 r8
+// SBBB r8 r8
+// SBBB m8 r8
+// SBBB imm8 m8
+// SBBB r8 m8
+func SBBB(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("SBBB: bad operands")
+}
+
+// SBBL: Subtract with Borrow.
+//
+// Forms:
+//
+// SBBL imm32 eax
+// SBBL imm8 r32
+// SBBL imm32 r32
+// SBBL r32 r32
+// SBBL m32 r32
+// SBBL imm8 m32
+// SBBL imm32 m32
+// SBBL r32 m32
+func SBBL(imr, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "SBBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ }
+ return nil, errors.New("SBBL: bad operands")
+}
+
+// SBBQ: Subtract with Borrow.
+//
+// Forms:
+//
+// SBBQ imm32 rax
+// SBBQ imm8 r64
+// SBBQ imm32 r64
+// SBBQ r64 r64
+// SBBQ m64 r64
+// SBBQ imm8 m64
+// SBBQ imm32 m64
+// SBBQ r64 m64
+func SBBQ(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SBBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SBBQ: bad operands")
+}
+
+// SBBW: Subtract with Borrow.
+//
+// Forms:
+//
+// SBBW imm16 ax
+// SBBW imm8 r16
+// SBBW imm16 r16
+// SBBW r16 r16
+// SBBW m16 r16
+// SBBW imm8 m16
+// SBBW imm16 m16
+// SBBW r16 m16
+func SBBW(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "SBBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("SBBW: bad operands")
+}
+
+// SETCC: Set byte if above or equal (CF == 0).
+//
+// Forms:
+//
+// SETCC r8
+// SETCC m8
+func SETCC(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETCC",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETCC",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETCC: bad operands")
+}
+
+// SETCS: Set byte if below (CF == 1).
+//
+// Forms:
+//
+// SETCS r8
+// SETCS m8
+func SETCS(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETCS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETCS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETCS: bad operands")
+}
+
+// SETEQ: Set byte if equal (ZF == 1).
+//
+// Forms:
+//
+// SETEQ r8
+// SETEQ m8
+func SETEQ(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETEQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETEQ",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETEQ: bad operands")
+}
+
+// SETGE: Set byte if greater or equal (SF == OF).
+//
+// Forms:
+//
+// SETGE r8
+// SETGE m8
+func SETGE(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETGE",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETGE",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETGE: bad operands")
+}
+
+// SETGT: Set byte if greater (ZF == 0 and SF == OF).
+//
+// Forms:
+//
+// SETGT r8
+// SETGT m8
+func SETGT(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETGT",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETGT",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETGT: bad operands")
+}
+
+// SETHI: Set byte if above (CF == 0 and ZF == 0).
+//
+// Forms:
+//
+// SETHI r8
+// SETHI m8
+func SETHI(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETHI",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETHI",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETHI: bad operands")
+}
+
+// SETLE: Set byte if less or equal (ZF == 1 or SF != OF).
+//
+// Forms:
+//
+// SETLE r8
+// SETLE m8
+func SETLE(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETLE",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETLE",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETLE: bad operands")
+}
+
+// SETLS: Set byte if below or equal (CF == 1 or ZF == 1).
+//
+// Forms:
+//
+// SETLS r8
+// SETLS m8
+func SETLS(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETLS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETLS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETLS: bad operands")
+}
+
+// SETLT: Set byte if less (SF != OF).
+//
+// Forms:
+//
+// SETLT r8
+// SETLT m8
+func SETLT(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETLT",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETLT",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETLT: bad operands")
+}
+
+// SETMI: Set byte if sign (SF == 1).
+//
+// Forms:
+//
+// SETMI r8
+// SETMI m8
+func SETMI(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETMI",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETMI",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETMI: bad operands")
+}
+
+// SETNE: Set byte if not equal (ZF == 0).
+//
+// Forms:
+//
+// SETNE r8
+// SETNE m8
+func SETNE(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETNE",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETNE",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETNE: bad operands")
+}
+
+// SETOC: Set byte if not overflow (OF == 0).
+//
+// Forms:
+//
+// SETOC r8
+// SETOC m8
+func SETOC(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETOC",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETOC",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETOC: bad operands")
+}
+
+// SETOS: Set byte if overflow (OF == 1).
+//
+// Forms:
+//
+// SETOS r8
+// SETOS m8
+func SETOS(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETOS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETOS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETOS: bad operands")
+}
+
+// SETPC: Set byte if not parity (PF == 0).
+//
+// Forms:
+//
+// SETPC r8
+// SETPC m8
+func SETPC(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETPC",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETPC",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETPC: bad operands")
+}
+
+// SETPL: Set byte if not sign (SF == 0).
+//
+// Forms:
+//
+// SETPL r8
+// SETPL m8
+func SETPL(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETPL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETPL",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETPL: bad operands")
+}
+
+// SETPS: Set byte if parity (PF == 1).
+//
+// Forms:
+//
+// SETPS r8
+// SETPS m8
+func SETPS(mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETPS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SETPS",
+ Operands: []operand.Op{mr},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SETPS: bad operands")
+}
+
+// SFENCE: Store Fence.
+//
+// Forms:
+//
+// SFENCE
+func SFENCE() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "SFENCE",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ ISA: []string{"MMX+"},
+ }, nil
+}
+
+// SHA1MSG1: Perform an Intermediate Calculation for the Next Four SHA1 Message Doublewords.
+//
+// Forms:
+//
+// SHA1MSG1 xmm xmm
+// SHA1MSG1 m128 xmm
+func SHA1MSG1(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1MSG1",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1MSG1",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ }
+ return nil, errors.New("SHA1MSG1: bad operands")
+}
+
+// SHA1MSG2: Perform a Final Calculation for the Next Four SHA1 Message Doublewords.
+//
+// Forms:
+//
+// SHA1MSG2 xmm xmm
+// SHA1MSG2 m128 xmm
+func SHA1MSG2(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1MSG2",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1MSG2",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ }
+ return nil, errors.New("SHA1MSG2: bad operands")
+}
+
+// SHA1NEXTE: Calculate SHA1 State Variable E after Four Rounds.
+//
+// Forms:
+//
+// SHA1NEXTE xmm xmm
+// SHA1NEXTE m128 xmm
+func SHA1NEXTE(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1NEXTE",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1NEXTE",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ }
+ return nil, errors.New("SHA1NEXTE: bad operands")
+}
+
+// SHA1RNDS4: Perform Four Rounds of SHA1 Operation.
+//
+// Forms:
+//
+// SHA1RNDS4 imm2u xmm xmm
+// SHA1RNDS4 imm2u m128 xmm
+func SHA1RNDS4(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM2U(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1RNDS4",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ case operand.IsIMM2U(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA1RNDS4",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ }
+ return nil, errors.New("SHA1RNDS4: bad operands")
+}
+
+// SHA256MSG1: Perform an Intermediate Calculation for the Next Four SHA256 Message Doublewords.
+//
+// Forms:
+//
+// SHA256MSG1 xmm xmm
+// SHA256MSG1 m128 xmm
+func SHA256MSG1(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA256MSG1",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA256MSG1",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ }
+ return nil, errors.New("SHA256MSG1: bad operands")
+}
+
+// SHA256MSG2: Perform a Final Calculation for the Next Four SHA256 Message Doublewords.
+//
+// Forms:
+//
+// SHA256MSG2 xmm xmm
+// SHA256MSG2 m128 xmm
+func SHA256MSG2(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA256MSG2",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHA256MSG2",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SHA"},
+ }, nil
+ }
+ return nil, errors.New("SHA256MSG2: bad operands")
+}
+
+// SHA256RNDS2: Perform Two Rounds of SHA256 Operation.
+//
+// Forms:
+//
+// SHA256RNDS2 xmm0 xmm xmm
+// SHA256RNDS2 xmm0 m128 xmm
+func SHA256RNDS2(x, mx, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM0(x) && operand.IsXMM(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "SHA256RNDS2",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SHA"},
+ }, nil
+ case operand.IsXMM0(x) && operand.IsM128(mx) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "SHA256RNDS2",
+ Operands: []operand.Op{x, mx, x1},
+ Inputs: []operand.Op{x, mx, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"SHA"},
+ }, nil
+ }
+ return nil, errors.New("SHA256RNDS2: bad operands")
+}
+
+// SHLB: Logical Shift Left.
+//
+// Forms:
+//
+// SHLB 1 r8
+// SHLB imm8 r8
+// SHLB cl r8
+// SHLB 1 m8
+// SHLB imm8 m8
+// SHLB cl m8
+func SHLB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHLB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SHLB: bad operands")
+}
+
+// SHLL: Logical Shift Left.
+//
+// Forms:
+//
+// SHLL 1 r32
+// SHLL imm8 r32
+// SHLL cl r32
+// SHLL 1 m32
+// SHLL imm8 m32
+// SHLL cl m32
+// SHLL imm8 r32 r32
+// SHLL cl r32 r32
+// SHLL imm8 r32 m32
+// SHLL cl r32 m32
+func SHLL(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsM32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsM32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsM32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR32(ops[1]) && operand.IsR32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR32(ops[1]) && operand.IsR32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR32(ops[1]) && operand.IsM32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR32(ops[1]) && operand.IsM32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ }
+ return nil, errors.New("SHLL: bad operands")
+}
+
+// SHLQ: Logical Shift Left.
+//
+// Forms:
+//
+// SHLQ 1 r64
+// SHLQ imm8 r64
+// SHLQ cl r64
+// SHLQ 1 m64
+// SHLQ imm8 m64
+// SHLQ cl m64
+// SHLQ imm8 r64 r64
+// SHLQ cl r64 r64
+// SHLQ imm8 r64 m64
+// SHLQ cl r64 m64
+func SHLQ(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR64(ops[1]) && operand.IsR64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR64(ops[1]) && operand.IsR64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR64(ops[1]) && operand.IsM64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR64(ops[1]) && operand.IsM64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ }
+ return nil, errors.New("SHLQ: bad operands")
+}
+
+// SHLW: Logical Shift Left.
+//
+// Forms:
+//
+// SHLW 1 r16
+// SHLW imm8 r16
+// SHLW cl r16
+// SHLW 1 m16
+// SHLW imm8 m16
+// SHLW cl m16
+// SHLW imm8 r16 r16
+// SHLW cl r16 r16
+// SHLW imm8 r16 m16
+// SHLW cl r16 m16
+func SHLW(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsM16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsM16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsM16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR16(ops[1]) && operand.IsR16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR16(ops[1]) && operand.IsR16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR16(ops[1]) && operand.IsM16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR16(ops[1]) && operand.IsM16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHLW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ }
+ return nil, errors.New("SHLW: bad operands")
+}
+
+// SHLXL: Logical Shift Left Without Affecting Flags.
+//
+// Forms:
+//
+// SHLXL r32 r32 r32
+// SHLXL r32 m32 r32
+func SHLXL(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsR32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "SHLXL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR32(r) && operand.IsM32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "SHLXL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("SHLXL: bad operands")
+}
+
+// SHLXQ: Logical Shift Left Without Affecting Flags.
+//
+// Forms:
+//
+// SHLXQ r64 r64 r64
+// SHLXQ r64 m64 r64
+func SHLXQ(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsR64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "SHLXQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR64(r) && operand.IsM64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "SHLXQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("SHLXQ: bad operands")
+}
+
+// SHRB: Logical Shift Right.
+//
+// Forms:
+//
+// SHRB 1 r8
+// SHRB imm8 r8
+// SHRB cl r8
+// SHRB 1 m8
+// SHRB imm8 m8
+// SHRB cl m8
+func SHRB(ci, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.Is1(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.Is1(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsCL(ci) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "SHRB",
+ Operands: []operand.Op{ci, mr},
+ Inputs: []operand.Op{ci, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SHRB: bad operands")
+}
+
+// SHRL: Logical Shift Right.
+//
+// Forms:
+//
+// SHRL 1 r32
+// SHRL imm8 r32
+// SHRL cl r32
+// SHRL 1 m32
+// SHRL imm8 m32
+// SHRL cl m32
+// SHRL imm8 r32 r32
+// SHRL cl r32 r32
+// SHRL imm8 r32 m32
+// SHRL cl r32 m32
+func SHRL(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsR32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsM32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsM32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsM32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR32(ops[1]) && operand.IsR32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR32(ops[1]) && operand.IsR32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR32(ops[1]) && operand.IsM32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR32(ops[1]) && operand.IsM32(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRL",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ }
+ return nil, errors.New("SHRL: bad operands")
+}
+
+// SHRQ: Logical Shift Right.
+//
+// Forms:
+//
+// SHRQ 1 r64
+// SHRQ imm8 r64
+// SHRQ cl r64
+// SHRQ 1 m64
+// SHRQ imm8 m64
+// SHRQ cl m64
+// SHRQ imm8 r64 r64
+// SHRQ cl r64 r64
+// SHRQ imm8 r64 m64
+// SHRQ cl r64 m64
+func SHRQ(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsR64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR64(ops[1]) && operand.IsR64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR64(ops[1]) && operand.IsR64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR64(ops[1]) && operand.IsM64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR64(ops[1]) && operand.IsM64(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRQ",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ }
+ return nil, errors.New("SHRQ: bad operands")
+}
+
+// SHRW: Logical Shift Right.
+//
+// Forms:
+//
+// SHRW 1 r16
+// SHRW imm8 r16
+// SHRW cl r16
+// SHRW 1 m16
+// SHRW imm8 m16
+// SHRW cl m16
+// SHRW imm8 r16 r16
+// SHRW cl r16 r16
+// SHRW imm8 r16 m16
+// SHRW cl r16 m16
+func SHRW(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsR16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.Is1(ops[0]) && operand.IsM16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsIMM8(ops[0]) && operand.IsM16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 2 && operand.IsCL(ops[0]) && operand.IsM16(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[1]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR16(ops[1]) && operand.IsR16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR16(ops[1]) && operand.IsR16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsIMM8(ops[0]) && operand.IsR16(ops[1]) && operand.IsM16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ case len(ops) == 3 && operand.IsCL(ops[0]) && operand.IsR16(ops[1]) && operand.IsM16(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "SHRW",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1], ops[2]},
+ Outputs: []operand.Op{ops[2]},
+ }, nil
+ }
+ return nil, errors.New("SHRW: bad operands")
+}
+
+// SHRXL: Logical Shift Right Without Affecting Flags.
+//
+// Forms:
+//
+// SHRXL r32 r32 r32
+// SHRXL r32 m32 r32
+func SHRXL(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsR32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "SHRXL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR32(r) && operand.IsM32(mr) && operand.IsR32(r1):
+ return &intrep.Instruction{
+ Opcode: "SHRXL",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("SHRXL: bad operands")
+}
+
+// SHRXQ: Logical Shift Right Without Affecting Flags.
+//
+// Forms:
+//
+// SHRXQ r64 r64 r64
+// SHRXQ r64 m64 r64
+func SHRXQ(r, mr, r1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsR64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "SHRXQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ case operand.IsR64(r) && operand.IsM64(mr) && operand.IsR64(r1):
+ return &intrep.Instruction{
+ Opcode: "SHRXQ",
+ Operands: []operand.Op{r, mr, r1},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r1},
+ ISA: []string{"BMI2"},
+ }, nil
+ }
+ return nil, errors.New("SHRXQ: bad operands")
+}
+
+// SHUFPD: Shuffle Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SHUFPD imm8 xmm xmm
+// SHUFPD imm8 m128 xmm
+func SHUFPD(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHUFPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHUFPD",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("SHUFPD: bad operands")
+}
+
+// SHUFPS: Shuffle Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SHUFPS imm8 xmm xmm
+// SHUFPS imm8 m128 xmm
+func SHUFPS(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHUFPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SHUFPS",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("SHUFPS: bad operands")
+}
+
+// SQRTPD: Compute Square Roots of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SQRTPD xmm xmm
+// SQRTPD m128 xmm
+func SQRTPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("SQRTPD: bad operands")
+}
+
+// SQRTPS: Compute Square Roots of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SQRTPS xmm xmm
+// SQRTPS m128 xmm
+func SQRTPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("SQRTPS: bad operands")
+}
+
+// SQRTSD: Compute Square Root of Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// SQRTSD xmm xmm
+// SQRTSD m64 xmm
+func SQRTSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("SQRTSD: bad operands")
+}
+
+// SQRTSS: Compute Square Root of Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// SQRTSS xmm xmm
+// SQRTSS m32 xmm
+func SQRTSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SQRTSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("SQRTSS: bad operands")
+}
+
+// STC: Set Carry Flag.
+//
+// Forms:
+//
+// STC
+func STC() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "STC",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// STD: Set Direction Flag.
+//
+// Forms:
+//
+// STD
+func STD() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "STD",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// STMXCSR: Store MXCSR Register State.
+//
+// Forms:
+//
+// STMXCSR m32
+func STMXCSR(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM32(m):
+ return &intrep.Instruction{
+ Opcode: "STMXCSR",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{m},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("STMXCSR: bad operands")
+}
+
+// SUBB: Subtract.
+//
+// Forms:
+//
+// SUBB imm8 al
+// SUBB imm8 r8
+// SUBB r8 r8
+// SUBB m8 r8
+// SUBB imm8 m8
+// SUBB r8 m8
+func SUBB(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("SUBB: bad operands")
+}
+
+// SUBL: Subtract.
+//
+// Forms:
+//
+// SUBL imm32 eax
+// SUBL imm8 r32
+// SUBL imm32 r32
+// SUBL r32 r32
+// SUBL m32 r32
+// SUBL imm8 m32
+// SUBL imm32 m32
+// SUBL r32 m32
+func SUBL(imr, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "SUBL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ }
+ return nil, errors.New("SUBL: bad operands")
+}
+
+// SUBPD: Subtract Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SUBPD xmm xmm
+// SUBPD m128 xmm
+func SUBPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("SUBPD: bad operands")
+}
+
+// SUBPS: Subtract Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SUBPS xmm xmm
+// SUBPS m128 xmm
+func SUBPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("SUBPS: bad operands")
+}
+
+// SUBQ: Subtract.
+//
+// Forms:
+//
+// SUBQ imm32 rax
+// SUBQ imm8 r64
+// SUBQ imm32 r64
+// SUBQ r64 r64
+// SUBQ m64 r64
+// SUBQ imm8 m64
+// SUBQ imm32 m64
+// SUBQ r64 m64
+func SUBQ(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "SUBQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("SUBQ: bad operands")
+}
+
+// SUBSD: Subtract Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SUBSD xmm xmm
+// SUBSD m64 xmm
+func SUBSD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBSD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("SUBSD: bad operands")
+}
+
+// SUBSS: Subtract Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// SUBSS xmm xmm
+// SUBSS m32 xmm
+func SUBSS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "SUBSS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("SUBSS: bad operands")
+}
+
+// SUBW: Subtract.
+//
+// Forms:
+//
+// SUBW imm16 ax
+// SUBW imm8 r16
+// SUBW imm16 r16
+// SUBW r16 r16
+// SUBW m16 r16
+// SUBW imm8 m16
+// SUBW imm16 m16
+// SUBW r16 m16
+func SUBW(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "SUBW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("SUBW: bad operands")
+}
+
+// SYSCALL: Fast System Call.
+//
+// Forms:
+//
+// SYSCALL
+func SYSCALL() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "SYSCALL",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{reg.R11, reg.RCX},
+ }, nil
+}
+
+// TESTB: Logical Compare.
+//
+// Forms:
+//
+// TESTB imm8 al
+// TESTB imm8 r8
+// TESTB r8 r8
+// TESTB imm8 m8
+// TESTB r8 m8
+func TESTB(ir, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(ir) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTB",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTB",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR8(ir) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTB",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{ir, amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM8(ir) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTB",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR8(ir) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTB",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{ir, amr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("TESTB: bad operands")
+}
+
+// TESTL: Logical Compare.
+//
+// Forms:
+//
+// TESTL imm32 eax
+// TESTL imm32 r32
+// TESTL r32 r32
+// TESTL imm32 m32
+// TESTL r32 m32
+func TESTL(ir, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(ir) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "TESTL",
+ Operands: []operand.Op{ir, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM32(ir) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "TESTL",
+ Operands: []operand.Op{ir, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR32(ir) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "TESTL",
+ Operands: []operand.Op{ir, emr},
+ Inputs: []operand.Op{ir, emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM32(ir) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "TESTL",
+ Operands: []operand.Op{ir, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR32(ir) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "TESTL",
+ Operands: []operand.Op{ir, emr},
+ Inputs: []operand.Op{ir, emr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("TESTL: bad operands")
+}
+
+// TESTQ: Logical Compare.
+//
+// Forms:
+//
+// TESTQ imm32 rax
+// TESTQ imm32 r64
+// TESTQ r64 r64
+// TESTQ imm32 m64
+// TESTQ r64 m64
+func TESTQ(ir, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(ir) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "TESTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM32(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "TESTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(ir) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "TESTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM32(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "TESTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR64(ir) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "TESTQ",
+ Operands: []operand.Op{ir, mr},
+ Inputs: []operand.Op{ir, mr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("TESTQ: bad operands")
+}
+
+// TESTW: Logical Compare.
+//
+// Forms:
+//
+// TESTW imm16 ax
+// TESTW imm16 r16
+// TESTW r16 r16
+// TESTW imm16 m16
+// TESTW r16 m16
+func TESTW(ir, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(ir) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTW",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM16(ir) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTW",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR16(ir) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTW",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{ir, amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsIMM16(ir) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTW",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{},
+ }, nil
+ case operand.IsR16(ir) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "TESTW",
+ Operands: []operand.Op{ir, amr},
+ Inputs: []operand.Op{ir, amr},
+ Outputs: []operand.Op{},
+ }, nil
+ }
+ return nil, errors.New("TESTW: bad operands")
+}
+
+// TZCNTL: Count the Number of Trailing Zero Bits.
+//
+// Forms:
+//
+// TZCNTL r32 r32
+// TZCNTL m32 r32
+func TZCNTL(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "TZCNTL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "TZCNTL",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("TZCNTL: bad operands")
+}
+
+// TZCNTQ: Count the Number of Trailing Zero Bits.
+//
+// Forms:
+//
+// TZCNTQ r64 r64
+// TZCNTQ m64 r64
+func TZCNTQ(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "TZCNTQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "TZCNTQ",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("TZCNTQ: bad operands")
+}
+
+// TZCNTW: Count the Number of Trailing Zero Bits.
+//
+// Forms:
+//
+// TZCNTW r16 r16
+// TZCNTW m16 r16
+func TZCNTW(mr, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "TZCNTW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ case operand.IsM16(mr) && operand.IsR16(r):
+ return &intrep.Instruction{
+ Opcode: "TZCNTW",
+ Operands: []operand.Op{mr, r},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{r},
+ ISA: []string{"BMI"},
+ }, nil
+ }
+ return nil, errors.New("TZCNTW: bad operands")
+}
+
+// UCOMISD: Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// UCOMISD xmm xmm
+// UCOMISD m64 xmm
+func UCOMISD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UCOMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UCOMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("UCOMISD: bad operands")
+}
+
+// UCOMISS: Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// UCOMISS xmm xmm
+// UCOMISS m32 xmm
+func UCOMISS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UCOMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UCOMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("UCOMISS: bad operands")
+}
+
+// UD2: Undefined Instruction.
+//
+// Forms:
+//
+// UD2
+func UD2() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "UD2",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ }, nil
+}
+
+// UNPCKHPD: Unpack and Interleave High Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// UNPCKHPD xmm xmm
+// UNPCKHPD m128 xmm
+func UNPCKHPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKHPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKHPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("UNPCKHPD: bad operands")
+}
+
+// UNPCKHPS: Unpack and Interleave High Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// UNPCKHPS xmm xmm
+// UNPCKHPS m128 xmm
+func UNPCKHPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKHPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKHPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("UNPCKHPS: bad operands")
+}
+
+// UNPCKLPD: Unpack and Interleave Low Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// UNPCKLPD xmm xmm
+// UNPCKLPD m128 xmm
+func UNPCKLPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKLPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKLPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("UNPCKLPD: bad operands")
+}
+
+// UNPCKLPS: Unpack and Interleave Low Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// UNPCKLPS xmm xmm
+// UNPCKLPS m128 xmm
+func UNPCKLPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKLPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "UNPCKLPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("UNPCKLPS: bad operands")
+}
+
+// VADDPD: Add Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VADDPD xmm xmm xmm
+// VADDPD m128 xmm xmm
+// VADDPD ymm ymm ymm
+// VADDPD m256 ymm ymm
+func VADDPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VADDPD: bad operands")
+}
+
+// VADDPS: Add Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VADDPS xmm xmm xmm
+// VADDPS m128 xmm xmm
+// VADDPS ymm ymm ymm
+// VADDPS m256 ymm ymm
+func VADDPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VADDPS: bad operands")
+}
+
+// VADDSD: Add Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VADDSD xmm xmm xmm
+// VADDSD m64 xmm xmm
+func VADDSD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VADDSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VADDSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VADDSD: bad operands")
+}
+
+// VADDSS: Add Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VADDSS xmm xmm xmm
+// VADDSS m32 xmm xmm
+func VADDSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VADDSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VADDSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VADDSS: bad operands")
+}
+
+// VADDSUBPD: Packed Double-FP Add/Subtract.
+//
+// Forms:
+//
+// VADDSUBPD xmm xmm xmm
+// VADDSUBPD m128 xmm xmm
+// VADDSUBPD ymm ymm ymm
+// VADDSUBPD m256 ymm ymm
+func VADDSUBPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VADDSUBPD: bad operands")
+}
+
+// VADDSUBPS: Packed Single-FP Add/Subtract.
+//
+// Forms:
+//
+// VADDSUBPS xmm xmm xmm
+// VADDSUBPS m128 xmm xmm
+// VADDSUBPS ymm ymm ymm
+// VADDSUBPS m256 ymm ymm
+func VADDSUBPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VADDSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VADDSUBPS: bad operands")
+}
+
+// VAESDEC: Perform One Round of an AES Decryption Flow.
+//
+// Forms:
+//
+// VAESDEC xmm xmm xmm
+// VAESDEC m128 xmm xmm
+func VAESDEC(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESDEC",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESDEC",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ }
+ return nil, errors.New("VAESDEC: bad operands")
+}
+
+// VAESDECLAST: Perform Last Round of an AES Decryption Flow.
+//
+// Forms:
+//
+// VAESDECLAST xmm xmm xmm
+// VAESDECLAST m128 xmm xmm
+func VAESDECLAST(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESDECLAST",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESDECLAST",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ }
+ return nil, errors.New("VAESDECLAST: bad operands")
+}
+
+// VAESENC: Perform One Round of an AES Encryption Flow.
+//
+// Forms:
+//
+// VAESENC xmm xmm xmm
+// VAESENC m128 xmm xmm
+func VAESENC(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESENC",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESENC",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ }
+ return nil, errors.New("VAESENC: bad operands")
+}
+
+// VAESENCLAST: Perform Last Round of an AES Encryption Flow.
+//
+// Forms:
+//
+// VAESENCLAST xmm xmm xmm
+// VAESENCLAST m128 xmm xmm
+func VAESENCLAST(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESENCLAST",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VAESENCLAST",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ }
+ return nil, errors.New("VAESENCLAST: bad operands")
+}
+
+// VAESIMC: Perform the AES InvMixColumn Transformation.
+//
+// Forms:
+//
+// VAESIMC xmm xmm
+// VAESIMC m128 xmm
+func VAESIMC(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VAESIMC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VAESIMC",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ }
+ return nil, errors.New("VAESIMC: bad operands")
+}
+
+// VAESKEYGENASSIST: AES Round Key Generation Assist.
+//
+// Forms:
+//
+// VAESKEYGENASSIST imm8 xmm xmm
+// VAESKEYGENASSIST imm8 m128 xmm
+func VAESKEYGENASSIST(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VAESKEYGENASSIST",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VAESKEYGENASSIST",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX", "AES"},
+ }, nil
+ }
+ return nil, errors.New("VAESKEYGENASSIST: bad operands")
+}
+
+// VANDNPD: Bitwise Logical AND NOT of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VANDNPD xmm xmm xmm
+// VANDNPD m128 xmm xmm
+// VANDNPD ymm ymm ymm
+// VANDNPD m256 ymm ymm
+func VANDNPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VANDNPD: bad operands")
+}
+
+// VANDNPS: Bitwise Logical AND NOT of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VANDNPS xmm xmm xmm
+// VANDNPS m128 xmm xmm
+// VANDNPS ymm ymm ymm
+// VANDNPS m256 ymm ymm
+func VANDNPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDNPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VANDNPS: bad operands")
+}
+
+// VANDPD: Bitwise Logical AND of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VANDPD xmm xmm xmm
+// VANDPD m128 xmm xmm
+// VANDPD ymm ymm ymm
+// VANDPD m256 ymm ymm
+func VANDPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VANDPD: bad operands")
+}
+
+// VANDPS: Bitwise Logical AND of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VANDPS xmm xmm xmm
+// VANDPS m128 xmm xmm
+// VANDPS ymm ymm ymm
+// VANDPS m256 ymm ymm
+func VANDPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VANDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VANDPS: bad operands")
+}
+
+// VBLENDPD: Blend Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// VBLENDPD imm8 xmm xmm xmm
+// VBLENDPD imm8 m128 xmm xmm
+// VBLENDPD imm8 ymm ymm ymm
+// VBLENDPD imm8 m256 ymm ymm
+func VBLENDPD(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VBLENDPD: bad operands")
+}
+
+// VBLENDPS: Blend Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// VBLENDPS imm8 xmm xmm xmm
+// VBLENDPS imm8 m128 xmm xmm
+// VBLENDPS imm8 ymm ymm ymm
+// VBLENDPS imm8 m256 ymm ymm
+func VBLENDPS(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VBLENDPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VBLENDPS: bad operands")
+}
+
+// VBLENDVPD: Variable Blend Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// VBLENDVPD xmm xmm xmm xmm
+// VBLENDVPD xmm m128 xmm xmm
+// VBLENDVPD ymm ymm ymm ymm
+// VBLENDVPD ymm m256 ymm ymm
+func VBLENDVPD(xy, mxy, xy1, xy2 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsXMM(mxy) && operand.IsXMM(xy1) && operand.IsXMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPD",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(xy) && operand.IsM128(mxy) && operand.IsXMM(xy1) && operand.IsXMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPD",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsYMM(mxy) && operand.IsYMM(xy1) && operand.IsYMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPD",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsM256(mxy) && operand.IsYMM(xy1) && operand.IsYMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPD",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VBLENDVPD: bad operands")
+}
+
+// VBLENDVPS: Variable Blend Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// VBLENDVPS xmm xmm xmm xmm
+// VBLENDVPS xmm m128 xmm xmm
+// VBLENDVPS ymm ymm ymm ymm
+// VBLENDVPS ymm m256 ymm ymm
+func VBLENDVPS(xy, mxy, xy1, xy2 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsXMM(mxy) && operand.IsXMM(xy1) && operand.IsXMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPS",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(xy) && operand.IsM128(mxy) && operand.IsXMM(xy1) && operand.IsXMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPS",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsYMM(mxy) && operand.IsYMM(xy1) && operand.IsYMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPS",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsM256(mxy) && operand.IsYMM(xy1) && operand.IsYMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VBLENDVPS",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VBLENDVPS: bad operands")
+}
+
+// VBROADCASTF128: Broadcast 128 Bit of Floating-Point Data.
+//
+// Forms:
+//
+// VBROADCASTF128 m128 ymm
+func VBROADCASTF128(m, y operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(m) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTF128",
+ Operands: []operand.Op{m, y},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VBROADCASTF128: bad operands")
+}
+
+// VBROADCASTI128: Broadcast 128 Bits of Integer Data.
+//
+// Forms:
+//
+// VBROADCASTI128 m128 ymm
+func VBROADCASTI128(m, y operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(m) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTI128",
+ Operands: []operand.Op{m, y},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VBROADCASTI128: bad operands")
+}
+
+// VBROADCASTSD: Broadcast Double-Precision Floating-Point Element.
+//
+// Forms:
+//
+// VBROADCASTSD xmm ymm
+// VBROADCASTSD m64 ymm
+func VBROADCASTSD(mx, y operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTSD",
+ Operands: []operand.Op{mx, y},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTSD",
+ Operands: []operand.Op{mx, y},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VBROADCASTSD: bad operands")
+}
+
+// VBROADCASTSS: Broadcast Single-Precision Floating-Point Element.
+//
+// Forms:
+//
+// VBROADCASTSS xmm xmm
+// VBROADCASTSS m32 xmm
+// VBROADCASTSS xmm ymm
+// VBROADCASTSS m32 ymm
+func VBROADCASTSS(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTSS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTSS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTSS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VBROADCASTSS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VBROADCASTSS: bad operands")
+}
+
+// VCMPPD: Compare Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VCMPPD imm8 xmm xmm xmm
+// VCMPPD imm8 m128 xmm xmm
+// VCMPPD imm8 ymm ymm ymm
+// VCMPPD imm8 m256 ymm ymm
+func VCMPPD(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCMPPD: bad operands")
+}
+
+// VCMPPS: Compare Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VCMPPS imm8 xmm xmm xmm
+// VCMPPS imm8 m128 xmm xmm
+// VCMPPS imm8 ymm ymm ymm
+// VCMPPS imm8 m256 ymm ymm
+func VCMPPS(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VCMPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCMPPS: bad operands")
+}
+
+// VCMPSD: Compare Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VCMPSD imm8 xmm xmm xmm
+// VCMPSD imm8 m64 xmm xmm
+func VCMPSD(i, mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCMPSD",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCMPSD",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCMPSD: bad operands")
+}
+
+// VCMPSS: Compare Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VCMPSS imm8 xmm xmm xmm
+// VCMPSS imm8 m32 xmm xmm
+func VCMPSS(i, mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCMPSS",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCMPSS",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCMPSS: bad operands")
+}
+
+// VCOMISD: Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// VCOMISD xmm xmm
+// VCOMISD m64 xmm
+func VCOMISD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCOMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCOMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCOMISD: bad operands")
+}
+
+// VCOMISS: Compare Scalar Ordered Single-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// VCOMISS xmm xmm
+// VCOMISS m32 xmm
+func VCOMISS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCOMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCOMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCOMISS: bad operands")
+}
+
+// VCVTDQ2PD: Convert Packed Dword Integers to Packed Double-Precision FP Values.
+//
+// Forms:
+//
+// VCVTDQ2PD xmm xmm
+// VCVTDQ2PD m64 xmm
+// VCVTDQ2PD xmm ymm
+// VCVTDQ2PD m128 ymm
+func VCVTDQ2PD(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTDQ2PD: bad operands")
+}
+
+// VCVTDQ2PS: Convert Packed Dword Integers to Packed Single-Precision FP Values.
+//
+// Forms:
+//
+// VCVTDQ2PS xmm xmm
+// VCVTDQ2PS m128 xmm
+// VCVTDQ2PS ymm ymm
+// VCVTDQ2PS m256 ymm
+func VCVTDQ2PS(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTDQ2PS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTDQ2PS: bad operands")
+}
+
+// VCVTPD2DQX: Convert Packed Double-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// VCVTPD2DQX xmm xmm
+// VCVTPD2DQX m128 xmm
+func VCVTPD2DQX(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2DQX",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2DQX",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPD2DQX: bad operands")
+}
+
+// VCVTPD2DQY: Convert Packed Double-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// VCVTPD2DQY ymm xmm
+// VCVTPD2DQY m256 xmm
+func VCVTPD2DQY(my, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsYMM(my) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2DQY",
+ Operands: []operand.Op{my, x},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(my) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2DQY",
+ Operands: []operand.Op{my, x},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPD2DQY: bad operands")
+}
+
+// VCVTPD2PSX: Convert Packed Double-Precision FP Values to Packed Single-Precision FP Values.
+//
+// Forms:
+//
+// VCVTPD2PSX xmm xmm
+// VCVTPD2PSX m128 xmm
+func VCVTPD2PSX(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2PSX",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2PSX",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPD2PSX: bad operands")
+}
+
+// VCVTPD2PSY: Convert Packed Double-Precision FP Values to Packed Single-Precision FP Values.
+//
+// Forms:
+//
+// VCVTPD2PSY ymm xmm
+// VCVTPD2PSY m256 xmm
+func VCVTPD2PSY(my, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsYMM(my) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2PSY",
+ Operands: []operand.Op{my, x},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(my) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTPD2PSY",
+ Operands: []operand.Op{my, x},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPD2PSY: bad operands")
+}
+
+// VCVTPH2PS: Convert Half-Precision FP Values to Single-Precision FP Values.
+//
+// Forms:
+//
+// VCVTPH2PS xmm xmm
+// VCVTPH2PS m64 xmm
+// VCVTPH2PS xmm ymm
+// VCVTPH2PS m128 ymm
+func VCVTPH2PS(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPH2PS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"F16C"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPH2PS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"F16C"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPH2PS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"F16C"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPH2PS",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"F16C"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPH2PS: bad operands")
+}
+
+// VCVTPS2DQ: Convert Packed Single-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// VCVTPS2DQ xmm xmm
+// VCVTPS2DQ m128 xmm
+// VCVTPS2DQ ymm ymm
+// VCVTPS2DQ m256 ymm
+func VCVTPS2DQ(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPS2DQ: bad operands")
+}
+
+// VCVTPS2PD: Convert Packed Single-Precision FP Values to Packed Double-Precision FP Values.
+//
+// Forms:
+//
+// VCVTPS2PD xmm xmm
+// VCVTPS2PD m64 xmm
+// VCVTPS2PD xmm ymm
+// VCVTPS2PD m128 ymm
+func VCVTPS2PD(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPS2PD: bad operands")
+}
+
+// VCVTPS2PH: Convert Single-Precision FP value to Half-Precision FP value.
+//
+// Forms:
+//
+// VCVTPS2PH imm8 xmm xmm
+// VCVTPS2PH imm8 ymm xmm
+// VCVTPS2PH imm8 xmm m64
+// VCVTPS2PH imm8 ymm m128
+func VCVTPS2PH(i, xy, mx operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(xy) && operand.IsXMM(mx):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PH",
+ Operands: []operand.Op{i, xy, mx},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"F16C"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(xy) && operand.IsXMM(mx):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PH",
+ Operands: []operand.Op{i, xy, mx},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"F16C"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(xy) && operand.IsM64(mx):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PH",
+ Operands: []operand.Op{i, xy, mx},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"F16C"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(xy) && operand.IsM128(mx):
+ return &intrep.Instruction{
+ Opcode: "VCVTPS2PH",
+ Operands: []operand.Op{i, xy, mx},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"F16C"},
+ }, nil
+ }
+ return nil, errors.New("VCVTPS2PH: bad operands")
+}
+
+// VCVTSD2SI: Convert Scalar Double-Precision FP Value to Integer.
+//
+// Forms:
+//
+// VCVTSD2SI xmm r32
+// VCVTSD2SI m64 r32
+func VCVTSD2SI(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSD2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSD2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSD2SI: bad operands")
+}
+
+// VCVTSD2SIQ: Convert Scalar Double-Precision FP Value to Integer.
+//
+// Forms:
+//
+// VCVTSD2SIQ xmm r64
+// VCVTSD2SIQ m64 r64
+func VCVTSD2SIQ(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSD2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSD2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSD2SIQ: bad operands")
+}
+
+// VCVTSD2SS: Convert Scalar Double-Precision FP Value to Scalar Single-Precision FP Value.
+//
+// Forms:
+//
+// VCVTSD2SS xmm xmm xmm
+// VCVTSD2SS m64 xmm xmm
+func VCVTSD2SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSD2SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSD2SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSD2SS: bad operands")
+}
+
+// VCVTSI2SDL: Convert Dword Integer to Scalar Double-Precision FP Value.
+//
+// Forms:
+//
+// VCVTSI2SDL r32 xmm xmm
+// VCVTSI2SDL m32 xmm xmm
+func VCVTSI2SDL(mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SDL",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SDL",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSI2SDL: bad operands")
+}
+
+// VCVTSI2SDQ: Convert Dword Integer to Scalar Double-Precision FP Value.
+//
+// Forms:
+//
+// VCVTSI2SDQ r64 xmm xmm
+// VCVTSI2SDQ m64 xmm xmm
+func VCVTSI2SDQ(mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SDQ",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SDQ",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSI2SDQ: bad operands")
+}
+
+// VCVTSI2SSL: Convert Dword Integer to Scalar Single-Precision FP Value.
+//
+// Forms:
+//
+// VCVTSI2SSL r32 xmm xmm
+// VCVTSI2SSL m32 xmm xmm
+func VCVTSI2SSL(mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SSL",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SSL",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSI2SSL: bad operands")
+}
+
+// VCVTSI2SSQ: Convert Dword Integer to Scalar Single-Precision FP Value.
+//
+// Forms:
+//
+// VCVTSI2SSQ r64 xmm xmm
+// VCVTSI2SSQ m64 xmm xmm
+func VCVTSI2SSQ(mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SSQ",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSI2SSQ",
+ Operands: []operand.Op{mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSI2SSQ: bad operands")
+}
+
+// VCVTSS2SD: Convert Scalar Single-Precision FP Value to Scalar Double-Precision FP Value.
+//
+// Forms:
+//
+// VCVTSS2SD xmm xmm xmm
+// VCVTSS2SD m32 xmm xmm
+func VCVTSS2SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSS2SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VCVTSS2SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSS2SD: bad operands")
+}
+
+// VCVTSS2SI: Convert Scalar Single-Precision FP Value to Dword Integer.
+//
+// Forms:
+//
+// VCVTSS2SI xmm r32
+// VCVTSS2SI m32 r32
+func VCVTSS2SI(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSS2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSS2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSS2SI: bad operands")
+}
+
+// VCVTSS2SIQ: Convert Scalar Single-Precision FP Value to Dword Integer.
+//
+// Forms:
+//
+// VCVTSS2SIQ xmm r64
+// VCVTSS2SIQ m32 r64
+func VCVTSS2SIQ(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSS2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTSS2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTSS2SIQ: bad operands")
+}
+
+// VCVTTPD2DQX: Convert with Truncation Packed Double-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// VCVTTPD2DQX xmm xmm
+// VCVTTPD2DQX m128 xmm
+func VCVTTPD2DQX(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPD2DQX",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPD2DQX",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTTPD2DQX: bad operands")
+}
+
+// VCVTTPD2DQY: Convert with Truncation Packed Double-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// VCVTTPD2DQY ymm xmm
+// VCVTTPD2DQY m256 xmm
+func VCVTTPD2DQY(my, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsYMM(my) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPD2DQY",
+ Operands: []operand.Op{my, x},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(my) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPD2DQY",
+ Operands: []operand.Op{my, x},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTTPD2DQY: bad operands")
+}
+
+// VCVTTPS2DQ: Convert with Truncation Packed Single-Precision FP Values to Packed Dword Integers.
+//
+// Forms:
+//
+// VCVTTPS2DQ xmm xmm
+// VCVTTPS2DQ m128 xmm
+// VCVTTPS2DQ ymm ymm
+// VCVTTPS2DQ m256 ymm
+func VCVTTPS2DQ(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VCVTTPS2DQ",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTTPS2DQ: bad operands")
+}
+
+// VCVTTSD2SI: Convert with Truncation Scalar Double-Precision FP Value to Signed Integer.
+//
+// Forms:
+//
+// VCVTTSD2SI xmm r32
+// VCVTTSD2SI m64 r32
+func VCVTTSD2SI(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSD2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSD2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTTSD2SI: bad operands")
+}
+
+// VCVTTSD2SIQ: Convert with Truncation Scalar Double-Precision FP Value to Signed Integer.
+//
+// Forms:
+//
+// VCVTTSD2SIQ xmm r64
+// VCVTTSD2SIQ m64 r64
+func VCVTTSD2SIQ(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSD2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSD2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTTSD2SIQ: bad operands")
+}
+
+// VCVTTSS2SI: Convert with Truncation Scalar Single-Precision FP Value to Dword Integer.
+//
+// Forms:
+//
+// VCVTTSS2SI xmm r32
+// VCVTTSS2SI m32 r32
+func VCVTTSS2SI(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSS2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSS2SI",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTTSS2SI: bad operands")
+}
+
+// VCVTTSS2SIQ: Convert with Truncation Scalar Single-Precision FP Value to Dword Integer.
+//
+// Forms:
+//
+// VCVTTSS2SIQ xmm r64
+// VCVTTSS2SIQ m32 r64
+func VCVTTSS2SIQ(mx, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSS2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsR64(r):
+ return &intrep.Instruction{
+ Opcode: "VCVTTSS2SIQ",
+ Operands: []operand.Op{mx, r},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VCVTTSS2SIQ: bad operands")
+}
+
+// VDIVPD: Divide Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VDIVPD xmm xmm xmm
+// VDIVPD m128 xmm xmm
+// VDIVPD ymm ymm ymm
+// VDIVPD m256 ymm ymm
+func VDIVPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VDIVPD: bad operands")
+}
+
+// VDIVPS: Divide Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VDIVPS xmm xmm xmm
+// VDIVPS m128 xmm xmm
+// VDIVPS ymm ymm ymm
+// VDIVPS m256 ymm ymm
+func VDIVPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDIVPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VDIVPS: bad operands")
+}
+
+// VDIVSD: Divide Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VDIVSD xmm xmm xmm
+// VDIVSD m64 xmm xmm
+func VDIVSD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VDIVSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VDIVSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VDIVSD: bad operands")
+}
+
+// VDIVSS: Divide Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VDIVSS xmm xmm xmm
+// VDIVSS m32 xmm xmm
+func VDIVSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VDIVSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VDIVSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VDIVSS: bad operands")
+}
+
+// VDPPD: Dot Product of Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// VDPPD imm8 xmm xmm xmm
+// VDPPD imm8 m128 xmm xmm
+func VDPPD(i, mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VDPPD",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VDPPD",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VDPPD: bad operands")
+}
+
+// VDPPS: Dot Product of Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// VDPPS imm8 xmm xmm xmm
+// VDPPS imm8 m128 xmm xmm
+// VDPPS imm8 ymm ymm ymm
+// VDPPS imm8 m256 ymm ymm
+func VDPPS(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VDPPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VDPPS: bad operands")
+}
+
+// VEXTRACTF128: Extract Packed Floating-Point Values.
+//
+// Forms:
+//
+// VEXTRACTF128 imm8 ymm xmm
+// VEXTRACTF128 imm8 ymm m128
+func VEXTRACTF128(i, y, mx operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsYMM(y) && operand.IsXMM(mx):
+ return &intrep.Instruction{
+ Opcode: "VEXTRACTF128",
+ Operands: []operand.Op{i, y, mx},
+ Inputs: []operand.Op{y},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(y) && operand.IsM128(mx):
+ return &intrep.Instruction{
+ Opcode: "VEXTRACTF128",
+ Operands: []operand.Op{i, y, mx},
+ Inputs: []operand.Op{y},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VEXTRACTF128: bad operands")
+}
+
+// VEXTRACTI128: Extract Packed Integer Values.
+//
+// Forms:
+//
+// VEXTRACTI128 imm8 ymm xmm
+// VEXTRACTI128 imm8 ymm m128
+func VEXTRACTI128(i, y, mx operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsYMM(y) && operand.IsXMM(mx):
+ return &intrep.Instruction{
+ Opcode: "VEXTRACTI128",
+ Operands: []operand.Op{i, y, mx},
+ Inputs: []operand.Op{y},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(y) && operand.IsM128(mx):
+ return &intrep.Instruction{
+ Opcode: "VEXTRACTI128",
+ Operands: []operand.Op{i, y, mx},
+ Inputs: []operand.Op{y},
+ Outputs: []operand.Op{mx},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VEXTRACTI128: bad operands")
+}
+
+// VEXTRACTPS: Extract Packed Single Precision Floating-Point Value.
+//
+// Forms:
+//
+// VEXTRACTPS imm8 xmm r32
+// VEXTRACTPS imm8 xmm m32
+func VEXTRACTPS(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "VEXTRACTPS",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "VEXTRACTPS",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VEXTRACTPS: bad operands")
+}
+
+// VFMADD132PD: Fused Multiply-Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD132PD xmm xmm xmm
+// VFMADD132PD m128 xmm xmm
+// VFMADD132PD ymm ymm ymm
+// VFMADD132PD m256 ymm ymm
+func VFMADD132PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD132PD: bad operands")
+}
+
+// VFMADD132PS: Fused Multiply-Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD132PS xmm xmm xmm
+// VFMADD132PS m128 xmm xmm
+// VFMADD132PS ymm ymm ymm
+// VFMADD132PS m256 ymm ymm
+func VFMADD132PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD132PS: bad operands")
+}
+
+// VFMADD132SD: Fused Multiply-Add of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD132SD xmm xmm xmm
+// VFMADD132SD m64 xmm xmm
+func VFMADD132SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD132SD: bad operands")
+}
+
+// VFMADD132SS: Fused Multiply-Add of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD132SS xmm xmm xmm
+// VFMADD132SS m32 xmm xmm
+func VFMADD132SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD132SS: bad operands")
+}
+
+// VFMADD213PD: Fused Multiply-Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD213PD xmm xmm xmm
+// VFMADD213PD m128 xmm xmm
+// VFMADD213PD ymm ymm ymm
+// VFMADD213PD m256 ymm ymm
+func VFMADD213PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD213PD: bad operands")
+}
+
+// VFMADD213PS: Fused Multiply-Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD213PS xmm xmm xmm
+// VFMADD213PS m128 xmm xmm
+// VFMADD213PS ymm ymm ymm
+// VFMADD213PS m256 ymm ymm
+func VFMADD213PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD213PS: bad operands")
+}
+
+// VFMADD213SD: Fused Multiply-Add of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD213SD xmm xmm xmm
+// VFMADD213SD m64 xmm xmm
+func VFMADD213SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD213SD: bad operands")
+}
+
+// VFMADD213SS: Fused Multiply-Add of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD213SS xmm xmm xmm
+// VFMADD213SS m32 xmm xmm
+func VFMADD213SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD213SS: bad operands")
+}
+
+// VFMADD231PD: Fused Multiply-Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD231PD xmm xmm xmm
+// VFMADD231PD m128 xmm xmm
+// VFMADD231PD ymm ymm ymm
+// VFMADD231PD m256 ymm ymm
+func VFMADD231PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD231PD: bad operands")
+}
+
+// VFMADD231PS: Fused Multiply-Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD231PS xmm xmm xmm
+// VFMADD231PS m128 xmm xmm
+// VFMADD231PS ymm ymm ymm
+// VFMADD231PS m256 ymm ymm
+func VFMADD231PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD231PS: bad operands")
+}
+
+// VFMADD231SD: Fused Multiply-Add of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD231SD xmm xmm xmm
+// VFMADD231SD m64 xmm xmm
+func VFMADD231SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD231SD: bad operands")
+}
+
+// VFMADD231SS: Fused Multiply-Add of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADD231SS xmm xmm xmm
+// VFMADD231SS m32 xmm xmm
+func VFMADD231SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMADD231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADD231SS: bad operands")
+}
+
+// VFMADDSUB132PD: Fused Multiply-Alternating Add/Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADDSUB132PD xmm xmm xmm
+// VFMADDSUB132PD m128 xmm xmm
+// VFMADDSUB132PD ymm ymm ymm
+// VFMADDSUB132PD m256 ymm ymm
+func VFMADDSUB132PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADDSUB132PD: bad operands")
+}
+
+// VFMADDSUB132PS: Fused Multiply-Alternating Add/Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADDSUB132PS xmm xmm xmm
+// VFMADDSUB132PS m128 xmm xmm
+// VFMADDSUB132PS ymm ymm ymm
+// VFMADDSUB132PS m256 ymm ymm
+func VFMADDSUB132PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADDSUB132PS: bad operands")
+}
+
+// VFMADDSUB213PD: Fused Multiply-Alternating Add/Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADDSUB213PD xmm xmm xmm
+// VFMADDSUB213PD m128 xmm xmm
+// VFMADDSUB213PD ymm ymm ymm
+// VFMADDSUB213PD m256 ymm ymm
+func VFMADDSUB213PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADDSUB213PD: bad operands")
+}
+
+// VFMADDSUB213PS: Fused Multiply-Alternating Add/Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADDSUB213PS xmm xmm xmm
+// VFMADDSUB213PS m128 xmm xmm
+// VFMADDSUB213PS ymm ymm ymm
+// VFMADDSUB213PS m256 ymm ymm
+func VFMADDSUB213PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADDSUB213PS: bad operands")
+}
+
+// VFMADDSUB231PD: Fused Multiply-Alternating Add/Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADDSUB231PD xmm xmm xmm
+// VFMADDSUB231PD m128 xmm xmm
+// VFMADDSUB231PD ymm ymm ymm
+// VFMADDSUB231PD m256 ymm ymm
+func VFMADDSUB231PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADDSUB231PD: bad operands")
+}
+
+// VFMADDSUB231PS: Fused Multiply-Alternating Add/Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMADDSUB231PS xmm xmm xmm
+// VFMADDSUB231PS m128 xmm xmm
+// VFMADDSUB231PS ymm ymm ymm
+// VFMADDSUB231PS m256 ymm ymm
+func VFMADDSUB231PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMADDSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMADDSUB231PS: bad operands")
+}
+
+// VFMSUB132PD: Fused Multiply-Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB132PD xmm xmm xmm
+// VFMSUB132PD m128 xmm xmm
+// VFMSUB132PD ymm ymm ymm
+// VFMSUB132PD m256 ymm ymm
+func VFMSUB132PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB132PD: bad operands")
+}
+
+// VFMSUB132PS: Fused Multiply-Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB132PS xmm xmm xmm
+// VFMSUB132PS m128 xmm xmm
+// VFMSUB132PS ymm ymm ymm
+// VFMSUB132PS m256 ymm ymm
+func VFMSUB132PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB132PS: bad operands")
+}
+
+// VFMSUB132SD: Fused Multiply-Subtract of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB132SD xmm xmm xmm
+// VFMSUB132SD m64 xmm xmm
+func VFMSUB132SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB132SD: bad operands")
+}
+
+// VFMSUB132SS: Fused Multiply-Subtract of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB132SS xmm xmm xmm
+// VFMSUB132SS m32 xmm xmm
+func VFMSUB132SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB132SS: bad operands")
+}
+
+// VFMSUB213PD: Fused Multiply-Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB213PD xmm xmm xmm
+// VFMSUB213PD m128 xmm xmm
+// VFMSUB213PD ymm ymm ymm
+// VFMSUB213PD m256 ymm ymm
+func VFMSUB213PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB213PD: bad operands")
+}
+
+// VFMSUB213PS: Fused Multiply-Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB213PS xmm xmm xmm
+// VFMSUB213PS m128 xmm xmm
+// VFMSUB213PS ymm ymm ymm
+// VFMSUB213PS m256 ymm ymm
+func VFMSUB213PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB213PS: bad operands")
+}
+
+// VFMSUB213SD: Fused Multiply-Subtract of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB213SD xmm xmm xmm
+// VFMSUB213SD m64 xmm xmm
+func VFMSUB213SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB213SD: bad operands")
+}
+
+// VFMSUB213SS: Fused Multiply-Subtract of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB213SS xmm xmm xmm
+// VFMSUB213SS m32 xmm xmm
+func VFMSUB213SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB213SS: bad operands")
+}
+
+// VFMSUB231PD: Fused Multiply-Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB231PD xmm xmm xmm
+// VFMSUB231PD m128 xmm xmm
+// VFMSUB231PD ymm ymm ymm
+// VFMSUB231PD m256 ymm ymm
+func VFMSUB231PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB231PD: bad operands")
+}
+
+// VFMSUB231PS: Fused Multiply-Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB231PS xmm xmm xmm
+// VFMSUB231PS m128 xmm xmm
+// VFMSUB231PS ymm ymm ymm
+// VFMSUB231PS m256 ymm ymm
+func VFMSUB231PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB231PS: bad operands")
+}
+
+// VFMSUB231SD: Fused Multiply-Subtract of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB231SD xmm xmm xmm
+// VFMSUB231SD m64 xmm xmm
+func VFMSUB231SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB231SD: bad operands")
+}
+
+// VFMSUB231SS: Fused Multiply-Subtract of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUB231SS xmm xmm xmm
+// VFMSUB231SS m32 xmm xmm
+func VFMSUB231SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUB231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUB231SS: bad operands")
+}
+
+// VFMSUBADD132PD: Fused Multiply-Alternating Subtract/Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUBADD132PD xmm xmm xmm
+// VFMSUBADD132PD m128 xmm xmm
+// VFMSUBADD132PD ymm ymm ymm
+// VFMSUBADD132PD m256 ymm ymm
+func VFMSUBADD132PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUBADD132PD: bad operands")
+}
+
+// VFMSUBADD132PS: Fused Multiply-Alternating Subtract/Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUBADD132PS xmm xmm xmm
+// VFMSUBADD132PS m128 xmm xmm
+// VFMSUBADD132PS ymm ymm ymm
+// VFMSUBADD132PS m256 ymm ymm
+func VFMSUBADD132PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUBADD132PS: bad operands")
+}
+
+// VFMSUBADD213PD: Fused Multiply-Alternating Subtract/Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUBADD213PD xmm xmm xmm
+// VFMSUBADD213PD m128 xmm xmm
+// VFMSUBADD213PD ymm ymm ymm
+// VFMSUBADD213PD m256 ymm ymm
+func VFMSUBADD213PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUBADD213PD: bad operands")
+}
+
+// VFMSUBADD213PS: Fused Multiply-Alternating Subtract/Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUBADD213PS xmm xmm xmm
+// VFMSUBADD213PS m128 xmm xmm
+// VFMSUBADD213PS ymm ymm ymm
+// VFMSUBADD213PS m256 ymm ymm
+func VFMSUBADD213PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUBADD213PS: bad operands")
+}
+
+// VFMSUBADD231PD: Fused Multiply-Alternating Subtract/Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUBADD231PD xmm xmm xmm
+// VFMSUBADD231PD m128 xmm xmm
+// VFMSUBADD231PD ymm ymm ymm
+// VFMSUBADD231PD m256 ymm ymm
+func VFMSUBADD231PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUBADD231PD: bad operands")
+}
+
+// VFMSUBADD231PS: Fused Multiply-Alternating Subtract/Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFMSUBADD231PS xmm xmm xmm
+// VFMSUBADD231PS m128 xmm xmm
+// VFMSUBADD231PS ymm ymm ymm
+// VFMSUBADD231PS m256 ymm ymm
+func VFMSUBADD231PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFMSUBADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFMSUBADD231PS: bad operands")
+}
+
+// VFNMADD132PD: Fused Negative Multiply-Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD132PD xmm xmm xmm
+// VFNMADD132PD m128 xmm xmm
+// VFNMADD132PD ymm ymm ymm
+// VFNMADD132PD m256 ymm ymm
+func VFNMADD132PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD132PD: bad operands")
+}
+
+// VFNMADD132PS: Fused Negative Multiply-Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD132PS xmm xmm xmm
+// VFNMADD132PS m128 xmm xmm
+// VFNMADD132PS ymm ymm ymm
+// VFNMADD132PS m256 ymm ymm
+func VFNMADD132PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD132PS: bad operands")
+}
+
+// VFNMADD132SD: Fused Negative Multiply-Add of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD132SD xmm xmm xmm
+// VFNMADD132SD m64 xmm xmm
+func VFNMADD132SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD132SD: bad operands")
+}
+
+// VFNMADD132SS: Fused Negative Multiply-Add of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD132SS xmm xmm xmm
+// VFNMADD132SS m32 xmm xmm
+func VFNMADD132SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD132SS: bad operands")
+}
+
+// VFNMADD213PD: Fused Negative Multiply-Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD213PD xmm xmm xmm
+// VFNMADD213PD m128 xmm xmm
+// VFNMADD213PD ymm ymm ymm
+// VFNMADD213PD m256 ymm ymm
+func VFNMADD213PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD213PD: bad operands")
+}
+
+// VFNMADD213PS: Fused Negative Multiply-Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD213PS xmm xmm xmm
+// VFNMADD213PS m128 xmm xmm
+// VFNMADD213PS ymm ymm ymm
+// VFNMADD213PS m256 ymm ymm
+func VFNMADD213PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD213PS: bad operands")
+}
+
+// VFNMADD213SD: Fused Negative Multiply-Add of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD213SD xmm xmm xmm
+// VFNMADD213SD m64 xmm xmm
+func VFNMADD213SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD213SD: bad operands")
+}
+
+// VFNMADD213SS: Fused Negative Multiply-Add of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD213SS xmm xmm xmm
+// VFNMADD213SS m32 xmm xmm
+func VFNMADD213SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD213SS: bad operands")
+}
+
+// VFNMADD231PD: Fused Negative Multiply-Add of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD231PD xmm xmm xmm
+// VFNMADD231PD m128 xmm xmm
+// VFNMADD231PD ymm ymm ymm
+// VFNMADD231PD m256 ymm ymm
+func VFNMADD231PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD231PD: bad operands")
+}
+
+// VFNMADD231PS: Fused Negative Multiply-Add of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD231PS xmm xmm xmm
+// VFNMADD231PS m128 xmm xmm
+// VFNMADD231PS ymm ymm ymm
+// VFNMADD231PS m256 ymm ymm
+func VFNMADD231PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD231PS: bad operands")
+}
+
+// VFNMADD231SD: Fused Negative Multiply-Add of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD231SD xmm xmm xmm
+// VFNMADD231SD m64 xmm xmm
+func VFNMADD231SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD231SD: bad operands")
+}
+
+// VFNMADD231SS: Fused Negative Multiply-Add of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMADD231SS xmm xmm xmm
+// VFNMADD231SS m32 xmm xmm
+func VFNMADD231SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMADD231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMADD231SS: bad operands")
+}
+
+// VFNMSUB132PD: Fused Negative Multiply-Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB132PD xmm xmm xmm
+// VFNMSUB132PD m128 xmm xmm
+// VFNMSUB132PD ymm ymm ymm
+// VFNMSUB132PD m256 ymm ymm
+func VFNMSUB132PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB132PD: bad operands")
+}
+
+// VFNMSUB132PS: Fused Negative Multiply-Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB132PS xmm xmm xmm
+// VFNMSUB132PS m128 xmm xmm
+// VFNMSUB132PS ymm ymm ymm
+// VFNMSUB132PS m256 ymm ymm
+func VFNMSUB132PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB132PS: bad operands")
+}
+
+// VFNMSUB132SD: Fused Negative Multiply-Subtract of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB132SD xmm xmm xmm
+// VFNMSUB132SD m64 xmm xmm
+func VFNMSUB132SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB132SD: bad operands")
+}
+
+// VFNMSUB132SS: Fused Negative Multiply-Subtract of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB132SS xmm xmm xmm
+// VFNMSUB132SS m32 xmm xmm
+func VFNMSUB132SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB132SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB132SS: bad operands")
+}
+
+// VFNMSUB213PD: Fused Negative Multiply-Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB213PD xmm xmm xmm
+// VFNMSUB213PD m128 xmm xmm
+// VFNMSUB213PD ymm ymm ymm
+// VFNMSUB213PD m256 ymm ymm
+func VFNMSUB213PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB213PD: bad operands")
+}
+
+// VFNMSUB213PS: Fused Negative Multiply-Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB213PS xmm xmm xmm
+// VFNMSUB213PS m128 xmm xmm
+// VFNMSUB213PS ymm ymm ymm
+// VFNMSUB213PS m256 ymm ymm
+func VFNMSUB213PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB213PS: bad operands")
+}
+
+// VFNMSUB213SD: Fused Negative Multiply-Subtract of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB213SD xmm xmm xmm
+// VFNMSUB213SD m64 xmm xmm
+func VFNMSUB213SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB213SD: bad operands")
+}
+
+// VFNMSUB213SS: Fused Negative Multiply-Subtract of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB213SS xmm xmm xmm
+// VFNMSUB213SS m32 xmm xmm
+func VFNMSUB213SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB213SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB213SS: bad operands")
+}
+
+// VFNMSUB231PD: Fused Negative Multiply-Subtract of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB231PD xmm xmm xmm
+// VFNMSUB231PD m128 xmm xmm
+// VFNMSUB231PD ymm ymm ymm
+// VFNMSUB231PD m256 ymm ymm
+func VFNMSUB231PD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB231PD: bad operands")
+}
+
+// VFNMSUB231PS: Fused Negative Multiply-Subtract of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB231PS xmm xmm xmm
+// VFNMSUB231PS m128 xmm xmm
+// VFNMSUB231PS ymm ymm ymm
+// VFNMSUB231PS m256 ymm ymm
+func VFNMSUB231PS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231PS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy, xy1},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB231PS: bad operands")
+}
+
+// VFNMSUB231SD: Fused Negative Multiply-Subtract of Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB231SD xmm xmm xmm
+// VFNMSUB231SD m64 xmm xmm
+func VFNMSUB231SD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231SD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB231SD: bad operands")
+}
+
+// VFNMSUB231SS: Fused Negative Multiply-Subtract of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VFNMSUB231SS xmm xmm xmm
+// VFNMSUB231SS m32 xmm xmm
+func VFNMSUB231SS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VFNMSUB231SS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x, x1},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"FMA3"},
+ }, nil
+ }
+ return nil, errors.New("VFNMSUB231SS: bad operands")
+}
+
+// VGATHERDPD: Gather Packed Double-Precision Floating-Point Values Using Signed Doubleword Indices.
+//
+// Forms:
+//
+// VGATHERDPD xmm vm32x xmm
+// VGATHERDPD ymm vm32x ymm
+func VGATHERDPD(xy, v, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsVM32X(v) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERDPD",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsVM32X(v) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERDPD",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VGATHERDPD: bad operands")
+}
+
+// VGATHERDPS: Gather Packed Single-Precision Floating-Point Values Using Signed Doubleword Indices.
+//
+// Forms:
+//
+// VGATHERDPS xmm vm32x xmm
+// VGATHERDPS ymm vm32y ymm
+func VGATHERDPS(xy, v, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsVM32X(v) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERDPS",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsVM32Y(v) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERDPS",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VGATHERDPS: bad operands")
+}
+
+// VGATHERQPD: Gather Packed Double-Precision Floating-Point Values Using Signed Quadword Indices.
+//
+// Forms:
+//
+// VGATHERQPD xmm vm64x xmm
+// VGATHERQPD ymm vm64y ymm
+func VGATHERQPD(xy, v, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsVM64X(v) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERQPD",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsVM64Y(v) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERQPD",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VGATHERQPD: bad operands")
+}
+
+// VGATHERQPS: Gather Packed Single-Precision Floating-Point Values Using Signed Quadword Indices.
+//
+// Forms:
+//
+// VGATHERQPS xmm vm64x xmm
+// VGATHERQPS xmm vm64y xmm
+func VGATHERQPS(x, v, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsVM64X(v) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERQPS",
+ Operands: []operand.Op{x, v, x1},
+ Inputs: []operand.Op{x, v, x1},
+ Outputs: []operand.Op{x, x1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(x) && operand.IsVM64Y(v) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VGATHERQPS",
+ Operands: []operand.Op{x, v, x1},
+ Inputs: []operand.Op{x, v, x1},
+ Outputs: []operand.Op{x, x1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VGATHERQPS: bad operands")
+}
+
+// VHADDPD: Packed Double-FP Horizontal Add.
+//
+// Forms:
+//
+// VHADDPD xmm xmm xmm
+// VHADDPD m128 xmm xmm
+// VHADDPD ymm ymm ymm
+// VHADDPD m256 ymm ymm
+func VHADDPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VHADDPD: bad operands")
+}
+
+// VHADDPS: Packed Single-FP Horizontal Add.
+//
+// Forms:
+//
+// VHADDPS xmm xmm xmm
+// VHADDPS m128 xmm xmm
+// VHADDPS ymm ymm ymm
+// VHADDPS m256 ymm ymm
+func VHADDPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHADDPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VHADDPS: bad operands")
+}
+
+// VHSUBPD: Packed Double-FP Horizontal Subtract.
+//
+// Forms:
+//
+// VHSUBPD xmm xmm xmm
+// VHSUBPD m128 xmm xmm
+// VHSUBPD ymm ymm ymm
+// VHSUBPD m256 ymm ymm
+func VHSUBPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VHSUBPD: bad operands")
+}
+
+// VHSUBPS: Packed Single-FP Horizontal Subtract.
+//
+// Forms:
+//
+// VHSUBPS xmm xmm xmm
+// VHSUBPS m128 xmm xmm
+// VHSUBPS ymm ymm ymm
+// VHSUBPS m256 ymm ymm
+func VHSUBPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VHSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VHSUBPS: bad operands")
+}
+
+// VINSERTF128: Insert Packed Floating-Point Values.
+//
+// Forms:
+//
+// VINSERTF128 imm8 xmm ymm ymm
+// VINSERTF128 imm8 m128 ymm ymm
+func VINSERTF128(i, mx, y, y1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VINSERTF128",
+ Operands: []operand.Op{i, mx, y, y1},
+ Inputs: []operand.Op{mx, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VINSERTF128",
+ Operands: []operand.Op{i, mx, y, y1},
+ Inputs: []operand.Op{mx, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VINSERTF128: bad operands")
+}
+
+// VINSERTI128: Insert Packed Integer Values.
+//
+// Forms:
+//
+// VINSERTI128 imm8 xmm ymm ymm
+// VINSERTI128 imm8 m128 ymm ymm
+func VINSERTI128(i, mx, y, y1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VINSERTI128",
+ Operands: []operand.Op{i, mx, y, y1},
+ Inputs: []operand.Op{mx, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VINSERTI128",
+ Operands: []operand.Op{i, mx, y, y1},
+ Inputs: []operand.Op{mx, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VINSERTI128: bad operands")
+}
+
+// VINSERTPS: Insert Packed Single Precision Floating-Point Value.
+//
+// Forms:
+//
+// VINSERTPS imm8 xmm xmm xmm
+// VINSERTPS imm8 m32 xmm xmm
+func VINSERTPS(i, mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VINSERTPS",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VINSERTPS",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VINSERTPS: bad operands")
+}
+
+// VLDDQU: Load Unaligned Integer 128 Bits.
+//
+// Forms:
+//
+// VLDDQU m128 xmm
+// VLDDQU m256 ymm
+func VLDDQU(m, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(m) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VLDDQU",
+ Operands: []operand.Op{m, xy},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(m) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VLDDQU",
+ Operands: []operand.Op{m, xy},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VLDDQU: bad operands")
+}
+
+// VLDMXCSR: Load MXCSR Register.
+//
+// Forms:
+//
+// VLDMXCSR m32
+func VLDMXCSR(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM32(m):
+ return &intrep.Instruction{
+ Opcode: "VLDMXCSR",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VLDMXCSR: bad operands")
+}
+
+// VMASKMOVDQU: Store Selected Bytes of Double Quadword.
+//
+// Forms:
+//
+// VMASKMOVDQU xmm xmm
+func VMASKMOVDQU(x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVDQU",
+ Operands: []operand.Op{x, x1},
+ Inputs: []operand.Op{x, x1, reg.RDI},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMASKMOVDQU: bad operands")
+}
+
+// VMASKMOVPD: Conditional Move Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMASKMOVPD m128 xmm xmm
+// VMASKMOVPD m256 ymm ymm
+// VMASKMOVPD xmm xmm m128
+// VMASKMOVPD ymm ymm m256
+func VMASKMOVPD(mxy, xy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMASKMOVPD: bad operands")
+}
+
+// VMASKMOVPS: Conditional Move Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMASKMOVPS m128 xmm xmm
+// VMASKMOVPS m256 ymm ymm
+// VMASKMOVPS xmm xmm m128
+// VMASKMOVPS ymm ymm m256
+func VMASKMOVPS(mxy, xy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPS",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPS",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPS",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMASKMOVPS",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMASKMOVPS: bad operands")
+}
+
+// VMAXPD: Return Maximum Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMAXPD xmm xmm xmm
+// VMAXPD m128 xmm xmm
+// VMAXPD ymm ymm ymm
+// VMAXPD m256 ymm ymm
+func VMAXPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMAXPD: bad operands")
+}
+
+// VMAXPS: Return Maximum Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMAXPS xmm xmm xmm
+// VMAXPS m128 xmm xmm
+// VMAXPS ymm ymm ymm
+// VMAXPS m256 ymm ymm
+func VMAXPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMAXPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMAXPS: bad operands")
+}
+
+// VMAXSD: Return Maximum Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VMAXSD xmm xmm xmm
+// VMAXSD m64 xmm xmm
+func VMAXSD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMAXSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMAXSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMAXSD: bad operands")
+}
+
+// VMAXSS: Return Maximum Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VMAXSS xmm xmm xmm
+// VMAXSS m32 xmm xmm
+func VMAXSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMAXSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMAXSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMAXSS: bad operands")
+}
+
+// VMINPD: Return Minimum Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMINPD xmm xmm xmm
+// VMINPD m128 xmm xmm
+// VMINPD ymm ymm ymm
+// VMINPD m256 ymm ymm
+func VMINPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMINPD: bad operands")
+}
+
+// VMINPS: Return Minimum Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMINPS xmm xmm xmm
+// VMINPS m128 xmm xmm
+// VMINPS ymm ymm ymm
+// VMINPS m256 ymm ymm
+func VMINPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMINPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMINPS: bad operands")
+}
+
+// VMINSD: Return Minimum Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VMINSD xmm xmm xmm
+// VMINSD m64 xmm xmm
+func VMINSD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMINSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMINSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMINSD: bad operands")
+}
+
+// VMINSS: Return Minimum Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VMINSS xmm xmm xmm
+// VMINSS m32 xmm xmm
+func VMINSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMINSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMINSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMINSS: bad operands")
+}
+
+// VMOVAPD: Move Aligned Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMOVAPD xmm xmm
+// VMOVAPD m128 xmm
+// VMOVAPD ymm ymm
+// VMOVAPD m256 ymm
+// VMOVAPD xmm m128
+// VMOVAPD ymm m256
+func VMOVAPD(mxy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVAPD: bad operands")
+}
+
+// VMOVAPS: Move Aligned Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMOVAPS xmm xmm
+// VMOVAPS m128 xmm
+// VMOVAPS ymm ymm
+// VMOVAPS m256 ymm
+// VMOVAPS xmm m128
+// VMOVAPS ymm m256
+func VMOVAPS(mxy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVAPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVAPS: bad operands")
+}
+
+// VMOVD: Move Doubleword.
+//
+// Forms:
+//
+// VMOVD xmm r32
+// VMOVD r32 xmm
+// VMOVD m32 xmm
+// VMOVD xmm m32
+func VMOVD(mrx, mrx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mrx) && operand.IsR32(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVD",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsR32(mrx) && operand.IsXMM(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVD",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mrx) && operand.IsXMM(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVD",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mrx) && operand.IsM32(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVD",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVD: bad operands")
+}
+
+// VMOVDDUP: Move One Double-FP and Duplicate.
+//
+// Forms:
+//
+// VMOVDDUP xmm xmm
+// VMOVDDUP m64 xmm
+// VMOVDDUP ymm ymm
+// VMOVDDUP m256 ymm
+func VMOVDDUP(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVDDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVDDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVDDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVDDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVDDUP: bad operands")
+}
+
+// VMOVDQA: Move Aligned Double Quadword.
+//
+// Forms:
+//
+// VMOVDQA xmm xmm
+// VMOVDQA m128 xmm
+// VMOVDQA ymm ymm
+// VMOVDQA m256 ymm
+// VMOVDQA xmm m128
+// VMOVDQA ymm m256
+func VMOVDQA(mxy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQA",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQA",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQA",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQA",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQA",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQA",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVDQA: bad operands")
+}
+
+// VMOVDQU: Move Unaligned Double Quadword.
+//
+// Forms:
+//
+// VMOVDQU xmm xmm
+// VMOVDQU m128 xmm
+// VMOVDQU ymm ymm
+// VMOVDQU m256 ymm
+// VMOVDQU xmm m128
+// VMOVDQU ymm m256
+func VMOVDQU(mxy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQU",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQU",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQU",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQU",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQU",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVDQU",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVDQU: bad operands")
+}
+
+// VMOVHLPS: Move Packed Single-Precision Floating-Point Values High to Low.
+//
+// Forms:
+//
+// VMOVHLPS xmm xmm xmm
+func VMOVHLPS(x, x1, x2 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsXMM(x1) && operand.IsXMM(x2):
+ return &intrep.Instruction{
+ Opcode: "VMOVHLPS",
+ Operands: []operand.Op{x, x1, x2},
+ Inputs: []operand.Op{x, x1},
+ Outputs: []operand.Op{x2},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVHLPS: bad operands")
+}
+
+// VMOVHPD: Move High Packed Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VMOVHPD xmm m64
+// VMOVHPD m64 xmm xmm
+func VMOVHPD(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.IsXMM(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVHPD",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 3 && operand.IsM64(ops[0]) && operand.IsXMM(ops[1]) && operand.IsXMM(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "VMOVHPD",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[2]},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVHPD: bad operands")
+}
+
+// VMOVHPS: Move High Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMOVHPS xmm m64
+// VMOVHPS m64 xmm xmm
+func VMOVHPS(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.IsXMM(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVHPS",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 3 && operand.IsM64(ops[0]) && operand.IsXMM(ops[1]) && operand.IsXMM(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "VMOVHPS",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[2]},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVHPS: bad operands")
+}
+
+// VMOVLHPS: Move Packed Single-Precision Floating-Point Values Low to High.
+//
+// Forms:
+//
+// VMOVLHPS xmm xmm xmm
+func VMOVLHPS(x, x1, x2 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsXMM(x1) && operand.IsXMM(x2):
+ return &intrep.Instruction{
+ Opcode: "VMOVLHPS",
+ Operands: []operand.Op{x, x1, x2},
+ Inputs: []operand.Op{x, x1},
+ Outputs: []operand.Op{x2},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVLHPS: bad operands")
+}
+
+// VMOVLPD: Move Low Packed Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VMOVLPD xmm m64
+// VMOVLPD m64 xmm xmm
+func VMOVLPD(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.IsXMM(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVLPD",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 3 && operand.IsM64(ops[0]) && operand.IsXMM(ops[1]) && operand.IsXMM(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "VMOVLPD",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[2]},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVLPD: bad operands")
+}
+
+// VMOVLPS: Move Low Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMOVLPS xmm m64
+// VMOVLPS m64 xmm xmm
+func VMOVLPS(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.IsXMM(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVLPS",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 3 && operand.IsM64(ops[0]) && operand.IsXMM(ops[1]) && operand.IsXMM(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "VMOVLPS",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[2]},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVLPS: bad operands")
+}
+
+// VMOVMSKPD: Extract Packed Double-Precision Floating-Point Sign Mask.
+//
+// Forms:
+//
+// VMOVMSKPD xmm r32
+// VMOVMSKPD ymm r32
+func VMOVMSKPD(xy, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VMOVMSKPD",
+ Operands: []operand.Op{xy, r},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VMOVMSKPD",
+ Operands: []operand.Op{xy, r},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVMSKPD: bad operands")
+}
+
+// VMOVMSKPS: Extract Packed Single-Precision Floating-Point Sign Mask.
+//
+// Forms:
+//
+// VMOVMSKPS xmm r32
+// VMOVMSKPS ymm r32
+func VMOVMSKPS(xy, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VMOVMSKPS",
+ Operands: []operand.Op{xy, r},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VMOVMSKPS",
+ Operands: []operand.Op{xy, r},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVMSKPS: bad operands")
+}
+
+// VMOVNTDQ: Store Double Quadword Using Non-Temporal Hint.
+//
+// Forms:
+//
+// VMOVNTDQ xmm m128
+// VMOVNTDQ ymm m256
+func VMOVNTDQ(xy, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTDQ",
+ Operands: []operand.Op{xy, m},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{m},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsM256(m):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTDQ",
+ Operands: []operand.Op{xy, m},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{m},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVNTDQ: bad operands")
+}
+
+// VMOVNTDQA: Load Double Quadword Non-Temporal Aligned Hint.
+//
+// Forms:
+//
+// VMOVNTDQA m128 xmm
+// VMOVNTDQA m256 ymm
+func VMOVNTDQA(m, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(m) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTDQA",
+ Operands: []operand.Op{m, xy},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(m) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTDQA",
+ Operands: []operand.Op{m, xy},
+ Inputs: []operand.Op{m},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VMOVNTDQA: bad operands")
+}
+
+// VMOVNTPD: Store Packed Double-Precision Floating-Point Values Using Non-Temporal Hint.
+//
+// Forms:
+//
+// VMOVNTPD xmm m128
+// VMOVNTPD ymm m256
+func VMOVNTPD(xy, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTPD",
+ Operands: []operand.Op{xy, m},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{m},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsM256(m):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTPD",
+ Operands: []operand.Op{xy, m},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{m},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVNTPD: bad operands")
+}
+
+// VMOVNTPS: Store Packed Single-Precision Floating-Point Values Using Non-Temporal Hint.
+//
+// Forms:
+//
+// VMOVNTPS xmm m128
+// VMOVNTPS ymm m256
+func VMOVNTPS(xy, m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsM128(m):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTPS",
+ Operands: []operand.Op{xy, m},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{m},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsM256(m):
+ return &intrep.Instruction{
+ Opcode: "VMOVNTPS",
+ Operands: []operand.Op{xy, m},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{m},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVNTPS: bad operands")
+}
+
+// VMOVQ: Move Quadword.
+//
+// Forms:
+//
+// VMOVQ xmm r64
+// VMOVQ r64 xmm
+// VMOVQ xmm xmm
+// VMOVQ m64 xmm
+// VMOVQ xmm m64
+func VMOVQ(mrx, mrx1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mrx) && operand.IsR64(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVQ",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsR64(mrx) && operand.IsXMM(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVQ",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mrx) && operand.IsXMM(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVQ",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mrx) && operand.IsXMM(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVQ",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mrx) && operand.IsM64(mrx1):
+ return &intrep.Instruction{
+ Opcode: "VMOVQ",
+ Operands: []operand.Op{mrx, mrx1},
+ Inputs: []operand.Op{mrx},
+ Outputs: []operand.Op{mrx1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVQ: bad operands")
+}
+
+// VMOVSD: Move Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VMOVSD m64 xmm
+// VMOVSD xmm m64
+// VMOVSD xmm xmm xmm
+func VMOVSD(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.IsM64(ops[0]) && operand.IsXMM(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVSD",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 2 && operand.IsXMM(ops[0]) && operand.IsM64(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVSD",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 3 && operand.IsXMM(ops[0]) && operand.IsXMM(ops[1]) && operand.IsXMM(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "VMOVSD",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[2]},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVSD: bad operands")
+}
+
+// VMOVSHDUP: Move Packed Single-FP High and Duplicate.
+//
+// Forms:
+//
+// VMOVSHDUP xmm xmm
+// VMOVSHDUP m128 xmm
+// VMOVSHDUP ymm ymm
+// VMOVSHDUP m256 ymm
+func VMOVSHDUP(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSHDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSHDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSHDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSHDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVSHDUP: bad operands")
+}
+
+// VMOVSLDUP: Move Packed Single-FP Low and Duplicate.
+//
+// Forms:
+//
+// VMOVSLDUP xmm xmm
+// VMOVSLDUP m128 xmm
+// VMOVSLDUP ymm ymm
+// VMOVSLDUP m256 ymm
+func VMOVSLDUP(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSLDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSLDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSLDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VMOVSLDUP",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVSLDUP: bad operands")
+}
+
+// VMOVSS: Move Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMOVSS m32 xmm
+// VMOVSS xmm m32
+// VMOVSS xmm xmm xmm
+func VMOVSS(ops ...operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case len(ops) == 2 && operand.IsM32(ops[0]) && operand.IsXMM(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVSS",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 2 && operand.IsXMM(ops[0]) && operand.IsM32(ops[1]):
+ return &intrep.Instruction{
+ Opcode: "VMOVSS",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0]},
+ Outputs: []operand.Op{ops[1]},
+ ISA: []string{"AVX"},
+ }, nil
+ case len(ops) == 3 && operand.IsXMM(ops[0]) && operand.IsXMM(ops[1]) && operand.IsXMM(ops[2]):
+ return &intrep.Instruction{
+ Opcode: "VMOVSS",
+ Operands: ops,
+ Inputs: []operand.Op{ops[0], ops[1]},
+ Outputs: []operand.Op{ops[2]},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVSS: bad operands")
+}
+
+// VMOVUPD: Move Unaligned Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMOVUPD xmm xmm
+// VMOVUPD m128 xmm
+// VMOVUPD ymm ymm
+// VMOVUPD m256 ymm
+// VMOVUPD xmm m128
+// VMOVUPD ymm m256
+func VMOVUPD(mxy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPD",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVUPD: bad operands")
+}
+
+// VMOVUPS: Move Unaligned Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMOVUPS xmm xmm
+// VMOVUPS m128 xmm
+// VMOVUPS ymm ymm
+// VMOVUPS m256 ymm
+// VMOVUPS xmm m128
+// VMOVUPS ymm m256
+func VMOVUPS(mxy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VMOVUPS",
+ Operands: []operand.Op{mxy, mxy1},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMOVUPS: bad operands")
+}
+
+// VMPSADBW: Compute Multiple Packed Sums of Absolute Difference.
+//
+// Forms:
+//
+// VMPSADBW imm8 xmm xmm xmm
+// VMPSADBW imm8 m128 xmm xmm
+// VMPSADBW imm8 ymm ymm ymm
+// VMPSADBW imm8 m256 ymm ymm
+func VMPSADBW(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMPSADBW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMPSADBW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMPSADBW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMPSADBW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VMPSADBW: bad operands")
+}
+
+// VMULPD: Multiply Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMULPD xmm xmm xmm
+// VMULPD m128 xmm xmm
+// VMULPD ymm ymm ymm
+// VMULPD m256 ymm ymm
+func VMULPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMULPD: bad operands")
+}
+
+// VMULPS: Multiply Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMULPS xmm xmm xmm
+// VMULPS m128 xmm xmm
+// VMULPS ymm ymm ymm
+// VMULPS m256 ymm ymm
+func VMULPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VMULPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMULPS: bad operands")
+}
+
+// VMULSD: Multiply Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMULSD xmm xmm xmm
+// VMULSD m64 xmm xmm
+func VMULSD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMULSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMULSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMULSD: bad operands")
+}
+
+// VMULSS: Multiply Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VMULSS xmm xmm xmm
+// VMULSS m32 xmm xmm
+func VMULSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMULSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VMULSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VMULSS: bad operands")
+}
+
+// VORPD: Bitwise Logical OR of Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VORPD xmm xmm xmm
+// VORPD m128 xmm xmm
+// VORPD ymm ymm ymm
+// VORPD m256 ymm ymm
+func VORPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VORPD: bad operands")
+}
+
+// VORPS: Bitwise Logical OR of Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VORPS xmm xmm xmm
+// VORPS m128 xmm xmm
+// VORPS ymm ymm ymm
+// VORPS m256 ymm ymm
+func VORPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VORPS: bad operands")
+}
+
+// VPABSB: Packed Absolute Value of Byte Integers.
+//
+// Forms:
+//
+// VPABSB xmm xmm
+// VPABSB m128 xmm
+// VPABSB ymm ymm
+// VPABSB m256 ymm
+func VPABSB(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSB",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSB",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSB",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSB",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPABSB: bad operands")
+}
+
+// VPABSD: Packed Absolute Value of Doubleword Integers.
+//
+// Forms:
+//
+// VPABSD xmm xmm
+// VPABSD m128 xmm
+// VPABSD ymm ymm
+// VPABSD m256 ymm
+func VPABSD(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPABSD: bad operands")
+}
+
+// VPABSW: Packed Absolute Value of Word Integers.
+//
+// Forms:
+//
+// VPABSW xmm xmm
+// VPABSW m128 xmm
+// VPABSW ymm ymm
+// VPABSW m256 ymm
+func VPABSW(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSW",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSW",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSW",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPABSW",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPABSW: bad operands")
+}
+
+// VPACKSSDW: Pack Doublewords into Words with Signed Saturation.
+//
+// Forms:
+//
+// VPACKSSDW xmm xmm xmm
+// VPACKSSDW m128 xmm xmm
+// VPACKSSDW ymm ymm ymm
+// VPACKSSDW m256 ymm ymm
+func VPACKSSDW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPACKSSDW: bad operands")
+}
+
+// VPACKSSWB: Pack Words into Bytes with Signed Saturation.
+//
+// Forms:
+//
+// VPACKSSWB xmm xmm xmm
+// VPACKSSWB m128 xmm xmm
+// VPACKSSWB ymm ymm ymm
+// VPACKSSWB m256 ymm ymm
+func VPACKSSWB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKSSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPACKSSWB: bad operands")
+}
+
+// VPACKUSDW: Pack Doublewords into Words with Unsigned Saturation.
+//
+// Forms:
+//
+// VPACKUSDW xmm xmm xmm
+// VPACKUSDW m128 xmm xmm
+// VPACKUSDW ymm ymm ymm
+// VPACKUSDW m256 ymm ymm
+func VPACKUSDW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPACKUSDW: bad operands")
+}
+
+// VPACKUSWB: Pack Words into Bytes with Unsigned Saturation.
+//
+// Forms:
+//
+// VPACKUSWB xmm xmm xmm
+// VPACKUSWB m128 xmm xmm
+// VPACKUSWB ymm ymm ymm
+// VPACKUSWB m256 ymm ymm
+func VPACKUSWB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPACKUSWB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPACKUSWB: bad operands")
+}
+
+// VPADDB: Add Packed Byte Integers.
+//
+// Forms:
+//
+// VPADDB xmm xmm xmm
+// VPADDB m128 xmm xmm
+// VPADDB ymm ymm ymm
+// VPADDB m256 ymm ymm
+func VPADDB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDB: bad operands")
+}
+
+// VPADDD: Add Packed Doubleword Integers.
+//
+// Forms:
+//
+// VPADDD xmm xmm xmm
+// VPADDD m128 xmm xmm
+// VPADDD ymm ymm ymm
+// VPADDD m256 ymm ymm
+func VPADDD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDD: bad operands")
+}
+
+// VPADDQ: Add Packed Quadword Integers.
+//
+// Forms:
+//
+// VPADDQ xmm xmm xmm
+// VPADDQ m128 xmm xmm
+// VPADDQ ymm ymm ymm
+// VPADDQ m256 ymm ymm
+func VPADDQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDQ: bad operands")
+}
+
+// VPADDSB: Add Packed Signed Byte Integers with Signed Saturation.
+//
+// Forms:
+//
+// VPADDSB xmm xmm xmm
+// VPADDSB m128 xmm xmm
+// VPADDSB ymm ymm ymm
+// VPADDSB m256 ymm ymm
+func VPADDSB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDSB: bad operands")
+}
+
+// VPADDSW: Add Packed Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// VPADDSW xmm xmm xmm
+// VPADDSW m128 xmm xmm
+// VPADDSW ymm ymm ymm
+// VPADDSW m256 ymm ymm
+func VPADDSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDSW: bad operands")
+}
+
+// VPADDUSB: Add Packed Unsigned Byte Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// VPADDUSB xmm xmm xmm
+// VPADDUSB m128 xmm xmm
+// VPADDUSB ymm ymm ymm
+// VPADDUSB m256 ymm ymm
+func VPADDUSB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDUSB: bad operands")
+}
+
+// VPADDUSW: Add Packed Unsigned Word Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// VPADDUSW xmm xmm xmm
+// VPADDUSW m128 xmm xmm
+// VPADDUSW ymm ymm ymm
+// VPADDUSW m256 ymm ymm
+func VPADDUSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDUSW: bad operands")
+}
+
+// VPADDW: Add Packed Word Integers.
+//
+// Forms:
+//
+// VPADDW xmm xmm xmm
+// VPADDW m128 xmm xmm
+// VPADDW ymm ymm ymm
+// VPADDW m256 ymm ymm
+func VPADDW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPADDW: bad operands")
+}
+
+// VPALIGNR: Packed Align Right.
+//
+// Forms:
+//
+// VPALIGNR imm8 xmm xmm xmm
+// VPALIGNR imm8 m128 xmm xmm
+// VPALIGNR imm8 ymm ymm ymm
+// VPALIGNR imm8 m256 ymm ymm
+func VPALIGNR(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPALIGNR",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPALIGNR",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPALIGNR",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPALIGNR",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPALIGNR: bad operands")
+}
+
+// VPAND: Packed Bitwise Logical AND.
+//
+// Forms:
+//
+// VPAND xmm xmm xmm
+// VPAND m128 xmm xmm
+// VPAND ymm ymm ymm
+// VPAND m256 ymm ymm
+func VPAND(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPAND: bad operands")
+}
+
+// VPANDN: Packed Bitwise Logical AND NOT.
+//
+// Forms:
+//
+// VPANDN xmm xmm xmm
+// VPANDN m128 xmm xmm
+// VPANDN ymm ymm ymm
+// VPANDN m256 ymm ymm
+func VPANDN(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPANDN",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPANDN",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPANDN",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPANDN",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPANDN: bad operands")
+}
+
+// VPAVGB: Average Packed Byte Integers.
+//
+// Forms:
+//
+// VPAVGB xmm xmm xmm
+// VPAVGB m128 xmm xmm
+// VPAVGB ymm ymm ymm
+// VPAVGB m256 ymm ymm
+func VPAVGB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPAVGB: bad operands")
+}
+
+// VPAVGW: Average Packed Word Integers.
+//
+// Forms:
+//
+// VPAVGW xmm xmm xmm
+// VPAVGW m128 xmm xmm
+// VPAVGW ymm ymm ymm
+// VPAVGW m256 ymm ymm
+func VPAVGW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPAVGW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPAVGW: bad operands")
+}
+
+// VPBLENDD: Blend Packed Doublewords.
+//
+// Forms:
+//
+// VPBLENDD imm8 xmm xmm xmm
+// VPBLENDD imm8 m128 xmm xmm
+// VPBLENDD imm8 ymm ymm ymm
+// VPBLENDD imm8 m256 ymm ymm
+func VPBLENDD(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPBLENDD: bad operands")
+}
+
+// VPBLENDVB: Variable Blend Packed Bytes.
+//
+// Forms:
+//
+// VPBLENDVB xmm xmm xmm xmm
+// VPBLENDVB xmm m128 xmm xmm
+// VPBLENDVB ymm ymm ymm ymm
+// VPBLENDVB ymm m256 ymm ymm
+func VPBLENDVB(xy, mxy, xy1, xy2 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsXMM(mxy) && operand.IsXMM(xy1) && operand.IsXMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDVB",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(xy) && operand.IsM128(mxy) && operand.IsXMM(xy1) && operand.IsXMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDVB",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsYMM(mxy) && operand.IsYMM(xy1) && operand.IsYMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDVB",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsM256(mxy) && operand.IsYMM(xy1) && operand.IsYMM(xy2):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDVB",
+ Operands: []operand.Op{xy, mxy, xy1, xy2},
+ Inputs: []operand.Op{xy, mxy, xy1},
+ Outputs: []operand.Op{xy2},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPBLENDVB: bad operands")
+}
+
+// VPBLENDW: Blend Packed Words.
+//
+// Forms:
+//
+// VPBLENDW imm8 xmm xmm xmm
+// VPBLENDW imm8 m128 xmm xmm
+// VPBLENDW imm8 ymm ymm ymm
+// VPBLENDW imm8 m256 ymm ymm
+func VPBLENDW(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPBLENDW",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPBLENDW: bad operands")
+}
+
+// VPBROADCASTB: Broadcast Byte Integer.
+//
+// Forms:
+//
+// VPBROADCASTB xmm xmm
+// VPBROADCASTB m8 xmm
+// VPBROADCASTB xmm ymm
+// VPBROADCASTB m8 ymm
+func VPBROADCASTB(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTB",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM8(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTB",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTB",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM8(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTB",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPBROADCASTB: bad operands")
+}
+
+// VPBROADCASTD: Broadcast Doubleword Integer.
+//
+// Forms:
+//
+// VPBROADCASTD xmm xmm
+// VPBROADCASTD m32 xmm
+// VPBROADCASTD xmm ymm
+// VPBROADCASTD m32 ymm
+func VPBROADCASTD(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPBROADCASTD: bad operands")
+}
+
+// VPBROADCASTQ: Broadcast Quadword Integer.
+//
+// Forms:
+//
+// VPBROADCASTQ xmm xmm
+// VPBROADCASTQ m64 xmm
+// VPBROADCASTQ xmm ymm
+// VPBROADCASTQ m64 ymm
+func VPBROADCASTQ(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPBROADCASTQ: bad operands")
+}
+
+// VPBROADCASTW: Broadcast Word Integer.
+//
+// Forms:
+//
+// VPBROADCASTW xmm xmm
+// VPBROADCASTW m16 xmm
+// VPBROADCASTW xmm ymm
+// VPBROADCASTW m16 ymm
+func VPBROADCASTW(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM16(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM16(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPBROADCASTW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPBROADCASTW: bad operands")
+}
+
+// VPCLMULQDQ: Carry-Less Quadword Multiplication.
+//
+// Forms:
+//
+// VPCLMULQDQ imm8 xmm xmm xmm
+// VPCLMULQDQ imm8 m128 xmm xmm
+func VPCLMULQDQ(i, mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPCLMULQDQ",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "PCLMULQDQ"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPCLMULQDQ",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX", "PCLMULQDQ"},
+ }, nil
+ }
+ return nil, errors.New("VPCLMULQDQ: bad operands")
+}
+
+// VPCMPEQB: Compare Packed Byte Data for Equality.
+//
+// Forms:
+//
+// VPCMPEQB xmm xmm xmm
+// VPCMPEQB m128 xmm xmm
+// VPCMPEQB ymm ymm ymm
+// VPCMPEQB m256 ymm ymm
+func VPCMPEQB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPEQB: bad operands")
+}
+
+// VPCMPEQD: Compare Packed Doubleword Data for Equality.
+//
+// Forms:
+//
+// VPCMPEQD xmm xmm xmm
+// VPCMPEQD m128 xmm xmm
+// VPCMPEQD ymm ymm ymm
+// VPCMPEQD m256 ymm ymm
+func VPCMPEQD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPEQD: bad operands")
+}
+
+// VPCMPEQQ: Compare Packed Quadword Data for Equality.
+//
+// Forms:
+//
+// VPCMPEQQ xmm xmm xmm
+// VPCMPEQQ m128 xmm xmm
+// VPCMPEQQ ymm ymm ymm
+// VPCMPEQQ m256 ymm ymm
+func VPCMPEQQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPEQQ: bad operands")
+}
+
+// VPCMPEQW: Compare Packed Word Data for Equality.
+//
+// Forms:
+//
+// VPCMPEQW xmm xmm xmm
+// VPCMPEQW m128 xmm xmm
+// VPCMPEQW ymm ymm ymm
+// VPCMPEQW m256 ymm ymm
+func VPCMPEQW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPEQW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPEQW: bad operands")
+}
+
+// VPCMPESTRI: Packed Compare Explicit Length Strings, Return Index.
+//
+// Forms:
+//
+// VPCMPESTRI imm8 xmm xmm
+// VPCMPESTRI imm8 m128 xmm
+func VPCMPESTRI(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPESTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPESTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPESTRI: bad operands")
+}
+
+// VPCMPESTRM: Packed Compare Explicit Length Strings, Return Mask.
+//
+// Forms:
+//
+// VPCMPESTRM imm8 xmm xmm
+// VPCMPESTRM imm8 m128 xmm
+func VPCMPESTRM(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPESTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPESTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x, reg.EAX, reg.EDX},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPESTRM: bad operands")
+}
+
+// VPCMPGTB: Compare Packed Signed Byte Integers for Greater Than.
+//
+// Forms:
+//
+// VPCMPGTB xmm xmm xmm
+// VPCMPGTB m128 xmm xmm
+// VPCMPGTB ymm ymm ymm
+// VPCMPGTB m256 ymm ymm
+func VPCMPGTB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPGTB: bad operands")
+}
+
+// VPCMPGTD: Compare Packed Signed Doubleword Integers for Greater Than.
+//
+// Forms:
+//
+// VPCMPGTD xmm xmm xmm
+// VPCMPGTD m128 xmm xmm
+// VPCMPGTD ymm ymm ymm
+// VPCMPGTD m256 ymm ymm
+func VPCMPGTD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPGTD: bad operands")
+}
+
+// VPCMPGTQ: Compare Packed Data for Greater Than.
+//
+// Forms:
+//
+// VPCMPGTQ xmm xmm xmm
+// VPCMPGTQ m128 xmm xmm
+// VPCMPGTQ ymm ymm ymm
+// VPCMPGTQ m256 ymm ymm
+func VPCMPGTQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPGTQ: bad operands")
+}
+
+// VPCMPGTW: Compare Packed Signed Word Integers for Greater Than.
+//
+// Forms:
+//
+// VPCMPGTW xmm xmm xmm
+// VPCMPGTW m128 xmm xmm
+// VPCMPGTW ymm ymm ymm
+// VPCMPGTW m256 ymm ymm
+func VPCMPGTW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPCMPGTW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPGTW: bad operands")
+}
+
+// VPCMPISTRI: Packed Compare Implicit Length Strings, Return Index.
+//
+// Forms:
+//
+// VPCMPISTRI imm8 xmm xmm
+// VPCMPISTRI imm8 m128 xmm
+func VPCMPISTRI(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPISTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPISTRI",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.ECX},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPISTRI: bad operands")
+}
+
+// VPCMPISTRM: Packed Compare Implicit Length Strings, Return Mask.
+//
+// Forms:
+//
+// VPCMPISTRM imm8 xmm xmm
+// VPCMPISTRM imm8 m128 xmm
+func VPCMPISTRM(i, mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPISTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPCMPISTRM",
+ Operands: []operand.Op{i, mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{reg.X0},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPCMPISTRM: bad operands")
+}
+
+// VPERM2F128: Permute Floating-Point Values.
+//
+// Forms:
+//
+// VPERM2F128 imm8 ymm ymm ymm
+// VPERM2F128 imm8 m256 ymm ymm
+func VPERM2F128(i, my, y, y1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsYMM(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERM2F128",
+ Operands: []operand.Op{i, my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERM2F128",
+ Operands: []operand.Op{i, my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPERM2F128: bad operands")
+}
+
+// VPERM2I128: Permute 128-Bit Integer Values.
+//
+// Forms:
+//
+// VPERM2I128 imm8 ymm ymm ymm
+// VPERM2I128 imm8 m256 ymm ymm
+func VPERM2I128(i, my, y, y1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsYMM(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERM2I128",
+ Operands: []operand.Op{i, my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERM2I128",
+ Operands: []operand.Op{i, my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPERM2I128: bad operands")
+}
+
+// VPERMD: Permute Doubleword Integers.
+//
+// Forms:
+//
+// VPERMD ymm ymm ymm
+// VPERMD m256 ymm ymm
+func VPERMD(my, y, y1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsYMM(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERMD",
+ Operands: []operand.Op{my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERMD",
+ Operands: []operand.Op{my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPERMD: bad operands")
+}
+
+// VPERMILPD: Permute Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VPERMILPD imm8 xmm xmm
+// VPERMILPD xmm xmm xmm
+// VPERMILPD m128 xmm xmm
+// VPERMILPD imm8 m128 xmm
+// VPERMILPD imm8 ymm ymm
+// VPERMILPD ymm ymm ymm
+// VPERMILPD m256 ymm ymm
+// VPERMILPD imm8 m256 ymm
+func VPERMILPD(imxy, mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imxy) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imxy) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imxy) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imxy) && operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imxy) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(imxy) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(imxy) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imxy) && operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPD",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPERMILPD: bad operands")
+}
+
+// VPERMILPS: Permute Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VPERMILPS imm8 xmm xmm
+// VPERMILPS xmm xmm xmm
+// VPERMILPS m128 xmm xmm
+// VPERMILPS imm8 m128 xmm
+// VPERMILPS imm8 ymm ymm
+// VPERMILPS ymm ymm ymm
+// VPERMILPS m256 ymm ymm
+// VPERMILPS imm8 m256 ymm
+func VPERMILPS(imxy, mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imxy) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imxy) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imxy) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imxy) && operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imxy) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(imxy) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(imxy) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{imxy, mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imxy) && operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPERMILPS",
+ Operands: []operand.Op{imxy, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPERMILPS: bad operands")
+}
+
+// VPERMPD: Permute Double-Precision Floating-Point Elements.
+//
+// Forms:
+//
+// VPERMPD imm8 ymm ymm
+// VPERMPD imm8 m256 ymm
+func VPERMPD(i, my, y operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsYMM(my) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VPERMPD",
+ Operands: []operand.Op{i, my, y},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(my) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VPERMPD",
+ Operands: []operand.Op{i, my, y},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPERMPD: bad operands")
+}
+
+// VPERMPS: Permute Single-Precision Floating-Point Elements.
+//
+// Forms:
+//
+// VPERMPS ymm ymm ymm
+// VPERMPS m256 ymm ymm
+func VPERMPS(my, y, y1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsYMM(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERMPS",
+ Operands: []operand.Op{my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(my) && operand.IsYMM(y) && operand.IsYMM(y1):
+ return &intrep.Instruction{
+ Opcode: "VPERMPS",
+ Operands: []operand.Op{my, y, y1},
+ Inputs: []operand.Op{my, y},
+ Outputs: []operand.Op{y1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPERMPS: bad operands")
+}
+
+// VPERMQ: Permute Quadword Integers.
+//
+// Forms:
+//
+// VPERMQ imm8 ymm ymm
+// VPERMQ imm8 m256 ymm
+func VPERMQ(i, my, y operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsYMM(my) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VPERMQ",
+ Operands: []operand.Op{i, my, y},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(my) && operand.IsYMM(y):
+ return &intrep.Instruction{
+ Opcode: "VPERMQ",
+ Operands: []operand.Op{i, my, y},
+ Inputs: []operand.Op{my},
+ Outputs: []operand.Op{y},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPERMQ: bad operands")
+}
+
+// VPEXTRB: Extract Byte.
+//
+// Forms:
+//
+// VPEXTRB imm8 xmm r32
+// VPEXTRB imm8 xmm m8
+func VPEXTRB(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRB",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRB",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPEXTRB: bad operands")
+}
+
+// VPEXTRD: Extract Doubleword.
+//
+// Forms:
+//
+// VPEXTRD imm8 xmm r32
+// VPEXTRD imm8 xmm m32
+func VPEXTRD(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRD",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRD",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPEXTRD: bad operands")
+}
+
+// VPEXTRQ: Extract Quadword.
+//
+// Forms:
+//
+// VPEXTRQ imm8 xmm r64
+// VPEXTRQ imm8 xmm m64
+func VPEXTRQ(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRQ",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRQ",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPEXTRQ: bad operands")
+}
+
+// VPEXTRW: Extract Word.
+//
+// Forms:
+//
+// VPEXTRW imm8 xmm r32
+// VPEXTRW imm8 xmm m16
+func VPEXTRW(i, x, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRW",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsXMM(x) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "VPEXTRW",
+ Operands: []operand.Op{i, x, mr},
+ Inputs: []operand.Op{x},
+ Outputs: []operand.Op{mr},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPEXTRW: bad operands")
+}
+
+// VPGATHERDD: Gather Packed Doubleword Values Using Signed Doubleword Indices.
+//
+// Forms:
+//
+// VPGATHERDD xmm vm32x xmm
+// VPGATHERDD ymm vm32y ymm
+func VPGATHERDD(xy, v, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsVM32X(v) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERDD",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsVM32Y(v) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERDD",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPGATHERDD: bad operands")
+}
+
+// VPGATHERDQ: Gather Packed Quadword Values Using Signed Doubleword Indices.
+//
+// Forms:
+//
+// VPGATHERDQ xmm vm32x xmm
+// VPGATHERDQ ymm vm32x ymm
+func VPGATHERDQ(xy, v, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsVM32X(v) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERDQ",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsVM32X(v) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERDQ",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPGATHERDQ: bad operands")
+}
+
+// VPGATHERQD: Gather Packed Doubleword Values Using Signed Quadword Indices.
+//
+// Forms:
+//
+// VPGATHERQD xmm vm64x xmm
+// VPGATHERQD xmm vm64y xmm
+func VPGATHERQD(x, v, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(x) && operand.IsVM64X(v) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERQD",
+ Operands: []operand.Op{x, v, x1},
+ Inputs: []operand.Op{x, v, x1},
+ Outputs: []operand.Op{x, x1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(x) && operand.IsVM64Y(v) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERQD",
+ Operands: []operand.Op{x, v, x1},
+ Inputs: []operand.Op{x, v, x1},
+ Outputs: []operand.Op{x, x1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPGATHERQD: bad operands")
+}
+
+// VPGATHERQQ: Gather Packed Quadword Values Using Signed Quadword Indices.
+//
+// Forms:
+//
+// VPGATHERQQ xmm vm64x xmm
+// VPGATHERQQ ymm vm64y ymm
+func VPGATHERQQ(xy, v, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsVM64X(v) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERQQ",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsVM64Y(v) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPGATHERQQ",
+ Operands: []operand.Op{xy, v, xy1},
+ Inputs: []operand.Op{xy, v, xy1},
+ Outputs: []operand.Op{xy, xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPGATHERQQ: bad operands")
+}
+
+// VPHADDD: Packed Horizontal Add Doubleword Integer.
+//
+// Forms:
+//
+// VPHADDD xmm xmm xmm
+// VPHADDD m128 xmm xmm
+// VPHADDD ymm ymm ymm
+// VPHADDD m256 ymm ymm
+func VPHADDD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPHADDD: bad operands")
+}
+
+// VPHADDSW: Packed Horizontal Add Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// VPHADDSW xmm xmm xmm
+// VPHADDSW m128 xmm xmm
+// VPHADDSW ymm ymm ymm
+// VPHADDSW m256 ymm ymm
+func VPHADDSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPHADDSW: bad operands")
+}
+
+// VPHADDW: Packed Horizontal Add Word Integers.
+//
+// Forms:
+//
+// VPHADDW xmm xmm xmm
+// VPHADDW m128 xmm xmm
+// VPHADDW ymm ymm ymm
+// VPHADDW m256 ymm ymm
+func VPHADDW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHADDW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPHADDW: bad operands")
+}
+
+// VPHMINPOSUW: Packed Horizontal Minimum of Unsigned Word Integers.
+//
+// Forms:
+//
+// VPHMINPOSUW xmm xmm
+// VPHMINPOSUW m128 xmm
+func VPHMINPOSUW(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPHMINPOSUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VPHMINPOSUW",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{x},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPHMINPOSUW: bad operands")
+}
+
+// VPHSUBD: Packed Horizontal Subtract Doubleword Integers.
+//
+// Forms:
+//
+// VPHSUBD xmm xmm xmm
+// VPHSUBD m128 xmm xmm
+// VPHSUBD ymm ymm ymm
+// VPHSUBD m256 ymm ymm
+func VPHSUBD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPHSUBD: bad operands")
+}
+
+// VPHSUBSW: Packed Horizontal Subtract Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// VPHSUBSW xmm xmm xmm
+// VPHSUBSW m128 xmm xmm
+// VPHSUBSW ymm ymm ymm
+// VPHSUBSW m256 ymm ymm
+func VPHSUBSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPHSUBSW: bad operands")
+}
+
+// VPHSUBW: Packed Horizontal Subtract Word Integers.
+//
+// Forms:
+//
+// VPHSUBW xmm xmm xmm
+// VPHSUBW m128 xmm xmm
+// VPHSUBW ymm ymm ymm
+// VPHSUBW m256 ymm ymm
+func VPHSUBW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPHSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPHSUBW: bad operands")
+}
+
+// VPINSRB: Insert Byte.
+//
+// Forms:
+//
+// VPINSRB imm8 r32 xmm xmm
+// VPINSRB imm8 m8 xmm xmm
+func VPINSRB(i, mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRB",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM8(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRB",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPINSRB: bad operands")
+}
+
+// VPINSRD: Insert Doubleword.
+//
+// Forms:
+//
+// VPINSRD imm8 r32 xmm xmm
+// VPINSRD imm8 m32 xmm xmm
+func VPINSRD(i, mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRD",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRD",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPINSRD: bad operands")
+}
+
+// VPINSRQ: Insert Quadword.
+//
+// Forms:
+//
+// VPINSRQ imm8 r64 xmm xmm
+// VPINSRQ imm8 m64 xmm xmm
+func VPINSRQ(i, mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR64(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRQ",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM64(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRQ",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPINSRQ: bad operands")
+}
+
+// VPINSRW: Insert Word.
+//
+// Forms:
+//
+// VPINSRW imm8 r32 xmm xmm
+// VPINSRW imm8 m16 xmm xmm
+func VPINSRW(i, mr, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsR32(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRW",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM16(mr) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VPINSRW",
+ Operands: []operand.Op{i, mr, x, x1},
+ Inputs: []operand.Op{mr, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPINSRW: bad operands")
+}
+
+// VPMADDUBSW: Multiply and Add Packed Signed and Unsigned Byte Integers.
+//
+// Forms:
+//
+// VPMADDUBSW xmm xmm xmm
+// VPMADDUBSW m128 xmm xmm
+// VPMADDUBSW ymm ymm ymm
+// VPMADDUBSW m256 ymm ymm
+func VPMADDUBSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMADDUBSW: bad operands")
+}
+
+// VPMADDWD: Multiply and Add Packed Signed Word Integers.
+//
+// Forms:
+//
+// VPMADDWD xmm xmm xmm
+// VPMADDWD m128 xmm xmm
+// VPMADDWD ymm ymm ymm
+// VPMADDWD m256 ymm ymm
+func VPMADDWD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMADDWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMADDWD: bad operands")
+}
+
+// VPMASKMOVD: Conditional Move Packed Doubleword Integers.
+//
+// Forms:
+//
+// VPMASKMOVD m128 xmm xmm
+// VPMASKMOVD m256 ymm ymm
+// VPMASKMOVD xmm xmm m128
+// VPMASKMOVD ymm ymm m256
+func VPMASKMOVD(mxy, xy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVD",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMASKMOVD: bad operands")
+}
+
+// VPMASKMOVQ: Conditional Move Packed Quadword Integers.
+//
+// Forms:
+//
+// VPMASKMOVQ m128 xmm xmm
+// VPMASKMOVQ m256 ymm ymm
+// VPMASKMOVQ xmm xmm m128
+// VPMASKMOVQ ymm ymm m256
+func VPMASKMOVQ(mxy, xy, mxy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVQ",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVQ",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsM128(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVQ",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsM256(mxy1):
+ return &intrep.Instruction{
+ Opcode: "VPMASKMOVQ",
+ Operands: []operand.Op{mxy, xy, mxy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{mxy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMASKMOVQ: bad operands")
+}
+
+// VPMAXSB: Maximum of Packed Signed Byte Integers.
+//
+// Forms:
+//
+// VPMAXSB xmm xmm xmm
+// VPMAXSB m128 xmm xmm
+// VPMAXSB ymm ymm ymm
+// VPMAXSB m256 ymm ymm
+func VPMAXSB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMAXSB: bad operands")
+}
+
+// VPMAXSD: Maximum of Packed Signed Doubleword Integers.
+//
+// Forms:
+//
+// VPMAXSD xmm xmm xmm
+// VPMAXSD m128 xmm xmm
+// VPMAXSD ymm ymm ymm
+// VPMAXSD m256 ymm ymm
+func VPMAXSD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMAXSD: bad operands")
+}
+
+// VPMAXSW: Maximum of Packed Signed Word Integers.
+//
+// Forms:
+//
+// VPMAXSW xmm xmm xmm
+// VPMAXSW m128 xmm xmm
+// VPMAXSW ymm ymm ymm
+// VPMAXSW m256 ymm ymm
+func VPMAXSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMAXSW: bad operands")
+}
+
+// VPMAXUB: Maximum of Packed Unsigned Byte Integers.
+//
+// Forms:
+//
+// VPMAXUB xmm xmm xmm
+// VPMAXUB m128 xmm xmm
+// VPMAXUB ymm ymm ymm
+// VPMAXUB m256 ymm ymm
+func VPMAXUB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMAXUB: bad operands")
+}
+
+// VPMAXUD: Maximum of Packed Unsigned Doubleword Integers.
+//
+// Forms:
+//
+// VPMAXUD xmm xmm xmm
+// VPMAXUD m128 xmm xmm
+// VPMAXUD ymm ymm ymm
+// VPMAXUD m256 ymm ymm
+func VPMAXUD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMAXUD: bad operands")
+}
+
+// VPMAXUW: Maximum of Packed Unsigned Word Integers.
+//
+// Forms:
+//
+// VPMAXUW xmm xmm xmm
+// VPMAXUW m128 xmm xmm
+// VPMAXUW ymm ymm ymm
+// VPMAXUW m256 ymm ymm
+func VPMAXUW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMAXUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMAXUW: bad operands")
+}
+
+// VPMINSB: Minimum of Packed Signed Byte Integers.
+//
+// Forms:
+//
+// VPMINSB xmm xmm xmm
+// VPMINSB m128 xmm xmm
+// VPMINSB ymm ymm ymm
+// VPMINSB m256 ymm ymm
+func VPMINSB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMINSB: bad operands")
+}
+
+// VPMINSD: Minimum of Packed Signed Doubleword Integers.
+//
+// Forms:
+//
+// VPMINSD xmm xmm xmm
+// VPMINSD m128 xmm xmm
+// VPMINSD ymm ymm ymm
+// VPMINSD m256 ymm ymm
+func VPMINSD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMINSD: bad operands")
+}
+
+// VPMINSW: Minimum of Packed Signed Word Integers.
+//
+// Forms:
+//
+// VPMINSW xmm xmm xmm
+// VPMINSW m128 xmm xmm
+// VPMINSW ymm ymm ymm
+// VPMINSW m256 ymm ymm
+func VPMINSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMINSW: bad operands")
+}
+
+// VPMINUB: Minimum of Packed Unsigned Byte Integers.
+//
+// Forms:
+//
+// VPMINUB xmm xmm xmm
+// VPMINUB m128 xmm xmm
+// VPMINUB ymm ymm ymm
+// VPMINUB m256 ymm ymm
+func VPMINUB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMINUB: bad operands")
+}
+
+// VPMINUD: Minimum of Packed Unsigned Doubleword Integers.
+//
+// Forms:
+//
+// VPMINUD xmm xmm xmm
+// VPMINUD m128 xmm xmm
+// VPMINUD ymm ymm ymm
+// VPMINUD m256 ymm ymm
+func VPMINUD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMINUD: bad operands")
+}
+
+// VPMINUW: Minimum of Packed Unsigned Word Integers.
+//
+// Forms:
+//
+// VPMINUW xmm xmm xmm
+// VPMINUW m128 xmm xmm
+// VPMINUW ymm ymm ymm
+// VPMINUW m256 ymm ymm
+func VPMINUW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMINUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMINUW: bad operands")
+}
+
+// VPMOVMSKB: Move Byte Mask.
+//
+// Forms:
+//
+// VPMOVMSKB xmm r32
+// VPMOVMSKB ymm r32
+func VPMOVMSKB(xy, r operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(xy) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VPMOVMSKB",
+ Operands: []operand.Op{xy, r},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(xy) && operand.IsR32(r):
+ return &intrep.Instruction{
+ Opcode: "VPMOVMSKB",
+ Operands: []operand.Op{xy, r},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{r},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVMSKB: bad operands")
+}
+
+// VPMOVSXBD: Move Packed Byte Integers to Doubleword Integers with Sign Extension.
+//
+// Forms:
+//
+// VPMOVSXBD xmm xmm
+// VPMOVSXBD m32 xmm
+// VPMOVSXBD xmm ymm
+// VPMOVSXBD m64 ymm
+func VPMOVSXBD(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVSXBD: bad operands")
+}
+
+// VPMOVSXBQ: Move Packed Byte Integers to Quadword Integers with Sign Extension.
+//
+// Forms:
+//
+// VPMOVSXBQ xmm xmm
+// VPMOVSXBQ m16 xmm
+// VPMOVSXBQ xmm ymm
+// VPMOVSXBQ m32 ymm
+func VPMOVSXBQ(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM16(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVSXBQ: bad operands")
+}
+
+// VPMOVSXBW: Move Packed Byte Integers to Word Integers with Sign Extension.
+//
+// Forms:
+//
+// VPMOVSXBW xmm xmm
+// VPMOVSXBW m64 xmm
+// VPMOVSXBW xmm ymm
+// VPMOVSXBW m128 ymm
+func VPMOVSXBW(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVSXBW: bad operands")
+}
+
+// VPMOVSXDQ: Move Packed Doubleword Integers to Quadword Integers with Sign Extension.
+//
+// Forms:
+//
+// VPMOVSXDQ xmm xmm
+// VPMOVSXDQ m64 xmm
+// VPMOVSXDQ xmm ymm
+// VPMOVSXDQ m128 ymm
+func VPMOVSXDQ(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVSXDQ: bad operands")
+}
+
+// VPMOVSXWD: Move Packed Word Integers to Doubleword Integers with Sign Extension.
+//
+// Forms:
+//
+// VPMOVSXWD xmm xmm
+// VPMOVSXWD m64 xmm
+// VPMOVSXWD xmm ymm
+// VPMOVSXWD m128 ymm
+func VPMOVSXWD(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVSXWD: bad operands")
+}
+
+// VPMOVSXWQ: Move Packed Word Integers to Quadword Integers with Sign Extension.
+//
+// Forms:
+//
+// VPMOVSXWQ xmm xmm
+// VPMOVSXWQ m32 xmm
+// VPMOVSXWQ xmm ymm
+// VPMOVSXWQ m64 ymm
+func VPMOVSXWQ(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVSXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVSXWQ: bad operands")
+}
+
+// VPMOVZXBD: Move Packed Byte Integers to Doubleword Integers with Zero Extension.
+//
+// Forms:
+//
+// VPMOVZXBD xmm xmm
+// VPMOVZXBD m32 xmm
+// VPMOVZXBD xmm ymm
+// VPMOVZXBD m64 ymm
+func VPMOVZXBD(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVZXBD: bad operands")
+}
+
+// VPMOVZXBQ: Move Packed Byte Integers to Quadword Integers with Zero Extension.
+//
+// Forms:
+//
+// VPMOVZXBQ xmm xmm
+// VPMOVZXBQ m16 xmm
+// VPMOVZXBQ xmm ymm
+// VPMOVZXBQ m32 ymm
+func VPMOVZXBQ(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM16(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVZXBQ: bad operands")
+}
+
+// VPMOVZXBW: Move Packed Byte Integers to Word Integers with Zero Extension.
+//
+// Forms:
+//
+// VPMOVZXBW xmm xmm
+// VPMOVZXBW m64 xmm
+// VPMOVZXBW xmm ymm
+// VPMOVZXBW m128 ymm
+func VPMOVZXBW(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXBW",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVZXBW: bad operands")
+}
+
+// VPMOVZXDQ: Move Packed Doubleword Integers to Quadword Integers with Zero Extension.
+//
+// Forms:
+//
+// VPMOVZXDQ xmm xmm
+// VPMOVZXDQ m64 xmm
+// VPMOVZXDQ xmm ymm
+// VPMOVZXDQ m128 ymm
+func VPMOVZXDQ(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXDQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVZXDQ: bad operands")
+}
+
+// VPMOVZXWD: Move Packed Word Integers to Doubleword Integers with Zero Extension.
+//
+// Forms:
+//
+// VPMOVZXWD xmm xmm
+// VPMOVZXWD m64 xmm
+// VPMOVZXWD xmm ymm
+// VPMOVZXWD m128 ymm
+func VPMOVZXWD(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWD",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVZXWD: bad operands")
+}
+
+// VPMOVZXWQ: Move Packed Word Integers to Quadword Integers with Zero Extension.
+//
+// Forms:
+//
+// VPMOVZXWQ xmm xmm
+// VPMOVZXWQ m32 xmm
+// VPMOVZXWQ xmm ymm
+// VPMOVZXWQ m64 ymm
+func VPMOVZXWQ(mx, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPMOVZXWQ",
+ Operands: []operand.Op{mx, xy},
+ Inputs: []operand.Op{mx},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMOVZXWQ: bad operands")
+}
+
+// VPMULDQ: Multiply Packed Signed Doubleword Integers and Store Quadword Result.
+//
+// Forms:
+//
+// VPMULDQ xmm xmm xmm
+// VPMULDQ m128 xmm xmm
+// VPMULDQ ymm ymm ymm
+// VPMULDQ m256 ymm ymm
+func VPMULDQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMULDQ: bad operands")
+}
+
+// VPMULHRSW: Packed Multiply Signed Word Integers and Store High Result with Round and Scale.
+//
+// Forms:
+//
+// VPMULHRSW xmm xmm xmm
+// VPMULHRSW m128 xmm xmm
+// VPMULHRSW ymm ymm ymm
+// VPMULHRSW m256 ymm ymm
+func VPMULHRSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHRSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHRSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHRSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHRSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMULHRSW: bad operands")
+}
+
+// VPMULHUW: Multiply Packed Unsigned Word Integers and Store High Result.
+//
+// Forms:
+//
+// VPMULHUW xmm xmm xmm
+// VPMULHUW m128 xmm xmm
+// VPMULHUW ymm ymm ymm
+// VPMULHUW m256 ymm ymm
+func VPMULHUW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHUW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMULHUW: bad operands")
+}
+
+// VPMULHW: Multiply Packed Signed Word Integers and Store High Result.
+//
+// Forms:
+//
+// VPMULHW xmm xmm xmm
+// VPMULHW m128 xmm xmm
+// VPMULHW ymm ymm ymm
+// VPMULHW m256 ymm ymm
+func VPMULHW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULHW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMULHW: bad operands")
+}
+
+// VPMULLD: Multiply Packed Signed Doubleword Integers and Store Low Result.
+//
+// Forms:
+//
+// VPMULLD xmm xmm xmm
+// VPMULLD m128 xmm xmm
+// VPMULLD ymm ymm ymm
+// VPMULLD m256 ymm ymm
+func VPMULLD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMULLD: bad operands")
+}
+
+// VPMULLW: Multiply Packed Signed Word Integers and Store Low Result.
+//
+// Forms:
+//
+// VPMULLW xmm xmm xmm
+// VPMULLW m128 xmm xmm
+// VPMULLW ymm ymm ymm
+// VPMULLW m256 ymm ymm
+func VPMULLW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULLW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMULLW: bad operands")
+}
+
+// VPMULUDQ: Multiply Packed Unsigned Doubleword Integers.
+//
+// Forms:
+//
+// VPMULUDQ xmm xmm xmm
+// VPMULUDQ m128 xmm xmm
+// VPMULUDQ ymm ymm ymm
+// VPMULUDQ m256 ymm ymm
+func VPMULUDQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULUDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULUDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULUDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPMULUDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPMULUDQ: bad operands")
+}
+
+// VPOR: Packed Bitwise Logical OR.
+//
+// Forms:
+//
+// VPOR xmm xmm xmm
+// VPOR m128 xmm xmm
+// VPOR ymm ymm ymm
+// VPOR m256 ymm ymm
+func VPOR(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPOR: bad operands")
+}
+
+// VPSADBW: Compute Sum of Absolute Differences.
+//
+// Forms:
+//
+// VPSADBW xmm xmm xmm
+// VPSADBW m128 xmm xmm
+// VPSADBW ymm ymm ymm
+// VPSADBW m256 ymm ymm
+func VPSADBW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSADBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSADBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSADBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSADBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSADBW: bad operands")
+}
+
+// VPSHUFB: Packed Shuffle Bytes.
+//
+// Forms:
+//
+// VPSHUFB xmm xmm xmm
+// VPSHUFB m128 xmm xmm
+// VPSHUFB ymm ymm ymm
+// VPSHUFB m256 ymm ymm
+func VPSHUFB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSHUFB: bad operands")
+}
+
+// VPSHUFD: Shuffle Packed Doublewords.
+//
+// Forms:
+//
+// VPSHUFD imm8 xmm xmm
+// VPSHUFD imm8 m128 xmm
+// VPSHUFD imm8 ymm ymm
+// VPSHUFD imm8 m256 ymm
+func VPSHUFD(i, mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSHUFD: bad operands")
+}
+
+// VPSHUFHW: Shuffle Packed High Words.
+//
+// Forms:
+//
+// VPSHUFHW imm8 xmm xmm
+// VPSHUFHW imm8 m128 xmm
+// VPSHUFHW imm8 ymm ymm
+// VPSHUFHW imm8 m256 ymm
+func VPSHUFHW(i, mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFHW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFHW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFHW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFHW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSHUFHW: bad operands")
+}
+
+// VPSHUFLW: Shuffle Packed Low Words.
+//
+// Forms:
+//
+// VPSHUFLW imm8 xmm xmm
+// VPSHUFLW imm8 m128 xmm
+// VPSHUFLW imm8 ymm ymm
+// VPSHUFLW imm8 m256 ymm
+func VPSHUFLW(i, mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFLW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFLW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFLW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPSHUFLW",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSHUFLW: bad operands")
+}
+
+// VPSIGNB: Packed Sign of Byte Integers.
+//
+// Forms:
+//
+// VPSIGNB xmm xmm xmm
+// VPSIGNB m128 xmm xmm
+// VPSIGNB ymm ymm ymm
+// VPSIGNB m256 ymm ymm
+func VPSIGNB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSIGNB: bad operands")
+}
+
+// VPSIGND: Packed Sign of Doubleword Integers.
+//
+// Forms:
+//
+// VPSIGND xmm xmm xmm
+// VPSIGND m128 xmm xmm
+// VPSIGND ymm ymm ymm
+// VPSIGND m256 ymm ymm
+func VPSIGND(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGND",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSIGND: bad operands")
+}
+
+// VPSIGNW: Packed Sign of Word Integers.
+//
+// Forms:
+//
+// VPSIGNW xmm xmm xmm
+// VPSIGNW m128 xmm xmm
+// VPSIGNW ymm ymm ymm
+// VPSIGNW m256 ymm ymm
+func VPSIGNW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSIGNW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSIGNW: bad operands")
+}
+
+// VPSLLD: Shift Packed Doubleword Data Left Logical.
+//
+// Forms:
+//
+// VPSLLD imm8 xmm xmm
+// VPSLLD xmm xmm xmm
+// VPSLLD m128 xmm xmm
+// VPSLLD imm8 ymm ymm
+// VPSLLD xmm ymm ymm
+// VPSLLD m128 ymm ymm
+func VPSLLD(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSLLD: bad operands")
+}
+
+// VPSLLDQ: Shift Packed Double Quadword Left Logical.
+//
+// Forms:
+//
+// VPSLLDQ imm8 xmm xmm
+// VPSLLDQ imm8 ymm ymm
+func VPSLLDQ(i, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLDQ",
+ Operands: []operand.Op{i, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLDQ",
+ Operands: []operand.Op{i, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSLLDQ: bad operands")
+}
+
+// VPSLLQ: Shift Packed Quadword Data Left Logical.
+//
+// Forms:
+//
+// VPSLLQ imm8 xmm xmm
+// VPSLLQ xmm xmm xmm
+// VPSLLQ m128 xmm xmm
+// VPSLLQ imm8 ymm ymm
+// VPSLLQ xmm ymm ymm
+// VPSLLQ m128 ymm ymm
+func VPSLLQ(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSLLQ: bad operands")
+}
+
+// VPSLLVD: Variable Shift Packed Doubleword Data Left Logical.
+//
+// Forms:
+//
+// VPSLLVD xmm xmm xmm
+// VPSLLVD m128 xmm xmm
+// VPSLLVD ymm ymm ymm
+// VPSLLVD m256 ymm ymm
+func VPSLLVD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSLLVD: bad operands")
+}
+
+// VPSLLVQ: Variable Shift Packed Quadword Data Left Logical.
+//
+// Forms:
+//
+// VPSLLVQ xmm xmm xmm
+// VPSLLVQ m128 xmm xmm
+// VPSLLVQ ymm ymm ymm
+// VPSLLVQ m256 ymm ymm
+func VPSLLVQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSLLVQ: bad operands")
+}
+
+// VPSLLW: Shift Packed Word Data Left Logical.
+//
+// Forms:
+//
+// VPSLLW imm8 xmm xmm
+// VPSLLW xmm xmm xmm
+// VPSLLW m128 xmm xmm
+// VPSLLW imm8 ymm ymm
+// VPSLLW xmm ymm ymm
+// VPSLLW m128 ymm ymm
+func VPSLLW(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSLLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSLLW: bad operands")
+}
+
+// VPSRAD: Shift Packed Doubleword Data Right Arithmetic.
+//
+// Forms:
+//
+// VPSRAD imm8 xmm xmm
+// VPSRAD xmm xmm xmm
+// VPSRAD m128 xmm xmm
+// VPSRAD imm8 ymm ymm
+// VPSRAD xmm ymm ymm
+// VPSRAD m128 ymm ymm
+func VPSRAD(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRAD: bad operands")
+}
+
+// VPSRAVD: Variable Shift Packed Doubleword Data Right Arithmetic.
+//
+// Forms:
+//
+// VPSRAVD xmm xmm xmm
+// VPSRAVD m128 xmm xmm
+// VPSRAVD ymm ymm ymm
+// VPSRAVD m256 ymm ymm
+func VPSRAVD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRAVD: bad operands")
+}
+
+// VPSRAW: Shift Packed Word Data Right Arithmetic.
+//
+// Forms:
+//
+// VPSRAW imm8 xmm xmm
+// VPSRAW xmm xmm xmm
+// VPSRAW m128 xmm xmm
+// VPSRAW imm8 ymm ymm
+// VPSRAW xmm ymm ymm
+// VPSRAW m128 ymm ymm
+func VPSRAW(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRAW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRAW: bad operands")
+}
+
+// VPSRLD: Shift Packed Doubleword Data Right Logical.
+//
+// Forms:
+//
+// VPSRLD imm8 xmm xmm
+// VPSRLD xmm xmm xmm
+// VPSRLD m128 xmm xmm
+// VPSRLD imm8 ymm ymm
+// VPSRLD xmm ymm ymm
+// VPSRLD m128 ymm ymm
+func VPSRLD(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLD",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRLD: bad operands")
+}
+
+// VPSRLDQ: Shift Packed Double Quadword Right Logical.
+//
+// Forms:
+//
+// VPSRLDQ imm8 xmm xmm
+// VPSRLDQ imm8 ymm ymm
+func VPSRLDQ(i, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLDQ",
+ Operands: []operand.Op{i, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLDQ",
+ Operands: []operand.Op{i, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRLDQ: bad operands")
+}
+
+// VPSRLQ: Shift Packed Quadword Data Right Logical.
+//
+// Forms:
+//
+// VPSRLQ imm8 xmm xmm
+// VPSRLQ xmm xmm xmm
+// VPSRLQ m128 xmm xmm
+// VPSRLQ imm8 ymm ymm
+// VPSRLQ xmm ymm ymm
+// VPSRLQ m128 ymm ymm
+func VPSRLQ(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLQ",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRLQ: bad operands")
+}
+
+// VPSRLVD: Variable Shift Packed Doubleword Data Right Logical.
+//
+// Forms:
+//
+// VPSRLVD xmm xmm xmm
+// VPSRLVD m128 xmm xmm
+// VPSRLVD ymm ymm ymm
+// VPSRLVD m256 ymm ymm
+func VPSRLVD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRLVD: bad operands")
+}
+
+// VPSRLVQ: Variable Shift Packed Quadword Data Right Logical.
+//
+// Forms:
+//
+// VPSRLVQ xmm xmm xmm
+// VPSRLVQ m128 xmm xmm
+// VPSRLVQ ymm ymm ymm
+// VPSRLVQ m256 ymm ymm
+func VPSRLVQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLVQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRLVQ: bad operands")
+}
+
+// VPSRLW: Shift Packed Word Data Right Logical.
+//
+// Forms:
+//
+// VPSRLW imm8 xmm xmm
+// VPSRLW xmm xmm xmm
+// VPSRLW m128 xmm xmm
+// VPSRLW imm8 ymm ymm
+// VPSRLW xmm ymm ymm
+// VPSRLW m128 ymm ymm
+func VPSRLW(imx, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsXMM(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM128(imx) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSRLW",
+ Operands: []operand.Op{imx, xy, xy1},
+ Inputs: []operand.Op{imx, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSRLW: bad operands")
+}
+
+// VPSUBB: Subtract Packed Byte Integers.
+//
+// Forms:
+//
+// VPSUBB xmm xmm xmm
+// VPSUBB m128 xmm xmm
+// VPSUBB ymm ymm ymm
+// VPSUBB m256 ymm ymm
+func VPSUBB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBB: bad operands")
+}
+
+// VPSUBD: Subtract Packed Doubleword Integers.
+//
+// Forms:
+//
+// VPSUBD xmm xmm xmm
+// VPSUBD m128 xmm xmm
+// VPSUBD ymm ymm ymm
+// VPSUBD m256 ymm ymm
+func VPSUBD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBD: bad operands")
+}
+
+// VPSUBQ: Subtract Packed Quadword Integers.
+//
+// Forms:
+//
+// VPSUBQ xmm xmm xmm
+// VPSUBQ m128 xmm xmm
+// VPSUBQ ymm ymm ymm
+// VPSUBQ m256 ymm ymm
+func VPSUBQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBQ: bad operands")
+}
+
+// VPSUBSB: Subtract Packed Signed Byte Integers with Signed Saturation.
+//
+// Forms:
+//
+// VPSUBSB xmm xmm xmm
+// VPSUBSB m128 xmm xmm
+// VPSUBSB ymm ymm ymm
+// VPSUBSB m256 ymm ymm
+func VPSUBSB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBSB: bad operands")
+}
+
+// VPSUBSW: Subtract Packed Signed Word Integers with Signed Saturation.
+//
+// Forms:
+//
+// VPSUBSW xmm xmm xmm
+// VPSUBSW m128 xmm xmm
+// VPSUBSW ymm ymm ymm
+// VPSUBSW m256 ymm ymm
+func VPSUBSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBSW: bad operands")
+}
+
+// VPSUBUSB: Subtract Packed Unsigned Byte Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// VPSUBUSB xmm xmm xmm
+// VPSUBUSB m128 xmm xmm
+// VPSUBUSB ymm ymm ymm
+// VPSUBUSB m256 ymm ymm
+func VPSUBUSB(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSB",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBUSB: bad operands")
+}
+
+// VPSUBUSW: Subtract Packed Unsigned Word Integers with Unsigned Saturation.
+//
+// Forms:
+//
+// VPSUBUSW xmm xmm xmm
+// VPSUBUSW m128 xmm xmm
+// VPSUBUSW ymm ymm ymm
+// VPSUBUSW m256 ymm ymm
+func VPSUBUSW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBUSW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBUSW: bad operands")
+}
+
+// VPSUBW: Subtract Packed Word Integers.
+//
+// Forms:
+//
+// VPSUBW xmm xmm xmm
+// VPSUBW m128 xmm xmm
+// VPSUBW ymm ymm ymm
+// VPSUBW m256 ymm ymm
+func VPSUBW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPSUBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPSUBW: bad operands")
+}
+
+// VPTEST: Packed Logical Compare.
+//
+// Forms:
+//
+// VPTEST xmm xmm
+// VPTEST m128 xmm
+// VPTEST ymm ymm
+// VPTEST m256 ymm
+func VPTEST(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPTEST",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPTEST",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPTEST",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VPTEST",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VPTEST: bad operands")
+}
+
+// VPUNPCKHBW: Unpack and Interleave High-Order Bytes into Words.
+//
+// Forms:
+//
+// VPUNPCKHBW xmm xmm xmm
+// VPUNPCKHBW m128 xmm xmm
+// VPUNPCKHBW ymm ymm ymm
+// VPUNPCKHBW m256 ymm ymm
+func VPUNPCKHBW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKHBW: bad operands")
+}
+
+// VPUNPCKHDQ: Unpack and Interleave High-Order Doublewords into Quadwords.
+//
+// Forms:
+//
+// VPUNPCKHDQ xmm xmm xmm
+// VPUNPCKHDQ m128 xmm xmm
+// VPUNPCKHDQ ymm ymm ymm
+// VPUNPCKHDQ m256 ymm ymm
+func VPUNPCKHDQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKHDQ: bad operands")
+}
+
+// VPUNPCKHQDQ: Unpack and Interleave High-Order Quadwords into Double Quadwords.
+//
+// Forms:
+//
+// VPUNPCKHQDQ xmm xmm xmm
+// VPUNPCKHQDQ m128 xmm xmm
+// VPUNPCKHQDQ ymm ymm ymm
+// VPUNPCKHQDQ m256 ymm ymm
+func VPUNPCKHQDQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKHQDQ: bad operands")
+}
+
+// VPUNPCKHWD: Unpack and Interleave High-Order Words into Doublewords.
+//
+// Forms:
+//
+// VPUNPCKHWD xmm xmm xmm
+// VPUNPCKHWD m128 xmm xmm
+// VPUNPCKHWD ymm ymm ymm
+// VPUNPCKHWD m256 ymm ymm
+func VPUNPCKHWD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKHWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKHWD: bad operands")
+}
+
+// VPUNPCKLBW: Unpack and Interleave Low-Order Bytes into Words.
+//
+// Forms:
+//
+// VPUNPCKLBW xmm xmm xmm
+// VPUNPCKLBW m128 xmm xmm
+// VPUNPCKLBW ymm ymm ymm
+// VPUNPCKLBW m256 ymm ymm
+func VPUNPCKLBW(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLBW",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKLBW: bad operands")
+}
+
+// VPUNPCKLDQ: Unpack and Interleave Low-Order Doublewords into Quadwords.
+//
+// Forms:
+//
+// VPUNPCKLDQ xmm xmm xmm
+// VPUNPCKLDQ m128 xmm xmm
+// VPUNPCKLDQ ymm ymm ymm
+// VPUNPCKLDQ m256 ymm ymm
+func VPUNPCKLDQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKLDQ: bad operands")
+}
+
+// VPUNPCKLQDQ: Unpack and Interleave Low-Order Quadwords into Double Quadwords.
+//
+// Forms:
+//
+// VPUNPCKLQDQ xmm xmm xmm
+// VPUNPCKLQDQ m128 xmm xmm
+// VPUNPCKLQDQ ymm ymm ymm
+// VPUNPCKLQDQ m256 ymm ymm
+func VPUNPCKLQDQ(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLQDQ",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKLQDQ: bad operands")
+}
+
+// VPUNPCKLWD: Unpack and Interleave Low-Order Words into Doublewords.
+//
+// Forms:
+//
+// VPUNPCKLWD xmm xmm xmm
+// VPUNPCKLWD m128 xmm xmm
+// VPUNPCKLWD ymm ymm ymm
+// VPUNPCKLWD m256 ymm ymm
+func VPUNPCKLWD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPUNPCKLWD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPUNPCKLWD: bad operands")
+}
+
+// VPXOR: Packed Bitwise Logical Exclusive OR.
+//
+// Forms:
+//
+// VPXOR xmm xmm xmm
+// VPXOR m128 xmm xmm
+// VPXOR ymm ymm ymm
+// VPXOR m256 ymm ymm
+func VPXOR(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPXOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPXOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPXOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VPXOR",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX2"},
+ }, nil
+ }
+ return nil, errors.New("VPXOR: bad operands")
+}
+
+// VRCPPS: Compute Approximate Reciprocals of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VRCPPS xmm xmm
+// VRCPPS m128 xmm
+// VRCPPS ymm ymm
+// VRCPPS m256 ymm
+func VRCPPS(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRCPPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRCPPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRCPPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRCPPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VRCPPS: bad operands")
+}
+
+// VRCPSS: Compute Approximate Reciprocal of Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VRCPSS xmm xmm xmm
+// VRCPSS m32 xmm xmm
+func VRCPSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VRCPSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VRCPSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VRCPSS: bad operands")
+}
+
+// VROUNDPD: Round Packed Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// VROUNDPD imm8 xmm xmm
+// VROUNDPD imm8 m128 xmm
+// VROUNDPD imm8 ymm ymm
+// VROUNDPD imm8 m256 ymm
+func VROUNDPD(i, mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPD",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VROUNDPD: bad operands")
+}
+
+// VROUNDPS: Round Packed Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// VROUNDPS imm8 xmm xmm
+// VROUNDPS imm8 m128 xmm
+// VROUNDPS imm8 ymm ymm
+// VROUNDPS imm8 m256 ymm
+func VROUNDPS(i, mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPS",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPS",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPS",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VROUNDPS",
+ Operands: []operand.Op{i, mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VROUNDPS: bad operands")
+}
+
+// VROUNDSD: Round Scalar Double Precision Floating-Point Values.
+//
+// Forms:
+//
+// VROUNDSD imm8 xmm xmm xmm
+// VROUNDSD imm8 m64 xmm xmm
+func VROUNDSD(i, mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VROUNDSD",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VROUNDSD",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VROUNDSD: bad operands")
+}
+
+// VROUNDSS: Round Scalar Single Precision Floating-Point Values.
+//
+// Forms:
+//
+// VROUNDSS imm8 xmm xmm xmm
+// VROUNDSS imm8 m32 xmm xmm
+func VROUNDSS(i, mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VROUNDSS",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VROUNDSS",
+ Operands: []operand.Op{i, mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VROUNDSS: bad operands")
+}
+
+// VRSQRTPS: Compute Reciprocals of Square Roots of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VRSQRTPS xmm xmm
+// VRSQRTPS m128 xmm
+// VRSQRTPS ymm ymm
+// VRSQRTPS m256 ymm
+func VRSQRTPS(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VRSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VRSQRTPS: bad operands")
+}
+
+// VRSQRTSS: Compute Reciprocal of Square Root of Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VRSQRTSS xmm xmm xmm
+// VRSQRTSS m32 xmm xmm
+func VRSQRTSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VRSQRTSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VRSQRTSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VRSQRTSS: bad operands")
+}
+
+// VSHUFPD: Shuffle Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSHUFPD imm8 xmm xmm xmm
+// VSHUFPD imm8 m128 xmm xmm
+// VSHUFPD imm8 ymm ymm ymm
+// VSHUFPD imm8 m256 ymm ymm
+func VSHUFPD(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPD",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSHUFPD: bad operands")
+}
+
+// VSHUFPS: Shuffle Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSHUFPS imm8 xmm xmm xmm
+// VSHUFPS imm8 m128 xmm xmm
+// VSHUFPS imm8 ymm ymm ymm
+// VSHUFPS imm8 m256 ymm ymm
+func VSHUFPS(i, mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(i) && operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsIMM8(i) && operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSHUFPS",
+ Operands: []operand.Op{i, mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSHUFPS: bad operands")
+}
+
+// VSQRTPD: Compute Square Roots of Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSQRTPD xmm xmm
+// VSQRTPD m128 xmm
+// VSQRTPD ymm ymm
+// VSQRTPD m256 ymm
+func VSQRTPD(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSQRTPD: bad operands")
+}
+
+// VSQRTPS: Compute Square Roots of Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSQRTPS xmm xmm
+// VSQRTPS m128 xmm
+// VSQRTPS ymm ymm
+// VSQRTPS m256 ymm
+func VSQRTPS(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VSQRTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy},
+ Outputs: []operand.Op{xy},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSQRTPS: bad operands")
+}
+
+// VSQRTSD: Compute Square Root of Scalar Double-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VSQRTSD xmm xmm xmm
+// VSQRTSD m64 xmm xmm
+func VSQRTSD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSQRTSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSQRTSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSQRTSD: bad operands")
+}
+
+// VSQRTSS: Compute Square Root of Scalar Single-Precision Floating-Point Value.
+//
+// Forms:
+//
+// VSQRTSS xmm xmm xmm
+// VSQRTSS m32 xmm xmm
+func VSQRTSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSQRTSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSQRTSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSQRTSS: bad operands")
+}
+
+// VSTMXCSR: Store MXCSR Register State.
+//
+// Forms:
+//
+// VSTMXCSR m32
+func VSTMXCSR(m operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsM32(m):
+ return &intrep.Instruction{
+ Opcode: "VSTMXCSR",
+ Operands: []operand.Op{m},
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{m},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSTMXCSR: bad operands")
+}
+
+// VSUBPD: Subtract Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSUBPD xmm xmm xmm
+// VSUBPD m128 xmm xmm
+// VSUBPD ymm ymm ymm
+// VSUBPD m256 ymm ymm
+func VSUBPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSUBPD: bad operands")
+}
+
+// VSUBPS: Subtract Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSUBPS xmm xmm xmm
+// VSUBPS m128 xmm xmm
+// VSUBPS ymm ymm ymm
+// VSUBPS m256 ymm ymm
+func VSUBPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VSUBPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSUBPS: bad operands")
+}
+
+// VSUBSD: Subtract Scalar Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSUBSD xmm xmm xmm
+// VSUBSD m64 xmm xmm
+func VSUBSD(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSUBSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSUBSD",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSUBSD: bad operands")
+}
+
+// VSUBSS: Subtract Scalar Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VSUBSS xmm xmm xmm
+// VSUBSS m32 xmm xmm
+func VSUBSS(mx, x, x1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSUBSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x) && operand.IsXMM(x1):
+ return &intrep.Instruction{
+ Opcode: "VSUBSS",
+ Operands: []operand.Op{mx, x, x1},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VSUBSS: bad operands")
+}
+
+// VTESTPD: Packed Double-Precision Floating-Point Bit Test.
+//
+// Forms:
+//
+// VTESTPD xmm xmm
+// VTESTPD m128 xmm
+// VTESTPD ymm ymm
+// VTESTPD m256 ymm
+func VTESTPD(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPD",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VTESTPD: bad operands")
+}
+
+// VTESTPS: Packed Single-Precision Floating-Point Bit Test.
+//
+// Forms:
+//
+// VTESTPS xmm xmm
+// VTESTPS m128 xmm
+// VTESTPS ymm ymm
+// VTESTPS m256 ymm
+func VTESTPS(mxy, xy operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy):
+ return &intrep.Instruction{
+ Opcode: "VTESTPS",
+ Operands: []operand.Op{mxy, xy},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VTESTPS: bad operands")
+}
+
+// VUCOMISD: Unordered Compare Scalar Double-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// VUCOMISD xmm xmm
+// VUCOMISD m64 xmm
+func VUCOMISD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VUCOMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM64(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VUCOMISD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VUCOMISD: bad operands")
+}
+
+// VUCOMISS: Unordered Compare Scalar Single-Precision Floating-Point Values and Set EFLAGS.
+//
+// Forms:
+//
+// VUCOMISS xmm xmm
+// VUCOMISS m32 xmm
+func VUCOMISS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VUCOMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM32(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "VUCOMISS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VUCOMISS: bad operands")
+}
+
+// VUNPCKHPD: Unpack and Interleave High Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VUNPCKHPD xmm xmm xmm
+// VUNPCKHPD m128 xmm xmm
+// VUNPCKHPD ymm ymm ymm
+// VUNPCKHPD m256 ymm ymm
+func VUNPCKHPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VUNPCKHPD: bad operands")
+}
+
+// VUNPCKHPS: Unpack and Interleave High Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VUNPCKHPS xmm xmm xmm
+// VUNPCKHPS m128 xmm xmm
+// VUNPCKHPS ymm ymm ymm
+// VUNPCKHPS m256 ymm ymm
+func VUNPCKHPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKHPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VUNPCKHPS: bad operands")
+}
+
+// VUNPCKLPD: Unpack and Interleave Low Packed Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VUNPCKLPD xmm xmm xmm
+// VUNPCKLPD m128 xmm xmm
+// VUNPCKLPD ymm ymm ymm
+// VUNPCKLPD m256 ymm ymm
+func VUNPCKLPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VUNPCKLPD: bad operands")
+}
+
+// VUNPCKLPS: Unpack and Interleave Low Packed Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VUNPCKLPS xmm xmm xmm
+// VUNPCKLPS m128 xmm xmm
+// VUNPCKLPS ymm ymm ymm
+// VUNPCKLPS m256 ymm ymm
+func VUNPCKLPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VUNPCKLPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VUNPCKLPS: bad operands")
+}
+
+// VXORPD: Bitwise Logical XOR for Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VXORPD xmm xmm xmm
+// VXORPD m128 xmm xmm
+// VXORPD ymm ymm ymm
+// VXORPD m256 ymm ymm
+func VXORPD(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPD",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VXORPD: bad operands")
+}
+
+// VXORPS: Bitwise Logical XOR for Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// VXORPS xmm xmm xmm
+// VXORPS m128 xmm xmm
+// VXORPS ymm ymm ymm
+// VXORPS m256 ymm ymm
+func VXORPS(mxy, xy, xy1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mxy) && operand.IsXMM(xy) && operand.IsXMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ case operand.IsYMM(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM256(mxy) && operand.IsYMM(xy) && operand.IsYMM(xy1):
+ return &intrep.Instruction{
+ Opcode: "VXORPS",
+ Operands: []operand.Op{mxy, xy, xy1},
+ Inputs: []operand.Op{mxy, xy},
+ Outputs: []operand.Op{xy1},
+ ISA: []string{"AVX"},
+ }, nil
+ }
+ return nil, errors.New("VXORPS: bad operands")
+}
+
+// VZEROALL: Zero All YMM Registers.
+//
+// Forms:
+//
+// VZEROALL
+func VZEROALL() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "VZEROALL",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+}
+
+// VZEROUPPER: Zero Upper Bits of YMM Registers.
+//
+// Forms:
+//
+// VZEROUPPER
+func VZEROUPPER() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "VZEROUPPER",
+ Operands: nil,
+ Inputs: []operand.Op{},
+ Outputs: []operand.Op{},
+ ISA: []string{"AVX"},
+ }, nil
+}
+
+// XADDB: Exchange and Add.
+//
+// Forms:
+//
+// XADDB r8 r8
+// XADDB r8 m8
+func XADDB(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(r) && operand.IsR8(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDB",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ case operand.IsR8(r) && operand.IsM8(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDB",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ }
+ return nil, errors.New("XADDB: bad operands")
+}
+
+// XADDL: Exchange and Add.
+//
+// Forms:
+//
+// XADDL r32 r32
+// XADDL r32 m32
+func XADDL(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(r) && operand.IsR32(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDL",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ case operand.IsR32(r) && operand.IsM32(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDL",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ }
+ return nil, errors.New("XADDL: bad operands")
+}
+
+// XADDQ: Exchange and Add.
+//
+// Forms:
+//
+// XADDQ r64 r64
+// XADDQ r64 m64
+func XADDQ(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(r) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDQ",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ case operand.IsR64(r) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDQ",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ }
+ return nil, errors.New("XADDQ: bad operands")
+}
+
+// XADDW: Exchange and Add.
+//
+// Forms:
+//
+// XADDW r16 r16
+// XADDW r16 m16
+func XADDW(r, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(r) && operand.IsR16(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDW",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ case operand.IsR16(r) && operand.IsM16(mr):
+ return &intrep.Instruction{
+ Opcode: "XADDW",
+ Operands: []operand.Op{r, mr},
+ Inputs: []operand.Op{r, mr},
+ Outputs: []operand.Op{r, mr},
+ }, nil
+ }
+ return nil, errors.New("XADDW: bad operands")
+}
+
+// XCHGB: Exchange Register/Memory with Register.
+//
+// Forms:
+//
+// XCHGB r8 r8
+// XCHGB m8 r8
+// XCHGB r8 m8
+func XCHGB(mr, mr1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR8(mr) && operand.IsR8(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGB",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ case operand.IsM8(mr) && operand.IsR8(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGB",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ case operand.IsR8(mr) && operand.IsM8(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGB",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ }
+ return nil, errors.New("XCHGB: bad operands")
+}
+
+// XCHGL: Exchange Register/Memory with Register.
+//
+// Forms:
+//
+// XCHGL r32 eax
+// XCHGL eax r32
+// XCHGL r32 r32
+// XCHGL m32 r32
+// XCHGL r32 m32
+func XCHGL(emr, emr1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR32(emr) && operand.IsEAX(emr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGL",
+ Operands: []operand.Op{emr, emr1},
+ Inputs: []operand.Op{emr, emr1},
+ Outputs: []operand.Op{emr, emr1},
+ }, nil
+ case operand.IsEAX(emr) && operand.IsR32(emr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGL",
+ Operands: []operand.Op{emr, emr1},
+ Inputs: []operand.Op{emr, emr1},
+ Outputs: []operand.Op{emr, emr1},
+ }, nil
+ case operand.IsR32(emr) && operand.IsR32(emr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGL",
+ Operands: []operand.Op{emr, emr1},
+ Inputs: []operand.Op{emr, emr1},
+ Outputs: []operand.Op{emr, emr1},
+ }, nil
+ case operand.IsM32(emr) && operand.IsR32(emr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGL",
+ Operands: []operand.Op{emr, emr1},
+ Inputs: []operand.Op{emr, emr1},
+ Outputs: []operand.Op{emr, emr1},
+ }, nil
+ case operand.IsR32(emr) && operand.IsM32(emr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGL",
+ Operands: []operand.Op{emr, emr1},
+ Inputs: []operand.Op{emr, emr1},
+ Outputs: []operand.Op{emr, emr1},
+ }, nil
+ }
+ return nil, errors.New("XCHGL: bad operands")
+}
+
+// XCHGQ: Exchange Register/Memory with Register.
+//
+// Forms:
+//
+// XCHGQ r64 rax
+// XCHGQ rax r64
+// XCHGQ r64 r64
+// XCHGQ m64 r64
+// XCHGQ r64 m64
+func XCHGQ(mr, mr1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR64(mr) && operand.IsRAX(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGQ",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ case operand.IsRAX(mr) && operand.IsR64(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGQ",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ case operand.IsR64(mr) && operand.IsR64(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGQ",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ case operand.IsM64(mr) && operand.IsR64(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGQ",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ case operand.IsR64(mr) && operand.IsM64(mr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGQ",
+ Operands: []operand.Op{mr, mr1},
+ Inputs: []operand.Op{mr, mr1},
+ Outputs: []operand.Op{mr, mr1},
+ }, nil
+ }
+ return nil, errors.New("XCHGQ: bad operands")
+}
+
+// XCHGW: Exchange Register/Memory with Register.
+//
+// Forms:
+//
+// XCHGW r16 ax
+// XCHGW ax r16
+// XCHGW r16 r16
+// XCHGW m16 r16
+// XCHGW r16 m16
+func XCHGW(amr, amr1 operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsR16(amr) && operand.IsAX(amr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGW",
+ Operands: []operand.Op{amr, amr1},
+ Inputs: []operand.Op{amr, amr1},
+ Outputs: []operand.Op{amr, amr1},
+ }, nil
+ case operand.IsAX(amr) && operand.IsR16(amr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGW",
+ Operands: []operand.Op{amr, amr1},
+ Inputs: []operand.Op{amr, amr1},
+ Outputs: []operand.Op{amr, amr1},
+ }, nil
+ case operand.IsR16(amr) && operand.IsR16(amr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGW",
+ Operands: []operand.Op{amr, amr1},
+ Inputs: []operand.Op{amr, amr1},
+ Outputs: []operand.Op{amr, amr1},
+ }, nil
+ case operand.IsM16(amr) && operand.IsR16(amr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGW",
+ Operands: []operand.Op{amr, amr1},
+ Inputs: []operand.Op{amr, amr1},
+ Outputs: []operand.Op{amr, amr1},
+ }, nil
+ case operand.IsR16(amr) && operand.IsM16(amr1):
+ return &intrep.Instruction{
+ Opcode: "XCHGW",
+ Operands: []operand.Op{amr, amr1},
+ Inputs: []operand.Op{amr, amr1},
+ Outputs: []operand.Op{amr, amr1},
+ }, nil
+ }
+ return nil, errors.New("XCHGW: bad operands")
+}
+
+// XGETBV: Get Value of Extended Control Register.
+//
+// Forms:
+//
+// XGETBV
+func XGETBV() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "XGETBV",
+ Operands: nil,
+ Inputs: []operand.Op{reg.ECX},
+ Outputs: []operand.Op{reg.EAX, reg.EDX},
+ }, nil
+}
+
+// XLAT: Table Look-up Translation.
+//
+// Forms:
+//
+// XLAT
+func XLAT() (*intrep.Instruction, error) {
+ return &intrep.Instruction{
+ Opcode: "XLAT",
+ Operands: nil,
+ Inputs: []operand.Op{reg.AL, reg.EBX},
+ Outputs: []operand.Op{reg.AL},
+ }, nil
+}
+
+// XORB: Logical Exclusive OR.
+//
+// Forms:
+//
+// XORB imm8 al
+// XORB imm8 r8
+// XORB r8 r8
+// XORB m8 r8
+// XORB imm8 m8
+// XORB r8 m8
+func XORB(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM8(imr) && operand.IsAL(amr):
+ return &intrep.Instruction{
+ Opcode: "XORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "XORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "XORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM8(imr) && operand.IsR8(amr):
+ return &intrep.Instruction{
+ Opcode: "XORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "XORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR8(imr) && operand.IsM8(amr):
+ return &intrep.Instruction{
+ Opcode: "XORB",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("XORB: bad operands")
+}
+
+// XORL: Logical Exclusive OR.
+//
+// Forms:
+//
+// XORL imm32 eax
+// XORL imm8 r32
+// XORL imm32 r32
+// XORL r32 r32
+// XORL m32 r32
+// XORL imm8 m32
+// XORL imm32 m32
+// XORL r32 m32
+func XORL(imr, emr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsEAX(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM32(imr) && operand.IsR32(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ case operand.IsR32(imr) && operand.IsM32(emr):
+ return &intrep.Instruction{
+ Opcode: "XORL",
+ Operands: []operand.Op{imr, emr},
+ Inputs: []operand.Op{imr, emr},
+ Outputs: []operand.Op{emr},
+ }, nil
+ }
+ return nil, errors.New("XORL: bad operands")
+}
+
+// XORPD: Bitwise Logical XOR for Double-Precision Floating-Point Values.
+//
+// Forms:
+//
+// XORPD xmm xmm
+// XORPD m128 xmm
+func XORPD(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "XORPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "XORPD",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE2"},
+ }, nil
+ }
+ return nil, errors.New("XORPD: bad operands")
+}
+
+// XORPS: Bitwise Logical XOR for Single-Precision Floating-Point Values.
+//
+// Forms:
+//
+// XORPS xmm xmm
+// XORPS m128 xmm
+func XORPS(mx, x operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsXMM(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "XORPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM128(mx) && operand.IsXMM(x):
+ return &intrep.Instruction{
+ Opcode: "XORPS",
+ Operands: []operand.Op{mx, x},
+ Inputs: []operand.Op{mx, x},
+ Outputs: []operand.Op{x},
+ ISA: []string{"SSE"},
+ }, nil
+ }
+ return nil, errors.New("XORPS: bad operands")
+}
+
+// XORQ: Logical Exclusive OR.
+//
+// Forms:
+//
+// XORQ imm32 rax
+// XORQ imm8 r64
+// XORQ imm32 r64
+// XORQ r64 r64
+// XORQ m64 r64
+// XORQ imm8 m64
+// XORQ imm32 m64
+// XORQ r64 m64
+func XORQ(imr, mr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM32(imr) && operand.IsRAX(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM64(imr) && operand.IsR64(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsIMM32(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ case operand.IsR64(imr) && operand.IsM64(mr):
+ return &intrep.Instruction{
+ Opcode: "XORQ",
+ Operands: []operand.Op{imr, mr},
+ Inputs: []operand.Op{imr, mr},
+ Outputs: []operand.Op{mr},
+ }, nil
+ }
+ return nil, errors.New("XORQ: bad operands")
+}
+
+// XORW: Logical Exclusive OR.
+//
+// Forms:
+//
+// XORW imm16 ax
+// XORW imm8 r16
+// XORW imm16 r16
+// XORW r16 r16
+// XORW m16 r16
+// XORW imm8 m16
+// XORW imm16 m16
+// XORW r16 m16
+func XORW(imr, amr operand.Op) (*intrep.Instruction, error) {
+ switch {
+ case operand.IsIMM16(imr) && operand.IsAX(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ CancellingInputs: true,
+ }, nil
+ case operand.IsM16(imr) && operand.IsR16(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM8(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsIMM16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ case operand.IsR16(imr) && operand.IsM16(amr):
+ return &intrep.Instruction{
+ Opcode: "XORW",
+ Operands: []operand.Op{imr, amr},
+ Inputs: []operand.Op{imr, amr},
+ Outputs: []operand.Op{amr},
+ }, nil
+ }
+ return nil, errors.New("XORW: bad operands")
+}
diff --git a/vendor/github.com/pion/datachannel/.gitignore b/vendor/github.com/pion/datachannel/.gitignore
new file mode 100644
index 0000000..23501a3
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/.gitignore
@@ -0,0 +1,2 @@
+# vim temporary files
+*.sw[poe]
diff --git a/vendor/github.com/pion/datachannel/.golangci.yml b/vendor/github.com/pion/datachannel/.golangci.yml
new file mode 100644
index 0000000..570f17b
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/.golangci.yml
@@ -0,0 +1,8 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/datachannel/DESIGN.md b/vendor/github.com/pion/datachannel/DESIGN.md
new file mode 100644
index 0000000..55d6c8f
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/DESIGN.md
@@ -0,0 +1,20 @@
+<h1 align="center">
+ Design
+</h1>
+
+### Portable
+Pion Data Channels is written in Go and extremely portable. Anywhere Golang runs, Pion Data Channels should work as well! Instead of dealing with complicated
+cross-compiling of multiple libraries, you now can run anywhere with one `go build`
+
+### Simple API
+The API is based on an io.ReadWriteCloser.
+
+### Readable
+If code comes from an RFC we try to make sure everything is commented with a link to the spec.
+This makes learning and debugging easier, this library was written to also serve as a guide for others.
+
+### Tested
+Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on.
+
+### Shared libraries
+Every pion product is built using shared libraries, allowing others to review and reuse our libraries.
diff --git a/vendor/github.com/pion/datachannel/LICENSE b/vendor/github.com/pion/datachannel/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/datachannel/README.md b/vendor/github.com/pion/datachannel/README.md
new file mode 100644
index 0000000..1132f62
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/README.md
@@ -0,0 +1,45 @@
+<h1 align="center">
+ <br>
+ Pion Data Channels
+ <br>
+</h1>
+<h4 align="center">A Go implementation of WebRTC Data Channels</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-datachannel-gray.svg?longCache=true&colorB=brightgreen" alt="Pion Data Channels"></a>
+ <!--<a href="https://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/badge.svg" alt="Sourcegraph Widget"></a>-->
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/datachannel"><img src="https://travis-ci.org/pion/datachannel.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/datachannel"><img src="https://godoc.org/github.com/pion/datachannel?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/datachannel"><img src="https://codecov.io/gh/pion/datachannel/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/datachannel"><img src="https://goreportcard.com/badge/github.com/pion/datachannel" alt="Go Report Card"></a>
+ <!--<a href="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>-->
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *Public API*
+* [Yutaka Takeda](https://github.com/enobufs) - *PR-SCTP*
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Norman Rasmussen](https://github.com/normanr) - *Fix Empty DataChannel messages*
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/datachannel/codecov.yml b/vendor/github.com/pion/datachannel/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/datachannel/datachannel.go b/vendor/github.com/pion/datachannel/datachannel.go
new file mode 100644
index 0000000..237ad72
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/datachannel.go
@@ -0,0 +1,378 @@
+// Package datachannel implements WebRTC Data Channels
+package datachannel
+
+import (
+ "fmt"
+ "io"
+ "sync/atomic"
+
+ "github.com/pion/logging"
+ "github.com/pion/sctp"
+ "github.com/pkg/errors"
+)
+
+const receiveMTU = 8192
+
+// Reader is an extended io.Reader
+// that also returns if the message is text.
+type Reader interface {
+ ReadDataChannel([]byte) (int, bool, error)
+}
+
+// Writer is an extended io.Writer
+// that also allows indicating if a message is text.
+type Writer interface {
+ WriteDataChannel([]byte, bool) (int, error)
+}
+
+// ReadWriteCloser is an extended io.ReadWriteCloser
+// that also implements our Reader and Writer.
+type ReadWriteCloser interface {
+ io.Reader
+ io.Writer
+ Reader
+ Writer
+ io.Closer
+}
+
+// DataChannel represents a data channel
+type DataChannel struct {
+ Config
+
+ // stats
+ messagesSent uint32
+ messagesReceived uint32
+ bytesSent uint64
+ bytesReceived uint64
+
+ stream *sctp.Stream
+ log logging.LeveledLogger
+}
+
+// Config is used to configure the data channel.
+type Config struct {
+ ChannelType ChannelType
+ Negotiated bool
+ Priority uint16
+ ReliabilityParameter uint32
+ Label string
+ Protocol string
+ LoggerFactory logging.LoggerFactory
+}
+
+func newDataChannel(stream *sctp.Stream, config *Config) (*DataChannel, error) {
+ return &DataChannel{
+ Config: *config,
+ stream: stream,
+ log: config.LoggerFactory.NewLogger("datachannel"),
+ }, nil
+}
+
+// Dial opens a data channels over SCTP
+func Dial(a *sctp.Association, id uint16, config *Config) (*DataChannel, error) {
+ stream, err := a.OpenStream(id, sctp.PayloadTypeWebRTCBinary)
+ if err != nil {
+ return nil, err
+ }
+
+ dc, err := Client(stream, config)
+ if err != nil {
+ return nil, err
+ }
+
+ return dc, nil
+}
+
+// Client opens a data channel over an SCTP stream
+func Client(stream *sctp.Stream, config *Config) (*DataChannel, error) {
+ msg := &channelOpen{
+ ChannelType: config.ChannelType,
+ Priority: config.Priority,
+ ReliabilityParameter: config.ReliabilityParameter,
+
+ Label: []byte(config.Label),
+ Protocol: []byte(config.Protocol),
+ }
+
+ if !config.Negotiated {
+ rawMsg, err := msg.Marshal()
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal ChannelOpen %v", err)
+ }
+
+ if _, err = stream.WriteSCTP(rawMsg, sctp.PayloadTypeWebRTCDCEP); err != nil {
+ return nil, fmt.Errorf("failed to send ChannelOpen %v", err)
+ }
+ }
+ return newDataChannel(stream, config)
+}
+
+// Accept is used to accept incoming data channels over SCTP
+func Accept(a *sctp.Association, config *Config) (*DataChannel, error) {
+ stream, err := a.AcceptStream()
+ if err != nil {
+ return nil, err
+ }
+
+ stream.SetDefaultPayloadType(sctp.PayloadTypeWebRTCBinary)
+
+ dc, err := Server(stream, config)
+ if err != nil {
+ return nil, err
+ }
+
+ return dc, nil
+}
+
+// Server accepts a data channel over an SCTP stream
+func Server(stream *sctp.Stream, config *Config) (*DataChannel, error) {
+ buffer := make([]byte, receiveMTU) // TODO: Can probably be smaller
+ n, ppi, err := stream.ReadSCTP(buffer)
+ if err != nil {
+ return nil, err
+ }
+
+ if ppi != sctp.PayloadTypeWebRTCDCEP {
+ return nil, fmt.Errorf("unexpected packet type: %s", ppi)
+ }
+
+ openMsg, err := parseExpectDataChannelOpen(buffer[:n])
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to parse DataChannelOpen packet")
+ }
+
+ config.ChannelType = openMsg.ChannelType
+ config.Priority = openMsg.Priority
+ config.ReliabilityParameter = openMsg.ReliabilityParameter
+ config.Label = string(openMsg.Label)
+ config.Protocol = string(openMsg.Protocol)
+
+ dataChannel, err := newDataChannel(stream, config)
+ if err != nil {
+ return nil, err
+ }
+
+ err = dataChannel.writeDataChannelAck()
+ if err != nil {
+ return nil, err
+ }
+
+ err = dataChannel.commitReliabilityParams()
+ if err != nil {
+ return nil, err
+ }
+ return dataChannel, nil
+}
+
+// Read reads a packet of len(p) bytes as binary data
+func (c *DataChannel) Read(p []byte) (int, error) {
+ n, _, err := c.ReadDataChannel(p)
+ return n, err
+}
+
+// ReadDataChannel reads a packet of len(p) bytes
+func (c *DataChannel) ReadDataChannel(p []byte) (int, bool, error) {
+ for {
+ n, ppi, err := c.stream.ReadSCTP(p)
+ if err == io.EOF {
+ // When the peer sees that an incoming stream was
+ // reset, it also resets its corresponding outgoing stream.
+ closeErr := c.stream.Close()
+ if closeErr != nil {
+ return 0, false, closeErr
+ }
+ }
+ if err != nil {
+ return 0, false, err
+ }
+
+ var isString bool
+ switch ppi {
+ case sctp.PayloadTypeWebRTCDCEP:
+ err = c.handleDCEP(p[:n])
+ if err != nil {
+ c.log.Errorf("Failed to handle DCEP: %s", err.Error())
+ continue
+ }
+ continue
+ case sctp.PayloadTypeWebRTCString, sctp.PayloadTypeWebRTCStringEmpty:
+ isString = true
+ }
+ switch ppi {
+ case sctp.PayloadTypeWebRTCBinaryEmpty, sctp.PayloadTypeWebRTCStringEmpty:
+ n = 0
+ }
+
+ atomic.AddUint32(&c.messagesReceived, 1)
+ atomic.AddUint64(&c.bytesReceived, uint64(n))
+
+ return n, isString, err
+ }
+}
+
+// MessagesSent returns the number of messages sent
+func (c *DataChannel) MessagesSent() uint32 {
+ return atomic.LoadUint32(&c.messagesSent)
+}
+
+// MessagesReceived returns the number of messages received
+func (c *DataChannel) MessagesReceived() uint32 {
+ return atomic.LoadUint32(&c.messagesReceived)
+}
+
+// BytesSent returns the number of bytes sent
+func (c *DataChannel) BytesSent() uint64 {
+ return atomic.LoadUint64(&c.bytesSent)
+}
+
+// BytesReceived returns the number of bytes received
+func (c *DataChannel) BytesReceived() uint64 {
+ return atomic.LoadUint64(&c.bytesReceived)
+}
+
+// StreamIdentifier returns the Stream identifier associated to the stream.
+func (c *DataChannel) StreamIdentifier() uint16 {
+ return c.stream.StreamIdentifier()
+}
+
+func (c *DataChannel) handleDCEP(data []byte) error {
+ msg, err := parse(data)
+ if err != nil {
+ return errors.Wrap(err, "Failed to parse DataChannel packet")
+ }
+
+ switch msg := msg.(type) {
+ case *channelOpen:
+ c.log.Debug("Received DATA_CHANNEL_OPEN")
+ err = c.writeDataChannelAck()
+ if err != nil {
+ return fmt.Errorf("failed to ACK channel open: %v", err)
+ }
+ // Note: DATA_CHANNEL_OPEN message is handled inside Server() method.
+ // Therefore, the message will not reach here.
+
+ case *channelAck:
+ c.log.Debug("Received DATA_CHANNEL_ACK")
+ err = c.commitReliabilityParams()
+ if err != nil {
+ return err
+ }
+ // TODO: handle ChannelAck (https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09#section-5.2)
+
+ default:
+ return fmt.Errorf("unhandled DataChannel message %v", msg)
+ }
+
+ return nil
+}
+
+// Write writes len(p) bytes from p as binary data
+func (c *DataChannel) Write(p []byte) (n int, err error) {
+ return c.WriteDataChannel(p, false)
+}
+
+// WriteDataChannel writes len(p) bytes from p
+func (c *DataChannel) WriteDataChannel(p []byte, isString bool) (n int, err error) {
+ // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-12#section-6.6
+ // SCTP does not support the sending of empty user messages. Therefore,
+ // if an empty message has to be sent, the appropriate PPID (WebRTC
+ // String Empty or WebRTC Binary Empty) is used and the SCTP user
+ // message of one zero byte is sent. When receiving an SCTP user
+ // message with one of these PPIDs, the receiver MUST ignore the SCTP
+ // user message and process it as an empty message.
+ var ppi sctp.PayloadProtocolIdentifier
+ switch {
+ case !isString && len(p) > 0:
+ ppi = sctp.PayloadTypeWebRTCBinary
+ case !isString && len(p) == 0:
+ ppi = sctp.PayloadTypeWebRTCBinaryEmpty
+ case isString && len(p) > 0:
+ ppi = sctp.PayloadTypeWebRTCString
+ case isString && len(p) == 0:
+ ppi = sctp.PayloadTypeWebRTCStringEmpty
+ }
+
+ atomic.AddUint32(&c.messagesSent, 1)
+ atomic.AddUint64(&c.bytesSent, uint64(len(p)))
+
+ if len(p) == 0 {
+ _, err := c.stream.WriteSCTP([]byte{0}, ppi)
+ return 0, err
+ }
+ return c.stream.WriteSCTP(p, ppi)
+}
+
+func (c *DataChannel) writeDataChannelAck() error {
+ ack := channelAck{}
+ ackMsg, err := ack.Marshal()
+ if err != nil {
+ return fmt.Errorf("failed to marshal ChannelOpen ACK: %v", err)
+ }
+
+ _, err = c.stream.WriteSCTP(ackMsg, sctp.PayloadTypeWebRTCDCEP)
+ if err != nil {
+ return fmt.Errorf("failed to send ChannelOpen ACK: %v", err)
+ }
+
+ return err
+}
+
+// Close closes the DataChannel and the underlying SCTP stream.
+func (c *DataChannel) Close() error {
+ // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.7
+ // Closing of a data channel MUST be signaled by resetting the
+ // corresponding outgoing streams [RFC6525]. This means that if one
+ // side decides to close the data channel, it resets the corresponding
+ // outgoing stream. When the peer sees that an incoming stream was
+ // reset, it also resets its corresponding outgoing stream. Once this
+ // is completed, the data channel is closed. Resetting a stream sets
+ // the Stream Sequence Numbers (SSNs) of the stream back to 'zero' with
+ // a corresponding notification to the application layer that the reset
+ // has been performed. Streams are available for reuse after a reset
+ // has been performed.
+ return c.stream.Close()
+}
+
+// BufferedAmount returns the number of bytes of data currently queued to be
+// sent over this stream.
+func (c *DataChannel) BufferedAmount() uint64 {
+ return c.stream.BufferedAmount()
+}
+
+// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing
+// data that is considered "low." Defaults to 0.
+func (c *DataChannel) BufferedAmountLowThreshold() uint64 {
+ return c.stream.BufferedAmountLowThreshold()
+}
+
+// SetBufferedAmountLowThreshold is used to update the threshold.
+// See BufferedAmountLowThreshold().
+func (c *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
+ c.stream.SetBufferedAmountLowThreshold(th)
+}
+
+// OnBufferedAmountLow sets the callback handler which would be called when the
+// number of bytes of outgoing data buffered is lower than the threshold.
+func (c *DataChannel) OnBufferedAmountLow(f func()) {
+ c.stream.OnBufferedAmountLow(f)
+}
+
+func (c *DataChannel) commitReliabilityParams() error {
+ switch c.Config.ChannelType {
+ case ChannelTypeReliable:
+ c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeReliable, c.Config.ReliabilityParameter)
+ case ChannelTypeReliableUnordered:
+ c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeReliable, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableRexmit:
+ c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeRexmit, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableRexmitUnordered:
+ c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeRexmit, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableTimed:
+ c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeTimed, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableTimedUnordered:
+ c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeTimed, c.Config.ReliabilityParameter)
+ default:
+ return fmt.Errorf("invalid ChannelType: %v ", c.Config.ChannelType)
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/datachannel/go.mod b/vendor/github.com/pion/datachannel/go.mod
new file mode 100644
index 0000000..e635596
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/go.mod
@@ -0,0 +1,11 @@
+module github.com/pion/datachannel
+
+require (
+ github.com/pion/logging v0.2.2
+ github.com/pion/sctp v1.7.10
+ github.com/pion/transport v0.10.1
+ github.com/pkg/errors v0.9.1
+ github.com/stretchr/testify v1.6.1
+)
+
+go 1.13
diff --git a/vendor/github.com/pion/datachannel/go.sum b/vendor/github.com/pion/datachannel/go.sum
new file mode 100644
index 0000000..f9ede08
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/go.sum
@@ -0,0 +1,38 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/sctp v1.7.10 h1:o3p3/hZB5Cx12RMGyWmItevJtZ6o2cpuxaw6GOS4x+8=
+github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
+github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM=
+github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/datachannel/message.go b/vendor/github.com/pion/datachannel/message.go
new file mode 100644
index 0000000..84665cb
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/message.go
@@ -0,0 +1,94 @@
+package datachannel
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+// message is a parsed DataChannel message
+type message interface {
+ Marshal() ([]byte, error)
+ Unmarshal([]byte) error
+}
+
+// messageType is the first byte in a DataChannel message that specifies type
+type messageType byte
+
+// DataChannel Message Types
+const (
+ dataChannelAck messageType = 0x02
+ dataChannelOpen messageType = 0x03
+)
+
+func (t messageType) String() string {
+ switch t {
+ case dataChannelAck:
+ return "DataChannelAck"
+ case dataChannelOpen:
+ return "DataChannelOpen"
+ default:
+ return fmt.Sprintf("Unknown MessageType: %d", t)
+ }
+}
+
+// parse accepts raw input and returns a DataChannel message
+func parse(raw []byte) (message, error) {
+ if len(raw) == 0 {
+ return nil, errors.Errorf("DataChannel message is not long enough to determine type ")
+ }
+
+ var msg message
+ switch messageType(raw[0]) {
+ case dataChannelOpen:
+ msg = &channelOpen{}
+ case dataChannelAck:
+ msg = &channelAck{}
+ default:
+ return nil, errors.Errorf("Unknown MessageType %v", messageType(raw[0]))
+ }
+
+ if err := msg.Unmarshal(raw); err != nil {
+ return nil, err
+ }
+
+ return msg, nil
+}
+
+// parseExpectDataChannelOpen parses a DataChannelOpen message
+// or throws an error
+func parseExpectDataChannelOpen(raw []byte) (*channelOpen, error) {
+ if len(raw) == 0 {
+ return nil, errors.Errorf("the DataChannel message is not long enough to determine type")
+ }
+
+ if actualTyp := messageType(raw[0]); actualTyp != dataChannelOpen {
+ return nil, errors.Errorf("expected DataChannelOpen but got %s", actualTyp)
+ }
+
+ msg := &channelOpen{}
+ if err := msg.Unmarshal(raw); err != nil {
+ return nil, err
+ }
+
+ return msg, nil
+}
+
+// parseExpectDataChannelAck parses a DataChannelAck message
+// or throws an error
+// func parseExpectDataChannelAck(raw []byte) (*channelAck, error) {
+// if len(raw) == 0 {
+// return nil, errors.Errorf("the DataChannel message is not long enough to determine type")
+// }
+//
+// if actualTyp := messageType(raw[0]); actualTyp != dataChannelAck {
+// return nil, errors.Errorf("expected DataChannelAck but got %s", actualTyp)
+// }
+//
+// msg := &channelAck{}
+// if err := msg.Unmarshal(raw); err != nil {
+// return nil, err
+// }
+//
+// return msg, nil
+// }
diff --git a/vendor/github.com/pion/datachannel/message_channel_ack.go b/vendor/github.com/pion/datachannel/message_channel_ack.go
new file mode 100644
index 0000000..fd20757
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/message_channel_ack.go
@@ -0,0 +1,22 @@
+package datachannel
+
+// channelAck is used to ACK a DataChannel open
+type channelAck struct{}
+
+const (
+ channelOpenAckLength = 4
+)
+
+// Marshal returns raw bytes for the given message
+func (c *channelAck) Marshal() ([]byte, error) {
+ raw := make([]byte, channelOpenAckLength)
+ raw[0] = uint8(dataChannelAck)
+
+ return raw, nil
+}
+
+// Unmarshal populates the struct with the given raw data
+func (c *channelAck) Unmarshal(raw []byte) error {
+ // Message type already checked in Parse and there is no further data
+ return nil
+}
diff --git a/vendor/github.com/pion/datachannel/message_channel_open.go b/vendor/github.com/pion/datachannel/message_channel_open.go
new file mode 100644
index 0000000..9dce036
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/message_channel_open.go
@@ -0,0 +1,123 @@
+package datachannel
+
+import (
+ "encoding/binary"
+
+ "github.com/pkg/errors"
+)
+
+/*
+channelOpen represents a DATA_CHANNEL_OPEN Message
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Message Type | Channel Type | Priority |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Reliability Parameter |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Label Length | Protocol Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Label |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Protocol |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+type channelOpen struct {
+ ChannelType ChannelType
+ Priority uint16
+ ReliabilityParameter uint32
+
+ Label []byte
+ Protocol []byte
+}
+
+const (
+ channelOpenHeaderLength = 12
+)
+
+// ChannelType determines the reliability of the WebRTC DataChannel
+type ChannelType byte
+
+// ChannelType enums
+const (
+ // ChannelTypeReliable determines the Data Channel provides a
+ // reliable in-order bi-directional communication.
+ ChannelTypeReliable ChannelType = 0x00
+ // ChannelTypeReliableUnordered determines the Data Channel
+ // provides a reliable unordered bi-directional communication.
+ ChannelTypeReliableUnordered ChannelType = 0x80
+ // ChannelTypePartialReliableRexmit determines the Data Channel
+ // provides a partially-reliable in-order bi-directional communication.
+ // User messages will not be retransmitted more times than specified in the Reliability Parameter.
+ ChannelTypePartialReliableRexmit ChannelType = 0x01
+ // ChannelTypePartialReliableRexmitUnordered determines
+ // the Data Channel provides a partial reliable unordered bi-directional communication.
+ // User messages will not be retransmitted more times than specified in the Reliability Parameter.
+ ChannelTypePartialReliableRexmitUnordered ChannelType = 0x81
+ // ChannelTypePartialReliableTimed determines the Data Channel
+ // provides a partial reliable in-order bi-directional communication.
+ // User messages might not be transmitted or retransmitted after
+ // a specified life-time given in milli- seconds in the Reliability Parameter.
+ // This life-time starts when providing the user message to the protocol stack.
+ ChannelTypePartialReliableTimed ChannelType = 0x02
+ // The Data Channel provides a partial reliable unordered bi-directional
+ // communication. User messages might not be transmitted or retransmitted
+ // after a specified life-time given in milli- seconds in the Reliability Parameter.
+ // This life-time starts when providing the user message to the protocol stack.
+ ChannelTypePartialReliableTimedUnordered ChannelType = 0x82
+)
+
+// ChannelPriority enums
+const (
+ ChannelPriorityBelowNormal uint16 = 128
+ ChannelPriorityNormal uint16 = 256
+ ChannelPriorityHigh uint16 = 512
+ ChannelPriorityExtraHigh uint16 = 1024
+)
+
+// Marshal returns raw bytes for the given message
+func (c *channelOpen) Marshal() ([]byte, error) {
+ labelLength := len(c.Label)
+ protocolLength := len(c.Protocol)
+
+ totalLen := channelOpenHeaderLength + labelLength + protocolLength
+ raw := make([]byte, totalLen)
+
+ raw[0] = uint8(dataChannelOpen)
+ raw[1] = byte(c.ChannelType)
+ binary.BigEndian.PutUint16(raw[2:], c.Priority)
+ binary.BigEndian.PutUint32(raw[4:], c.ReliabilityParameter)
+ binary.BigEndian.PutUint16(raw[8:], uint16(labelLength))
+ binary.BigEndian.PutUint16(raw[10:], uint16(protocolLength))
+ endLabel := channelOpenHeaderLength + labelLength
+ copy(raw[channelOpenHeaderLength:endLabel], c.Label)
+ copy(raw[endLabel:endLabel+protocolLength], c.Protocol)
+
+ return raw, nil
+}
+
+// Unmarshal populates the struct with the given raw data
+func (c *channelOpen) Unmarshal(raw []byte) error {
+ if len(raw) < channelOpenHeaderLength {
+ return errors.Errorf("Length of input is not long enough to satisfy header %d", len(raw))
+ }
+ c.ChannelType = ChannelType(raw[1])
+ c.Priority = binary.BigEndian.Uint16(raw[2:])
+ c.ReliabilityParameter = binary.BigEndian.Uint32(raw[4:])
+
+ labelLength := binary.BigEndian.Uint16(raw[8:])
+ protocolLength := binary.BigEndian.Uint16(raw[10:])
+
+ if len(raw) != int(channelOpenHeaderLength+labelLength+protocolLength) {
+ return errors.Errorf("Label + Protocol length don't match full packet length")
+ }
+
+ c.Label = raw[channelOpenHeaderLength : channelOpenHeaderLength+labelLength]
+ c.Protocol = raw[channelOpenHeaderLength+labelLength : channelOpenHeaderLength+labelLength+protocolLength]
+ return nil
+}
diff --git a/vendor/github.com/pion/datachannel/renovate.json b/vendor/github.com/pion/datachannel/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/datachannel/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/dtls/v2/.editorconfig b/vendor/github.com/pion/dtls/v2/.editorconfig
new file mode 100644
index 0000000..d2b3206
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/.editorconfig
@@ -0,0 +1,21 @@
+# http://editorconfig.org/
+
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+end_of_line = lf
+
+[*.go]
+indent_style = tab
+indent_size = 4
+
+[{*.yml,*.yaml}]
+indent_style = space
+indent_size = 2
+
+# Makefiles always use tabs for indentation
+[Makefile]
+indent_style = tab
diff --git a/vendor/github.com/pion/dtls/v2/.gitignore b/vendor/github.com/pion/dtls/v2/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/dtls/v2/.golangci.yml b/vendor/github.com/pion/dtls/v2/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/dtls/v2/LICENSE b/vendor/github.com/pion/dtls/v2/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/dtls/v2/Makefile b/vendor/github.com/pion/dtls/v2/Makefile
new file mode 100644
index 0000000..1df38b2
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/Makefile
@@ -0,0 +1,6 @@
+fuzz-build-record-layer: fuzz-prepare
+ go-fuzz-build -tags gofuzz -func FuzzRecordLayer
+fuzz-run-record-layer:
+ go-fuzz -bin dtls-fuzz.zip -workdir fuzz
+fuzz-prepare:
+ @GO111MODULE=on go mod vendor
diff --git a/vendor/github.com/pion/dtls/v2/README.md b/vendor/github.com/pion/dtls/v2/README.md
new file mode 100644
index 0000000..62cac7c
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/README.md
@@ -0,0 +1,155 @@
+<h1 align="center">
+ <br>
+ Pion DTLS
+ <br>
+</h1>
+<h4 align="center">A Go implementation of DTLS</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-dtls-gray.svg?longCache=true&colorB=brightgreen" alt="Pion DTLS"></a>
+ <a href="https://sourcegraph.com/github.com/pion/dtls"><img src="https://sourcegraph.com/github.com/pion/dtls/-/badge.svg" alt="Sourcegraph Widget"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/dtls"><img src="https://travis-ci.org/pion/dtls.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/dtls"><img src="https://godoc.org/github.com/pion/dtls?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/dtls"><img src="https://codecov.io/gh/pion/dtls/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/dtls"><img src="https://goreportcard.com/badge/github.com/pion/dtls" alt="Go Report Card"></a>
+ <a href="https://www.codacy.com/app/Sean-Der/dtls"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+Native [DTLS 1.2][rfc6347] implementation in the Go programming language.
+
+A long term goal is a professional security review, and maye inclusion in stdlib.
+
+[rfc6347]: https://tools.ietf.org/html/rfc6347
+
+### Goals/Progress
+This will only be targeting DTLS 1.2, and the most modern/common cipher suites.
+We would love contributes that fall under the 'Planned Features' and fixing any bugs!
+
+#### Current features
+* DTLS 1.2 Client/Server
+* Key Exchange via ECDHE(curve25519, nistp256, nistp384) and PSK
+* Packet loss and re-ordering is handled during handshaking
+* Key export ([RFC 5705][rfc5705])
+* Serialization and Resumption of sessions
+* Extended Master Secret extension ([RFC 7627][rfc7627])
+
+[rfc5705]: https://tools.ietf.org/html/rfc5705
+[rfc7627]: https://tools.ietf.org/html/rfc7627
+
+#### Supported ciphers
+
+##### ECDHE
+* TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655])
+* TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
+* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
+* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
+* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
+* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
+
+##### PSK
+* TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655])
+* TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
+* TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487])
+* TLS_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5487][rfc5487])
+
+[rfc5289]: https://tools.ietf.org/html/rfc5289
+[rfc8422]: https://tools.ietf.org/html/rfc8422
+[rfc6655]: https://tools.ietf.org/html/rfc6655
+[rfc5487]: https://tools.ietf.org/html/rfc5487
+
+#### Planned Features
+* Chacha20Poly1305
+
+#### Excluded Features
+* DTLS 1.0
+* Renegotiation
+* Compression
+
+### Using
+
+This library needs at least Go 1.13, and you should have [Go modules
+enabled](https://github.com/golang/go/wiki/Modules).
+
+#### Pion DTLS
+For a DTLS 1.2 Server that listens on 127.0.0.1:4444
+```sh
+go run examples/listen/selfsign/main.go
+```
+
+For a DTLS 1.2 Client that connects to 127.0.0.1:4444
+```sh
+go run examples/dial/selfsign/main.go
+```
+
+#### OpenSSL
+Pion DTLS can connect to itself and OpenSSL.
+```
+ // Generate a certificate
+ openssl ecparam -out key.pem -name prime256v1 -genkey
+ openssl req -new -sha256 -key key.pem -out server.csr
+ openssl x509 -req -sha256 -days 365 -in server.csr -signkey key.pem -out cert.pem
+
+ // Use with examples/dial/selfsign/main.go
+ openssl s_server -dtls1_2 -cert cert.pem -key key.pem -accept 4444
+
+ // Use with examples/listen/selfsign/main.go
+ openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -debug -cert cert.pem -key key.pem
+```
+
+### Using with PSK
+Pion DTLS also comes with examples that do key exchange via PSK
+
+
+#### Pion DTLS
+```sh
+go run examples/listen/psk/main.go
+```
+
+```sh
+go run examples/dial/psk/main.go
+```
+
+#### OpenSSL
+```
+ // Use with examples/dial/psk/main.go
+ openssl s_server -dtls1_2 -accept 4444 -nocert -psk abc123 -cipher PSK-AES128-CCM8
+
+ // Use with examples/listen/psk/main.go
+ openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -psk abc123 -cipher PSK-AES128-CCM8
+```
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *Public API*
+* [Chris Hiszpanski](https://github.com/thinkski) - *Support Signature Algorithms Extension*
+* [Iñigo Garcia Olaizola](https://github.com/igolaizola) - *Serialization & resumption, cert verification, E2E*
+* [Daniele Sluijters](https://github.com/daenney) - *AES-CCM support*
+* [Jin Lei](https://github.com/jinleileiking) - *Logging*
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Lander Noterman](https://github.com/LanderN)
+* [Aleksandr Razumov](https://github.com/ernado) - *Fuzzing*
+* [Ryan Gordon](https://github.com/ryangordon)
+* [Stefan Tatschner](https://rumpelsepp.org/contact.html)
+* [Hayden James](https://github.com/hjames9)
+* [Jozef Kralik](https://github.com/jkralik)
+* [Robert Eperjesi](https://github.com/epes)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Julien Salleyron](https://github.com/juliens) - *Server Name Indication*
+* [Jeroen de Bruijn](https://github.com/vidavidorra)
+* [bjdgyc](https://github.com/bjdgyc)
+* [Jeffrey Stoke (Jeff Ctor)](https://github.com/jeffreystoke) - *Fragmentbuffer Fix*
+* [Frank Olbricht](https://github.com/folbricht)
+* [ZHENK](https://github.com/scorpionknifes)
+* [Carson Hoffman](https://github.com/CarsonHoffman)
+* [Vadim Filimonov](https://github.com/fffilimonov)
+* [Jim Wert](https://github.com/bocajim)
+* [Alvaro Viebrantz](https://github.com/alvarowolfx)
+* [Kegan Dougal](https://github.com/Kegsay)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/dtls/v2/certificate.go b/vendor/github.com/pion/dtls/v2/certificate.go
new file mode 100644
index 0000000..c99e1c9
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/certificate.go
@@ -0,0 +1,67 @@
+package dtls
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "strings"
+)
+
+func (c *handshakeConfig) getCertificate(serverName string) (*tls.Certificate, error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.nameToCertificate == nil {
+ nameToCertificate := make(map[string]*tls.Certificate)
+ for i := range c.localCertificates {
+ cert := &c.localCertificates[i]
+ x509Cert := cert.Leaf
+ if x509Cert == nil {
+ var parseErr error
+ x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0])
+ if parseErr != nil {
+ continue
+ }
+ }
+ if len(x509Cert.Subject.CommonName) > 0 {
+ nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert
+ }
+ for _, san := range x509Cert.DNSNames {
+ nameToCertificate[strings.ToLower(san)] = cert
+ }
+ }
+ c.nameToCertificate = nameToCertificate
+ }
+
+ if len(c.localCertificates) == 0 {
+ return nil, errNoCertificates
+ }
+
+ if len(c.localCertificates) == 1 {
+ // There's only one choice, so no point doing any work.
+ return &c.localCertificates[0], nil
+ }
+
+ if len(serverName) == 0 {
+ return &c.localCertificates[0], nil
+ }
+
+ name := strings.TrimRight(strings.ToLower(serverName), ".")
+
+ if cert, ok := c.nameToCertificate[name]; ok {
+ return cert, nil
+ }
+
+ // try replacing labels in the name with wildcards until we get a
+ // match.
+ labels := strings.Split(name, ".")
+ for i := range labels {
+ labels[i] = "*"
+ candidate := strings.Join(labels, ".")
+ if cert, ok := c.nameToCertificate[candidate]; ok {
+ return cert, nil
+ }
+ }
+
+ // If nothing matches, return the first certificate.
+ return &c.localCertificates[0], nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/cipher_suite.go b/vendor/github.com/pion/dtls/v2/cipher_suite.go
new file mode 100644
index 0000000..ed10609
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/cipher_suite.go
@@ -0,0 +1,213 @@
+package dtls
+
+import (
+ "fmt"
+ "hash"
+
+ "github.com/pion/dtls/v2/internal/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// CipherSuiteID is an ID for our supported CipherSuites
+type CipherSuiteID = ciphersuite.ID
+
+// Supported Cipher Suites
+const (
+ // AES-128-CCM
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM //nolint:golint,stylecheck
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:golint,stylecheck
+
+ // AES-128-GCM-SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck
+
+ // AES-256-CBC-SHA
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck
+
+ TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:golint,stylecheck
+ TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:golint,stylecheck
+ TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck
+ TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:golint,stylecheck
+)
+
+// CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite
+type CipherSuiteAuthenticationType = ciphersuite.AuthenticationType
+
+// AuthenticationType Enums
+const (
+ CipherSuiteAuthenticationTypeCertificate CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeCertificate
+ CipherSuiteAuthenticationTypePreSharedKey CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypePreSharedKey
+ CipherSuiteAuthenticationTypeAnonymous CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeAnonymous
+)
+
+var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14
+
+// CipherSuite is an interface that all DTLS CipherSuites must satisfy
+type CipherSuite interface {
+ // String of CipherSuite, only used for logging
+ String() string
+
+ // ID of CipherSuite.
+ ID() CipherSuiteID
+
+ // What type of Certificate does this CipherSuite use
+ CertificateType() clientcertificate.Type
+
+ // What Hash function is used during verification
+ HashFunc() func() hash.Hash
+
+ // AuthenticationType controls what authentication method is using during the handshake
+ AuthenticationType() CipherSuiteAuthenticationType
+
+ // Called when keying material has been generated, should initialize the internal cipher
+ Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error
+ IsInitialized() bool
+
+ Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
+ Decrypt(in []byte) ([]byte, error)
+}
+
+// CipherSuiteName provides the same functionality as tls.CipherSuiteName
+// that appeared first in Go 1.14.
+//
+// Our implementation differs slightly in that it takes in a CiperSuiteID,
+// like the rest of our library, instead of a uint16 like crypto/tls.
+func CipherSuiteName(id CipherSuiteID) string {
+ suite := cipherSuiteForID(id, nil)
+ if suite != nil {
+ return suite.String()
+ }
+ return fmt.Sprintf("0x%04X", uint16(id))
+}
+
+// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+// A cipherSuite is a specific combination of key agreement, cipher and MAC
+// function.
+func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) CipherSuite {
+ switch id { //nolint:exhaustive
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm()
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8()
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ return &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ return &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}
+ case TLS_PSK_WITH_AES_128_CCM:
+ return ciphersuite.NewTLSPskWithAes128Ccm()
+ case TLS_PSK_WITH_AES_128_CCM_8:
+ return ciphersuite.NewTLSPskWithAes128Ccm8()
+ case TLS_PSK_WITH_AES_128_GCM_SHA256:
+ return &ciphersuite.TLSPskWithAes128GcmSha256{}
+ case TLS_PSK_WITH_AES_128_CBC_SHA256:
+ return &ciphersuite.TLSPskWithAes128CbcSha256{}
+ }
+
+ if customCiphers != nil {
+ for _, c := range customCiphers() {
+ if c.ID() == id {
+ return c
+ }
+ }
+ }
+
+ return nil
+}
+
+// CipherSuites we support in order of preference
+func defaultCipherSuites() []CipherSuite {
+ return []CipherSuite{
+ &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
+ &ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
+ }
+}
+
+func allCipherSuites() []CipherSuite {
+ return []CipherSuite{
+ ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(),
+ ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(),
+ &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
+ &ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
+ ciphersuite.NewTLSPskWithAes128Ccm(),
+ ciphersuite.NewTLSPskWithAes128Ccm8(),
+ &ciphersuite.TLSPskWithAes128GcmSha256{},
+ }
+}
+
+func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 {
+ rtrn := []uint16{}
+ for _, c := range cipherSuites {
+ rtrn = append(rtrn, uint16(c.ID()))
+ }
+ return rtrn
+}
+
+func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) {
+ cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) {
+ cipherSuites := []CipherSuite{}
+ for _, id := range ids {
+ c := cipherSuiteForID(id, nil)
+ if c == nil {
+ return nil, &invalidCipherSuite{id}
+ }
+ cipherSuites = append(cipherSuites, c)
+ }
+ return cipherSuites, nil
+ }
+
+ var (
+ cipherSuites []CipherSuite
+ err error
+ i int
+ )
+ if userSelectedSuites != nil {
+ cipherSuites, err = cipherSuitesForIDs(userSelectedSuites)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ cipherSuites = defaultCipherSuites()
+ }
+
+ // Put CustomCipherSuites before ID selected suites
+ if customCipherSuites != nil {
+ cipherSuites = append(customCipherSuites(), cipherSuites...)
+ }
+
+ var foundCertificateSuite, foundPSKSuite, foundAnonymousSuite bool
+ for _, c := range cipherSuites {
+ switch {
+ case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
+ foundCertificateSuite = true
+ case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey:
+ foundPSKSuite = true
+ case c.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous:
+ foundAnonymousSuite = true
+ default:
+ continue
+ }
+ cipherSuites[i] = c
+ i++
+ }
+
+ switch {
+ case includeCertificateSuites && !foundCertificateSuite && !foundAnonymousSuite:
+ return nil, errNoAvailableCertificateCipherSuite
+ case includePSKSuites && !foundPSKSuite:
+ return nil, errNoAvailablePSKCipherSuite
+ case i == 0:
+ return nil, errNoAvailableCipherSuites
+ }
+
+ return cipherSuites[:i], nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go b/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go
new file mode 100644
index 0000000..7bba16e
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go
@@ -0,0 +1,40 @@
+// +build go1.14
+
+package dtls
+
+import (
+ "crypto/tls"
+)
+
+// VersionDTLS12 is the DTLS version in the same style as
+// VersionTLSXX from crypto/tls
+const VersionDTLS12 = 0xfefd
+
+// Convert from our cipherSuite interface to a tls.CipherSuite struct
+func toTLSCipherSuite(c CipherSuite) *tls.CipherSuite {
+ return &tls.CipherSuite{
+ ID: uint16(c.ID()),
+ Name: c.String(),
+ SupportedVersions: []uint16{VersionDTLS12},
+ Insecure: false,
+ }
+}
+
+// CipherSuites returns a list of cipher suites currently implemented by this
+// package, excluding those with security issues, which are returned by
+// InsecureCipherSuites.
+func CipherSuites() []*tls.CipherSuite {
+ suites := allCipherSuites()
+ res := make([]*tls.CipherSuite, len(suites))
+ for i, c := range suites {
+ res[i] = toTLSCipherSuite(c)
+ }
+ return res
+}
+
+// InsecureCipherSuites returns a list of cipher suites currently implemented by
+// this package and which have security issues.
+func InsecureCipherSuites() []*tls.CipherSuite {
+ var res []*tls.CipherSuite
+ return res
+}
diff --git a/vendor/github.com/pion/dtls/v2/codecov.yml b/vendor/github.com/pion/dtls/v2/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/dtls/v2/compression_method.go b/vendor/github.com/pion/dtls/v2/compression_method.go
new file mode 100644
index 0000000..693eb7a
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/compression_method.go
@@ -0,0 +1,9 @@
+package dtls
+
+import "github.com/pion/dtls/v2/pkg/protocol"
+
+func defaultCompressionMethods() []*protocol.CompressionMethod {
+ return []*protocol.CompressionMethod{
+ {},
+ }
+}
diff --git a/vendor/github.com/pion/dtls/v2/config.go b/vendor/github.com/pion/dtls/v2/config.go
new file mode 100644
index 0000000..5ad42da
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/config.go
@@ -0,0 +1,193 @@
+package dtls
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/tls"
+ "crypto/x509"
+ "io"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+const keyLogLabelTLS12 = "CLIENT_RANDOM"
+
+// Config is used to configure a DTLS client or server.
+// After a Config is passed to a DTLS function it must not be modified.
+type Config struct {
+ // Certificates contains certificate chain to present to the other side of the connection.
+ // Server MUST set this if PSK is non-nil
+ // client SHOULD sets this so CertificateRequests can be handled if PSK is non-nil
+ Certificates []tls.Certificate
+
+ // CipherSuites is a list of supported cipher suites.
+ // If CipherSuites is nil, a default list is used
+ CipherSuites []CipherSuiteID
+
+ // CustomCipherSuites is a list of CipherSuites that can be
+ // provided by the user. This allow users to user Ciphers that are reserved
+ // for private usage.
+ CustomCipherSuites func() []CipherSuite
+
+ // SignatureSchemes contains the signature and hash schemes that the peer requests to verify.
+ SignatureSchemes []tls.SignatureScheme
+
+ // SRTPProtectionProfiles are the supported protection profiles
+ // Clients will send this via use_srtp and assert that the server properly responds
+ // Servers will assert that clients send one of these profiles and will respond as needed
+ SRTPProtectionProfiles []SRTPProtectionProfile
+
+ // ClientAuth determines the server's policy for
+ // TLS Client Authentication. The default is NoClientCert.
+ ClientAuth ClientAuthType
+
+ // RequireExtendedMasterSecret determines if the "Extended Master Secret" extension
+ // should be disabled, requested, or required (default requested).
+ ExtendedMasterSecret ExtendedMasterSecretType
+
+ // FlightInterval controls how often we send outbound handshake messages
+ // defaults to time.Second
+ FlightInterval time.Duration
+
+ // PSK sets the pre-shared key used by this DTLS connection
+ // If PSK is non-nil only PSK CipherSuites will be used
+ PSK PSKCallback
+ PSKIdentityHint []byte
+
+ // InsecureSkipVerify controls whether a client verifies the
+ // server's certificate chain and host name.
+ // If InsecureSkipVerify is true, TLS accepts any certificate
+ // presented by the server and any host name in that certificate.
+ // In this mode, TLS is susceptible to man-in-the-middle attacks.
+ // This should be used only for testing.
+ InsecureSkipVerify bool
+
+ // InsecureHashes allows the use of hashing algorithms that are known
+ // to be vulnerable.
+ InsecureHashes bool
+
+ // VerifyPeerCertificate, if not nil, is called after normal
+ // certificate verification by either a client or server. It
+ // receives the certificate provided by the peer and also a flag
+ // that tells if normal verification has succeedded. If it returns a
+ // non-nil error, the handshake is aborted and that error results.
+ //
+ // If normal verification fails then the handshake will abort before
+ // considering this callback. If normal verification is disabled by
+ // setting InsecureSkipVerify, or (for a server) when ClientAuth is
+ // RequestClientCert or RequireAnyClientCert, then this callback will
+ // be considered but the verifiedChains will always be nil.
+ VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+
+ // RootCAs defines the set of root certificate authorities
+ // that one peer uses when verifying the other peer's certificates.
+ // If RootCAs is nil, TLS uses the host's root CA set.
+ RootCAs *x509.CertPool
+
+ // ClientCAs defines the set of root certificate authorities
+ // that servers use if required to verify a client certificate
+ // by the policy in ClientAuth.
+ ClientCAs *x509.CertPool
+
+ // ServerName is used to verify the hostname on the returned
+ // certificates unless InsecureSkipVerify is given.
+ ServerName string
+
+ LoggerFactory logging.LoggerFactory
+
+ // ConnectContextMaker is a function to make a context used in Dial(),
+ // Client(), Server(), and Accept(). If nil, the default ConnectContextMaker
+ // is used. It can be implemented as following.
+ //
+ // func ConnectContextMaker() (context.Context, func()) {
+ // return context.WithTimeout(context.Background(), 30*time.Second)
+ // }
+ ConnectContextMaker func() (context.Context, func())
+
+ // MTU is the length at which handshake messages will be fragmented to
+ // fit within the maximum transmission unit (default is 1200 bytes)
+ MTU int
+
+ // ReplayProtectionWindow is the size of the replay attack protection window.
+ // Duplication of the sequence number is checked in this window size.
+ // Packet with sequence number older than this value compared to the latest
+ // accepted packet will be discarded. (default is 64)
+ ReplayProtectionWindow int
+
+ // KeyLogWriter optionally specifies a destination for TLS master secrets
+ // in NSS key log format that can be used to allow external programs
+ // such as Wireshark to decrypt TLS connections.
+ // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.
+ // Use of KeyLogWriter compromises security and should only be
+ // used for debugging.
+ KeyLogWriter io.Writer
+}
+
+func defaultConnectContextMaker() (context.Context, func()) {
+ return context.WithTimeout(context.Background(), 30*time.Second)
+}
+
+func (c *Config) connectContextMaker() (context.Context, func()) {
+ if c.ConnectContextMaker == nil {
+ return defaultConnectContextMaker()
+ }
+ return c.ConnectContextMaker()
+}
+
+const defaultMTU = 1200 // bytes
+
+// PSKCallback is called once we have the remote's PSKIdentityHint.
+// If the remote provided none it will be nil
+type PSKCallback func([]byte) ([]byte, error)
+
+// ClientAuthType declares the policy the server will follow for
+// TLS Client Authentication.
+type ClientAuthType int
+
+// ClientAuthType enums
+const (
+ NoClientCert ClientAuthType = iota
+ RequestClientCert
+ RequireAnyClientCert
+ VerifyClientCertIfGiven
+ RequireAndVerifyClientCert
+)
+
+// ExtendedMasterSecretType declares the policy the client and server
+// will follow for the Extended Master Secret extension
+type ExtendedMasterSecretType int
+
+// ExtendedMasterSecretType enums
+const (
+ RequestExtendedMasterSecret ExtendedMasterSecretType = iota
+ RequireExtendedMasterSecret
+ DisableExtendedMasterSecret
+)
+
+func validateConfig(config *Config) error {
+ switch {
+ case config == nil:
+ return errNoConfigProvided
+ case config.PSKIdentityHint != nil && config.PSK == nil:
+ return errIdentityNoPSK
+ }
+
+ for _, cert := range config.Certificates {
+ if cert.Certificate == nil {
+ return errInvalidCertificate
+ }
+ if cert.PrivateKey != nil {
+ switch cert.PrivateKey.(type) {
+ case ed25519.PrivateKey:
+ case *ecdsa.PrivateKey:
+ default:
+ return errInvalidPrivateKey
+ }
+ }
+ }
+
+ _, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil)
+ return err
+}
diff --git a/vendor/github.com/pion/dtls/v2/conn.go b/vendor/github.com/pion/dtls/v2/conn.go
new file mode 100644
index 0000000..42d732f
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/conn.go
@@ -0,0 +1,978 @@
+package dtls
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/dtls/v2/internal/closer"
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+ "github.com/pion/logging"
+ "github.com/pion/transport/connctx"
+ "github.com/pion/transport/deadline"
+ "github.com/pion/transport/replaydetector"
+)
+
+const (
+ initialTickerInterval = time.Second
+ cookieLength = 20
+ defaultNamedCurve = elliptic.X25519
+ inboundBufferSize = 8192
+ // Default replay protection window is specified by RFC 6347 Section 4.1.2.6
+ defaultReplayProtectionWindow = 64
+)
+
+func invalidKeyingLabels() map[string]bool {
+ return map[string]bool{
+ "client finished": true,
+ "server finished": true,
+ "master secret": true,
+ "key expansion": true,
+ }
+}
+
+// Conn represents a DTLS connection
+type Conn struct {
+ lock sync.RWMutex // Internal lock (must not be public)
+ nextConn connctx.ConnCtx // Embedded Conn, typically a udpconn we read/write from
+ fragmentBuffer *fragmentBuffer // out-of-order and missing fragment handling
+ handshakeCache *handshakeCache // caching of handshake messages for verifyData generation
+ decrypted chan interface{} // Decrypted Application Data or error, pull by calling `Read`
+
+ state State // Internal state
+
+ maximumTransmissionUnit int
+
+ handshakeCompletedSuccessfully atomic.Value
+
+ encryptedPackets [][]byte
+
+ connectionClosedByUser bool
+ closeLock sync.Mutex
+ closed *closer.Closer
+ handshakeLoopsFinished sync.WaitGroup
+
+ readDeadline *deadline.Deadline
+ writeDeadline *deadline.Deadline
+
+ log logging.LeveledLogger
+
+ reading chan struct{}
+ handshakeRecv chan chan struct{}
+ cancelHandshaker func()
+ cancelHandshakeReader func()
+
+ fsm *handshakeFSM
+
+ replayProtectionWindow uint
+}
+
+func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient bool, initialState *State) (*Conn, error) {
+ err := validateConfig(config)
+ if err != nil {
+ return nil, err
+ }
+
+ if nextConn == nil {
+ return nil, errNilNextConn
+ }
+
+ cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil)
+ if err != nil {
+ return nil, err
+ }
+
+ signatureSchemes, err := signaturehash.ParseSignatureSchemes(config.SignatureSchemes, config.InsecureHashes)
+ if err != nil {
+ return nil, err
+ }
+
+ workerInterval := initialTickerInterval
+ if config.FlightInterval != 0 {
+ workerInterval = config.FlightInterval
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ logger := loggerFactory.NewLogger("dtls")
+
+ mtu := config.MTU
+ if mtu <= 0 {
+ mtu = defaultMTU
+ }
+
+ replayProtectionWindow := config.ReplayProtectionWindow
+ if replayProtectionWindow <= 0 {
+ replayProtectionWindow = defaultReplayProtectionWindow
+ }
+
+ c := &Conn{
+ nextConn: connctx.New(nextConn),
+ fragmentBuffer: newFragmentBuffer(),
+ handshakeCache: newHandshakeCache(),
+ maximumTransmissionUnit: mtu,
+
+ decrypted: make(chan interface{}, 1),
+ log: logger,
+
+ readDeadline: deadline.New(),
+ writeDeadline: deadline.New(),
+
+ reading: make(chan struct{}, 1),
+ handshakeRecv: make(chan chan struct{}),
+ closed: closer.NewCloser(),
+ cancelHandshaker: func() {},
+
+ replayProtectionWindow: uint(replayProtectionWindow),
+
+ state: State{
+ isClient: isClient,
+ },
+ }
+
+ c.setRemoteEpoch(0)
+ c.setLocalEpoch(0)
+
+ serverName := config.ServerName
+ // Use host from conn address when serverName is not provided
+ if isClient && serverName == "" && nextConn.RemoteAddr() != nil {
+ remoteAddr := nextConn.RemoteAddr().String()
+ var host string
+ host, _, err = net.SplitHostPort(remoteAddr)
+ if err != nil {
+ serverName = remoteAddr
+ } else {
+ serverName = host
+ }
+ }
+
+ hsCfg := &handshakeConfig{
+ localPSKCallback: config.PSK,
+ localPSKIdentityHint: config.PSKIdentityHint,
+ localCipherSuites: cipherSuites,
+ localSignatureSchemes: signatureSchemes,
+ extendedMasterSecret: config.ExtendedMasterSecret,
+ localSRTPProtectionProfiles: config.SRTPProtectionProfiles,
+ serverName: serverName,
+ clientAuth: config.ClientAuth,
+ localCertificates: config.Certificates,
+ insecureSkipVerify: config.InsecureSkipVerify,
+ verifyPeerCertificate: config.VerifyPeerCertificate,
+ rootCAs: config.RootCAs,
+ clientCAs: config.ClientCAs,
+ customCipherSuites: config.CustomCipherSuites,
+ retransmitInterval: workerInterval,
+ log: logger,
+ initialEpoch: 0,
+ keyLogWriter: config.KeyLogWriter,
+ }
+
+ var initialFlight flightVal
+ var initialFSMState handshakeState
+
+ if initialState != nil {
+ if c.state.isClient {
+ initialFlight = flight5
+ } else {
+ initialFlight = flight6
+ }
+ initialFSMState = handshakeFinished
+
+ c.state = *initialState
+ } else {
+ if c.state.isClient {
+ initialFlight = flight1
+ } else {
+ initialFlight = flight0
+ }
+ initialFSMState = handshakePreparing
+ }
+ // Do handshake
+ if err := c.handshake(ctx, hsCfg, initialFlight, initialFSMState); err != nil {
+ return nil, err
+ }
+
+ c.log.Trace("Handshake Completed")
+
+ return c, nil
+}
+
+// Dial connects to the given network address and establishes a DTLS connection on top.
+// Connection handshake will timeout using ConnectContextMaker in the Config.
+// If you want to specify the timeout duration, use DialWithContext() instead.
+func Dial(network string, raddr *net.UDPAddr, config *Config) (*Conn, error) {
+ ctx, cancel := config.connectContextMaker()
+ defer cancel()
+
+ return DialWithContext(ctx, network, raddr, config)
+}
+
+// Client establishes a DTLS connection over an existing connection.
+// Connection handshake will timeout using ConnectContextMaker in the Config.
+// If you want to specify the timeout duration, use ClientWithContext() instead.
+func Client(conn net.Conn, config *Config) (*Conn, error) {
+ ctx, cancel := config.connectContextMaker()
+ defer cancel()
+
+ return ClientWithContext(ctx, conn, config)
+}
+
+// Server listens for incoming DTLS connections.
+// Connection handshake will timeout using ConnectContextMaker in the Config.
+// If you want to specify the timeout duration, use ServerWithContext() instead.
+func Server(conn net.Conn, config *Config) (*Conn, error) {
+ ctx, cancel := config.connectContextMaker()
+ defer cancel()
+
+ return ServerWithContext(ctx, conn, config)
+}
+
+// DialWithContext connects to the given network address and establishes a DTLS connection on top.
+func DialWithContext(ctx context.Context, network string, raddr *net.UDPAddr, config *Config) (*Conn, error) {
+ pConn, err := net.DialUDP(network, nil, raddr)
+ if err != nil {
+ return nil, err
+ }
+ return ClientWithContext(ctx, pConn, config)
+}
+
+// ClientWithContext establishes a DTLS connection over an existing connection.
+func ClientWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) {
+ switch {
+ case config == nil:
+ return nil, errNoConfigProvided
+ case config.PSK != nil && config.PSKIdentityHint == nil:
+ return nil, errPSKAndIdentityMustBeSetForClient
+ }
+
+ return createConn(ctx, conn, config, true, nil)
+}
+
+// ServerWithContext listens for incoming DTLS connections.
+func ServerWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) {
+ if config == nil {
+ return nil, errNoConfigProvided
+ }
+
+ return createConn(ctx, conn, config, false, nil)
+}
+
+// Read reads data from the connection.
+func (c *Conn) Read(p []byte) (n int, err error) {
+ if !c.isHandshakeCompletedSuccessfully() {
+ return 0, errHandshakeInProgress
+ }
+
+ select {
+ case <-c.readDeadline.Done():
+ return 0, errDeadlineExceeded
+ default:
+ }
+
+ for {
+ select {
+ case <-c.readDeadline.Done():
+ return 0, errDeadlineExceeded
+ case out, ok := <-c.decrypted:
+ if !ok {
+ return 0, io.EOF
+ }
+ switch val := out.(type) {
+ case ([]byte):
+ if len(p) < len(val) {
+ return 0, errBufferTooSmall
+ }
+ copy(p, val)
+ return len(val), nil
+ case (error):
+ return 0, val
+ }
+ }
+ }
+}
+
+// Write writes len(p) bytes from p to the DTLS connection
+func (c *Conn) Write(p []byte) (int, error) {
+ if c.isConnectionClosed() {
+ return 0, ErrConnClosed
+ }
+
+ select {
+ case <-c.writeDeadline.Done():
+ return 0, errDeadlineExceeded
+ default:
+ }
+
+ if !c.isHandshakeCompletedSuccessfully() {
+ return 0, errHandshakeInProgress
+ }
+
+ return len(p), c.writePackets(c.writeDeadline, []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Epoch: c.getLocalEpoch(),
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ApplicationData{
+ Data: p,
+ },
+ },
+ shouldEncrypt: true,
+ },
+ })
+}
+
+// Close closes the connection.
+func (c *Conn) Close() error {
+ err := c.close(true)
+ c.handshakeLoopsFinished.Wait()
+ return err
+}
+
+// ConnectionState returns basic DTLS details about the connection.
+// Note that this replaced the `Export` function of v1.
+func (c *Conn) ConnectionState() State {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return *c.state.clone()
+}
+
+// SelectedSRTPProtectionProfile returns the selected SRTPProtectionProfile
+func (c *Conn) SelectedSRTPProtectionProfile() (SRTPProtectionProfile, bool) {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+
+ if c.state.srtpProtectionProfile == 0 {
+ return 0, false
+ }
+
+ return c.state.srtpProtectionProfile, true
+}
+
+func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ var rawPackets [][]byte
+
+ for _, p := range pkts {
+ if h, ok := p.record.Content.(*handshake.Handshake); ok {
+ handshakeRaw, err := p.record.Marshal()
+ if err != nil {
+ return err
+ }
+
+ c.log.Tracef("[handshake:%v] -> %s (epoch: %d, seq: %d)",
+ srvCliStr(c.state.isClient), h.Header.Type.String(),
+ p.record.Header.Epoch, h.Header.MessageSequence)
+ c.handshakeCache.push(handshakeRaw[recordlayer.HeaderSize:], p.record.Header.Epoch, h.Header.MessageSequence, h.Header.Type, c.state.isClient)
+
+ rawHandshakePackets, err := c.processHandshakePacket(p, h)
+ if err != nil {
+ return err
+ }
+ rawPackets = append(rawPackets, rawHandshakePackets...)
+ } else {
+ rawPacket, err := c.processPacket(p)
+ if err != nil {
+ return err
+ }
+ rawPackets = append(rawPackets, rawPacket)
+ }
+ }
+ if len(rawPackets) == 0 {
+ return nil
+ }
+ compactedRawPackets := c.compactRawPackets(rawPackets)
+
+ for _, compactedRawPackets := range compactedRawPackets {
+ if _, err := c.nextConn.WriteContext(ctx, compactedRawPackets); err != nil {
+ return netError(err)
+ }
+ }
+
+ return nil
+}
+
+func (c *Conn) compactRawPackets(rawPackets [][]byte) [][]byte {
+ combinedRawPackets := make([][]byte, 0)
+ currentCombinedRawPacket := make([]byte, 0)
+
+ for _, rawPacket := range rawPackets {
+ if len(currentCombinedRawPacket) > 0 && len(currentCombinedRawPacket)+len(rawPacket) >= c.maximumTransmissionUnit {
+ combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket)
+ currentCombinedRawPacket = []byte{}
+ }
+ currentCombinedRawPacket = append(currentCombinedRawPacket, rawPacket...)
+ }
+
+ combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket)
+
+ return combinedRawPackets
+}
+
+func (c *Conn) processPacket(p *packet) ([]byte, error) {
+ epoch := p.record.Header.Epoch
+ for len(c.state.localSequenceNumber) <= int(epoch) {
+ c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0))
+ }
+ seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1
+ if seq > recordlayer.MaxSequenceNumber {
+ // RFC 6347 Section 4.1.0
+ // The implementation must either abandon an association or rehandshake
+ // prior to allowing the sequence number to wrap.
+ return nil, errSequenceNumberOverflow
+ }
+ p.record.Header.SequenceNumber = seq
+
+ rawPacket, err := p.record.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ if p.shouldEncrypt {
+ var err error
+ rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return rawPacket, nil
+}
+
+func (c *Conn) processHandshakePacket(p *packet, h *handshake.Handshake) ([][]byte, error) {
+ rawPackets := make([][]byte, 0)
+
+ handshakeFragments, err := c.fragmentHandshake(h)
+ if err != nil {
+ return nil, err
+ }
+ epoch := p.record.Header.Epoch
+ for len(c.state.localSequenceNumber) <= int(epoch) {
+ c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0))
+ }
+
+ for _, handshakeFragment := range handshakeFragments {
+ seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1
+ if seq > recordlayer.MaxSequenceNumber {
+ return nil, errSequenceNumberOverflow
+ }
+
+ recordlayerHeader := &recordlayer.Header{
+ Version: p.record.Header.Version,
+ ContentType: p.record.Header.ContentType,
+ ContentLen: uint16(len(handshakeFragment)),
+ Epoch: p.record.Header.Epoch,
+ SequenceNumber: seq,
+ }
+
+ recordlayerHeaderBytes, err := recordlayerHeader.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ p.record.Header = *recordlayerHeader
+
+ rawPacket := append(recordlayerHeaderBytes, handshakeFragment...)
+ if p.shouldEncrypt {
+ var err error
+ rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ rawPackets = append(rawPackets, rawPacket)
+ }
+
+ return rawPackets, nil
+}
+
+func (c *Conn) fragmentHandshake(h *handshake.Handshake) ([][]byte, error) {
+ content, err := h.Message.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ fragmentedHandshakes := make([][]byte, 0)
+
+ contentFragments := splitBytes(content, c.maximumTransmissionUnit)
+ if len(contentFragments) == 0 {
+ contentFragments = [][]byte{
+ {},
+ }
+ }
+
+ offset := 0
+ for _, contentFragment := range contentFragments {
+ contentFragmentLen := len(contentFragment)
+
+ headerFragment := &handshake.Header{
+ Type: h.Header.Type,
+ Length: h.Header.Length,
+ MessageSequence: h.Header.MessageSequence,
+ FragmentOffset: uint32(offset),
+ FragmentLength: uint32(contentFragmentLen),
+ }
+
+ offset += contentFragmentLen
+
+ headerFragmentRaw, err := headerFragment.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ fragmentedHandshake := append(headerFragmentRaw, contentFragment...)
+ fragmentedHandshakes = append(fragmentedHandshakes, fragmentedHandshake)
+ }
+
+ return fragmentedHandshakes, nil
+}
+
+var poolReadBuffer = sync.Pool{ //nolint:gochecknoglobals
+ New: func() interface{} {
+ b := make([]byte, inboundBufferSize)
+ return &b
+ },
+}
+
+func (c *Conn) readAndBuffer(ctx context.Context) error {
+ bufptr := poolReadBuffer.Get().(*[]byte)
+ defer poolReadBuffer.Put(bufptr)
+
+ b := *bufptr
+ i, err := c.nextConn.ReadContext(ctx, b)
+ if err != nil {
+ return netError(err)
+ }
+
+ pkts, err := recordlayer.UnpackDatagram(b[:i])
+ if err != nil {
+ return err
+ }
+
+ var hasHandshake bool
+ for _, p := range pkts {
+ hs, alert, err := c.handleIncomingPacket(p, true)
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err == nil {
+ err = alertErr
+ }
+ }
+ }
+ if hs {
+ hasHandshake = true
+ }
+ switch e := err.(type) {
+ case nil:
+ case *errAlert:
+ if e.IsFatalOrCloseNotify() {
+ return e
+ }
+ default:
+ return e
+ }
+ }
+ if hasHandshake {
+ done := make(chan struct{})
+ select {
+ case c.handshakeRecv <- done:
+ // If the other party may retransmit the flight,
+ // we should respond even if it not a new message.
+ <-done
+ case <-c.fsm.Done():
+ }
+ }
+ return nil
+}
+
+func (c *Conn) handleQueuedPackets(ctx context.Context) error {
+ pkts := c.encryptedPackets
+ c.encryptedPackets = nil
+
+ for _, p := range pkts {
+ _, alert, err := c.handleIncomingPacket(p, false) // don't re-enqueue
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err == nil {
+ err = alertErr
+ }
+ }
+ }
+ switch e := err.(type) {
+ case nil:
+ case *errAlert:
+ if e.IsFatalOrCloseNotify() {
+ return e
+ }
+ default:
+ return e
+ }
+ }
+ return nil
+}
+
+func (c *Conn) handleIncomingPacket(buf []byte, enqueue bool) (bool, *alert.Alert, error) { //nolint:gocognit
+ h := &recordlayer.Header{}
+ if err := h.Unmarshal(buf); err != nil {
+ // Decode error must be silently discarded
+ // [RFC6347 Section-4.1.2.7]
+ c.log.Debugf("discarded broken packet: %v", err)
+ return false, nil, nil
+ }
+
+ // Validate epoch
+ remoteEpoch := c.getRemoteEpoch()
+ if h.Epoch > remoteEpoch {
+ if h.Epoch > remoteEpoch+1 {
+ c.log.Debugf("discarded future packet (epoch: %d, seq: %d)",
+ h.Epoch, h.SequenceNumber,
+ )
+ return false, nil, nil
+ }
+ if enqueue {
+ c.log.Debug("received packet of next epoch, queuing packet")
+ c.encryptedPackets = append(c.encryptedPackets, buf)
+ }
+ return false, nil, nil
+ }
+
+ // Anti-replay protection
+ for len(c.state.replayDetector) <= int(h.Epoch) {
+ c.state.replayDetector = append(c.state.replayDetector,
+ replaydetector.New(c.replayProtectionWindow, recordlayer.MaxSequenceNumber),
+ )
+ }
+ markPacketAsValid, ok := c.state.replayDetector[int(h.Epoch)].Check(h.SequenceNumber)
+ if !ok {
+ c.log.Debugf("discarded duplicated packet (epoch: %d, seq: %d)",
+ h.Epoch, h.SequenceNumber,
+ )
+ return false, nil, nil
+ }
+
+ // Decrypt
+ if h.Epoch != 0 {
+ if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() {
+ if enqueue {
+ c.encryptedPackets = append(c.encryptedPackets, buf)
+ c.log.Debug("handshake not finished, queuing packet")
+ }
+ return false, nil, nil
+ }
+
+ var err error
+ buf, err = c.state.cipherSuite.Decrypt(buf)
+ if err != nil {
+ c.log.Debugf("%s: decrypt failed: %s", srvCliStr(c.state.isClient), err)
+ return false, nil, nil
+ }
+ }
+
+ isHandshake, err := c.fragmentBuffer.push(append([]byte{}, buf...))
+ if err != nil {
+ // Decode error must be silently discarded
+ // [RFC6347 Section-4.1.2.7]
+ c.log.Debugf("defragment failed: %s", err)
+ return false, nil, nil
+ } else if isHandshake {
+ markPacketAsValid()
+ for out, epoch := c.fragmentBuffer.pop(); out != nil; out, epoch = c.fragmentBuffer.pop() {
+ rawHandshake := &handshake.Handshake{}
+ if err := rawHandshake.Unmarshal(out); err != nil {
+ c.log.Debugf("%s: handshake parse failed: %s", srvCliStr(c.state.isClient), err)
+ continue
+ }
+
+ _ = c.handshakeCache.push(out, epoch, rawHandshake.Header.MessageSequence, rawHandshake.Header.Type, !c.state.isClient)
+ }
+
+ return true, nil, nil
+ }
+
+ r := &recordlayer.RecordLayer{}
+ if err := r.Unmarshal(buf); err != nil {
+ return false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, err
+ }
+
+ switch content := r.Content.(type) {
+ case *alert.Alert:
+ c.log.Tracef("%s: <- %s", srvCliStr(c.state.isClient), content.String())
+ var a *alert.Alert
+ if content.Description == alert.CloseNotify {
+ // Respond with a close_notify [RFC5246 Section 7.2.1]
+ a = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify}
+ }
+ markPacketAsValid()
+ return false, a, &errAlert{content}
+ case *protocol.ChangeCipherSpec:
+ if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() {
+ if enqueue {
+ c.encryptedPackets = append(c.encryptedPackets, buf)
+ c.log.Debugf("CipherSuite not initialized, queuing packet")
+ }
+ return false, nil, nil
+ }
+
+ newRemoteEpoch := h.Epoch + 1
+ c.log.Tracef("%s: <- ChangeCipherSpec (epoch: %d)", srvCliStr(c.state.isClient), newRemoteEpoch)
+
+ if c.getRemoteEpoch()+1 == newRemoteEpoch {
+ c.setRemoteEpoch(newRemoteEpoch)
+ markPacketAsValid()
+ }
+ case *protocol.ApplicationData:
+ if h.Epoch == 0 {
+ return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero
+ }
+
+ markPacketAsValid()
+
+ select {
+ case c.decrypted <- content.Data:
+ case <-c.closed.Done():
+ }
+
+ default:
+ return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, content.ContentType())
+ }
+ return false, nil, nil
+}
+
+func (c *Conn) recvHandshake() <-chan chan struct{} {
+ return c.handshakeRecv
+}
+
+func (c *Conn) notify(ctx context.Context, level alert.Level, desc alert.Description) error {
+ return c.writePackets(ctx, []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Epoch: c.getLocalEpoch(),
+ Version: protocol.Version1_2,
+ },
+ Content: &alert.Alert{
+ Level: level,
+ Description: desc,
+ },
+ },
+ shouldEncrypt: c.isHandshakeCompletedSuccessfully(),
+ },
+ })
+}
+
+func (c *Conn) setHandshakeCompletedSuccessfully() {
+ c.handshakeCompletedSuccessfully.Store(struct{ bool }{true})
+}
+
+func (c *Conn) isHandshakeCompletedSuccessfully() bool {
+ boolean, _ := c.handshakeCompletedSuccessfully.Load().(struct{ bool })
+ return boolean.bool
+}
+
+func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFlight flightVal, initialState handshakeState) error { //nolint:gocognit
+ c.fsm = newHandshakeFSM(&c.state, c.handshakeCache, cfg, initialFlight)
+
+ done := make(chan struct{})
+ ctxRead, cancelRead := context.WithCancel(context.Background())
+ c.cancelHandshakeReader = cancelRead
+ cfg.onFlightState = func(f flightVal, s handshakeState) {
+ if s == handshakeFinished && !c.isHandshakeCompletedSuccessfully() {
+ c.setHandshakeCompletedSuccessfully()
+ close(done)
+ }
+ }
+
+ ctxHs, cancel := context.WithCancel(context.Background())
+ c.cancelHandshaker = cancel
+
+ firstErr := make(chan error, 1)
+
+ c.handshakeLoopsFinished.Add(2)
+
+ // Handshake routine should be live until close.
+ // The other party may request retransmission of the last flight to cope with packet drop.
+ go func() {
+ defer c.handshakeLoopsFinished.Done()
+ err := c.fsm.Run(ctxHs, c, initialState)
+ if !errors.Is(err, context.Canceled) {
+ select {
+ case firstErr <- err:
+ default:
+ }
+ }
+ }()
+ go func() {
+ defer func() {
+ // Escaping read loop.
+ // It's safe to close decrypted channnel now.
+ close(c.decrypted)
+
+ // Force stop handshaker when the underlying connection is closed.
+ cancel()
+ }()
+ defer c.handshakeLoopsFinished.Done()
+ for {
+ if err := c.readAndBuffer(ctxRead); err != nil {
+ switch e := err.(type) {
+ case *errAlert:
+ if !e.IsFatalOrCloseNotify() {
+ if c.isHandshakeCompletedSuccessfully() {
+ // Pass the error to Read()
+ select {
+ case c.decrypted <- err:
+ case <-c.closed.Done():
+ }
+ }
+ continue // non-fatal alert must not stop read loop
+ }
+ case error:
+ switch err {
+ case context.DeadlineExceeded, context.Canceled, io.EOF:
+ default:
+ if c.isHandshakeCompletedSuccessfully() {
+ // Keep read loop and pass the read error to Read()
+ select {
+ case c.decrypted <- err:
+ case <-c.closed.Done():
+ }
+ continue // non-fatal alert must not stop read loop
+ }
+ }
+ }
+ select {
+ case firstErr <- err:
+ default:
+ }
+
+ if e, ok := err.(*errAlert); ok {
+ if e.IsFatalOrCloseNotify() {
+ _ = c.close(false)
+ }
+ }
+ return
+ }
+ }
+ }()
+
+ select {
+ case err := <-firstErr:
+ cancelRead()
+ cancel()
+ return c.translateHandshakeCtxError(err)
+ case <-ctx.Done():
+ cancelRead()
+ cancel()
+ return c.translateHandshakeCtxError(ctx.Err())
+ case <-done:
+ return nil
+ }
+}
+
+func (c *Conn) translateHandshakeCtxError(err error) error {
+ if err == nil {
+ return nil
+ }
+ if errors.Is(err, context.Canceled) && c.isHandshakeCompletedSuccessfully() {
+ return nil
+ }
+ return &HandshakeError{Err: err}
+}
+
+func (c *Conn) close(byUser bool) error {
+ c.cancelHandshaker()
+ c.cancelHandshakeReader()
+
+ if c.isHandshakeCompletedSuccessfully() && byUser {
+ // Discard error from notify() to return non-error on the first user call of Close()
+ // even if the underlying connection is already closed.
+ _ = c.notify(context.Background(), alert.Warning, alert.CloseNotify)
+ }
+
+ c.closeLock.Lock()
+ // Don't return ErrConnClosed at the first time of the call from user.
+ closedByUser := c.connectionClosedByUser
+ if byUser {
+ c.connectionClosedByUser = true
+ }
+ c.closed.Close()
+ c.closeLock.Unlock()
+
+ if closedByUser {
+ return ErrConnClosed
+ }
+
+ return c.nextConn.Close()
+}
+
+func (c *Conn) isConnectionClosed() bool {
+ select {
+ case <-c.closed.Done():
+ return true
+ default:
+ return false
+ }
+}
+
+func (c *Conn) setLocalEpoch(epoch uint16) {
+ c.state.localEpoch.Store(epoch)
+}
+
+func (c *Conn) getLocalEpoch() uint16 {
+ return c.state.localEpoch.Load().(uint16)
+}
+
+func (c *Conn) setRemoteEpoch(epoch uint16) {
+ c.state.remoteEpoch.Store(epoch)
+}
+
+func (c *Conn) getRemoteEpoch() uint16 {
+ return c.state.remoteEpoch.Load().(uint16)
+}
+
+// LocalAddr implements net.Conn.LocalAddr
+func (c *Conn) LocalAddr() net.Addr {
+ return c.nextConn.LocalAddr()
+}
+
+// RemoteAddr implements net.Conn.RemoteAddr
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.nextConn.RemoteAddr()
+}
+
+// SetDeadline implements net.Conn.SetDeadline
+func (c *Conn) SetDeadline(t time.Time) error {
+ c.readDeadline.Set(t)
+ return c.SetWriteDeadline(t)
+}
+
+// SetReadDeadline implements net.Conn.SetReadDeadline
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ c.readDeadline.Set(t)
+ // Read deadline is fully managed by this layer.
+ // Don't set read deadline to underlying connection.
+ return nil
+}
+
+// SetWriteDeadline implements net.Conn.SetWriteDeadline
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ c.writeDeadline.Set(t)
+ // Write deadline is also fully managed by this layer.
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/crypto.go b/vendor/github.com/pion/dtls/v2/crypto.go
new file mode 100644
index 0000000..768ee47
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/crypto.go
@@ -0,0 +1,221 @@
+package dtls
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/asn1"
+ "encoding/binary"
+ "math/big"
+ "time"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+)
+
+type ecdsaSignature struct {
+ R, S *big.Int
+}
+
+func valueKeyMessage(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve) []byte {
+ serverECDHParams := make([]byte, 4)
+ serverECDHParams[0] = 3 // named curve
+ binary.BigEndian.PutUint16(serverECDHParams[1:], uint16(namedCurve))
+ serverECDHParams[3] = byte(len(publicKey))
+
+ plaintext := []byte{}
+ plaintext = append(plaintext, clientRandom...)
+ plaintext = append(plaintext, serverRandom...)
+ plaintext = append(plaintext, serverECDHParams...)
+ plaintext = append(plaintext, publicKey...)
+
+ return plaintext
+}
+
+// If the client provided a "signature_algorithms" extension, then all
+// certificates provided by the server MUST be signed by a
+// hash/signature algorithm pair that appears in that extension
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.2
+func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) {
+ msg := valueKeyMessage(clientRandom, serverRandom, publicKey, namedCurve)
+ switch p := privateKey.(type) {
+ case ed25519.PrivateKey:
+ // https://crypto.stackexchange.com/a/55483
+ return p.Sign(rand.Reader, msg, crypto.Hash(0))
+ case *ecdsa.PrivateKey:
+ hashed := hashAlgorithm.Digest(msg)
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ case *rsa.PrivateKey:
+ hashed := hashAlgorithm.Digest(msg)
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ }
+
+ return nil, errKeySignatureGenerateUnimplemented
+}
+
+func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hash.Algorithm, rawCertificates [][]byte) error { //nolint:dupl
+ if len(rawCertificates) == 0 {
+ return errLengthMismatch
+ }
+ certificate, err := x509.ParseCertificate(rawCertificates[0])
+ if err != nil {
+ return err
+ }
+
+ switch p := certificate.PublicKey.(type) {
+ case ed25519.PublicKey:
+ if ok := ed25519.Verify(p, message, remoteKeySignature); !ok {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *ecdsa.PublicKey:
+ ecdsaSig := &ecdsaSignature{}
+ if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil {
+ return err
+ }
+ if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+ return errInvalidECDSASignature
+ }
+ hashed := hashAlgorithm.Digest(message)
+ if !ecdsa.Verify(p, hashed, ecdsaSig.R, ecdsaSig.S) {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *rsa.PublicKey:
+ switch certificate.SignatureAlgorithm {
+ case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
+ hashed := hashAlgorithm.Digest(message)
+ return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature)
+ default:
+ return errKeySignatureVerifyUnimplemented
+ }
+ }
+
+ return errKeySignatureVerifyUnimplemented
+}
+
+// If the server has sent a CertificateRequest message, the client MUST send the Certificate
+// message. The ClientKeyExchange message is now sent, and the content
+// of that message will depend on the public key algorithm selected
+// between the ClientHello and the ServerHello. If the client has sent
+// a certificate with signing ability, a digitally-signed
+// CertificateVerify message is sent to explicitly verify possession of
+// the private key in the certificate.
+// https://tools.ietf.org/html/rfc5246#section-7.3
+func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) {
+ h := sha256.New()
+ if _, err := h.Write(handshakeBodies); err != nil {
+ return nil, err
+ }
+ hashed := h.Sum(nil)
+
+ switch p := privateKey.(type) {
+ case ed25519.PrivateKey:
+ // https://crypto.stackexchange.com/a/55483
+ return p.Sign(rand.Reader, hashed, crypto.Hash(0))
+ case *ecdsa.PrivateKey:
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ case *rsa.PrivateKey:
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ }
+
+ return nil, errInvalidSignatureAlgorithm
+}
+
+func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hash.Algorithm, remoteKeySignature []byte, rawCertificates [][]byte) error { //nolint:dupl
+ if len(rawCertificates) == 0 {
+ return errLengthMismatch
+ }
+ certificate, err := x509.ParseCertificate(rawCertificates[0])
+ if err != nil {
+ return err
+ }
+
+ switch p := certificate.PublicKey.(type) {
+ case ed25519.PublicKey:
+ if ok := ed25519.Verify(p, handshakeBodies, remoteKeySignature); !ok {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *ecdsa.PublicKey:
+ ecdsaSig := &ecdsaSignature{}
+ if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil {
+ return err
+ }
+ if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+ return errInvalidECDSASignature
+ }
+ hash := hashAlgorithm.Digest(handshakeBodies)
+ if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *rsa.PublicKey:
+ switch certificate.SignatureAlgorithm {
+ case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
+ hash := hashAlgorithm.Digest(handshakeBodies)
+ return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hash, remoteKeySignature)
+ default:
+ return errKeySignatureVerifyUnimplemented
+ }
+ }
+
+ return errKeySignatureVerifyUnimplemented
+}
+
+func loadCerts(rawCertificates [][]byte) ([]*x509.Certificate, error) {
+ if len(rawCertificates) == 0 {
+ return nil, errLengthMismatch
+ }
+
+ certs := make([]*x509.Certificate, 0, len(rawCertificates))
+ for _, rawCert := range rawCertificates {
+ cert, err := x509.ParseCertificate(rawCert)
+ if err != nil {
+ return nil, err
+ }
+ certs = append(certs, cert)
+ }
+ return certs, nil
+}
+
+func verifyClientCert(rawCertificates [][]byte, roots *x509.CertPool) (chains [][]*x509.Certificate, err error) {
+ certificate, err := loadCerts(rawCertificates)
+ if err != nil {
+ return nil, err
+ }
+ intermediateCAPool := x509.NewCertPool()
+ for _, cert := range certificate[1:] {
+ intermediateCAPool.AddCert(cert)
+ }
+ opts := x509.VerifyOptions{
+ Roots: roots,
+ CurrentTime: time.Now(),
+ Intermediates: intermediateCAPool,
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+ }
+ return certificate[0].Verify(opts)
+}
+
+func verifyServerCert(rawCertificates [][]byte, roots *x509.CertPool, serverName string) (chains [][]*x509.Certificate, err error) {
+ certificate, err := loadCerts(rawCertificates)
+ if err != nil {
+ return nil, err
+ }
+ intermediateCAPool := x509.NewCertPool()
+ for _, cert := range certificate[1:] {
+ intermediateCAPool.AddCert(cert)
+ }
+ opts := x509.VerifyOptions{
+ Roots: roots,
+ CurrentTime: time.Now(),
+ DNSName: serverName,
+ Intermediates: intermediateCAPool,
+ }
+ return certificate[0].Verify(opts)
+}
diff --git a/vendor/github.com/pion/dtls/v2/dtls.go b/vendor/github.com/pion/dtls/v2/dtls.go
new file mode 100644
index 0000000..125b904
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/dtls.go
@@ -0,0 +1,2 @@
+// Package dtls implements Datagram Transport Layer Security (DTLS) 1.2
+package dtls
diff --git a/vendor/github.com/pion/dtls/v2/errors.go b/vendor/github.com/pion/dtls/v2/errors.go
new file mode 100644
index 0000000..2e16388
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/errors.go
@@ -0,0 +1,141 @@
+package dtls
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "os"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "golang.org/x/xerrors"
+)
+
+// Typed errors
+var (
+ ErrConnClosed = &FatalError{Err: errors.New("conn is closed")} //nolint:goerr113
+
+ errDeadlineExceeded = &TimeoutError{Err: xerrors.Errorf("read/write timeout: %w", context.DeadlineExceeded)}
+ errInvalidContentType = &TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
+
+ errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errContextUnsupported = &TemporaryError{Err: errors.New("context is not supported for ExportKeyingMaterial")} //nolint:goerr113
+ errHandshakeInProgress = &TemporaryError{Err: errors.New("handshake is in progress")} //nolint:goerr113
+ errReservedExportKeyingMaterial = &TemporaryError{Err: errors.New("ExportKeyingMaterial can not be used with a reserved label")} //nolint:goerr113
+ errApplicationDataEpochZero = &TemporaryError{Err: errors.New("ApplicationData with epoch of 0")} //nolint:goerr113
+ errUnhandledContextType = &TemporaryError{Err: errors.New("unhandled contentType")} //nolint:goerr113
+
+ errCertificateVerifyNoCertificate = &FatalError{Err: errors.New("client sent certificate verify but we have no certificate to verify")} //nolint:goerr113
+ errCipherSuiteNoIntersection = &FatalError{Err: errors.New("client+server do not support any shared cipher suites")} //nolint:goerr113
+ errClientCertificateNotVerified = &FatalError{Err: errors.New("client sent certificate but did not verify it")} //nolint:goerr113
+ errClientCertificateRequired = &FatalError{Err: errors.New("server required client verification, but got none")} //nolint:goerr113
+ errClientNoMatchingSRTPProfile = &FatalError{Err: errors.New("server responded with SRTP Profile we do not support")} //nolint:goerr113
+ errClientRequiredButNoServerEMS = &FatalError{Err: errors.New("client required Extended Master Secret extension, but server does not support it")} //nolint:goerr113
+ errCookieMismatch = &FatalError{Err: errors.New("client+server cookie does not match")} //nolint:goerr113
+ errIdentityNoPSK = &FatalError{Err: errors.New("PSK Identity Hint provided but PSK is nil")} //nolint:goerr113
+ errInvalidCertificate = &FatalError{Err: errors.New("no certificate provided")} //nolint:goerr113
+ errInvalidCipherSuite = &FatalError{Err: errors.New("invalid or unknown cipher suite")} //nolint:goerr113
+ errInvalidECDSASignature = &FatalError{Err: errors.New("ECDSA signature contained zero or negative values")} //nolint:goerr113
+ errInvalidPrivateKey = &FatalError{Err: errors.New("invalid private key type")} //nolint:goerr113
+ errInvalidSignatureAlgorithm = &FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113
+ errKeySignatureMismatch = &FatalError{Err: errors.New("expected and actual key signature do not match")} //nolint:goerr113
+ errNilNextConn = &FatalError{Err: errors.New("Conn can not be created with a nil nextConn")} //nolint:goerr113
+ errNoAvailableCipherSuites = &FatalError{Err: errors.New("connection can not be created, no CipherSuites satisfy this Config")} //nolint:goerr113
+ errNoAvailablePSKCipherSuite = &FatalError{Err: errors.New("connection can not be created, pre-shared key present but no compatible CipherSuite")} //nolint:goerr113
+ errNoAvailableCertificateCipherSuite = &FatalError{Err: errors.New("connection can not be created, certificate present but no compatible CipherSuite")} //nolint:goerr113
+ errNoAvailableSignatureSchemes = &FatalError{Err: errors.New("connection can not be created, no SignatureScheme satisfy this Config")} //nolint:goerr113
+ errNoCertificates = &FatalError{Err: errors.New("no certificates configured")} //nolint:goerr113
+ errNoConfigProvided = &FatalError{Err: errors.New("no config provided")} //nolint:goerr113
+ errNoSupportedEllipticCurves = &FatalError{Err: errors.New("client requested zero or more elliptic curves that are not supported by the server")} //nolint:goerr113
+ errUnsupportedProtocolVersion = &FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
+ errPSKAndIdentityMustBeSetForClient = &FatalError{Err: errors.New("PSK and PSK Identity Hint must both be set for client")} //nolint:goerr113
+ errRequestedButNoSRTPExtension = &FatalError{Err: errors.New("SRTP support was requested but server did not respond with use_srtp extension")} //nolint:goerr113
+ errServerNoMatchingSRTPProfile = &FatalError{Err: errors.New("client requested SRTP but we have no matching profiles")} //nolint:goerr113
+ errServerRequiredButNoClientEMS = &FatalError{Err: errors.New("server requires the Extended Master Secret extension, but the client does not support it")} //nolint:goerr113
+ errVerifyDataMismatch = &FatalError{Err: errors.New("expected and actual verify data does not match")} //nolint:goerr113
+
+ errInvalidFlight = &InternalError{Err: errors.New("invalid flight number")} //nolint:goerr113
+ errKeySignatureGenerateUnimplemented = &InternalError{Err: errors.New("unable to generate key signature, unimplemented")} //nolint:goerr113
+ errKeySignatureVerifyUnimplemented = &InternalError{Err: errors.New("unable to verify key signature, unimplemented")} //nolint:goerr113
+ errLengthMismatch = &InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
+ errSequenceNumberOverflow = &InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
+ errInvalidFSMTransition = &InternalError{Err: errors.New("invalid state machine transition")} //nolint:goerr113
+)
+
+// FatalError indicates that the DTLS connection is no longer available.
+// It is mainly caused by wrong configuration of server or client.
+type FatalError = protocol.FatalError
+
+// InternalError indicates and internal error caused by the implementation, and the DTLS connection is no longer available.
+// It is mainly caused by bugs or tried to use unimplemented features.
+type InternalError = protocol.InternalError
+
+// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary.
+type TemporaryError = protocol.TemporaryError
+
+// TimeoutError indicates that the request was timed out.
+type TimeoutError = protocol.TimeoutError
+
+// HandshakeError indicates that the handshake failed.
+type HandshakeError = protocol.HandshakeError
+
+// invalidCipherSuite indicates an attempt at using an unsupported cipher suite.
+type invalidCipherSuite struct {
+ id CipherSuiteID
+}
+
+func (e *invalidCipherSuite) Error() string {
+ return fmt.Sprintf("CipherSuite with id(%d) is not valid", e.id)
+}
+
+func (e *invalidCipherSuite) Is(err error) bool {
+ if other, ok := err.(*invalidCipherSuite); ok {
+ return e.id == other.id
+ }
+ return false
+}
+
+// errAlert wraps DTLS alert notification as an error
+type errAlert struct {
+ *alert.Alert
+}
+
+func (e *errAlert) Error() string {
+ return fmt.Sprintf("alert: %s", e.Alert.String())
+}
+
+func (e *errAlert) IsFatalOrCloseNotify() bool {
+ return e.Level == alert.Fatal || e.Description == alert.CloseNotify
+}
+
+func (e *errAlert) Is(err error) bool {
+ if other, ok := err.(*errAlert); ok {
+ return e.Level == other.Level && e.Description == other.Description
+ }
+ return false
+}
+
+// netError translates an error from underlying Conn to corresponding net.Error.
+func netError(err error) error {
+ switch err {
+ case io.EOF, context.Canceled, context.DeadlineExceeded:
+ // Return io.EOF and context errors as is.
+ return err
+ }
+ switch e := err.(type) {
+ case (*net.OpError):
+ if se, ok := e.Err.(*os.SyscallError); ok {
+ if se.Timeout() {
+ return &TimeoutError{Err: err}
+ }
+ if isOpErrorTemporary(se) {
+ return &TemporaryError{Err: err}
+ }
+ }
+ case (net.Error):
+ return err
+ }
+ return &FatalError{Err: err}
+}
diff --git a/vendor/github.com/pion/dtls/v2/errors_errno.go b/vendor/github.com/pion/dtls/v2/errors_errno.go
new file mode 100644
index 0000000..a9a439b
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/errors_errno.go
@@ -0,0 +1,25 @@
+// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows
+
+// For systems having syscall.Errno.
+// Update build targets by following command:
+// $ grep -R ECONN $(go env GOROOT)/src/syscall/zerrors_*.go \
+// | tr "." "_" | cut -d"_" -f"2" | sort | uniq
+
+package dtls
+
+import (
+ "os"
+ "syscall"
+)
+
+func isOpErrorTemporary(err *os.SyscallError) bool {
+ if ne, ok := err.Err.(syscall.Errno); ok {
+ switch ne {
+ case syscall.ECONNREFUSED:
+ return true
+ default:
+ return false
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/pion/dtls/v2/errors_noerrno.go b/vendor/github.com/pion/dtls/v2/errors_noerrno.go
new file mode 100644
index 0000000..fcc37ce
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/errors_noerrno.go
@@ -0,0 +1,14 @@
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!nacl,!nacljs,!netbsd,!openbsd,!solaris,!windows
+
+// For systems without syscall.Errno.
+// Build targets must be inverse of errors_errno.go
+
+package dtls
+
+import (
+ "os"
+)
+
+func isOpErrorTemporary(err *os.SyscallError) bool {
+ return false
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight.go b/vendor/github.com/pion/dtls/v2/flight.go
new file mode 100644
index 0000000..580ee48
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight.go
@@ -0,0 +1,75 @@
+package dtls
+
+/*
+ DTLS messages are grouped into a series of message flights, according
+ to the diagrams below. Although each flight of messages may consist
+ of a number of messages, they should be viewed as monolithic for the
+ purpose of timeout and retransmission.
+ https://tools.ietf.org/html/rfc4347#section-4.2.4
+ Client Server
+ ------ ------
+ Waiting Flight 0
+
+ ClientHello --------> Flight 1
+
+ <------- HelloVerifyRequest Flight 2
+
+ ClientHello --------> Flight 3
+
+ ServerHello \
+ Certificate* \
+ ServerKeyExchange* Flight 4
+ CertificateRequest* /
+ <-------- ServerHelloDone /
+
+ Certificate* \
+ ClientKeyExchange \
+ CertificateVerify* Flight 5
+ [ChangeCipherSpec] /
+ Finished --------> /
+
+ [ChangeCipherSpec] \ Flight 6
+ <-------- Finished /
+
+*/
+
+type flightVal uint8
+
+const (
+ flight0 flightVal = iota + 1
+ flight1
+ flight2
+ flight3
+ flight4
+ flight5
+ flight6
+)
+
+func (f flightVal) String() string {
+ switch f {
+ case flight0:
+ return "Flight 0"
+ case flight1:
+ return "Flight 1"
+ case flight2:
+ return "Flight 2"
+ case flight3:
+ return "Flight 3"
+ case flight4:
+ return "Flight 4"
+ case flight5:
+ return "Flight 5"
+ case flight6:
+ return "Flight 6"
+ default:
+ return "Invalid Flight"
+ }
+}
+
+func (f flightVal) isLastSendFlight() bool {
+ return f == flight6
+}
+
+func (f flightVal) isLastRecvFlight() bool {
+ return f == flight5
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight0handler.go b/vendor/github.com/pion/dtls/v2/flight0handler.go
new file mode 100644
index 0000000..949d7c0
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight0handler.go
@@ -0,0 +1,102 @@
+package dtls
+
+import (
+ "context"
+ "crypto/rand"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+)
+
+func flight0Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ seq, msgs, ok := cache.fullPullMap(0,
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+ state.handshakeRecvSequence = seq
+
+ var clientHello *handshake.MessageClientHello
+
+ // Validate type
+ if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if !clientHello.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+
+ state.remoteRandom = clientHello.Random
+
+ cipherSuites := []CipherSuite{}
+ for _, id := range clientHello.CipherSuiteIDs {
+ if c := cipherSuiteForID(CipherSuiteID(id), cfg.customCipherSuites); c != nil {
+ cipherSuites = append(cipherSuites, c)
+ }
+ }
+
+ if state.cipherSuite, ok = findMatchingCipherSuite(cipherSuites, cfg.localCipherSuites); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
+ }
+
+ for _, val := range clientHello.Extensions {
+ switch e := val.(type) {
+ case *extension.SupportedEllipticCurves:
+ if len(e.EllipticCurves) == 0 {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoSupportedEllipticCurves
+ }
+ state.namedCurve = e.EllipticCurves[0]
+ case *extension.UseSRTP:
+ profile, ok := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles)
+ if !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerNoMatchingSRTPProfile
+ }
+ state.srtpProtectionProfile = profile
+ case *extension.UseExtendedMasterSecret:
+ if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
+ state.extendedMasterSecret = true
+ }
+ case *extension.ServerName:
+ state.serverName = e.ServerName // remote server name
+ }
+ }
+
+ if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerRequiredButNoClientEMS
+ }
+
+ if state.localKeypair == nil {
+ var err error
+ state.localKeypair, err = elliptic.GenerateKeypair(state.namedCurve)
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
+ }
+ }
+
+ return flight2, nil, nil
+}
+
+func flight0Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ // Initialize
+ state.cookie = make([]byte, cookieLength)
+ if _, err := rand.Read(state.cookie); err != nil {
+ return nil, nil, err
+ }
+
+ var zeroEpoch uint16
+ state.localEpoch.Store(zeroEpoch)
+ state.remoteEpoch.Store(zeroEpoch)
+ state.namedCurve = defaultNamedCurve
+
+ if err := state.localRandom.Populate(); err != nil {
+ return nil, nil, err
+ }
+
+ return nil, nil, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight1handler.go b/vendor/github.com/pion/dtls/v2/flight1handler.go
new file mode 100644
index 0000000..9229292
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight1handler.go
@@ -0,0 +1,112 @@
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight1Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ // HelloVerifyRequest can be skipped by the server,
+ // so allow ServerHello during flight1 also
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+ handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, true},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ if _, ok := msgs[handshake.TypeServerHello]; ok {
+ // Flight1 and flight2 were skipped.
+ // Parse as flight3.
+ return flight3Parse(ctx, c, state, cache, cfg)
+ }
+
+ if h, ok := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); ok {
+ // DTLS 1.2 clients must not assume that the server will use the protocol version
+ // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
+ if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+ state.cookie = append([]byte{}, h.Cookie...)
+ state.handshakeRecvSequence = seq
+ return flight3, nil, nil
+ }
+
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+}
+
+func flight1Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ var zeroEpoch uint16
+ state.localEpoch.Store(zeroEpoch)
+ state.remoteEpoch.Store(zeroEpoch)
+ state.namedCurve = defaultNamedCurve
+ state.cookie = nil
+
+ if err := state.localRandom.Populate(); err != nil {
+ return nil, nil, err
+ }
+
+ extensions := []extension.Extension{
+ &extension.SupportedSignatureAlgorithms{
+ SignatureHashAlgorithms: cfg.localSignatureSchemes,
+ },
+ &extension.RenegotiationInfo{
+ RenegotiatedConnection: 0,
+ },
+ }
+ if cfg.localPSKCallback == nil {
+ extensions = append(extensions, []extension.Extension{
+ &extension.SupportedEllipticCurves{
+ EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384},
+ },
+ &extension.SupportedPointFormats{
+ PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
+ },
+ }...)
+ }
+
+ if len(cfg.localSRTPProtectionProfiles) > 0 {
+ extensions = append(extensions, &extension.UseSRTP{
+ ProtectionProfiles: cfg.localSRTPProtectionProfiles,
+ })
+ }
+
+ if cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
+ cfg.extendedMasterSecret == RequireExtendedMasterSecret {
+ extensions = append(extensions, &extension.UseExtendedMasterSecret{
+ Supported: true,
+ })
+ }
+
+ if len(cfg.serverName) > 0 {
+ extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName})
+ }
+
+ return []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageClientHello{
+ Version: protocol.Version1_2,
+ Cookie: state.cookie,
+ Random: state.localRandom,
+ CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
+ CompressionMethods: defaultCompressionMethods(),
+ Extensions: extensions,
+ },
+ },
+ },
+ },
+ }, nil, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight2handler.go b/vendor/github.com/pion/dtls/v2/flight2handler.go
new file mode 100644
index 0000000..bb8e91d
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight2handler.go
@@ -0,0 +1,61 @@
+package dtls
+
+import (
+ "bytes"
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight2Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ )
+ if !ok {
+ // Client may retransmit the first ClientHello when HelloVerifyRequest is dropped.
+ // Parse as flight 0 in this case.
+ return flight0Parse(ctx, c, state, cache, cfg)
+ }
+ state.handshakeRecvSequence = seq
+
+ var clientHello *handshake.MessageClientHello
+
+ // Validate type
+ if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if !clientHello.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+
+ if len(clientHello.Cookie) == 0 {
+ return 0, nil, nil
+ }
+ if !bytes.Equal(state.cookie, clientHello.Cookie) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.AccessDenied}, errCookieMismatch
+ }
+ return flight4, nil, nil
+}
+
+func flight2Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ state.handshakeSendSequence = 0
+ return []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageHelloVerifyRequest{
+ Version: protocol.Version1_2,
+ Cookie: state.cookie,
+ },
+ },
+ },
+ },
+ }, nil, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight3handler.go b/vendor/github.com/pion/dtls/v2/flight3handler.go
new file mode 100644
index 0000000..f953be8
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight3handler.go
@@ -0,0 +1,194 @@
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit
+ // Clients may receive multiple HelloVerifyRequest messages with different cookies.
+ // Clients SHOULD handle this by sending a new ClientHello with a cookie in response
+ // to the new HelloVerifyRequest. RFC 6347 Section 4.2.1
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+ handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
+ )
+ if ok {
+ if h, msgOk := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); msgOk {
+ // DTLS 1.2 clients must not assume that the server will use the protocol version
+ // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
+ if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+ state.cookie = append([]byte{}, h.Cookie...)
+ state.handshakeRecvSequence = seq
+ return flight3, nil, nil
+ }
+ }
+
+ if cfg.localPSKCallback != nil {
+ seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence,
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ )
+ } else {
+ seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence,
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ )
+ }
+ if !ok {
+ // Don't have enough messages. Keep reading
+ return 0, nil, nil
+ }
+ state.handshakeRecvSequence = seq
+
+ if h, ok := msgs[handshake.TypeServerHello].(*handshake.MessageServerHello); ok {
+ if !h.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+ for _, v := range h.Extensions {
+ switch e := v.(type) {
+ case *extension.UseSRTP:
+ profile, ok := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles)
+ if !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, errClientNoMatchingSRTPProfile
+ }
+ state.srtpProtectionProfile = profile
+ case *extension.UseExtendedMasterSecret:
+ if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
+ state.extendedMasterSecret = true
+ }
+ }
+ }
+ if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS
+ }
+ if len(cfg.localSRTPProtectionProfiles) > 0 && state.srtpProtectionProfile == 0 {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension
+ }
+
+ remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*h.CipherSuiteID), cfg.customCipherSuites)
+ if remoteCipherSuite == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
+ }
+
+ selectedCipherSuite, ok := findMatchingCipherSuite([]CipherSuite{remoteCipherSuite}, cfg.localCipherSuites)
+ if !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
+ }
+
+ state.cipherSuite = selectedCipherSuite
+ state.remoteRandom = h.Random
+ cfg.log.Tracef("[handshake] use cipher suite: %s", selectedCipherSuite.String())
+ }
+
+ if h, ok := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); ok {
+ state.PeerCertificates = h.Certificate
+ } else if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errInvalidCertificate
+ }
+
+ if h, ok := msgs[handshake.TypeServerKeyExchange].(*handshake.MessageServerKeyExchange); ok {
+ alertPtr, err := handleServerKeyExchange(c, state, cfg, h)
+ if err != nil {
+ return 0, alertPtr, err
+ }
+ }
+
+ if _, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok {
+ state.remoteRequestedCertificate = true
+ }
+
+ return flight5, nil, nil
+}
+
+func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange) (*alert.Alert, error) {
+ var err error
+ if cfg.localPSKCallback != nil {
+ var psk []byte
+ if psk, err = cfg.localPSKCallback(h.IdentityHint); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.IdentityHint = h.IdentityHint
+ state.preMasterSecret = prf.PSKPreMasterSecret(psk)
+ } else {
+ if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ if state.preMasterSecret, err = prf.PreMasterSecret(h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ return nil, nil
+}
+
+func flight3Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ extensions := []extension.Extension{
+ &extension.SupportedSignatureAlgorithms{
+ SignatureHashAlgorithms: cfg.localSignatureSchemes,
+ },
+ &extension.RenegotiationInfo{
+ RenegotiatedConnection: 0,
+ },
+ }
+ if cfg.localPSKCallback == nil {
+ extensions = append(extensions, []extension.Extension{
+ &extension.SupportedEllipticCurves{
+ EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384},
+ },
+ &extension.SupportedPointFormats{
+ PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
+ },
+ }...)
+ }
+
+ if len(cfg.localSRTPProtectionProfiles) > 0 {
+ extensions = append(extensions, &extension.UseSRTP{
+ ProtectionProfiles: cfg.localSRTPProtectionProfiles,
+ })
+ }
+
+ if cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
+ cfg.extendedMasterSecret == RequireExtendedMasterSecret {
+ extensions = append(extensions, &extension.UseExtendedMasterSecret{
+ Supported: true,
+ })
+ }
+
+ if len(cfg.serverName) > 0 {
+ extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName})
+ }
+
+ return []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageClientHello{
+ Version: protocol.Version1_2,
+ Cookie: state.cookie,
+ Random: state.localRandom,
+ CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
+ CompressionMethods: defaultCompressionMethods(),
+ Extensions: extensions,
+ },
+ },
+ },
+ },
+ }, nil, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight4handler.go b/vendor/github.com/pion/dtls/v2/flight4handler.go
new file mode 100644
index 0000000..d791c9d
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight4handler.go
@@ -0,0 +1,337 @@
+package dtls
+
+import (
+ "context"
+ "crypto/x509"
+
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ // Validate type
+ var clientKeyExchange *handshake.MessageClientKeyExchange
+ if clientKeyExchange, ok = msgs[handshake.TypeClientKeyExchange].(*handshake.MessageClientKeyExchange); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if h, hasCert := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); hasCert {
+ state.PeerCertificates = h.Certificate
+ }
+
+ if h, hasCertVerify := msgs[handshake.TypeCertificateVerify].(*handshake.MessageCertificateVerify); hasCertVerify {
+ if state.PeerCertificates == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errCertificateVerifyNoCertificate
+ }
+
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ )
+
+ // Verify that the pair of hash algorithm and signiture is listed.
+ var validSignatureScheme bool
+ for _, ss := range cfg.localSignatureSchemes {
+ if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm {
+ validSignatureScheme = true
+ break
+ }
+ }
+ if !validSignatureScheme {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
+ }
+
+ if err := verifyCertificateVerify(plainText, h.HashAlgorithm, h.Signature, state.PeerCertificates); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ var chains [][]*x509.Certificate
+ var err error
+ var verified bool
+ if cfg.clientAuth >= VerifyClientCertIfGiven {
+ if chains, err = verifyClientCert(state.PeerCertificates, cfg.clientCAs); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ verified = true
+ }
+ if cfg.verifyPeerCertificate != nil {
+ if err := cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+ state.peerCertificatesVerified = verified
+ }
+
+ if !state.cipherSuite.IsInitialized() {
+ serverRandom := state.localRandom.MarshalFixed()
+ clientRandom := state.remoteRandom.MarshalFixed()
+
+ var err error
+ var preMasterSecret []byte
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey {
+ var psk []byte
+ if psk, err = cfg.localPSKCallback(clientKeyExchange.IdentityHint); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.IdentityHint = clientKeyExchange.IdentityHint
+ preMasterSecret = prf.PSKPreMasterSecret(psk)
+ } else {
+ preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
+ }
+ }
+
+ if state.extendedMasterSecret {
+ var sessionHash []byte
+ sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch)
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ state.masterSecret, err = prf.ExtendedMasterSecret(preMasterSecret, sessionHash, state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ } else {
+ state.masterSecret, err = prf.MasterSecret(preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ if err := state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], false); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
+ }
+
+ // Now, encrypted packets can be handled
+ if err := c.handleQueuedPackets(ctx); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ seq, msgs, ok = cache.fullPullMap(seq,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+ state.handshakeRecvSequence = seq
+
+ if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous {
+ return flight6, nil, nil
+ }
+
+ switch cfg.clientAuth {
+ case RequireAnyClientCert:
+ if state.PeerCertificates == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
+ }
+ case VerifyClientCertIfGiven:
+ if state.PeerCertificates != nil && !state.peerCertificatesVerified {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
+ }
+ case RequireAndVerifyClientCert:
+ if state.PeerCertificates == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
+ }
+ if !state.peerCertificatesVerified {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
+ }
+ case NoClientCert, RequestClientCert:
+ return flight6, nil, nil
+ }
+
+ return flight6, nil, nil
+}
+
+func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ extensions := []extension.Extension{&extension.RenegotiationInfo{
+ RenegotiatedConnection: 0,
+ }}
+ if (cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
+ cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret {
+ extensions = append(extensions, &extension.UseExtendedMasterSecret{
+ Supported: true,
+ })
+ }
+ if state.srtpProtectionProfile != 0 {
+ extensions = append(extensions, &extension.UseSRTP{
+ ProtectionProfiles: []SRTPProtectionProfile{state.srtpProtectionProfile},
+ })
+ }
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
+ extensions = append(extensions, []extension.Extension{
+ &extension.SupportedEllipticCurves{
+ EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384},
+ },
+ &extension.SupportedPointFormats{
+ PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
+ },
+ }...)
+ }
+
+ var pkts []*packet
+ cipherSuiteID := uint16(state.cipherSuite.ID())
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerHello{
+ Version: protocol.Version1_2,
+ Random: state.localRandom,
+ CipherSuiteID: &cipherSuiteID,
+ CompressionMethod: defaultCompressionMethods()[0],
+ Extensions: extensions,
+ },
+ },
+ },
+ })
+
+ switch {
+ case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
+ certificate, err := cfg.getCertificate(cfg.serverName)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
+ }
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificate{
+ Certificate: certificate.Certificate,
+ },
+ },
+ },
+ })
+
+ serverRandom := state.localRandom.MarshalFixed()
+ clientRandom := state.remoteRandom.MarshalFixed()
+
+ // Find compatible signature scheme
+ signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, certificate.PrivateKey)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err
+ }
+
+ signature, err := generateKeySignature(clientRandom[:], serverRandom[:], state.localKeypair.PublicKey, state.namedCurve, certificate.PrivateKey, signatureHashAlgo.Hash)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.localKeySignature = signature
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerKeyExchange{
+ EllipticCurveType: elliptic.CurveTypeNamedCurve,
+ NamedCurve: state.namedCurve,
+ PublicKey: state.localKeypair.PublicKey,
+ HashAlgorithm: signatureHashAlgo.Hash,
+ SignatureAlgorithm: signatureHashAlgo.Signature,
+ Signature: state.localKeySignature,
+ },
+ },
+ },
+ })
+
+ if cfg.clientAuth > NoClientCert {
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificateRequest{
+ CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign},
+ SignatureHashAlgorithms: cfg.localSignatureSchemes,
+ },
+ },
+ },
+ })
+ }
+ case cfg.localPSKIdentityHint != nil:
+ // To help the client in selecting which identity to use, the server
+ // can provide a "PSK identity hint" in the ServerKeyExchange message.
+ // If no hint is provided, the ServerKeyExchange message is omitted.
+ //
+ // https://tools.ietf.org/html/rfc4279#section-2
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerKeyExchange{
+ IdentityHint: cfg.localPSKIdentityHint,
+ },
+ },
+ },
+ })
+ case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous:
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerKeyExchange{
+ EllipticCurveType: elliptic.CurveTypeNamedCurve,
+ NamedCurve: state.namedCurve,
+ PublicKey: state.localKeypair.PublicKey,
+ },
+ },
+ },
+ })
+ }
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerHelloDone{},
+ },
+ },
+ })
+
+ return pkts, nil, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight5handler.go b/vendor/github.com/pion/dtls/v2/flight5handler.go
new file mode 100644
index 0000000..baa1d5c
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight5handler.go
@@ -0,0 +1,323 @@
+package dtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/x509"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight5Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ var finished *handshake.MessageFinished
+ if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+
+ expectedVerifyData, err := prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ if !bytes.Equal(expectedVerifyData, finished.VerifyData) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
+ }
+
+ return flight5, nil, nil
+}
+
+func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit
+ var certBytes [][]byte
+ var privateKey crypto.PrivateKey
+ if len(cfg.localCertificates) > 0 {
+ certificate, err := cfg.getCertificate(cfg.serverName)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
+ }
+ certBytes = certificate.Certificate
+ privateKey = certificate.PrivateKey
+ }
+
+ var pkts []*packet
+
+ if state.remoteRequestedCertificate {
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificate{
+ Certificate: certBytes,
+ },
+ },
+ },
+ })
+ }
+
+ clientKeyExchange := &handshake.MessageClientKeyExchange{}
+ if cfg.localPSKCallback == nil {
+ clientKeyExchange.PublicKey = state.localKeypair.PublicKey
+ } else {
+ clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: clientKeyExchange,
+ },
+ },
+ })
+
+ serverKeyExchangeData := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ )
+
+ serverKeyExchange := &handshake.MessageServerKeyExchange{}
+
+ // handshakeMessageServerKeyExchange is optional for PSK
+ if len(serverKeyExchangeData) == 0 {
+ alertPtr, err := handleServerKeyExchange(c, state, cfg, &handshake.MessageServerKeyExchange{})
+ if err != nil {
+ return nil, alertPtr, err
+ }
+ } else {
+ rawHandshake := &handshake.Handshake{}
+ err := rawHandshake.Unmarshal(serverKeyExchangeData)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, err
+ }
+
+ switch h := rawHandshake.Message.(type) {
+ case *handshake.MessageServerKeyExchange:
+ serverKeyExchange = h
+ default:
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errInvalidContentType
+ }
+ }
+
+ // Append not-yet-sent packets
+ merged := []byte{}
+ seqPred := uint16(state.handshakeSendSequence)
+ for _, p := range pkts {
+ h, ok := p.record.Content.(*handshake.Handshake)
+ if !ok {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType
+ }
+ h.Header.MessageSequence = seqPred
+ seqPred++
+ raw, err := h.Marshal()
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ merged = append(merged, raw...)
+ }
+
+ if alertPtr, err := initalizeCipherSuite(state, cache, cfg, serverKeyExchange, merged); err != nil {
+ return nil, alertPtr, err
+ }
+
+ // If the client has sent a certificate with signing ability, a digitally-signed
+ // CertificateVerify message is sent to explicitly verify possession of the
+ // private key in the certificate.
+ if state.remoteRequestedCertificate && len(cfg.localCertificates) > 0 {
+ plainText := append(cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ ), merged...)
+
+ // Find compatible signature scheme
+ signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, privateKey)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err
+ }
+
+ certVerify, err := generateCertificateVerify(plainText, privateKey, signatureHashAlgo.Hash)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.localCertificatesVerify = certVerify
+
+ p := &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificateVerify{
+ HashAlgorithm: signatureHashAlgo.Hash,
+ SignatureAlgorithm: signatureHashAlgo.Signature,
+ Signature: state.localCertificatesVerify,
+ },
+ },
+ },
+ }
+ pkts = append(pkts, p)
+
+ h, ok := p.record.Content.(*handshake.Handshake)
+ if !ok {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType
+ }
+ h.Header.MessageSequence = seqPred
+ // seqPred++ // this is the last use of seqPred
+ raw, err := h.Marshal()
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ merged = append(merged, raw...)
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ChangeCipherSpec{},
+ },
+ })
+
+ if len(state.localVerifyData) == 0 {
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+
+ var err error
+ state.localVerifyData, err = prf.VerifyDataClient(state.masterSecret, append(plainText, merged...), state.cipherSuite.HashFunc())
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ Epoch: 1,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageFinished{
+ VerifyData: state.localVerifyData,
+ },
+ },
+ },
+ shouldEncrypt: true,
+ resetLocalSequenceNumber: true,
+ })
+
+ return pkts, nil, nil
+}
+
+func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange, sendingPlainText []byte) (*alert.Alert, error) { //nolint:gocognit
+ if state.cipherSuite.IsInitialized() {
+ return nil, nil
+ }
+
+ clientRandom := state.localRandom.MarshalFixed()
+ serverRandom := state.remoteRandom.MarshalFixed()
+
+ var err error
+
+ if state.extendedMasterSecret {
+ var sessionHash []byte
+ sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch, sendingPlainText)
+ if err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ state.masterSecret, err = prf.ExtendedMasterSecret(state.preMasterSecret, sessionHash, state.cipherSuite.HashFunc())
+ if err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
+ }
+ } else {
+ state.masterSecret, err = prf.MasterSecret(state.preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc())
+ if err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
+ // Verify that the pair of hash algorithm and signiture is listed.
+ var validSignatureScheme bool
+ for _, ss := range cfg.localSignatureSchemes {
+ if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm {
+ validSignatureScheme = true
+ break
+ }
+ }
+ if !validSignatureScheme {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
+ }
+
+ expectedMsg := valueKeyMessage(clientRandom[:], serverRandom[:], h.PublicKey, h.NamedCurve)
+ if err = verifyKeySignature(expectedMsg, h.Signature, h.HashAlgorithm, state.PeerCertificates); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ var chains [][]*x509.Certificate
+ if !cfg.insecureSkipVerify {
+ if chains, err = verifyServerCert(state.PeerCertificates, cfg.rootCAs, cfg.serverName); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+ if cfg.verifyPeerCertificate != nil {
+ if err = cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+ }
+
+ if err = state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], true); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
+
+ return nil, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/flight6handler.go b/vendor/github.com/pion/dtls/v2/flight6handler.go
new file mode 100644
index 0000000..10de5ad
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flight6handler.go
@@ -0,0 +1,82 @@
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight6Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ // Other party retransmitted the last flight.
+ return flight6, nil, nil
+}
+
+func flight6Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ var pkts []*packet
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ChangeCipherSpec{},
+ },
+ })
+
+ if len(state.localVerifyData) == 0 {
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+
+ var err error
+ state.localVerifyData, err = prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ Epoch: 1,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageFinished{
+ VerifyData: state.localVerifyData,
+ },
+ },
+ },
+ shouldEncrypt: true,
+ resetLocalSequenceNumber: true,
+ },
+ )
+ return pkts, nil, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/flighthandler.go b/vendor/github.com/pion/dtls/v2/flighthandler.go
new file mode 100644
index 0000000..b364c09
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/flighthandler.go
@@ -0,0 +1,57 @@
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+)
+
+// Parse received handshakes and return next flightVal
+type flightParser func(context.Context, flightConn, *State, *handshakeCache, *handshakeConfig) (flightVal, *alert.Alert, error)
+
+// Generate flights
+type flightGenerator func(flightConn, *State, *handshakeCache, *handshakeConfig) ([]*packet, *alert.Alert, error)
+
+func (f flightVal) getFlightParser() (flightParser, error) {
+ switch f {
+ case flight0:
+ return flight0Parse, nil
+ case flight1:
+ return flight1Parse, nil
+ case flight2:
+ return flight2Parse, nil
+ case flight3:
+ return flight3Parse, nil
+ case flight4:
+ return flight4Parse, nil
+ case flight5:
+ return flight5Parse, nil
+ case flight6:
+ return flight6Parse, nil
+ default:
+ return nil, errInvalidFlight
+ }
+}
+
+func (f flightVal) getFlightGenerator() (gen flightGenerator, retransmit bool, err error) {
+ switch f {
+ case flight0:
+ return flight0Generate, true, nil
+ case flight1:
+ return flight1Generate, true, nil
+ case flight2:
+ // https://tools.ietf.org/html/rfc6347#section-3.2.1
+ // HelloVerifyRequests must not be retransmitted.
+ return flight2Generate, false, nil
+ case flight3:
+ return flight3Generate, true, nil
+ case flight4:
+ return flight4Generate, true, nil
+ case flight5:
+ return flight5Generate, true, nil
+ case flight6:
+ return flight6Generate, true, nil
+ default:
+ return nil, false, errInvalidFlight
+ }
+}
diff --git a/vendor/github.com/pion/dtls/v2/fragment_buffer.go b/vendor/github.com/pion/dtls/v2/fragment_buffer.go
new file mode 100644
index 0000000..0274993
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/fragment_buffer.go
@@ -0,0 +1,111 @@
+package dtls
+
+import (
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+type fragment struct {
+ recordLayerHeader recordlayer.Header
+ handshakeHeader handshake.Header
+ data []byte
+}
+
+type fragmentBuffer struct {
+ // map of MessageSequenceNumbers that hold slices of fragments
+ cache map[uint16][]*fragment
+
+ currentMessageSequenceNumber uint16
+}
+
+func newFragmentBuffer() *fragmentBuffer {
+ return &fragmentBuffer{cache: map[uint16][]*fragment{}}
+}
+
+// Attempts to push a DTLS packet to the fragmentBuffer
+// when it returns true it means the fragmentBuffer has inserted and the buffer shouldn't be handled
+// when an error returns it is fatal, and the DTLS connection should be stopped
+func (f *fragmentBuffer) push(buf []byte) (bool, error) {
+ frag := new(fragment)
+ if err := frag.recordLayerHeader.Unmarshal(buf); err != nil {
+ return false, err
+ }
+
+ // fragment isn't a handshake, we don't need to handle it
+ if frag.recordLayerHeader.ContentType != protocol.ContentTypeHandshake {
+ return false, nil
+ }
+
+ for buf = buf[recordlayer.HeaderSize:]; len(buf) != 0; frag = new(fragment) {
+ if err := frag.handshakeHeader.Unmarshal(buf); err != nil {
+ return false, err
+ }
+
+ if _, ok := f.cache[frag.handshakeHeader.MessageSequence]; !ok {
+ f.cache[frag.handshakeHeader.MessageSequence] = []*fragment{}
+ }
+
+ // end index should be the length of handshake header but if the handshake
+ // was fragmented, we should keep them all
+ end := int(handshake.HeaderLength + frag.handshakeHeader.Length)
+ if size := len(buf); end > size {
+ end = size
+ }
+
+ // Discard all headers, when rebuilding the packet we will re-build
+ frag.data = append([]byte{}, buf[handshake.HeaderLength:end]...)
+ f.cache[frag.handshakeHeader.MessageSequence] = append(f.cache[frag.handshakeHeader.MessageSequence], frag)
+ buf = buf[end:]
+ }
+
+ return true, nil
+}
+
+func (f *fragmentBuffer) pop() (content []byte, epoch uint16) {
+ frags, ok := f.cache[f.currentMessageSequenceNumber]
+ if !ok {
+ return nil, 0
+ }
+
+ // Go doesn't support recursive lambdas
+ var appendMessage func(targetOffset uint32) bool
+
+ rawMessage := []byte{}
+ appendMessage = func(targetOffset uint32) bool {
+ for _, f := range frags {
+ if f.handshakeHeader.FragmentOffset == targetOffset {
+ fragmentEnd := (f.handshakeHeader.FragmentOffset + f.handshakeHeader.FragmentLength)
+ if fragmentEnd != f.handshakeHeader.Length {
+ if !appendMessage(fragmentEnd) {
+ return false
+ }
+ }
+
+ rawMessage = append(f.data, rawMessage...)
+ return true
+ }
+ }
+ return false
+ }
+
+ // Recursively collect up
+ if !appendMessage(0) {
+ return nil, 0
+ }
+
+ firstHeader := frags[0].handshakeHeader
+ firstHeader.FragmentOffset = 0
+ firstHeader.FragmentLength = firstHeader.Length
+
+ rawHeader, err := firstHeader.Marshal()
+ if err != nil {
+ return nil, 0
+ }
+
+ messageEpoch := frags[0].recordLayerHeader.Epoch
+
+ delete(f.cache, f.currentMessageSequenceNumber)
+ f.currentMessageSequenceNumber++
+ return append(rawHeader, rawMessage...), messageEpoch
+}
diff --git a/vendor/github.com/pion/dtls/v2/fuzz.go b/vendor/github.com/pion/dtls/v2/fuzz.go
new file mode 100644
index 0000000..56c1bf2
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/fuzz.go
@@ -0,0 +1,38 @@
+// +build gofuzz
+
+package dtls
+
+import "fmt"
+
+func partialHeaderMismatch(a, b recordlayer.Header) bool {
+ // Ignoring content length for now.
+ a.contentLen = b.contentLen
+ return a != b
+}
+
+func FuzzRecordLayer(data []byte) int {
+ var r recordLayer
+ if err := r.Unmarshal(data); err != nil {
+ return 0
+ }
+ buf, err := r.Marshal()
+ if err != nil {
+ return 1
+ }
+ if len(buf) == 0 {
+ panic("zero buff") // nolint
+ }
+ var nr recordLayer
+ if err = nr.Unmarshal(data); err != nil {
+ panic(err) // nolint
+ }
+ if partialHeaderMismatch(nr.recordlayer.Header, r.recordlayer.Header) {
+ panic( // nolint
+ fmt.Sprintf("header mismatch: %+v != %+v",
+ nr.recordlayer.Header, r.recordlayer.Header,
+ ),
+ )
+ }
+
+ return 1
+}
diff --git a/vendor/github.com/pion/dtls/v2/go.mod b/vendor/github.com/pion/dtls/v2/go.mod
new file mode 100644
index 0000000..33b60e0
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/go.mod
@@ -0,0 +1,12 @@
+module github.com/pion/dtls/v2
+
+require (
+ github.com/pion/logging v0.2.2
+ github.com/pion/transport v0.12.2
+ github.com/pion/udp v0.1.0
+ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
+ golang.org/x/net v0.0.0-20210119194325-5f4716e94777
+ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
+)
+
+go 1.13
diff --git a/vendor/github.com/pion/dtls/v2/go.sum b/vendor/github.com/pion/dtls/v2/go.sum
new file mode 100644
index 0000000..fa7c410
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/go.sum
@@ -0,0 +1,48 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80=
+github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
+github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys=
+github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
+github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
+github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+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/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/dtls/v2/handshake_cache.go b/vendor/github.com/pion/dtls/v2/handshake_cache.go
new file mode 100644
index 0000000..063a858
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/handshake_cache.go
@@ -0,0 +1,171 @@
+package dtls
+
+import (
+ "sync"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+)
+
+type handshakeCacheItem struct {
+ typ handshake.Type
+ isClient bool
+ epoch uint16
+ messageSequence uint16
+ data []byte
+}
+
+type handshakeCachePullRule struct {
+ typ handshake.Type
+ epoch uint16
+ isClient bool
+ optional bool
+}
+
+type handshakeCache struct {
+ cache []*handshakeCacheItem
+ mu sync.Mutex
+}
+
+func newHandshakeCache() *handshakeCache {
+ return &handshakeCache{}
+}
+
+func (h *handshakeCache) push(data []byte, epoch, messageSequence uint16, typ handshake.Type, isClient bool) bool { //nolint
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ for _, i := range h.cache {
+ if i.messageSequence == messageSequence &&
+ i.isClient == isClient {
+ return false
+ }
+ }
+
+ h.cache = append(h.cache, &handshakeCacheItem{
+ data: append([]byte{}, data...),
+ epoch: epoch,
+ messageSequence: messageSequence,
+ typ: typ,
+ isClient: isClient,
+ })
+ return true
+}
+
+// returns a list handshakes that match the requested rules
+// the list will contain null entries for rules that can't be satisfied
+// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies)
+func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCacheItem {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ out := make([]*handshakeCacheItem, len(rules))
+ for i, r := range rules {
+ for _, c := range h.cache {
+ if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch {
+ switch {
+ case out[i] == nil:
+ out[i] = c
+ case out[i].messageSequence < c.messageSequence:
+ out[i] = c
+ }
+ }
+ }
+ }
+
+ return out
+}
+
+// fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map.
+func (h *handshakeCache) fullPullMap(startSeq int, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ ci := make(map[handshake.Type]*handshakeCacheItem)
+ for _, r := range rules {
+ var item *handshakeCacheItem
+ for _, c := range h.cache {
+ if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch {
+ switch {
+ case item == nil:
+ item = c
+ case item.messageSequence < c.messageSequence:
+ item = c
+ }
+ }
+ }
+ if !r.optional && item == nil {
+ // Missing mandatory message.
+ return startSeq, nil, false
+ }
+ ci[r.typ] = item
+ }
+ out := make(map[handshake.Type]handshake.Message)
+ seq := startSeq
+ for _, r := range rules {
+ t := r.typ
+ i := ci[t]
+ if i == nil {
+ continue
+ }
+ rawHandshake := &handshake.Handshake{}
+ if err := rawHandshake.Unmarshal(i.data); err != nil {
+ return startSeq, nil, false
+ }
+ if uint16(seq) != rawHandshake.Header.MessageSequence {
+ // There is a gap. Some messages are not arrived.
+ return startSeq, nil, false
+ }
+ seq++
+ out[t] = rawHandshake.Message
+ }
+ return seq, out, true
+}
+
+// pullAndMerge calls pull and then merges the results, ignoring any null entries
+func (h *handshakeCache) pullAndMerge(rules ...handshakeCachePullRule) []byte {
+ merged := []byte{}
+
+ for _, p := range h.pull(rules...) {
+ if p != nil {
+ merged = append(merged, p.data...)
+ }
+ }
+ return merged
+}
+
+// sessionHash returns the session hash for Extended Master Secret support
+// https://tools.ietf.org/html/draft-ietf-tls-session-hash-06#section-4
+func (h *handshakeCache) sessionHash(hf prf.HashFunc, epoch uint16, additional ...[]byte) ([]byte, error) {
+ merged := []byte{}
+
+ // Order defined by https://tools.ietf.org/html/rfc5246#section-7.3
+ handshakeBuffer := h.pull(
+ handshakeCachePullRule{handshake.TypeClientHello, epoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, epoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, epoch, true, false},
+ )
+
+ for _, p := range handshakeBuffer {
+ if p == nil {
+ continue
+ }
+
+ merged = append(merged, p.data...)
+ }
+ for _, a := range additional {
+ merged = append(merged, a...)
+ }
+
+ hash := hf()
+ if _, err := hash.Write(merged); err != nil {
+ return []byte{}, err
+ }
+
+ return hash.Sum(nil), nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/handshaker.go b/vendor/github.com/pion/dtls/v2/handshaker.go
new file mode 100644
index 0000000..0f5077e
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/handshaker.go
@@ -0,0 +1,334 @@
+package dtls
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/logging"
+)
+
+// [RFC6347 Section-4.2.4]
+// +-----------+
+// +---> | PREPARING | <--------------------+
+// | +-----------+ |
+// | | |
+// | | Buffer next flight |
+// | | |
+// | \|/ |
+// | +-----------+ |
+// | | SENDING |<------------------+ | Send
+// | +-----------+ | | HelloRequest
+// Receive | | | |
+// next | | Send flight | | or
+// flight | +--------+ | |
+// | | | Set retransmit timer | | Receive
+// | | \|/ | | HelloRequest
+// | | +-----------+ | | Send
+// +--)--| WAITING |-------------------+ | ClientHello
+// | | +-----------+ Timer expires | |
+// | | | | |
+// | | +------------------------+ |
+// Receive | | Send Read retransmit |
+// last | | last |
+// flight | | flight |
+// | | |
+// \|/\|/ |
+// +-----------+ |
+// | FINISHED | -------------------------------+
+// +-----------+
+// | /|\
+// | |
+// +---+
+// Read retransmit
+// Retransmit last flight
+
+type handshakeState uint8
+
+const (
+ handshakeErrored handshakeState = iota
+ handshakePreparing
+ handshakeSending
+ handshakeWaiting
+ handshakeFinished
+)
+
+func (s handshakeState) String() string {
+ switch s {
+ case handshakeErrored:
+ return "Errored"
+ case handshakePreparing:
+ return "Preparing"
+ case handshakeSending:
+ return "Sending"
+ case handshakeWaiting:
+ return "Waiting"
+ case handshakeFinished:
+ return "Finished"
+ default:
+ return "Unknown"
+ }
+}
+
+type handshakeFSM struct {
+ currentFlight flightVal
+ flights []*packet
+ retransmit bool
+ state *State
+ cache *handshakeCache
+ cfg *handshakeConfig
+ closed chan struct{}
+}
+
+type handshakeConfig struct {
+ localPSKCallback PSKCallback
+ localPSKIdentityHint []byte
+ localCipherSuites []CipherSuite // Available CipherSuites
+ localSignatureSchemes []signaturehash.Algorithm // Available signature schemes
+ extendedMasterSecret ExtendedMasterSecretType // Policy for the Extended Master Support extension
+ localSRTPProtectionProfiles []SRTPProtectionProfile // Available SRTPProtectionProfiles, if empty no SRTP support
+ serverName string
+ clientAuth ClientAuthType // If we are a client should we request a client certificate
+ localCertificates []tls.Certificate
+ nameToCertificate map[string]*tls.Certificate
+ insecureSkipVerify bool
+ verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+ rootCAs *x509.CertPool
+ clientCAs *x509.CertPool
+ retransmitInterval time.Duration
+ customCipherSuites func() []CipherSuite
+
+ onFlightState func(flightVal, handshakeState)
+ log logging.LeveledLogger
+ keyLogWriter io.Writer
+
+ initialEpoch uint16
+
+ mu sync.Mutex
+}
+
+type flightConn interface {
+ notify(ctx context.Context, level alert.Level, desc alert.Description) error
+ writePackets(context.Context, []*packet) error
+ recvHandshake() <-chan chan struct{}
+ setLocalEpoch(epoch uint16)
+ handleQueuedPackets(context.Context) error
+}
+
+func (c *handshakeConfig) writeKeyLog(label string, clientRandom, secret []byte) {
+ if c.keyLogWriter == nil {
+ return
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ _, err := c.keyLogWriter.Write([]byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret)))
+ if err != nil {
+ c.log.Debugf("failed to write key log file: %s", err)
+ }
+}
+
+func srvCliStr(isClient bool) string {
+ if isClient {
+ return "client"
+ }
+ return "server"
+}
+
+func newHandshakeFSM(
+ s *State, cache *handshakeCache, cfg *handshakeConfig,
+ initialFlight flightVal,
+) *handshakeFSM {
+ return &handshakeFSM{
+ currentFlight: initialFlight,
+ state: s,
+ cache: cache,
+ cfg: cfg,
+ closed: make(chan struct{}),
+ }
+}
+
+func (s *handshakeFSM) Run(ctx context.Context, c flightConn, initialState handshakeState) error {
+ state := initialState
+ defer func() {
+ close(s.closed)
+ }()
+ for {
+ s.cfg.log.Tracef("[handshake:%s] %s: %s", srvCliStr(s.state.isClient), s.currentFlight.String(), state.String())
+ if s.cfg.onFlightState != nil {
+ s.cfg.onFlightState(s.currentFlight, state)
+ }
+ var err error
+ switch state {
+ case handshakePreparing:
+ state, err = s.prepare(ctx, c)
+ case handshakeSending:
+ state, err = s.send(ctx, c)
+ case handshakeWaiting:
+ state, err = s.wait(ctx, c)
+ case handshakeFinished:
+ state, err = s.finish(ctx, c)
+ default:
+ return errInvalidFSMTransition
+ }
+ if err != nil {
+ return err
+ }
+ }
+}
+
+func (s *handshakeFSM) Done() <-chan struct{} {
+ return s.closed
+}
+
+func (s *handshakeFSM) prepare(ctx context.Context, c flightConn) (handshakeState, error) {
+ s.flights = nil
+ // Prepare flights
+ var (
+ a *alert.Alert
+ err error
+ pkts []*packet
+ )
+ gen, retransmit, errFlight := s.currentFlight.getFlightGenerator()
+ if errFlight != nil {
+ err = errFlight
+ a = &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}
+ } else {
+ pkts, a, err = gen(c, s.state, s.cache, s.cfg)
+ s.retransmit = retransmit
+ }
+ if a != nil {
+ if alertErr := c.notify(ctx, a.Level, a.Description); alertErr != nil {
+ if err != nil {
+ err = alertErr
+ }
+ }
+ }
+ if err != nil {
+ return handshakeErrored, err
+ }
+
+ s.flights = pkts
+ epoch := s.cfg.initialEpoch
+ nextEpoch := epoch
+ for _, p := range s.flights {
+ p.record.Header.Epoch += epoch
+ if p.record.Header.Epoch > nextEpoch {
+ nextEpoch = p.record.Header.Epoch
+ }
+ if h, ok := p.record.Content.(*handshake.Handshake); ok {
+ h.Header.MessageSequence = uint16(s.state.handshakeSendSequence)
+ s.state.handshakeSendSequence++
+ }
+ }
+ if epoch != nextEpoch {
+ s.cfg.log.Tracef("[handshake:%s] -> changeCipherSpec (epoch: %d)", srvCliStr(s.state.isClient), nextEpoch)
+ c.setLocalEpoch(nextEpoch)
+ }
+ return handshakeSending, nil
+}
+
+func (s *handshakeFSM) send(ctx context.Context, c flightConn) (handshakeState, error) {
+ // Send flights
+ if err := c.writePackets(ctx, s.flights); err != nil {
+ return handshakeErrored, err
+ }
+
+ if s.currentFlight.isLastSendFlight() {
+ return handshakeFinished, nil
+ }
+ return handshakeWaiting, nil
+}
+
+func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState, error) { //nolint:gocognit
+ parse, errFlight := s.currentFlight.getFlightParser()
+ if errFlight != nil {
+ if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil {
+ if errFlight != nil {
+ return handshakeErrored, alertErr
+ }
+ }
+ return handshakeErrored, errFlight
+ }
+
+ retransmitTimer := time.NewTimer(s.cfg.retransmitInterval)
+ for {
+ select {
+ case done := <-c.recvHandshake():
+ nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg)
+ close(done)
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err != nil {
+ err = alertErr
+ }
+ }
+ }
+ if err != nil {
+ return handshakeErrored, err
+ }
+ if nextFlight == 0 {
+ break
+ }
+ s.cfg.log.Tracef("[handshake:%s] %s -> %s", srvCliStr(s.state.isClient), s.currentFlight.String(), nextFlight.String())
+ if nextFlight.isLastRecvFlight() && s.currentFlight == nextFlight {
+ return handshakeFinished, nil
+ }
+ s.currentFlight = nextFlight
+ return handshakePreparing, nil
+
+ case <-retransmitTimer.C:
+ if !s.retransmit {
+ return handshakeWaiting, nil
+ }
+ return handshakeSending, nil
+ case <-ctx.Done():
+ return handshakeErrored, ctx.Err()
+ }
+ }
+}
+
+func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState, error) {
+ parse, errFlight := s.currentFlight.getFlightParser()
+ if errFlight != nil {
+ if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil {
+ if errFlight != nil {
+ return handshakeErrored, alertErr
+ }
+ }
+ return handshakeErrored, errFlight
+ }
+
+ retransmitTimer := time.NewTimer(s.cfg.retransmitInterval)
+ select {
+ case done := <-c.recvHandshake():
+ nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg)
+ close(done)
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err != nil {
+ err = alertErr
+ }
+ }
+ }
+ if err != nil {
+ return handshakeErrored, err
+ }
+ if nextFlight == 0 {
+ break
+ }
+ <-retransmitTimer.C
+ // Retransmit last flight
+ return handshakeSending, nil
+
+ case <-ctx.Done():
+ return handshakeErrored, ctx.Err()
+ }
+ return handshakeFinished, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go
new file mode 100644
index 0000000..dcc5379
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go
@@ -0,0 +1,108 @@
+package ciphersuite
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// Aes128Ccm is a base class used by multiple AES-CCM Ciphers
+type Aes128Ccm struct {
+ ccm atomic.Value // *cryptoCCM
+ clientCertificateType clientcertificate.Type
+ id ID
+ psk bool
+ cryptoCCMTagLen ciphersuite.CCMTagLen
+}
+
+func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen) *Aes128Ccm {
+ return &Aes128Ccm{
+ clientCertificateType: clientCertificateType,
+ id: id,
+ psk: psk,
+ cryptoCCMTagLen: cryptoCCMTagLen,
+ }
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *Aes128Ccm) CertificateType() clientcertificate.Type {
+ return c.clientCertificateType
+}
+
+// ID returns the ID of the CipherSuite
+func (c *Aes128Ccm) ID() ID {
+ return c.id
+}
+
+func (c *Aes128Ccm) String() string {
+ return c.id.String()
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *Aes128Ccm) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *Aes128Ccm) AuthenticationType() AuthenticationType {
+ if c.psk {
+ return AuthenticationTypePreSharedKey
+ }
+ return AuthenticationTypeCertificate
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *Aes128Ccm) IsInitialized() bool {
+ return c.ccm.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *Aes128Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 0
+ prfKeyLen = 16
+ prfIvLen = 4
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var ccm *ciphersuite.CCM
+ if isClient {
+ ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV)
+ } else {
+ ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV)
+ }
+ c.ccm.Store(ccm)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *Aes128Ccm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ ccm := c.ccm.Load()
+ if ccm == nil {
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return ccm.(*ciphersuite.CCM).Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *Aes128Ccm) Decrypt(raw []byte) ([]byte, error) {
+ ccm := c.ccm.Load()
+ if ccm == nil {
+ return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
+ }
+
+ return ccm.(*ciphersuite.CCM).Decrypt(raw)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go
new file mode 100644
index 0000000..3a4fb43
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go
@@ -0,0 +1,71 @@
+// Package ciphersuite provides TLS Ciphers as registered with the IANA https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
+package ciphersuite
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var errCipherSuiteNotInit = &protocol.TemporaryError{Err: errors.New("CipherSuite has not been initialized")} //nolint:goerr113
+
+// ID is an ID for our supported CipherSuites
+type ID uint16
+
+func (i ID) String() string {
+ switch i {
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
+ case TLS_PSK_WITH_AES_128_CCM:
+ return "TLS_PSK_WITH_AES_128_CCM"
+ case TLS_PSK_WITH_AES_128_CCM_8:
+ return "TLS_PSK_WITH_AES_128_CCM_8"
+ case TLS_PSK_WITH_AES_128_GCM_SHA256:
+ return "TLS_PSK_WITH_AES_128_GCM_SHA256"
+ case TLS_PSK_WITH_AES_128_CBC_SHA256:
+ return "TLS_PSK_WITH_AES_128_CBC_SHA256"
+ default:
+ return fmt.Sprintf("unknown(%v)", uint16(i))
+ }
+}
+
+// Supported Cipher Suites
+const (
+ // AES-128-CCM
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM ID = 0xc0ac //nolint:golint,stylecheck
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ID = 0xc0ae //nolint:golint,stylecheck
+
+ // AES-128-GCM-SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ID = 0xc02b //nolint:golint,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ID = 0xc02f //nolint:golint,stylecheck
+
+ // AES-256-CBC-SHA
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ID = 0xc00a //nolint:golint,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ID = 0xc014 //nolint:golint,stylecheck
+
+ TLS_PSK_WITH_AES_128_CCM ID = 0xc0a4 //nolint:golint,stylecheck
+ TLS_PSK_WITH_AES_128_CCM_8 ID = 0xc0a8 //nolint:golint,stylecheck
+ TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:golint,stylecheck
+ TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:golint,stylecheck
+)
+
+// AuthenticationType controls what authentication method is using during the handshake
+type AuthenticationType int
+
+// AuthenticationType Enums
+const (
+ AuthenticationTypeCertificate AuthenticationType = iota + 1
+ AuthenticationTypePreSharedKey
+ AuthenticationTypeAnonymous
+)
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
new file mode 100644
index 0000000..ac73556
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
@@ -0,0 +1,11 @@
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSEcdheEcdsaWithAes128Ccm constructs a TLS_ECDHE_ECDSA_WITH_AES_128_CCM Cipher
+func NewTLSEcdheEcdsaWithAes128Ccm() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM, false, ciphersuite.CCMTagLength)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
new file mode 100644
index 0000000..49b1a83
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
@@ -0,0 +1,11 @@
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSEcdheEcdsaWithAes128Ccm8 creates a new TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuite
+func NewTLSEcdheEcdsaWithAes128Ccm8() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, false, ciphersuite.CCMTagLength8)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
new file mode 100644
index 0000000..b491320
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
@@ -0,0 +1,92 @@
+package ciphersuite
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSEcdheEcdsaWithAes128GcmSha256 represents a TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuite
+type TLSEcdheEcdsaWithAes128GcmSha256 struct {
+ gcm atomic.Value // *cryptoGCM
+}
+
+// CertificateType returns what type of certficate this CipherSuite exchanges
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.ECDSASign
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) ID() ID {
+ return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+}
+
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) String() string {
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) AuthenticationType() AuthenticationType {
+ return AuthenticationTypeCertificate
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) IsInitialized() bool {
+ return c.gcm.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 0
+ prfKeyLen = 16
+ prfIvLen = 4
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var gcm *ciphersuite.GCM
+ if isClient {
+ gcm, err = ciphersuite.NewGCM(keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV)
+ } else {
+ gcm, err = ciphersuite.NewGCM(keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV)
+ }
+ c.gcm.Store(gcm)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ gcm := c.gcm.Load()
+ if gcm == nil {
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return gcm.(*ciphersuite.GCM).Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(raw []byte) ([]byte, error) {
+ gcm := c.gcm.Load()
+ if gcm == nil {
+ return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
+ }
+
+ return gcm.(*ciphersuite.GCM).Decrypt(raw)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
new file mode 100644
index 0000000..f7a33ad
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
@@ -0,0 +1,101 @@
+package ciphersuite
+
+import (
+ "crypto/sha1" //nolint: gosec,gci
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSEcdheEcdsaWithAes256CbcSha represents a TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuite
+type TLSEcdheEcdsaWithAes256CbcSha struct {
+ cbc atomic.Value // *cryptoCBC
+}
+
+// CertificateType returns what type of certficate this CipherSuite exchanges
+func (c *TLSEcdheEcdsaWithAes256CbcSha) CertificateType() clientcertificate.Type {
+ return clientcertificate.ECDSASign
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheEcdsaWithAes256CbcSha) ID() ID {
+ return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+}
+
+func (c *TLSEcdheEcdsaWithAes256CbcSha) String() string {
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSEcdheEcdsaWithAes256CbcSha) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdheEcdsaWithAes256CbcSha) AuthenticationType() AuthenticationType {
+ return AuthenticationTypeCertificate
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSEcdheEcdsaWithAes256CbcSha) IsInitialized() bool {
+ return c.cbc.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSEcdheEcdsaWithAes256CbcSha) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 20
+ prfKeyLen = 32
+ prfIvLen = 16
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var cbc *ciphersuite.CBC
+ if isClient {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ sha1.New,
+ )
+ } else {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ sha1.New,
+ )
+ }
+ c.cbc.Store(cbc)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes256CbcSha) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ cbc := c.cbc.Load()
+ if cbc == nil { // !c.isInitialized()
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return cbc.(*ciphersuite.CBC).Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(raw []byte) ([]byte, error) {
+ cbc := c.cbc.Load()
+ if cbc == nil { // !c.isInitialized()
+ return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
+ }
+
+ return cbc.(*ciphersuite.CBC).Decrypt(raw)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
new file mode 100644
index 0000000..70400c3
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
@@ -0,0 +1,22 @@
+package ciphersuite
+
+import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+
+// TLSEcdheRsaWithAes128GcmSha256 implements the TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuite
+type TLSEcdheRsaWithAes128GcmSha256 struct {
+ TLSEcdheEcdsaWithAes128GcmSha256
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSEcdheRsaWithAes128GcmSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.RSASign
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheRsaWithAes128GcmSha256) ID() ID {
+ return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+}
+
+func (c *TLSEcdheRsaWithAes128GcmSha256) String() string {
+ return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go
new file mode 100644
index 0000000..0d82dc3
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go
@@ -0,0 +1,22 @@
+package ciphersuite
+
+import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+
+// TLSEcdheRsaWithAes256CbcSha implements the TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuite
+type TLSEcdheRsaWithAes256CbcSha struct {
+ TLSEcdheEcdsaWithAes256CbcSha
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSEcdheRsaWithAes256CbcSha) CertificateType() clientcertificate.Type {
+ return clientcertificate.RSASign
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheRsaWithAes256CbcSha) ID() ID {
+ return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+}
+
+func (c *TLSEcdheRsaWithAes256CbcSha) String() string {
+ return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
new file mode 100644
index 0000000..43e5e38
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
@@ -0,0 +1,100 @@
+package ciphersuite
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSPskWithAes128CbcSha256 implements the TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuite
+type TLSPskWithAes128CbcSha256 struct {
+ cbc atomic.Value // *cryptoCBC
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSPskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.Type(0)
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSPskWithAes128CbcSha256) ID() ID {
+ return TLS_PSK_WITH_AES_128_CBC_SHA256
+}
+
+func (c *TLSPskWithAes128CbcSha256) String() string {
+ return "TLS_PSK_WITH_AES_128_CBC_SHA256"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSPskWithAes128CbcSha256) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSPskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
+ return AuthenticationTypePreSharedKey
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSPskWithAes128CbcSha256) IsInitialized() bool {
+ return c.cbc.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSPskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 32
+ prfKeyLen = 16
+ prfIvLen = 16
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var cbc *ciphersuite.CBC
+ if isClient {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ c.HashFunc(),
+ )
+ } else {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ c.HashFunc(),
+ )
+ }
+ c.cbc.Store(cbc)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSPskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ cbc := c.cbc.Load()
+ if cbc == nil { // !c.isInitialized()
+ return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
+ }
+
+ return cbc.(*ciphersuite.CBC).Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSPskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) {
+ cbc := c.cbc.Load()
+ if cbc == nil { // !c.isInitialized()
+ return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
+ }
+
+ return cbc.(*ciphersuite.CBC).Decrypt(raw)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
new file mode 100644
index 0000000..8c13bb1
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
@@ -0,0 +1,11 @@
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSPskWithAes128Ccm returns the TLS_PSK_WITH_AES_128_CCM CipherSuite
+func NewTLSPskWithAes128Ccm() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM, true, ciphersuite.CCMTagLength)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
new file mode 100644
index 0000000..d04abb4
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
@@ -0,0 +1,11 @@
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSPskWithAes128Ccm8 returns the TLS_PSK_WITH_AES_128_CCM_8 CipherSuite
+func NewTLSPskWithAes128Ccm8() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM_8, true, ciphersuite.CCMTagLength8)
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
new file mode 100644
index 0000000..5f10335
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
@@ -0,0 +1,27 @@
+package ciphersuite
+
+import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+
+// TLSPskWithAes128GcmSha256 implements the TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuite
+type TLSPskWithAes128GcmSha256 struct {
+ TLSEcdheEcdsaWithAes128GcmSha256
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSPskWithAes128GcmSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.Type(0)
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSPskWithAes128GcmSha256) ID() ID {
+ return TLS_PSK_WITH_AES_128_GCM_SHA256
+}
+
+func (c *TLSPskWithAes128GcmSha256) String() string {
+ return "TLS_PSK_WITH_AES_128_GCM_SHA256"
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSPskWithAes128GcmSha256) AuthenticationType() AuthenticationType {
+ return AuthenticationTypePreSharedKey
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/closer/closer.go b/vendor/github.com/pion/dtls/v2/internal/closer/closer.go
new file mode 100644
index 0000000..b99e13e
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/closer/closer.go
@@ -0,0 +1,45 @@
+// Package closer provides signaling channel for shutdown
+package closer
+
+import (
+ "context"
+)
+
+// Closer allows for each signaling a channel for shutdown
+type Closer struct {
+ ctx context.Context
+ closeFunc func()
+}
+
+// NewCloser creates a new instance of Closer
+func NewCloser() *Closer {
+ ctx, closeFunc := context.WithCancel(context.Background())
+ return &Closer{
+ ctx: ctx,
+ closeFunc: closeFunc,
+ }
+}
+
+// NewCloserWithParent creates a new instance of Closer with a parent context
+func NewCloserWithParent(ctx context.Context) *Closer {
+ ctx, closeFunc := context.WithCancel(ctx)
+ return &Closer{
+ ctx: ctx,
+ closeFunc: closeFunc,
+ }
+}
+
+// Done returns a channel signaling when it is done
+func (c *Closer) Done() <-chan struct{} {
+ return c.ctx.Done()
+}
+
+// Err returns an error of the context
+func (c *Closer) Err() error {
+ return c.ctx.Err()
+}
+
+// Close sends a signal to trigger the ctx done channel
+func (c *Closer) Close() {
+ c.closeFunc()
+}
diff --git a/vendor/github.com/pion/dtls/v2/internal/util/util.go b/vendor/github.com/pion/dtls/v2/internal/util/util.go
new file mode 100644
index 0000000..746a670
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/internal/util/util.go
@@ -0,0 +1,39 @@
+// Package util contains small helpers used across the repo
+package util
+
+import (
+ "encoding/binary"
+)
+
+// BigEndianUint24 returns the value of a big endian uint24
+func BigEndianUint24(raw []byte) uint32 {
+ if len(raw) < 3 {
+ return 0
+ }
+
+ rawCopy := make([]byte, 4)
+ copy(rawCopy[1:], raw)
+ return binary.BigEndian.Uint32(rawCopy)
+}
+
+// PutBigEndianUint24 encodes a uint24 and places into out
+func PutBigEndianUint24(out []byte, in uint32) {
+ tmp := make([]byte, 4)
+ binary.BigEndian.PutUint32(tmp, in)
+ copy(out, tmp[1:])
+}
+
+// PutBigEndianUint48 encodes a uint64 and places into out
+func PutBigEndianUint48(out []byte, in uint64) {
+ tmp := make([]byte, 8)
+ binary.BigEndian.PutUint64(tmp, in)
+ copy(out, tmp[2:])
+}
+
+// Max returns the larger value
+func Max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/vendor/github.com/pion/dtls/v2/listener.go b/vendor/github.com/pion/dtls/v2/listener.go
new file mode 100644
index 0000000..bf80345
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/listener.go
@@ -0,0 +1,80 @@
+package dtls
+
+import (
+ "net"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+ "github.com/pion/udp"
+)
+
+// Listen creates a DTLS listener
+func Listen(network string, laddr *net.UDPAddr, config *Config) (net.Listener, error) {
+ if err := validateConfig(config); err != nil {
+ return nil, err
+ }
+
+ lc := udp.ListenConfig{
+ AcceptFilter: func(packet []byte) bool {
+ pkts, err := recordlayer.UnpackDatagram(packet)
+ if err != nil || len(pkts) < 1 {
+ return false
+ }
+ h := &recordlayer.Header{}
+ if err := h.Unmarshal(pkts[0]); err != nil {
+ return false
+ }
+ return h.ContentType == protocol.ContentTypeHandshake
+ },
+ }
+ parent, err := lc.Listen(network, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return &listener{
+ config: config,
+ parent: parent,
+ }, nil
+}
+
+// NewListener creates a DTLS listener which accepts connections from an inner Listener.
+func NewListener(inner net.Listener, config *Config) (net.Listener, error) {
+ if err := validateConfig(config); err != nil {
+ return nil, err
+ }
+
+ return &listener{
+ config: config,
+ parent: inner,
+ }, nil
+}
+
+// listener represents a DTLS listener
+type listener struct {
+ config *Config
+ parent net.Listener
+}
+
+// Accept waits for and returns the next connection to the listener.
+// You have to either close or read on all connection that are created.
+// Connection handshake will timeout using ConnectContextMaker in the Config.
+// If you want to specify the timeout duration, set ConnectContextMaker.
+func (l *listener) Accept() (net.Conn, error) {
+ c, err := l.parent.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return Server(c, l.config)
+}
+
+// Close closes the listener.
+// Any blocked Accept operations will be unblocked and return errors.
+// Already Accepted connections are not closed.
+func (l *listener) Close() error {
+ return l.parent.Close()
+}
+
+// Addr returns the listener's network address.
+func (l *listener) Addr() net.Addr {
+ return l.parent.Addr()
+}
diff --git a/vendor/github.com/pion/dtls/v2/packet.go b/vendor/github.com/pion/dtls/v2/packet.go
new file mode 100644
index 0000000..8366a3c
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/packet.go
@@ -0,0 +1,9 @@
+package dtls
+
+import "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+
+type packet struct {
+ record *recordlayer.RecordLayer
+ shouldEncrypt bool
+ resetLocalSequenceNumber bool
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go
new file mode 100644
index 0000000..20e3436
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go
@@ -0,0 +1,251 @@
+// Package ccm implements a CCM, Counter with CBC-MAC
+// as per RFC 3610.
+//
+// See https://tools.ietf.org/html/rfc3610
+//
+// This code was lifted from https://github.com/bocajim/dtls/blob/a3300364a283fcb490d28a93d7fcfa7ba437fbbe/ccm/ccm.go
+// and as such was not written by the Pions authors. Like Pions this
+// code is licensed under MIT.
+//
+// A request for including CCM into the Go standard library
+// can be found as issue #27484 on the https://github.com/golang/go/
+// repository.
+package ccm
+
+import (
+ "crypto/cipher"
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "math"
+)
+
+// ccm represents a Counter with CBC-MAC with a specific key.
+type ccm struct {
+ b cipher.Block
+ M uint8
+ L uint8
+}
+
+const ccmBlockSize = 16
+
+// CCM is a block cipher in Counter with CBC-MAC mode.
+// Providing authenticated encryption with associated data via the cipher.AEAD interface.
+type CCM interface {
+ cipher.AEAD
+ // MaxLength returns the maxium length of plaintext in calls to Seal.
+ // The maximum length of ciphertext in calls to Open is MaxLength()+Overhead().
+ // The maximum length is related to CCM's `L` parameter (15-noncesize) and
+ // is 1<<(8*L) - 1 (but also limited by the maxium size of an int).
+ MaxLength() int
+}
+
+var (
+ errInvalidBlockSize = errors.New("ccm: NewCCM requires 128-bit block cipher")
+ errInvalidTagSize = errors.New("ccm: tagsize must be 4, 6, 8, 10, 12, 14, or 16")
+ errInvalidNonceSize = errors.New("ccm: invalid nonce size")
+)
+
+// NewCCM returns the given 128-bit block cipher wrapped in CCM.
+// The tagsize must be an even integer between 4 and 16 inclusive
+// and is used as CCM's `M` parameter.
+// The noncesize must be an integer between 7 and 13 inclusive,
+// 15-noncesize is used as CCM's `L` parameter.
+func NewCCM(b cipher.Block, tagsize, noncesize int) (CCM, error) {
+ if b.BlockSize() != ccmBlockSize {
+ return nil, errInvalidBlockSize
+ }
+ if tagsize < 4 || tagsize > 16 || tagsize&1 != 0 {
+ return nil, errInvalidTagSize
+ }
+ lensize := 15 - noncesize
+ if lensize < 2 || lensize > 8 {
+ return nil, errInvalidNonceSize
+ }
+ c := &ccm{b: b, M: uint8(tagsize), L: uint8(lensize)}
+ return c, nil
+}
+
+func (c *ccm) NonceSize() int { return 15 - int(c.L) }
+func (c *ccm) Overhead() int { return int(c.M) }
+func (c *ccm) MaxLength() int { return maxlen(c.L, c.Overhead()) }
+
+func maxlen(l uint8, tagsize int) int {
+ max := (uint64(1) << (8 * l)) - 1
+ if m64 := uint64(math.MaxInt64) - uint64(tagsize); l > 8 || max > m64 {
+ max = m64 // The maximum lentgh on a 64bit arch
+ }
+ if max != uint64(int(max)) {
+ return math.MaxInt32 - tagsize // We have only 32bit int's
+ }
+ return int(max)
+}
+
+// MaxNonceLength returns the maximum nonce length for a given plaintext length.
+// A return value <= 0 indicates that plaintext length is too large for
+// any nonce length.
+func MaxNonceLength(pdatalen int) int {
+ const tagsize = 16
+ for L := 2; L <= 8; L++ {
+ if maxlen(uint8(L), tagsize) >= pdatalen {
+ return 15 - L
+ }
+ }
+ return 0
+}
+
+func (c *ccm) cbcRound(mac, data []byte) {
+ for i := 0; i < ccmBlockSize; i++ {
+ mac[i] ^= data[i]
+ }
+ c.b.Encrypt(mac, mac)
+}
+
+func (c *ccm) cbcData(mac, data []byte) {
+ for len(data) >= ccmBlockSize {
+ c.cbcRound(mac, data[:ccmBlockSize])
+ data = data[ccmBlockSize:]
+ }
+ if len(data) > 0 {
+ var block [ccmBlockSize]byte
+ copy(block[:], data)
+ c.cbcRound(mac, block[:])
+ }
+}
+
+var errPlaintextTooLong = errors.New("ccm: plaintext too large")
+
+func (c *ccm) tag(nonce, plaintext, adata []byte) ([]byte, error) {
+ var mac [ccmBlockSize]byte
+
+ if len(adata) > 0 {
+ mac[0] |= 1 << 6
+ }
+ mac[0] |= (c.M - 2) << 2
+ mac[0] |= c.L - 1
+ if len(nonce) != c.NonceSize() {
+ return nil, errInvalidNonceSize
+ }
+ if len(plaintext) > c.MaxLength() {
+ return nil, errPlaintextTooLong
+ }
+ binary.BigEndian.PutUint64(mac[ccmBlockSize-8:], uint64(len(plaintext)))
+ copy(mac[1:ccmBlockSize-c.L], nonce)
+ c.b.Encrypt(mac[:], mac[:])
+
+ var block [ccmBlockSize]byte
+ if n := uint64(len(adata)); n > 0 {
+ // First adata block includes adata length
+ i := 2
+ if n <= 0xfeff {
+ binary.BigEndian.PutUint16(block[:i], uint16(n))
+ } else {
+ block[0] = 0xfe
+ block[1] = 0xff
+ if n < uint64(1<<32) {
+ i = 2 + 4
+ binary.BigEndian.PutUint32(block[2:i], uint32(n))
+ } else {
+ i = 2 + 8
+ binary.BigEndian.PutUint64(block[2:i], n)
+ }
+ }
+ i = copy(block[i:], adata)
+ c.cbcRound(mac[:], block[:])
+ c.cbcData(mac[:], adata[i:])
+ }
+
+ if len(plaintext) > 0 {
+ c.cbcData(mac[:], plaintext)
+ }
+
+ return mac[:c.M], nil
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+// From crypto/cipher/gcm.go
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+ if total := len(in) + n; cap(in) >= total {
+ head = in[:total]
+ } else {
+ head = make([]byte, total)
+ copy(head, in)
+ }
+ tail = head[len(in):]
+ return
+}
+
+// Seal encrypts and authenticates plaintext, authenticates the
+// additional data and appends the result to dst, returning the updated
+// slice. The nonce must be NonceSize() bytes long and unique for all
+// time, for a given key.
+// The plaintext must be no longer than MaxLength() bytes long.
+//
+// The plaintext and dst may alias exactly or not at all.
+func (c *ccm) Seal(dst, nonce, plaintext, adata []byte) []byte {
+ tag, err := c.tag(nonce, plaintext, adata)
+ if err != nil {
+ // The cipher.AEAD interface doesn't allow for an error return.
+ panic(err) // nolint
+ }
+
+ var iv, s0 [ccmBlockSize]byte
+ iv[0] = c.L - 1
+ copy(iv[1:ccmBlockSize-c.L], nonce)
+ c.b.Encrypt(s0[:], iv[:])
+ for i := 0; i < int(c.M); i++ {
+ tag[i] ^= s0[i]
+ }
+ iv[len(iv)-1] |= 1
+ stream := cipher.NewCTR(c.b, iv[:])
+ ret, out := sliceForAppend(dst, len(plaintext)+int(c.M))
+ stream.XORKeyStream(out, plaintext)
+ copy(out[len(plaintext):], tag)
+ return ret
+}
+
+var (
+ errOpen = errors.New("ccm: message authentication failed")
+ errCiphertextTooShort = errors.New("ccm: ciphertext too short")
+ errCiphertextTooLong = errors.New("ccm: ciphertext too long")
+)
+
+func (c *ccm) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
+ if len(ciphertext) < int(c.M) {
+ return nil, errCiphertextTooShort
+ }
+ if len(ciphertext) > c.MaxLength()+c.Overhead() {
+ return nil, errCiphertextTooLong
+ }
+
+ tag := make([]byte, int(c.M))
+ copy(tag, ciphertext[len(ciphertext)-int(c.M):])
+ ciphertextWithoutTag := ciphertext[:len(ciphertext)-int(c.M)]
+
+ var iv, s0 [ccmBlockSize]byte
+ iv[0] = c.L - 1
+ copy(iv[1:ccmBlockSize-c.L], nonce)
+ c.b.Encrypt(s0[:], iv[:])
+ for i := 0; i < int(c.M); i++ {
+ tag[i] ^= s0[i]
+ }
+ iv[len(iv)-1] |= 1
+ stream := cipher.NewCTR(c.b, iv[:])
+
+ // Cannot decrypt directly to dst since we're not supposed to
+ // reveal the plaintext to the caller if authentication fails.
+ plaintext := make([]byte, len(ciphertextWithoutTag))
+ stream.XORKeyStream(plaintext, ciphertextWithoutTag)
+ expectedTag, err := c.tag(nonce, plaintext, adata)
+ if err != nil {
+ return nil, err
+ }
+
+ if subtle.ConstantTimeCompare(tag, expectedTag) != 1 {
+ return nil, errOpen
+ }
+ return append(dst, plaintext...), nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go
new file mode 100644
index 0000000..8ff1634
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go
@@ -0,0 +1,164 @@
+package ciphersuite
+
+import ( //nolint:gci
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/rand"
+ "encoding/binary"
+ "hash"
+
+ "github.com/pion/dtls/v2/internal/util"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// block ciphers using cipher block chaining.
+type cbcMode interface {
+ cipher.BlockMode
+ SetIV([]byte)
+}
+
+// CBC Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
+type CBC struct {
+ writeCBC, readCBC cbcMode
+ writeMac, readMac []byte
+ h prf.HashFunc
+}
+
+// NewCBC creates a DTLS CBC Cipher
+func NewCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMac []byte, h prf.HashFunc) (*CBC, error) {
+ writeBlock, err := aes.NewCipher(localKey)
+ if err != nil {
+ return nil, err
+ }
+
+ readBlock, err := aes.NewCipher(remoteKey)
+ if err != nil {
+ return nil, err
+ }
+
+ return &CBC{
+ writeCBC: cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode),
+ writeMac: localMac,
+
+ readCBC: cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode),
+ readMac: remoteMac,
+ h: h,
+ }, nil
+}
+
+// Encrypt encrypt a DTLS RecordLayer message
+func (c *CBC) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ payload := raw[recordlayer.HeaderSize:]
+ raw = raw[:recordlayer.HeaderSize]
+ blockSize := c.writeCBC.BlockSize()
+
+ // Generate + Append MAC
+ h := pkt.Header
+
+ MAC, err := c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, payload, c.writeMac, c.h)
+ if err != nil {
+ return nil, err
+ }
+ payload = append(payload, MAC...)
+
+ // Generate + Append padding
+ padding := make([]byte, blockSize-len(payload)%blockSize)
+ paddingLen := len(padding)
+ for i := 0; i < paddingLen; i++ {
+ padding[i] = byte(paddingLen - 1)
+ }
+ payload = append(payload, padding...)
+
+ // Generate IV
+ iv := make([]byte, blockSize)
+ if _, err := rand.Read(iv); err != nil {
+ return nil, err
+ }
+
+ // Set IV + Encrypt + Prepend IV
+ c.writeCBC.SetIV(iv)
+ c.writeCBC.CryptBlocks(payload, payload)
+ payload = append(iv, payload...)
+
+ // Prepend unencrypte header with encrypted payload
+ raw = append(raw, payload...)
+
+ // Update recordLayer size to include IV+MAC+Padding
+ binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize))
+
+ return raw, nil
+}
+
+// Decrypt decrypts a DTLS RecordLayer message
+func (c *CBC) Decrypt(in []byte) ([]byte, error) {
+ body := in[recordlayer.HeaderSize:]
+ blockSize := c.readCBC.BlockSize()
+ mac := c.h()
+
+ var h recordlayer.Header
+ err := h.Unmarshal(in)
+ switch {
+ case err != nil:
+ return nil, err
+ case h.ContentType == protocol.ContentTypeChangeCipherSpec:
+ // Nothing to encrypt with ChangeCipherSpec
+ return in, nil
+ case len(body)%blockSize != 0 || len(body) < blockSize+util.Max(mac.Size()+1, blockSize):
+ return nil, errNotEnoughRoomForNonce
+ }
+
+ // Set + remove per record IV
+ c.readCBC.SetIV(body[:blockSize])
+ body = body[blockSize:]
+
+ // Decrypt
+ c.readCBC.CryptBlocks(body, body)
+
+ // Padding+MAC needs to be checked in constant time
+ // Otherwise we reveal information about the level of correctness
+ paddingLen, paddingGood := examinePadding(body)
+ if paddingGood != 255 {
+ return nil, errInvalidMAC
+ }
+
+ macSize := mac.Size()
+ if len(body) < macSize {
+ return nil, errInvalidMAC
+ }
+
+ dataEnd := len(body) - macSize - paddingLen
+
+ expectedMAC := body[dataEnd : dataEnd+macSize]
+ actualMAC, err := c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, body[:dataEnd], c.readMac, c.h)
+
+ // Compute Local MAC and compare
+ if err != nil || !hmac.Equal(actualMAC, expectedMAC) {
+ return nil, errInvalidMAC
+ }
+
+ return append(in[:recordlayer.HeaderSize], body[:dataEnd]...), nil
+}
+
+func (c *CBC) hmac(epoch uint16, sequenceNumber uint64, contentType protocol.ContentType, protocolVersion protocol.Version, payload []byte, key []byte, hf func() hash.Hash) ([]byte, error) {
+ h := hmac.New(hf, key)
+
+ msg := make([]byte, 13)
+
+ binary.BigEndian.PutUint16(msg, epoch)
+ util.PutBigEndianUint48(msg[2:], sequenceNumber)
+ msg[8] = byte(contentType)
+ msg[9] = protocolVersion.Major
+ msg[10] = protocolVersion.Minor
+ binary.BigEndian.PutUint16(msg[11:], uint16(len(payload)))
+
+ if _, err := h.Write(msg); err != nil {
+ return nil, err
+ } else if _, err := h.Write(payload); err != nil {
+ return nil, err
+ }
+
+ return h.Sum(nil), nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go
new file mode 100644
index 0000000..354b1cc
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go
@@ -0,0 +1,104 @@
+package ciphersuite
+
+import (
+ "crypto/aes"
+ "crypto/rand"
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ccm"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// CCMTagLen is the length of Authentication Tag
+type CCMTagLen int
+
+// CCM Enums
+const (
+ CCMTagLength8 CCMTagLen = 8
+ CCMTagLength CCMTagLen = 16
+ ccmNonceLength = 12
+)
+
+// CCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
+type CCM struct {
+ localCCM, remoteCCM ccm.CCM
+ localWriteIV, remoteWriteIV []byte
+ tagLen CCMTagLen
+}
+
+// NewCCM creates a DTLS GCM Cipher
+func NewCCM(tagLen CCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*CCM, error) {
+ localBlock, err := aes.NewCipher(localKey)
+ if err != nil {
+ return nil, err
+ }
+ localCCM, err := ccm.NewCCM(localBlock, int(tagLen), ccmNonceLength)
+ if err != nil {
+ return nil, err
+ }
+
+ remoteBlock, err := aes.NewCipher(remoteKey)
+ if err != nil {
+ return nil, err
+ }
+ remoteCCM, err := ccm.NewCCM(remoteBlock, int(tagLen), ccmNonceLength)
+ if err != nil {
+ return nil, err
+ }
+
+ return &CCM{
+ localCCM: localCCM,
+ localWriteIV: localWriteIV,
+ remoteCCM: remoteCCM,
+ remoteWriteIV: remoteWriteIV,
+ tagLen: tagLen,
+ }, nil
+}
+
+// Encrypt encrypt a DTLS RecordLayer message
+func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ payload := raw[recordlayer.HeaderSize:]
+ raw = raw[:recordlayer.HeaderSize]
+
+ nonce := append(append([]byte{}, c.localWriteIV[:4]...), make([]byte, 8)...)
+ if _, err := rand.Read(nonce[4:]); err != nil {
+ return nil, err
+ }
+
+ additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
+ encryptedPayload := c.localCCM.Seal(nil, nonce, payload, additionalData)
+
+ encryptedPayload = append(nonce[4:], encryptedPayload...)
+ raw = append(raw, encryptedPayload...)
+
+ // Update recordLayer size to include explicit nonce
+ binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize))
+ return raw, nil
+}
+
+// Decrypt decrypts a DTLS RecordLayer message
+func (c *CCM) Decrypt(in []byte) ([]byte, error) {
+ var h recordlayer.Header
+ err := h.Unmarshal(in)
+ switch {
+ case err != nil:
+ return nil, err
+ case h.ContentType == protocol.ContentTypeChangeCipherSpec:
+ // Nothing to encrypt with ChangeCipherSpec
+ return in, nil
+ case len(in) <= (8 + recordlayer.HeaderSize):
+ return nil, errNotEnoughRoomForNonce
+ }
+
+ nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...)
+ out := in[recordlayer.HeaderSize+8:]
+
+ additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen))
+ out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errDecryptPacket, err)
+ }
+ return append(in[:recordlayer.HeaderSize], out...), nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go
new file mode 100644
index 0000000..72beffd
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go
@@ -0,0 +1,72 @@
+// Package ciphersuite provides the crypto operations needed for a DTLS CipherSuite
+package ciphersuite
+
+import (
+ "encoding/binary"
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+var (
+ errNotEnoughRoomForNonce = &protocol.InternalError{Err: errors.New("buffer not long enough to contain nonce")} //nolint:goerr113
+ errDecryptPacket = &protocol.TemporaryError{Err: errors.New("failed to decrypt packet")} //nolint:goerr113
+ errInvalidMAC = &protocol.TemporaryError{Err: errors.New("invalid mac")} //nolint:goerr113
+)
+
+func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte {
+ var additionalData [13]byte
+ // SequenceNumber MUST be set first
+ // we only want uint48, clobbering an extra 2 (using uint64, Golang doesn't have uint48)
+ binary.BigEndian.PutUint64(additionalData[:], h.SequenceNumber)
+ binary.BigEndian.PutUint16(additionalData[:], h.Epoch)
+ additionalData[8] = byte(h.ContentType)
+ additionalData[9] = h.Version.Major
+ additionalData[10] = h.Version.Minor
+ binary.BigEndian.PutUint16(additionalData[len(additionalData)-2:], uint16(payloadLen))
+
+ return additionalData[:]
+}
+
+// examinePadding returns, in constant time, the length of the padding to remove
+// from the end of payload. It also returns a byte which is equal to 255 if the
+// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.
+//
+// https://github.com/golang/go/blob/039c2081d1178f90a8fa2f4e6958693129f8de33/src/crypto/tls/conn.go#L245
+func examinePadding(payload []byte) (toRemove int, good byte) {
+ if len(payload) < 1 {
+ return 0, 0
+ }
+
+ paddingLen := payload[len(payload)-1]
+ t := uint(len(payload)-1) - uint(paddingLen)
+ // if len(payload) >= (paddingLen - 1) then the MSB of t is zero
+ good = byte(int32(^t) >> 31)
+
+ // The maximum possible padding length plus the actual length field
+ toCheck := 256
+ // The length of the padded data is public, so we can use an if here
+ if toCheck > len(payload) {
+ toCheck = len(payload)
+ }
+
+ for i := 0; i < toCheck; i++ {
+ t := uint(paddingLen) - uint(i)
+ // if i <= paddingLen then the MSB of t is zero
+ mask := byte(int32(^t) >> 31)
+ b := payload[len(payload)-1-i]
+ good &^= mask&paddingLen ^ mask&b
+ }
+
+ // We AND together the bits of good and replicate the result across
+ // all the bits.
+ good &= good << 4
+ good &= good << 2
+ good &= good << 1
+ good = uint8(int8(good) >> 7)
+
+ toRemove = int(paddingLen) + 1
+
+ return toRemove, good
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go
new file mode 100644
index 0000000..af986d4
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go
@@ -0,0 +1,100 @@
+package ciphersuite
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+const (
+ gcmTagLength = 16
+ gcmNonceLength = 12
+)
+
+// GCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
+type GCM struct {
+ localGCM, remoteGCM cipher.AEAD
+ localWriteIV, remoteWriteIV []byte
+}
+
+// NewGCM creates a DTLS GCM Cipher
+func NewGCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*GCM, error) {
+ localBlock, err := aes.NewCipher(localKey)
+ if err != nil {
+ return nil, err
+ }
+ localGCM, err := cipher.NewGCM(localBlock)
+ if err != nil {
+ return nil, err
+ }
+
+ remoteBlock, err := aes.NewCipher(remoteKey)
+ if err != nil {
+ return nil, err
+ }
+ remoteGCM, err := cipher.NewGCM(remoteBlock)
+ if err != nil {
+ return nil, err
+ }
+
+ return &GCM{
+ localGCM: localGCM,
+ localWriteIV: localWriteIV,
+ remoteGCM: remoteGCM,
+ remoteWriteIV: remoteWriteIV,
+ }, nil
+}
+
+// Encrypt encrypt a DTLS RecordLayer message
+func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ payload := raw[recordlayer.HeaderSize:]
+ raw = raw[:recordlayer.HeaderSize]
+
+ nonce := make([]byte, gcmNonceLength)
+ copy(nonce, g.localWriteIV[:4])
+ if _, err := rand.Read(nonce[4:]); err != nil {
+ return nil, err
+ }
+
+ additionalData := generateAEADAdditionalData(&pkt.Header, len(payload))
+ encryptedPayload := g.localGCM.Seal(nil, nonce, payload, additionalData)
+ r := make([]byte, len(raw)+len(nonce[4:])+len(encryptedPayload))
+ copy(r, raw)
+ copy(r[len(raw):], nonce[4:])
+ copy(r[len(raw)+len(nonce[4:]):], encryptedPayload)
+
+ // Update recordLayer size to include explicit nonce
+ binary.BigEndian.PutUint16(r[recordlayer.HeaderSize-2:], uint16(len(r)-recordlayer.HeaderSize))
+ return r, nil
+}
+
+// Decrypt decrypts a DTLS RecordLayer message
+func (g *GCM) Decrypt(in []byte) ([]byte, error) {
+ var h recordlayer.Header
+ err := h.Unmarshal(in)
+ switch {
+ case err != nil:
+ return nil, err
+ case h.ContentType == protocol.ContentTypeChangeCipherSpec:
+ // Nothing to encrypt with ChangeCipherSpec
+ return in, nil
+ case len(in) <= (8 + recordlayer.HeaderSize):
+ return nil, errNotEnoughRoomForNonce
+ }
+
+ nonce := make([]byte, 0, gcmNonceLength)
+ nonce = append(append(nonce, g.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...)
+ out := in[recordlayer.HeaderSize+8:]
+
+ additionalData := generateAEADAdditionalData(&h, len(out)-gcmTagLength)
+ out, err = g.remoteGCM.Open(out[:0], nonce, out, additionalData)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errDecryptPacket, err)
+ }
+ return append(in[:recordlayer.HeaderSize], out...), nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go
new file mode 100644
index 0000000..c222c01
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go
@@ -0,0 +1,22 @@
+// Package clientcertificate provides all the support Client Certificate types
+package clientcertificate
+
+// Type is used to communicate what
+// type of certificate is being transported
+//
+//https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2
+type Type byte
+
+// ClientCertificateType enums
+const (
+ RSASign Type = 1
+ ECDSASign Type = 64
+)
+
+// Types returns all valid ClientCertificate Types
+func Types() map[Type]bool {
+ return map[Type]bool{
+ RSASign: true,
+ ECDSASign: true,
+ }
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go
new file mode 100644
index 0000000..5b0e4fa
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go
@@ -0,0 +1,99 @@
+// Package elliptic provides elliptic curve cryptography for DTLS
+package elliptic
+
+import (
+ "crypto/elliptic"
+ "crypto/rand"
+ "errors"
+
+ "golang.org/x/crypto/curve25519"
+)
+
+var errInvalidNamedCurve = errors.New("invalid named curve")
+
+// CurvePointFormat is used to represent the IANA registered curve points
+//
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
+type CurvePointFormat byte
+
+// CurvePointFormat enums
+const (
+ CurvePointFormatUncompressed CurvePointFormat = 0
+)
+
+// Keypair is a Curve with a Private/Public Keypair
+type Keypair struct {
+ Curve Curve
+ PublicKey []byte
+ PrivateKey []byte
+}
+
+// CurveType is used to represent the IANA registered curve types for TLS
+//
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
+type CurveType byte
+
+// CurveType enums
+const (
+ CurveTypeNamedCurve CurveType = 0x03
+)
+
+// CurveTypes returns all known curves
+func CurveTypes() map[CurveType]struct{} {
+ return map[CurveType]struct{}{
+ CurveTypeNamedCurve: {},
+ }
+}
+
+// Curve is used to represent the IANA registered curves for TLS
+//
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
+type Curve uint16
+
+// Curve enums
+const (
+ P256 Curve = 0x0017
+ P384 Curve = 0x0018
+ X25519 Curve = 0x001d
+)
+
+// Curves returns all curves we implement
+func Curves() map[Curve]bool {
+ return map[Curve]bool{
+ X25519: true,
+ P256: true,
+ P384: true,
+ }
+}
+
+// GenerateKeypair generates a keypair for the given Curve
+func GenerateKeypair(c Curve) (*Keypair, error) {
+ switch c { //nolint:golint
+ case X25519:
+ tmp := make([]byte, 32)
+ if _, err := rand.Read(tmp); err != nil {
+ return nil, err
+ }
+
+ var public, private [32]byte
+ copy(private[:], tmp)
+
+ curve25519.ScalarBaseMult(&public, &private)
+ return &Keypair{X25519, public[:], private[:]}, nil
+ case P256:
+ return ellipticCurveKeypair(P256, elliptic.P256(), elliptic.P256())
+ case P384:
+ return ellipticCurveKeypair(P384, elliptic.P384(), elliptic.P384())
+ default:
+ return nil, errInvalidNamedCurve
+ }
+}
+
+func ellipticCurveKeypair(nc Curve, c1, c2 elliptic.Curve) (*Keypair, error) {
+ privateKey, x, y, err := elliptic.GenerateKey(c1, rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Keypair{nc, elliptic.Marshal(c2, x, y), privateKey}, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go
new file mode 100644
index 0000000..215b44e
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go
@@ -0,0 +1,50 @@
+// Package fingerprint provides a helper to create fingerprint string from certificate
+package fingerprint
+
+import (
+ "crypto"
+ "crypto/x509"
+ "errors"
+ "fmt"
+)
+
+var (
+ errHashUnavailable = errors.New("fingerprint: hash algorithm is not linked into the binary")
+ errInvalidFingerprintLength = errors.New("fingerprint: invalid fingerprint length")
+)
+
+// Fingerprint creates a fingerprint for a certificate using the specified hash algorithm
+func Fingerprint(cert *x509.Certificate, algo crypto.Hash) (string, error) {
+ if !algo.Available() {
+ return "", errHashUnavailable
+ }
+ h := algo.New()
+ for i := 0; i < len(cert.Raw); {
+ n, _ := h.Write(cert.Raw[i:])
+ // Hash.Writer is specified to be never returning an error.
+ // https://golang.org/pkg/hash/#Hash
+ i += n
+ }
+ digest := []byte(fmt.Sprintf("%x", h.Sum(nil)))
+
+ digestlen := len(digest)
+ if digestlen == 0 {
+ return "", nil
+ }
+ if digestlen%2 != 0 {
+ return "", errInvalidFingerprintLength
+ }
+ res := make([]byte, digestlen>>1+digestlen-1)
+
+ pos := 0
+ for i, c := range digest {
+ res[pos] = c
+ pos++
+ if (i)%2 != 0 && i < digestlen-1 {
+ res[pos] = byte(':')
+ pos++
+ }
+ }
+
+ return string(res), nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go
new file mode 100644
index 0000000..09107db
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go
@@ -0,0 +1,37 @@
+package fingerprint
+
+import (
+ "crypto"
+ "errors"
+)
+
+var errInvalidHashAlgorithm = errors.New("fingerprint: invalid hash algorithm")
+
+func nameToHash() map[string]crypto.Hash {
+ return map[string]crypto.Hash{
+ "md5": crypto.MD5, // [RFC3279]
+ "sha-1": crypto.SHA1, // [RFC3279]
+ "sha-224": crypto.SHA224, // [RFC4055]
+ "sha-256": crypto.SHA256, // [RFC4055]
+ "sha-384": crypto.SHA384, // [RFC4055]
+ "sha-512": crypto.SHA512, // [RFC4055]
+ }
+}
+
+// HashFromString allows looking up a hash algorithm by it's string representation
+func HashFromString(s string) (crypto.Hash, error) {
+ if h, ok := nameToHash()[s]; ok {
+ return h, nil
+ }
+ return 0, errInvalidHashAlgorithm
+}
+
+// StringFromHash allows looking up a string representation of the crypto.Hash.
+func StringFromHash(hash crypto.Hash) (string, error) {
+ for s, h := range nameToHash() {
+ if h == hash {
+ return s, nil
+ }
+ }
+ return "", errInvalidHashAlgorithm
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go
new file mode 100644
index 0000000..660326f
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go
@@ -0,0 +1,126 @@
+// Package hash provides TLS HashAlgorithm as defined in TLS 1.2
+package hash
+
+import ( //nolint:gci
+ "crypto"
+ "crypto/md5" //nolint:gosec
+ "crypto/sha1" //nolint:gosec
+ "crypto/sha256"
+ "crypto/sha512"
+)
+
+// Algorithm is used to indicate the hash algorithm used
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
+type Algorithm uint16
+
+// Supported hash algorithms
+const (
+ None Algorithm = 0 // Blacklisted
+ MD5 Algorithm = 1 // Blacklisted
+ SHA1 Algorithm = 2 // Blacklisted
+ SHA224 Algorithm = 3
+ SHA256 Algorithm = 4
+ SHA384 Algorithm = 5
+ SHA512 Algorithm = 6
+ Ed25519 Algorithm = 8
+)
+
+// String makes hashAlgorithm printable
+func (a Algorithm) String() string {
+ switch a {
+ case None:
+ return "none"
+ case MD5:
+ return "md5" // [RFC3279]
+ case SHA1:
+ return "sha-1" // [RFC3279]
+ case SHA224:
+ return "sha-224" // [RFC4055]
+ case SHA256:
+ return "sha-256" // [RFC4055]
+ case SHA384:
+ return "sha-384" // [RFC4055]
+ case SHA512:
+ return "sha-512" // [RFC4055]
+ case Ed25519:
+ return "null"
+ default:
+ return "unknown or unsupported hash algorithm"
+ }
+}
+
+// Digest performs a digest on the passed value
+func (a Algorithm) Digest(b []byte) []byte {
+ switch a {
+ case None:
+ return nil
+ case MD5:
+ hash := md5.Sum(b) // #nosec
+ return hash[:]
+ case SHA1:
+ hash := sha1.Sum(b) // #nosec
+ return hash[:]
+ case SHA224:
+ hash := sha256.Sum224(b)
+ return hash[:]
+ case SHA256:
+ hash := sha256.Sum256(b)
+ return hash[:]
+ case SHA384:
+ hash := sha512.Sum384(b)
+ return hash[:]
+ case SHA512:
+ hash := sha512.Sum512(b)
+ return hash[:]
+ default:
+ return nil
+ }
+}
+
+// Insecure returns if the given HashAlgorithm is considered secure in DTLS 1.2
+func (a Algorithm) Insecure() bool {
+ switch a {
+ case None, MD5, SHA1:
+ return true
+ default:
+ return false
+ }
+}
+
+// CryptoHash returns the crypto.Hash implementation for the given HashAlgorithm
+func (a Algorithm) CryptoHash() crypto.Hash {
+ switch a {
+ case None:
+ return crypto.Hash(0)
+ case MD5:
+ return crypto.MD5
+ case SHA1:
+ return crypto.SHA1
+ case SHA224:
+ return crypto.SHA224
+ case SHA256:
+ return crypto.SHA256
+ case SHA384:
+ return crypto.SHA384
+ case SHA512:
+ return crypto.SHA512
+ case Ed25519:
+ return crypto.Hash(0)
+ default:
+ return crypto.Hash(0)
+ }
+}
+
+// Algorithms returns all the supported Hash Algorithms
+func Algorithms() map[Algorithm]struct{} {
+ return map[Algorithm]struct{}{
+ None: {},
+ MD5: {},
+ SHA1: {},
+ SHA224: {},
+ SHA256: {},
+ SHA384: {},
+ SHA512: {},
+ Ed25519: {},
+ }
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go
new file mode 100644
index 0000000..d33df19
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go
@@ -0,0 +1,224 @@
+// Package prf implements TLS 1.2 Pseudorandom functions
+package prf
+
+import ( //nolint:gci
+ ellipticStdlib "crypto/elliptic"
+ "crypto/hmac"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "hash"
+ "math"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "golang.org/x/crypto/curve25519"
+)
+
+const (
+ masterSecretLabel = "master secret"
+ extendedMasterSecretLabel = "extended master secret"
+ keyExpansionLabel = "key expansion"
+ verifyDataClientLabel = "client finished"
+ verifyDataServerLabel = "server finished"
+)
+
+// HashFunc allows callers to decide what hash is used in PRF
+type HashFunc func() hash.Hash
+
+// EncryptionKeys is all the state needed for a TLS CipherSuite
+type EncryptionKeys struct {
+ MasterSecret []byte
+ ClientMACKey []byte
+ ServerMACKey []byte
+ ClientWriteKey []byte
+ ServerWriteKey []byte
+ ClientWriteIV []byte
+ ServerWriteIV []byte
+}
+
+var errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:goerr113
+
+func (e *EncryptionKeys) String() string {
+ return fmt.Sprintf(`encryptionKeys:
+- masterSecret: %#v
+- clientMACKey: %#v
+- serverMACKey: %#v
+- clientWriteKey: %#v
+- serverWriteKey: %#v
+- clientWriteIV: %#v
+- serverWriteIV: %#v
+`,
+ e.MasterSecret,
+ e.ClientMACKey,
+ e.ServerMACKey,
+ e.ClientWriteKey,
+ e.ServerWriteKey,
+ e.ClientWriteIV,
+ e.ServerWriteIV)
+}
+
+// PSKPreMasterSecret generates the PSK Premaster Secret
+// The premaster secret is formed as follows: if the PSK is N octets
+// long, concatenate a uint16 with the value N, N zero octets, a second
+// uint16 with the value N, and the PSK itself.
+//
+// https://tools.ietf.org/html/rfc4279#section-2
+func PSKPreMasterSecret(psk []byte) []byte {
+ pskLen := uint16(len(psk))
+
+ out := append(make([]byte, 2+pskLen+2), psk...)
+ binary.BigEndian.PutUint16(out, pskLen)
+ binary.BigEndian.PutUint16(out[2+pskLen:], pskLen)
+
+ return out
+}
+
+// PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve
+func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
+ switch curve {
+ case elliptic.X25519:
+ return curve25519.X25519(privateKey, publicKey)
+ case elliptic.P256:
+ return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P256(), ellipticStdlib.P256())
+ case elliptic.P384:
+ return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P384(), ellipticStdlib.P384())
+ default:
+ return nil, errInvalidNamedCurve
+ }
+}
+
+func ellipticCurvePreMasterSecret(publicKey, privateKey []byte, c1, c2 ellipticStdlib.Curve) ([]byte, error) {
+ x, y := ellipticStdlib.Unmarshal(c1, publicKey)
+ if x == nil || y == nil {
+ return nil, errInvalidNamedCurve
+ }
+
+ result, _ := c2.ScalarMult(x, y, privateKey)
+ preMasterSecret := make([]byte, (c2.Params().BitSize+7)>>3)
+ resultBytes := result.Bytes()
+ copy(preMasterSecret[len(preMasterSecret)-len(resultBytes):], resultBytes)
+ return preMasterSecret, nil
+}
+
+// PHash is PRF is the SHA-256 hash function is used for all cipher suites
+// defined in this TLS 1.2 document and in TLS documents published prior to this
+// document when TLS 1.2 is negotiated. New cipher suites MUST explicitly
+// specify a PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a
+// stronger standard hash function.
+//
+// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+// HMAC_hash(secret, A(2) + seed) +
+// HMAC_hash(secret, A(3) + seed) + ...
+//
+// A() is defined as:
+//
+// A(0) = seed
+// A(i) = HMAC_hash(secret, A(i-1))
+//
+// P_hash can be iterated as many times as necessary to produce the
+// required quantity of data. For example, if P_SHA256 is being used to
+// create 80 bytes of data, it will have to be iterated three times
+// (through A(3)), creating 96 bytes of output data; the last 16 bytes
+// of the final iteration will then be discarded, leaving 80 bytes of
+// output data.
+//
+// https://tools.ietf.org/html/rfc4346w
+func PHash(secret, seed []byte, requestedLength int, h HashFunc) ([]byte, error) {
+ hmacSHA256 := func(key, data []byte) ([]byte, error) {
+ mac := hmac.New(h, key)
+ if _, err := mac.Write(data); err != nil {
+ return nil, err
+ }
+ return mac.Sum(nil), nil
+ }
+
+ var err error
+ lastRound := seed
+ out := []byte{}
+
+ iterations := int(math.Ceil(float64(requestedLength) / float64(h().Size())))
+ for i := 0; i < iterations; i++ {
+ lastRound, err = hmacSHA256(secret, lastRound)
+ if err != nil {
+ return nil, err
+ }
+ withSecret, err := hmacSHA256(secret, append(lastRound, seed...))
+ if err != nil {
+ return nil, err
+ }
+ out = append(out, withSecret...)
+ }
+
+ return out[:requestedLength], nil
+}
+
+// ExtendedMasterSecret generates a Extended MasterSecret as defined in
+// https://tools.ietf.org/html/rfc7627
+func ExtendedMasterSecret(preMasterSecret, sessionHash []byte, h HashFunc) ([]byte, error) {
+ seed := append([]byte(extendedMasterSecretLabel), sessionHash...)
+ return PHash(preMasterSecret, seed, 48, h)
+}
+
+// MasterSecret generates a TLS 1.2 MasterSecret
+func MasterSecret(preMasterSecret, clientRandom, serverRandom []byte, h HashFunc) ([]byte, error) {
+ seed := append(append([]byte(masterSecretLabel), clientRandom...), serverRandom...)
+ return PHash(preMasterSecret, seed, 48, h)
+}
+
+// GenerateEncryptionKeys is the final step TLS 1.2 PRF. Given all state generated so far generates
+// the final keys need for encryption
+func GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int, h HashFunc) (*EncryptionKeys, error) {
+ seed := append(append([]byte(keyExpansionLabel), serverRandom...), clientRandom...)
+ keyMaterial, err := PHash(masterSecret, seed, (2*macLen)+(2*keyLen)+(2*ivLen), h)
+ if err != nil {
+ return nil, err
+ }
+
+ clientMACKey := keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+
+ serverMACKey := keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+
+ clientWriteKey := keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+
+ serverWriteKey := keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+
+ clientWriteIV := keyMaterial[:ivLen]
+ keyMaterial = keyMaterial[ivLen:]
+
+ serverWriteIV := keyMaterial[:ivLen]
+
+ return &EncryptionKeys{
+ MasterSecret: masterSecret,
+ ClientMACKey: clientMACKey,
+ ServerMACKey: serverMACKey,
+ ClientWriteKey: clientWriteKey,
+ ServerWriteKey: serverWriteKey,
+ ClientWriteIV: clientWriteIV,
+ ServerWriteIV: serverWriteIV,
+ }, nil
+}
+
+func prfVerifyData(masterSecret, handshakeBodies []byte, label string, hashFunc HashFunc) ([]byte, error) {
+ h := hashFunc()
+ if _, err := h.Write(handshakeBodies); err != nil {
+ return nil, err
+ }
+
+ seed := append([]byte(label), h.Sum(nil)...)
+ return PHash(masterSecret, seed, 12, hashFunc)
+}
+
+// VerifyDataClient is caled on the Client Side to either verify or generate the VerifyData message
+func VerifyDataClient(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
+ return prfVerifyData(masterSecret, handshakeBodies, verifyDataClientLabel, h)
+}
+
+// VerifyDataServer is caled on the Server Side to either verify or generate the VerifyData message
+func VerifyDataServer(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
+ return prfVerifyData(masterSecret, handshakeBodies, verifyDataServerLabel, h)
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go
new file mode 100644
index 0000000..d9150eb
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go
@@ -0,0 +1,24 @@
+// Package signature provides our implemented Signature Algorithms
+package signature
+
+// Algorithm as defined in TLS 1.2
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
+type Algorithm uint16
+
+// SignatureAlgorithm enums
+const (
+ Anonymous Algorithm = 0
+ RSA Algorithm = 1
+ ECDSA Algorithm = 3
+ Ed25519 Algorithm = 7
+)
+
+// Algorithms returns all implemented Signature Algorithms
+func Algorithms() map[Algorithm]struct{} {
+ return map[Algorithm]struct{}{
+ Anonymous: {},
+ RSA: {},
+ ECDSA: {},
+ Ed25519: {},
+ }
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go
new file mode 100644
index 0000000..9d9d3b3
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go
@@ -0,0 +1,9 @@
+package signaturehash
+
+import "errors"
+
+var (
+ errNoAvailableSignatureSchemes = errors.New("connection can not be created, no SignatureScheme satisfy this Config")
+ errInvalidSignatureAlgorithm = errors.New("invalid signature algorithm")
+ errInvalidHashAlgorithm = errors.New("invalid hash algorithm")
+)
diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go
new file mode 100644
index 0000000..f2017bc
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go
@@ -0,0 +1,93 @@
+// Package signaturehash provides the SignatureHashAlgorithm as defined in TLS 1.2
+package signaturehash
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/tls"
+
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+ "golang.org/x/xerrors"
+)
+
+// Algorithm is a signature/hash algorithm pairs which may be used in
+// digital signatures.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+type Algorithm struct {
+ Hash hash.Algorithm
+ Signature signature.Algorithm
+}
+
+// Algorithms are all the know SignatureHash Algorithms
+func Algorithms() []Algorithm {
+ return []Algorithm{
+ {hash.SHA256, signature.ECDSA},
+ {hash.SHA384, signature.ECDSA},
+ {hash.SHA512, signature.ECDSA},
+ {hash.SHA256, signature.RSA},
+ {hash.SHA384, signature.RSA},
+ {hash.SHA512, signature.RSA},
+ {hash.Ed25519, signature.Ed25519},
+ }
+}
+
+// SelectSignatureScheme returns most preferred and compatible scheme.
+func SelectSignatureScheme(sigs []Algorithm, privateKey crypto.PrivateKey) (Algorithm, error) {
+ for _, ss := range sigs {
+ if ss.isCompatible(privateKey) {
+ return ss, nil
+ }
+ }
+ return Algorithm{}, errNoAvailableSignatureSchemes
+}
+
+// isCompatible checks that given private key is compatible with the signature scheme.
+func (a *Algorithm) isCompatible(privateKey crypto.PrivateKey) bool {
+ switch privateKey.(type) {
+ case ed25519.PrivateKey:
+ return a.Signature == signature.Ed25519
+ case *ecdsa.PrivateKey:
+ return a.Signature == signature.ECDSA
+ case *rsa.PrivateKey:
+ return a.Signature == signature.RSA
+ default:
+ return false
+ }
+}
+
+// ParseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
+// It returns default signature scheme list if no SignatureScheme is passed.
+func ParseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]Algorithm, error) {
+ if len(sigs) == 0 {
+ return Algorithms(), nil
+ }
+ out := []Algorithm{}
+ for _, ss := range sigs {
+ sig := signature.Algorithm(ss & 0xFF)
+ if _, ok := signature.Algorithms()[sig]; !ok {
+ return nil,
+ xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm)
+ }
+ h := hash.Algorithm(ss >> 8)
+ if _, ok := hash.Algorithms()[h]; !ok || (ok && h == hash.None) {
+ return nil, xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm)
+ }
+ if h.Insecure() && !insecureHashes {
+ continue
+ }
+ out = append(out, Algorithm{
+ Hash: h,
+ Signature: sig,
+ })
+ }
+
+ if len(out) == 0 {
+ return nil, errNoAvailableSignatureSchemes
+ }
+
+ return out, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go
new file mode 100644
index 0000000..9eb2e6a
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go
@@ -0,0 +1,160 @@
+// Package alert implements TLS alert protocol https://tools.ietf.org/html/rfc5246#section-7.2
+package alert
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+
+// Level is the level of the TLS Alert
+type Level byte
+
+// Level enums
+const (
+ Warning Level = 1
+ Fatal Level = 2
+)
+
+func (l Level) String() string {
+ switch l {
+ case Warning:
+ return "Warning"
+ case Fatal:
+ return "Fatal"
+ default:
+ return "Invalid alert level"
+ }
+}
+
+// Description is the extended info of the TLS Alert
+type Description byte
+
+// Description enums
+const (
+ CloseNotify Description = 0
+ UnexpectedMessage Description = 10
+ BadRecordMac Description = 20
+ DecryptionFailed Description = 21
+ RecordOverflow Description = 22
+ DecompressionFailure Description = 30
+ HandshakeFailure Description = 40
+ NoCertificate Description = 41
+ BadCertificate Description = 42
+ UnsupportedCertificate Description = 43
+ CertificateRevoked Description = 44
+ CertificateExpired Description = 45
+ CertificateUnknown Description = 46
+ IllegalParameter Description = 47
+ UnknownCA Description = 48
+ AccessDenied Description = 49
+ DecodeError Description = 50
+ DecryptError Description = 51
+ ExportRestriction Description = 60
+ ProtocolVersion Description = 70
+ InsufficientSecurity Description = 71
+ InternalError Description = 80
+ UserCanceled Description = 90
+ NoRenegotiation Description = 100
+ UnsupportedExtension Description = 110
+)
+
+func (d Description) String() string {
+ switch d {
+ case CloseNotify:
+ return "CloseNotify"
+ case UnexpectedMessage:
+ return "UnexpectedMessage"
+ case BadRecordMac:
+ return "BadRecordMac"
+ case DecryptionFailed:
+ return "DecryptionFailed"
+ case RecordOverflow:
+ return "RecordOverflow"
+ case DecompressionFailure:
+ return "DecompressionFailure"
+ case HandshakeFailure:
+ return "HandshakeFailure"
+ case NoCertificate:
+ return "NoCertificate"
+ case BadCertificate:
+ return "BadCertificate"
+ case UnsupportedCertificate:
+ return "UnsupportedCertificate"
+ case CertificateRevoked:
+ return "CertificateRevoked"
+ case CertificateExpired:
+ return "CertificateExpired"
+ case CertificateUnknown:
+ return "CertificateUnknown"
+ case IllegalParameter:
+ return "IllegalParameter"
+ case UnknownCA:
+ return "UnknownCA"
+ case AccessDenied:
+ return "AccessDenied"
+ case DecodeError:
+ return "DecodeError"
+ case DecryptError:
+ return "DecryptError"
+ case ExportRestriction:
+ return "ExportRestriction"
+ case ProtocolVersion:
+ return "ProtocolVersion"
+ case InsufficientSecurity:
+ return "InsufficientSecurity"
+ case InternalError:
+ return "InternalError"
+ case UserCanceled:
+ return "UserCanceled"
+ case NoRenegotiation:
+ return "NoRenegotiation"
+ case UnsupportedExtension:
+ return "UnsupportedExtension"
+ default:
+ return "Invalid alert description"
+ }
+}
+
+// Alert is one of the content types supported by the TLS record layer.
+// Alert messages convey the severity of the message
+// (warning or fatal) and a description of the alert. Alert messages
+// with a level of fatal result in the immediate termination of the
+// connection. In this case, other connections corresponding to the
+// session may continue, but the session identifier MUST be invalidated,
+// preventing the failed session from being used to establish new
+// connections. Like other messages, alert messages are encrypted and
+// compressed, as specified by the current connection state.
+// https://tools.ietf.org/html/rfc5246#section-7.2
+type Alert struct {
+ Level Level
+ Description Description
+}
+
+// ContentType returns the ContentType of this Content
+func (a Alert) ContentType() protocol.ContentType {
+ return protocol.ContentTypeAlert
+}
+
+// Marshal returns the encoded alert
+func (a *Alert) Marshal() ([]byte, error) {
+ return []byte{byte(a.Level), byte(a.Description)}, nil
+}
+
+// Unmarshal populates the alert from binary data
+func (a *Alert) Unmarshal(data []byte) error {
+ if len(data) != 2 {
+ return errBufferTooSmall
+ }
+
+ a.Level = Level(data[0])
+ a.Description = Description(data[1])
+ return nil
+}
+
+func (a *Alert) String() string {
+ return fmt.Sprintf("Alert %s: %s", a.Level, a.Description)
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go
new file mode 100644
index 0000000..e5fd6f5
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go
@@ -0,0 +1,26 @@
+package protocol
+
+// ApplicationData messages are carried by the record layer and are
+// fragmented, compressed, and encrypted based on the current connection
+// state. The messages are treated as transparent data to the record
+// layer.
+// https://tools.ietf.org/html/rfc5246#section-10
+type ApplicationData struct {
+ Data []byte
+}
+
+// ContentType returns the ContentType of this content
+func (a ApplicationData) ContentType() ContentType {
+ return ContentTypeApplicationData
+}
+
+// Marshal encodes the ApplicationData to binary
+func (a *ApplicationData) Marshal() ([]byte, error) {
+ return append([]byte{}, a.Data...), nil
+}
+
+// Unmarshal populates the ApplicationData from binary
+func (a *ApplicationData) Unmarshal(data []byte) error {
+ a.Data = append([]byte{}, data...)
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go
new file mode 100644
index 0000000..67b0052
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go
@@ -0,0 +1,28 @@
+package protocol
+
+// ChangeCipherSpec protocol exists to signal transitions in
+// ciphering strategies. The protocol consists of a single message,
+// which is encrypted and compressed under the current (not the pending)
+// connection state. The message consists of a single byte of value 1.
+// https://tools.ietf.org/html/rfc5246#section-7.1
+type ChangeCipherSpec struct {
+}
+
+// ContentType returns the ContentType of this content
+func (c ChangeCipherSpec) ContentType() ContentType {
+ return ContentTypeChangeCipherSpec
+}
+
+// Marshal encodes the ChangeCipherSpec to binary
+func (c *ChangeCipherSpec) Marshal() ([]byte, error) {
+ return []byte{0x01}, nil
+}
+
+// Unmarshal populates the ChangeCipherSpec from binary
+func (c *ChangeCipherSpec) Unmarshal(data []byte) error {
+ if len(data) == 1 && data[0] == 0x01 {
+ return nil
+ }
+
+ return errInvalidCipherSpec
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go
new file mode 100644
index 0000000..678e816
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go
@@ -0,0 +1,48 @@
+package protocol
+
+// CompressionMethodID is the ID for a CompressionMethod
+type CompressionMethodID byte
+
+const (
+ compressionMethodNull CompressionMethodID = 0
+)
+
+// CompressionMethod represents a TLS Compression Method
+type CompressionMethod struct {
+ ID CompressionMethodID
+}
+
+// CompressionMethods returns all supported CompressionMethods
+func CompressionMethods() map[CompressionMethodID]*CompressionMethod {
+ return map[CompressionMethodID]*CompressionMethod{
+ compressionMethodNull: {ID: compressionMethodNull},
+ }
+}
+
+// DecodeCompressionMethods the given compression methods
+func DecodeCompressionMethods(buf []byte) ([]*CompressionMethod, error) {
+ if len(buf) < 1 {
+ return nil, errBufferTooSmall
+ }
+ compressionMethodsCount := int(buf[0])
+ c := []*CompressionMethod{}
+ for i := 0; i < compressionMethodsCount; i++ {
+ if len(buf) <= i+1 {
+ return nil, errBufferTooSmall
+ }
+ id := CompressionMethodID(buf[i+1])
+ if compressionMethod, ok := CompressionMethods()[id]; ok {
+ c = append(c, compressionMethod)
+ }
+ }
+ return c, nil
+}
+
+// EncodeCompressionMethods the given compression methods
+func EncodeCompressionMethods(c []*CompressionMethod) []byte {
+ out := []byte{byte(len(c))}
+ for i := len(c); i > 0; i-- {
+ out = append(out, byte(c[i-1].ID))
+ }
+ return out
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go
new file mode 100644
index 0000000..47e5c96
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go
@@ -0,0 +1,21 @@
+package protocol
+
+// ContentType represents the IANA Registered ContentTypes
+//
+// https://tools.ietf.org/html/rfc4346#section-6.2.1
+type ContentType uint8
+
+// ContentType enums
+const (
+ ContentTypeChangeCipherSpec ContentType = 20
+ ContentTypeAlert ContentType = 21
+ ContentTypeHandshake ContentType = 22
+ ContentTypeApplicationData ContentType = 23
+)
+
+// Content is the top level distinguisher for a DTLS Datagram
+type Content interface {
+ ContentType() ContentType
+ Marshal() ([]byte, error)
+ Unmarshal(data []byte) error
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go
new file mode 100644
index 0000000..e52014a
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go
@@ -0,0 +1,104 @@
+package protocol
+
+import (
+ "errors"
+ "fmt"
+ "net"
+)
+
+var (
+ errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errInvalidCipherSpec = &FatalError{Err: errors.New("cipher spec invalid")} //nolint:goerr113
+)
+
+// FatalError indicates that the DTLS connection is no longer available.
+// It is mainly caused by wrong configuration of server or client.
+type FatalError struct {
+ Err error
+}
+
+// InternalError indicates and internal error caused by the implementation, and the DTLS connection is no longer available.
+// It is mainly caused by bugs or tried to use unimplemented features.
+type InternalError struct {
+ Err error
+}
+
+// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary.
+type TemporaryError struct {
+ Err error
+}
+
+// TimeoutError indicates that the request was timed out.
+type TimeoutError struct {
+ Err error
+}
+
+// HandshakeError indicates that the handshake failed.
+type HandshakeError struct {
+ Err error
+}
+
+// Timeout implements net.Error.Timeout()
+func (*FatalError) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*FatalError) Temporary() bool { return false }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *FatalError) Unwrap() error { return e.Err }
+
+func (e *FatalError) Error() string { return fmt.Sprintf("dtls fatal: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*InternalError) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*InternalError) Temporary() bool { return false }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *InternalError) Unwrap() error { return e.Err }
+
+func (e *InternalError) Error() string { return fmt.Sprintf("dtls internal: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*TemporaryError) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*TemporaryError) Temporary() bool { return true }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *TemporaryError) Unwrap() error { return e.Err }
+
+func (e *TemporaryError) Error() string { return fmt.Sprintf("dtls temporary: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*TimeoutError) Timeout() bool { return true }
+
+// Temporary implements net.Error.Temporary()
+func (*TimeoutError) Temporary() bool { return true }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *TimeoutError) Unwrap() error { return e.Err }
+
+func (e *TimeoutError) Error() string { return fmt.Sprintf("dtls timeout: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (e *HandshakeError) Timeout() bool {
+ if netErr, ok := e.Err.(net.Error); ok {
+ return netErr.Timeout()
+ }
+ return false
+}
+
+// Temporary implements net.Error.Temporary()
+func (e *HandshakeError) Temporary() bool {
+ if netErr, ok := e.Err.(net.Error); ok {
+ return netErr.Temporary()
+ }
+ return false
+}
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *HandshakeError) Unwrap() error { return e.Err }
+
+func (e *HandshakeError) Error() string { return fmt.Sprintf("handshake error: %v", e.Err) }
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go
new file mode 100644
index 0000000..23ed9b2
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go
@@ -0,0 +1,14 @@
+package extension
+
+import (
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var (
+ errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errInvalidExtensionType = &protocol.FatalError{Err: errors.New("invalid extension type")} //nolint:goerr113
+ errInvalidSNIFormat = &protocol.FatalError{Err: errors.New("invalid server name format")} //nolint:goerr113
+ errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
+)
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go
new file mode 100644
index 0000000..39b1fc8
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go
@@ -0,0 +1,96 @@
+// Package extension implements the extension values in the ClientHello/ServerHello
+package extension
+
+import "encoding/binary"
+
+// TypeValue is the 2 byte value for a TLS Extension as registered in the IANA
+//
+// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
+type TypeValue uint16
+
+// TypeValue constants
+const (
+ ServerNameTypeValue TypeValue = 0
+ SupportedEllipticCurvesTypeValue TypeValue = 10
+ SupportedPointFormatsTypeValue TypeValue = 11
+ SupportedSignatureAlgorithmsTypeValue TypeValue = 13
+ UseSRTPTypeValue TypeValue = 14
+ UseExtendedMasterSecretTypeValue TypeValue = 23
+ RenegotiationInfoTypeValue TypeValue = 65281
+)
+
+// Extension represents a single TLS extension
+type Extension interface {
+ Marshal() ([]byte, error)
+ Unmarshal(data []byte) error
+ TypeValue() TypeValue
+}
+
+// Unmarshal many extensions at once
+func Unmarshal(buf []byte) ([]Extension, error) {
+ switch {
+ case len(buf) == 0:
+ return []Extension{}, nil
+ case len(buf) < 2:
+ return nil, errBufferTooSmall
+ }
+
+ declaredLen := binary.BigEndian.Uint16(buf)
+ if len(buf)-2 != int(declaredLen) {
+ return nil, errLengthMismatch
+ }
+
+ extensions := []Extension{}
+ unmarshalAndAppend := func(data []byte, e Extension) error {
+ err := e.Unmarshal(data)
+ if err != nil {
+ return err
+ }
+ extensions = append(extensions, e)
+ return nil
+ }
+
+ for offset := 2; offset < len(buf); {
+ if len(buf) < (offset + 2) {
+ return nil, errBufferTooSmall
+ }
+ var err error
+ switch TypeValue(binary.BigEndian.Uint16(buf[offset:])) {
+ case ServerNameTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &ServerName{})
+ case SupportedEllipticCurvesTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &SupportedEllipticCurves{})
+ case UseSRTPTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &UseSRTP{})
+ case UseExtendedMasterSecretTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &UseExtendedMasterSecret{})
+ case RenegotiationInfoTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &RenegotiationInfo{})
+ default:
+ }
+ if err != nil {
+ return nil, err
+ }
+ if len(buf) < (offset + 4) {
+ return nil, errBufferTooSmall
+ }
+ extensionLength := binary.BigEndian.Uint16(buf[offset+2:])
+ offset += (4 + int(extensionLength))
+ }
+ return extensions, nil
+}
+
+// Marshal many extensions at once
+func Marshal(e []Extension) ([]byte, error) {
+ extensions := []byte{}
+ for _, e := range e {
+ raw, err := e.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ extensions = append(extensions, raw...)
+ }
+ out := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(out, uint16(len(extensions)))
+ return append(out, extensions...), nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go
new file mode 100644
index 0000000..8378c3d
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go
@@ -0,0 +1,43 @@
+package extension
+
+import "encoding/binary"
+
+const (
+ renegotiationInfoHeaderSize = 5
+)
+
+// RenegotiationInfo allows a Client/Server to
+// communicate their renegotation support
+//
+// https://tools.ietf.org/html/rfc5746
+type RenegotiationInfo struct {
+ RenegotiatedConnection uint8
+}
+
+// TypeValue returns the extension TypeValue
+func (r RenegotiationInfo) TypeValue() TypeValue {
+ return RenegotiationInfoTypeValue
+}
+
+// Marshal encodes the extension
+func (r *RenegotiationInfo) Marshal() ([]byte, error) {
+ out := make([]byte, renegotiationInfoHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(r.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(1)) // length
+ out[4] = r.RenegotiatedConnection
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (r *RenegotiationInfo) Unmarshal(data []byte) error {
+ if len(data) < renegotiationInfoHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != r.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ r.RenegotiatedConnection = data[4]
+
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go
new file mode 100644
index 0000000..a08033f
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go
@@ -0,0 +1,78 @@
+package extension
+
+import (
+ "strings"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+const serverNameTypeDNSHostName = 0
+
+// ServerName allows the client to inform the server the specific
+// name it wishs to contact. Useful if multiple DNS names resolve
+// to one IP
+//
+// https://tools.ietf.org/html/rfc6066#section-3
+type ServerName struct {
+ ServerName string
+}
+
+// TypeValue returns the extension TypeValue
+func (s ServerName) TypeValue() TypeValue {
+ return ServerNameTypeValue
+}
+
+// Marshal encodes the extension
+func (s *ServerName) Marshal() ([]byte, error) {
+ var b cryptobyte.Builder
+ b.AddUint16(uint16(s.TypeValue()))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(serverNameTypeDNSHostName)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(s.ServerName))
+ })
+ })
+ })
+ return b.Bytes()
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *ServerName) Unmarshal(data []byte) error {
+ val := cryptobyte.String(data)
+ var extension uint16
+ val.ReadUint16(&extension)
+ if TypeValue(extension) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ var extData cryptobyte.String
+ val.ReadUint16LengthPrefixed(&extData)
+
+ var nameList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
+ return errInvalidSNIFormat
+ }
+ for !nameList.Empty() {
+ var nameType uint8
+ var serverName cryptobyte.String
+ if !nameList.ReadUint8(&nameType) ||
+ !nameList.ReadUint16LengthPrefixed(&serverName) ||
+ serverName.Empty() {
+ return errInvalidSNIFormat
+ }
+ if nameType != serverNameTypeDNSHostName {
+ continue
+ }
+ if len(s.ServerName) != 0 {
+ // Multiple names of the same name_type are prohibited.
+ return errInvalidSNIFormat
+ }
+ s.ServerName = string(serverName)
+ // An SNI value may not include a trailing dot.
+ if strings.HasSuffix(s.ServerName, ".") {
+ return errInvalidSNIFormat
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go
new file mode 100644
index 0000000..2c4d1d4
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go
@@ -0,0 +1,21 @@
+package extension
+
+// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing
+// https://tools.ietf.org/html/rfc5764#section-4.1.2
+type SRTPProtectionProfile uint16
+
+const (
+ SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = 0x0001 // nolint
+ SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = 0x0002 // nolint
+ SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = 0x0007 // nolint
+ SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = 0x0008 // nolint
+)
+
+func srtpProtectionProfiles() map[SRTPProtectionProfile]bool {
+ return map[SRTPProtectionProfile]bool{
+ SRTP_AES128_CM_HMAC_SHA1_80: true,
+ SRTP_AES128_CM_HMAC_SHA1_32: true,
+ SRTP_AEAD_AES_128_GCM: true,
+ SRTP_AEAD_AES_256_GCM: true,
+ }
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go
new file mode 100644
index 0000000..8f077fc
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go
@@ -0,0 +1,62 @@
+package extension
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+)
+
+const (
+ supportedGroupsHeaderSize = 6
+)
+
+// SupportedEllipticCurves allows a Client/Server to communicate
+// what curves they both support
+//
+// https://tools.ietf.org/html/rfc8422#section-5.1.1
+type SupportedEllipticCurves struct {
+ EllipticCurves []elliptic.Curve
+}
+
+// TypeValue returns the extension TypeValue
+func (s SupportedEllipticCurves) TypeValue() TypeValue {
+ return SupportedEllipticCurvesTypeValue
+}
+
+// Marshal encodes the extension
+func (s *SupportedEllipticCurves) Marshal() ([]byte, error) {
+ out := make([]byte, supportedGroupsHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.EllipticCurves)*2)))
+ binary.BigEndian.PutUint16(out[4:], uint16(len(s.EllipticCurves)*2))
+
+ for _, v := range s.EllipticCurves {
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
+ }
+
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *SupportedEllipticCurves) Unmarshal(data []byte) error {
+ if len(data) <= supportedGroupsHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ groupCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
+ if supportedGroupsHeaderSize+(groupCount*2) > len(data) {
+ return errLengthMismatch
+ }
+
+ for i := 0; i < groupCount; i++ {
+ supportedGroupID := elliptic.Curve(binary.BigEndian.Uint16(data[(supportedGroupsHeaderSize + (i * 2)):]))
+ if _, ok := elliptic.Curves()[supportedGroupID]; ok {
+ s.EllipticCurves = append(s.EllipticCurves, supportedGroupID)
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go
new file mode 100644
index 0000000..873d078
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go
@@ -0,0 +1,62 @@
+package extension
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+)
+
+const (
+ supportedPointFormatsSize = 5
+)
+
+// SupportedPointFormats allows a Client/Server to negotiate
+// the EllipticCurvePointFormats
+//
+// https://tools.ietf.org/html/rfc4492#section-5.1.2
+type SupportedPointFormats struct {
+ PointFormats []elliptic.CurvePointFormat
+}
+
+// TypeValue returns the extension TypeValue
+func (s SupportedPointFormats) TypeValue() TypeValue {
+ return SupportedPointFormatsTypeValue
+}
+
+// Marshal encodes the extension
+func (s *SupportedPointFormats) Marshal() ([]byte, error) {
+ out := make([]byte, supportedPointFormatsSize)
+
+ binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(1+(len(s.PointFormats))))
+ out[4] = byte(len(s.PointFormats))
+
+ for _, v := range s.PointFormats {
+ out = append(out, byte(v))
+ }
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *SupportedPointFormats) Unmarshal(data []byte) error {
+ if len(data) <= supportedPointFormatsSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ pointFormatCount := int(binary.BigEndian.Uint16(data[4:]))
+ if supportedGroupsHeaderSize+(pointFormatCount) > len(data) {
+ return errLengthMismatch
+ }
+
+ for i := 0; i < pointFormatCount; i++ {
+ p := elliptic.CurvePointFormat(data[supportedPointFormatsSize+i])
+ switch p {
+ case elliptic.CurvePointFormatUncompressed:
+ s.PointFormats = append(s.PointFormats, p)
+ default:
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go
new file mode 100644
index 0000000..ee284f6
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go
@@ -0,0 +1,70 @@
+package extension
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+)
+
+const (
+ supportedSignatureAlgorithmsHeaderSize = 6
+)
+
+// SupportedSignatureAlgorithms allows a Client/Server to
+// negotiate what SignatureHash Algorithms they both support
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+type SupportedSignatureAlgorithms struct {
+ SignatureHashAlgorithms []signaturehash.Algorithm
+}
+
+// TypeValue returns the extension TypeValue
+func (s SupportedSignatureAlgorithms) TypeValue() TypeValue {
+ return SupportedSignatureAlgorithmsTypeValue
+}
+
+// Marshal encodes the extension
+func (s *SupportedSignatureAlgorithms) Marshal() ([]byte, error) {
+ out := make([]byte, supportedSignatureAlgorithmsHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.SignatureHashAlgorithms)*2)))
+ binary.BigEndian.PutUint16(out[4:], uint16(len(s.SignatureHashAlgorithms)*2))
+ for _, v := range s.SignatureHashAlgorithms {
+ out = append(out, []byte{0x00, 0x00}...)
+ out[len(out)-2] = byte(v.Hash)
+ out[len(out)-1] = byte(v.Signature)
+ }
+
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *SupportedSignatureAlgorithms) Unmarshal(data []byte) error {
+ if len(data) <= supportedSignatureAlgorithmsHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ algorithmCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
+ if supportedSignatureAlgorithmsHeaderSize+(algorithmCount*2) > len(data) {
+ return errLengthMismatch
+ }
+ for i := 0; i < algorithmCount; i++ {
+ supportedHashAlgorithm := hash.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)])
+ supportedSignatureAlgorithm := signature.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)+1])
+ if _, ok := hash.Algorithms()[supportedHashAlgorithm]; ok {
+ if _, ok := signature.Algorithms()[supportedSignatureAlgorithm]; ok {
+ s.SignatureHashAlgorithms = append(s.SignatureHashAlgorithms, signaturehash.Algorithm{
+ Hash: supportedHashAlgorithm,
+ Signature: supportedSignatureAlgorithm,
+ })
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go
new file mode 100644
index 0000000..04ddc95
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go
@@ -0,0 +1,45 @@
+package extension
+
+import "encoding/binary"
+
+const (
+ useExtendedMasterSecretHeaderSize = 4
+)
+
+// UseExtendedMasterSecret defines a TLS extension that contextually binds the
+// master secret to a log of the full handshake that computes it, thus
+// preventing MITM attacks.
+type UseExtendedMasterSecret struct {
+ Supported bool
+}
+
+// TypeValue returns the extension TypeValue
+func (u UseExtendedMasterSecret) TypeValue() TypeValue {
+ return UseExtendedMasterSecretTypeValue
+}
+
+// Marshal encodes the extension
+func (u *UseExtendedMasterSecret) Marshal() ([]byte, error) {
+ if !u.Supported {
+ return []byte{}, nil
+ }
+
+ out := make([]byte, useExtendedMasterSecretHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(u.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(0)) // length
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (u *UseExtendedMasterSecret) Unmarshal(data []byte) error {
+ if len(data) < useExtendedMasterSecretHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ u.Supported = true
+
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go
new file mode 100644
index 0000000..729fa3a
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go
@@ -0,0 +1,59 @@
+package extension
+
+import "encoding/binary"
+
+const (
+ useSRTPHeaderSize = 6
+)
+
+// UseSRTP allows a Client/Server to negotiate what SRTPProtectionProfiles
+// they both support
+//
+// https://tools.ietf.org/html/rfc8422
+type UseSRTP struct {
+ ProtectionProfiles []SRTPProtectionProfile
+}
+
+// TypeValue returns the extension TypeValue
+func (u UseSRTP) TypeValue() TypeValue {
+ return UseSRTPTypeValue
+}
+
+// Marshal encodes the extension
+func (u *UseSRTP) Marshal() ([]byte, error) {
+ out := make([]byte, useSRTPHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(u.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(2+(len(u.ProtectionProfiles)*2)+ /* MKI Length */ 1))
+ binary.BigEndian.PutUint16(out[4:], uint16(len(u.ProtectionProfiles)*2))
+
+ for _, v := range u.ProtectionProfiles {
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
+ }
+
+ out = append(out, 0x00) /* MKI Length */
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (u *UseSRTP) Unmarshal(data []byte) error {
+ if len(data) <= useSRTPHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ profileCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
+ if supportedGroupsHeaderSize+(profileCount*2) > len(data) {
+ return errLengthMismatch
+ }
+
+ for i := 0; i < profileCount; i++ {
+ supportedProfile := SRTPProtectionProfile(binary.BigEndian.Uint16(data[(useSRTPHeaderSize + (i * 2)):]))
+ if _, ok := srtpProtectionProfiles()[supportedProfile]; ok {
+ u.ProtectionProfiles = append(u.ProtectionProfiles, supportedProfile)
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go
new file mode 100644
index 0000000..e8fbdea
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go
@@ -0,0 +1,29 @@
+package handshake
+
+import "encoding/binary"
+
+func decodeCipherSuiteIDs(buf []byte) ([]uint16, error) {
+ if len(buf) < 2 {
+ return nil, errBufferTooSmall
+ }
+ cipherSuitesCount := int(binary.BigEndian.Uint16(buf[0:])) / 2
+ rtrn := make([]uint16, cipherSuitesCount)
+ for i := 0; i < cipherSuitesCount; i++ {
+ if len(buf) < (i*2 + 4) {
+ return nil, errBufferTooSmall
+ }
+
+ rtrn[i] = binary.BigEndian.Uint16(buf[(i*2)+2:])
+ }
+ return rtrn, nil
+}
+
+func encodeCipherSuiteIDs(cipherSuiteIDs []uint16) []byte {
+ out := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(cipherSuiteIDs)*2))
+ for _, id := range cipherSuiteIDs {
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], id)
+ }
+ return out
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go
new file mode 100644
index 0000000..ac77c04
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go
@@ -0,0 +1,25 @@
+package handshake
+
+import (
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// Typed errors
+var (
+ errUnableToMarshalFragmented = &protocol.InternalError{Err: errors.New("unable to marshal fragmented handshakes")} //nolint:goerr113
+ errHandshakeMessageUnset = &protocol.InternalError{Err: errors.New("handshake message unset, unable to marshal")} //nolint:goerr113
+ errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
+ errInvalidClientKeyExchange = &protocol.FatalError{Err: errors.New("unable to determine if ClientKeyExchange is a public key or PSK Identity")} //nolint:goerr113
+ errInvalidHashAlgorithm = &protocol.FatalError{Err: errors.New("invalid hash algorithm")} //nolint:goerr113
+ errInvalidSignatureAlgorithm = &protocol.FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113
+ errCookieTooLong = &protocol.FatalError{Err: errors.New("cookie must not be longer then 255 bytes")} //nolint:goerr113
+ errInvalidEllipticCurveType = &protocol.FatalError{Err: errors.New("invalid or unknown elliptic curve type")} //nolint:goerr113
+ errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:goerr113
+ errCipherSuiteUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a cipher suite")} //nolint:goerr113
+ errCompressionMethodUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a compression method")} //nolint:goerr113
+ errInvalidCompressionMethod = &protocol.FatalError{Err: errors.New("invalid or unknown compression method")} //nolint:goerr113
+ errNotImplemented = &protocol.InternalError{Err: errors.New("feature has not been implemented yet")} //nolint:goerr113
+)
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go
new file mode 100644
index 0000000..4aa493e
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go
@@ -0,0 +1,145 @@
+// Package handshake provides the DTLS wire protocol for handshakes
+package handshake
+
+import (
+ "github.com/pion/dtls/v2/internal/util"
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// Type is the unique identifier for each handshake message
+// https://tools.ietf.org/html/rfc5246#section-7.4
+type Type uint8
+
+// Types of DTLS Handshake messages we know about
+const (
+ TypeHelloRequest Type = 0
+ TypeClientHello Type = 1
+ TypeServerHello Type = 2
+ TypeHelloVerifyRequest Type = 3
+ TypeCertificate Type = 11
+ TypeServerKeyExchange Type = 12
+ TypeCertificateRequest Type = 13
+ TypeServerHelloDone Type = 14
+ TypeCertificateVerify Type = 15
+ TypeClientKeyExchange Type = 16
+ TypeFinished Type = 20
+)
+
+// String returns the string representation of this type
+func (t Type) String() string {
+ switch t {
+ case TypeHelloRequest:
+ return "HelloRequest"
+ case TypeClientHello:
+ return "ClientHello"
+ case TypeServerHello:
+ return "ServerHello"
+ case TypeHelloVerifyRequest:
+ return "HelloVerifyRequest"
+ case TypeCertificate:
+ return "TypeCertificate"
+ case TypeServerKeyExchange:
+ return "ServerKeyExchange"
+ case TypeCertificateRequest:
+ return "CertificateRequest"
+ case TypeServerHelloDone:
+ return "ServerHelloDone"
+ case TypeCertificateVerify:
+ return "CertificateVerify"
+ case TypeClientKeyExchange:
+ return "ClientKeyExchange"
+ case TypeFinished:
+ return "Finished"
+ }
+ return ""
+}
+
+// Message is the body of a Handshake datagram
+type Message interface {
+ Marshal() ([]byte, error)
+ Unmarshal(data []byte) error
+
+ Type() Type
+}
+
+// Handshake protocol is responsible for selecting a cipher spec and
+// generating a master secret, which together comprise the primary
+// cryptographic parameters associated with a secure session. The
+// handshake protocol can also optionally authenticate parties who have
+// certificates signed by a trusted certificate authority.
+// https://tools.ietf.org/html/rfc5246#section-7.3
+type Handshake struct {
+ Header Header
+ Message Message
+}
+
+// ContentType returns what kind of content this message is carying
+func (h Handshake) ContentType() protocol.ContentType {
+ return protocol.ContentTypeHandshake
+}
+
+// Marshal encodes a handshake into a binary message
+func (h *Handshake) Marshal() ([]byte, error) {
+ if h.Message == nil {
+ return nil, errHandshakeMessageUnset
+ } else if h.Header.FragmentOffset != 0 {
+ return nil, errUnableToMarshalFragmented
+ }
+
+ msg, err := h.Message.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ h.Header.Length = uint32(len(msg))
+ h.Header.FragmentLength = h.Header.Length
+ h.Header.Type = h.Message.Type()
+ header, err := h.Header.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(header, msg...), nil
+}
+
+// Unmarshal decodes a handshake from a binary message
+func (h *Handshake) Unmarshal(data []byte) error {
+ if err := h.Header.Unmarshal(data); err != nil {
+ return err
+ }
+
+ reportedLen := util.BigEndianUint24(data[1:])
+ if uint32(len(data)-HeaderLength) != reportedLen {
+ return errLengthMismatch
+ } else if reportedLen != h.Header.FragmentLength {
+ return errLengthMismatch
+ }
+
+ switch Type(data[0]) {
+ case TypeHelloRequest:
+ return errNotImplemented
+ case TypeClientHello:
+ h.Message = &MessageClientHello{}
+ case TypeHelloVerifyRequest:
+ h.Message = &MessageHelloVerifyRequest{}
+ case TypeServerHello:
+ h.Message = &MessageServerHello{}
+ case TypeCertificate:
+ h.Message = &MessageCertificate{}
+ case TypeServerKeyExchange:
+ h.Message = &MessageServerKeyExchange{}
+ case TypeCertificateRequest:
+ h.Message = &MessageCertificateRequest{}
+ case TypeServerHelloDone:
+ h.Message = &MessageServerHelloDone{}
+ case TypeClientKeyExchange:
+ h.Message = &MessageClientKeyExchange{}
+ case TypeFinished:
+ h.Message = &MessageFinished{}
+ case TypeCertificateVerify:
+ h.Message = &MessageCertificateVerify{}
+ default:
+ return errNotImplemented
+ }
+ return h.Message.Unmarshal(data[HeaderLength:])
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go
new file mode 100644
index 0000000..cb6a224
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go
@@ -0,0 +1,50 @@
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/internal/util"
+)
+
+// HeaderLength msg_len for Handshake messages assumes an extra
+// 12 bytes for sequence, fragment and version information vs TLS
+const HeaderLength = 12
+
+// Header is the static first 12 bytes of each RecordLayer
+// of type Handshake. These fields allow us to support message loss, reordering, and
+// message fragmentation,
+//
+// https://tools.ietf.org/html/rfc6347#section-4.2.2
+type Header struct {
+ Type Type
+ Length uint32 // uint24 in spec
+ MessageSequence uint16
+ FragmentOffset uint32 // uint24 in spec
+ FragmentLength uint32 // uint24 in spec
+}
+
+// Marshal encodes the Header
+func (h *Header) Marshal() ([]byte, error) {
+ out := make([]byte, HeaderLength)
+
+ out[0] = byte(h.Type)
+ util.PutBigEndianUint24(out[1:], h.Length)
+ binary.BigEndian.PutUint16(out[4:], h.MessageSequence)
+ util.PutBigEndianUint24(out[6:], h.FragmentOffset)
+ util.PutBigEndianUint24(out[9:], h.FragmentLength)
+ return out, nil
+}
+
+// Unmarshal populates the header from encoded data
+func (h *Header) Unmarshal(data []byte) error {
+ if len(data) < HeaderLength {
+ return errBufferTooSmall
+ }
+
+ h.Type = Type(data[0])
+ h.Length = util.BigEndianUint24(data[1:])
+ h.MessageSequence = binary.BigEndian.Uint16(data[4:])
+ h.FragmentOffset = util.BigEndianUint24(data[6:])
+ h.FragmentLength = util.BigEndianUint24(data[9:])
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go
new file mode 100644
index 0000000..05fb746
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go
@@ -0,0 +1,66 @@
+package handshake
+
+import (
+ "github.com/pion/dtls/v2/internal/util"
+)
+
+// MessageCertificate is a DTLS Handshake Message
+// it can contain either a Client or Server Certificate
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.2
+type MessageCertificate struct {
+ Certificate [][]byte
+}
+
+// Type returns the Handshake Type
+func (m MessageCertificate) Type() Type {
+ return TypeCertificate
+}
+
+const (
+ handshakeMessageCertificateLengthFieldSize = 3
+)
+
+// Marshal encodes the Handshake
+func (m *MessageCertificate) Marshal() ([]byte, error) {
+ out := make([]byte, handshakeMessageCertificateLengthFieldSize)
+
+ for _, r := range m.Certificate {
+ // Certificate Length
+ out = append(out, make([]byte, handshakeMessageCertificateLengthFieldSize)...)
+ util.PutBigEndianUint24(out[len(out)-handshakeMessageCertificateLengthFieldSize:], uint32(len(r)))
+
+ // Certificate body
+ out = append(out, append([]byte{}, r...)...)
+ }
+
+ // Total Payload Size
+ util.PutBigEndianUint24(out[0:], uint32(len(out[handshakeMessageCertificateLengthFieldSize:])))
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageCertificate) Unmarshal(data []byte) error {
+ if len(data) < handshakeMessageCertificateLengthFieldSize {
+ return errBufferTooSmall
+ }
+
+ if certificateBodyLen := int(util.BigEndianUint24(data)); certificateBodyLen+handshakeMessageCertificateLengthFieldSize != len(data) {
+ return errLengthMismatch
+ }
+
+ offset := handshakeMessageCertificateLengthFieldSize
+ for offset < len(data) {
+ certificateLen := int(util.BigEndianUint24(data[offset:]))
+ offset += handshakeMessageCertificateLengthFieldSize
+
+ if offset+certificateLen > len(data) {
+ return errLengthMismatch
+ }
+
+ m.Certificate = append(m.Certificate, append([]byte{}, data[offset:offset+certificateLen]...))
+ offset += certificateLen
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go
new file mode 100644
index 0000000..e711f39
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go
@@ -0,0 +1,100 @@
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+)
+
+/*
+MessageCertificateRequest is so a non-anonymous server can optionally
+request a certificate from the client, if appropriate for the selected cipher
+suite. This message, if sent, will immediately follow the ServerKeyExchange
+message (if it is sent; otherwise, this message follows the
+server's Certificate message).
+
+https://tools.ietf.org/html/rfc5246#section-7.4.4
+*/
+type MessageCertificateRequest struct {
+ CertificateTypes []clientcertificate.Type
+ SignatureHashAlgorithms []signaturehash.Algorithm
+}
+
+const (
+ messageCertificateRequestMinLength = 5
+)
+
+// Type returns the Handshake Type
+func (m MessageCertificateRequest) Type() Type {
+ return TypeCertificateRequest
+}
+
+// Marshal encodes the Handshake
+func (m *MessageCertificateRequest) Marshal() ([]byte, error) {
+ out := []byte{byte(len(m.CertificateTypes))}
+ for _, v := range m.CertificateTypes {
+ out = append(out, byte(v))
+ }
+
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.SignatureHashAlgorithms)*2))
+ for _, v := range m.SignatureHashAlgorithms {
+ out = append(out, byte(v.Hash))
+ out = append(out, byte(v.Signature))
+ }
+
+ out = append(out, []byte{0x00, 0x00}...) // Distinguished Names Length
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageCertificateRequest) Unmarshal(data []byte) error {
+ if len(data) < messageCertificateRequestMinLength {
+ return errBufferTooSmall
+ }
+
+ offset := 0
+ certificateTypesLength := int(data[0])
+ offset++
+
+ if (offset + certificateTypesLength) > len(data) {
+ return errBufferTooSmall
+ }
+
+ for i := 0; i < certificateTypesLength; i++ {
+ certType := clientcertificate.Type(data[offset+i])
+ if _, ok := clientcertificate.Types()[certType]; ok {
+ m.CertificateTypes = append(m.CertificateTypes, certType)
+ }
+ }
+ offset += certificateTypesLength
+ if len(data) < offset+2 {
+ return errBufferTooSmall
+ }
+ signatureHashAlgorithmsLength := int(binary.BigEndian.Uint16(data[offset:]))
+ offset += 2
+
+ if (offset + signatureHashAlgorithmsLength) > len(data) {
+ return errBufferTooSmall
+ }
+
+ for i := 0; i < signatureHashAlgorithmsLength; i += 2 {
+ if len(data) < (offset + i + 2) {
+ return errBufferTooSmall
+ }
+ h := hash.Algorithm(data[offset+i])
+ s := signature.Algorithm(data[offset+i+1])
+
+ if _, ok := hash.Algorithms()[h]; !ok {
+ continue
+ } else if _, ok := signature.Algorithms()[s]; !ok {
+ continue
+ }
+ m.SignatureHashAlgorithms = append(m.SignatureHashAlgorithms, signaturehash.Algorithm{Signature: s, Hash: h})
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go
new file mode 100644
index 0000000..fb5e463
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go
@@ -0,0 +1,61 @@
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+)
+
+// MessageCertificateVerify provide explicit verification of a
+// client certificate.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.8
+type MessageCertificateVerify struct {
+ HashAlgorithm hash.Algorithm
+ SignatureAlgorithm signature.Algorithm
+ Signature []byte
+}
+
+const handshakeMessageCertificateVerifyMinLength = 4
+
+// Type returns the Handshake Type
+func (m MessageCertificateVerify) Type() Type {
+ return TypeCertificateVerify
+}
+
+// Marshal encodes the Handshake
+func (m *MessageCertificateVerify) Marshal() ([]byte, error) {
+ out := make([]byte, 1+1+2+len(m.Signature))
+
+ out[0] = byte(m.HashAlgorithm)
+ out[1] = byte(m.SignatureAlgorithm)
+ binary.BigEndian.PutUint16(out[2:], uint16(len(m.Signature)))
+ copy(out[4:], m.Signature)
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageCertificateVerify) Unmarshal(data []byte) error {
+ if len(data) < handshakeMessageCertificateVerifyMinLength {
+ return errBufferTooSmall
+ }
+
+ m.HashAlgorithm = hash.Algorithm(data[0])
+ if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok {
+ return errInvalidHashAlgorithm
+ }
+
+ m.SignatureAlgorithm = signature.Algorithm(data[1])
+ if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok {
+ return errInvalidSignatureAlgorithm
+ }
+
+ signatureLength := int(binary.BigEndian.Uint16(data[2:]))
+ if (signatureLength + 4) != len(data) {
+ return errBufferTooSmall
+ }
+
+ m.Signature = append([]byte{}, data[4:]...)
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go
new file mode 100644
index 0000000..cb8a100
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go
@@ -0,0 +1,125 @@
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+)
+
+/*
+MessageClientHello is for when a client first connects to a server it is
+required to send the client hello as its first message. The client can also send a
+client hello in response to a hello request or on its own
+initiative in order to renegotiate the security parameters in an
+existing connection.
+*/
+type MessageClientHello struct {
+ Version protocol.Version
+ Random Random
+ Cookie []byte
+
+ CipherSuiteIDs []uint16
+ CompressionMethods []*protocol.CompressionMethod
+ Extensions []extension.Extension
+}
+
+const handshakeMessageClientHelloVariableWidthStart = 34
+
+// Type returns the Handshake Type
+func (m MessageClientHello) Type() Type {
+ return TypeClientHello
+}
+
+// Marshal encodes the Handshake
+func (m *MessageClientHello) Marshal() ([]byte, error) {
+ if len(m.Cookie) > 255 {
+ return nil, errCookieTooLong
+ }
+
+ out := make([]byte, handshakeMessageClientHelloVariableWidthStart)
+ out[0] = m.Version.Major
+ out[1] = m.Version.Minor
+
+ rand := m.Random.MarshalFixed()
+ copy(out[2:], rand[:])
+
+ out = append(out, 0x00) // SessionID
+
+ out = append(out, byte(len(m.Cookie)))
+ out = append(out, m.Cookie...)
+ out = append(out, encodeCipherSuiteIDs(m.CipherSuiteIDs)...)
+ out = append(out, protocol.EncodeCompressionMethods(m.CompressionMethods)...)
+
+ extensions, err := extension.Marshal(m.Extensions)
+ if err != nil {
+ return nil, err
+ }
+
+ return append(out, extensions...), nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageClientHello) Unmarshal(data []byte) error {
+ if len(data) < 2+RandomLength {
+ return errBufferTooSmall
+ }
+
+ m.Version.Major = data[0]
+ m.Version.Minor = data[1]
+
+ var random [RandomLength]byte
+ copy(random[:], data[2:])
+ m.Random.UnmarshalFixed(random)
+
+ // rest of packet has variable width sections
+ currOffset := handshakeMessageClientHelloVariableWidthStart
+ currOffset += int(data[currOffset]) + 1 // SessionID
+
+ currOffset++
+ if len(data) <= currOffset {
+ return errBufferTooSmall
+ }
+ n := int(data[currOffset-1])
+ if len(data) <= currOffset+n {
+ return errBufferTooSmall
+ }
+ m.Cookie = append([]byte{}, data[currOffset:currOffset+n]...)
+ currOffset += len(m.Cookie)
+
+ // Cipher Suites
+ if len(data) < currOffset {
+ return errBufferTooSmall
+ }
+ cipherSuiteIDs, err := decodeCipherSuiteIDs(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.CipherSuiteIDs = cipherSuiteIDs
+ if len(data) < currOffset+2 {
+ return errBufferTooSmall
+ }
+ currOffset += int(binary.BigEndian.Uint16(data[currOffset:])) + 2
+
+ // Compression Methods
+ if len(data) < currOffset {
+ return errBufferTooSmall
+ }
+ compressionMethods, err := protocol.DecodeCompressionMethods(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.CompressionMethods = compressionMethods
+ if len(data) < currOffset {
+ return errBufferTooSmall
+ }
+ currOffset += int(data[currOffset]) + 1
+
+ // Extensions
+ extensions, err := extension.Unmarshal(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.Extensions = extensions
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go
new file mode 100644
index 0000000..f8fc369
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go
@@ -0,0 +1,56 @@
+package handshake
+
+import (
+ "encoding/binary"
+)
+
+// MessageClientKeyExchange is a DTLS Handshake Message
+// With this message, the premaster secret is set, either by direct
+// transmission of the RSA-encrypted secret or by the transmission of
+// Diffie-Hellman parameters that will allow each side to agree upon
+// the same premaster secret.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.7
+type MessageClientKeyExchange struct {
+ IdentityHint []byte
+ PublicKey []byte
+}
+
+// Type returns the Handshake Type
+func (m MessageClientKeyExchange) Type() Type {
+ return TypeClientKeyExchange
+}
+
+// Marshal encodes the Handshake
+func (m *MessageClientKeyExchange) Marshal() ([]byte, error) {
+ switch {
+ case (m.IdentityHint != nil && m.PublicKey != nil) || (m.IdentityHint == nil && m.PublicKey == nil):
+ return nil, errInvalidClientKeyExchange
+ case m.PublicKey != nil:
+ return append([]byte{byte(len(m.PublicKey))}, m.PublicKey...), nil
+ default:
+ out := append([]byte{0x00, 0x00}, m.IdentityHint...)
+ binary.BigEndian.PutUint16(out, uint16(len(out)-2))
+ return out, nil
+ }
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageClientKeyExchange) Unmarshal(data []byte) error {
+ if len(data) < 2 {
+ return errBufferTooSmall
+ }
+
+ // If parsed as PSK return early and only populate PSK Identity Hint
+ if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) {
+ m.IdentityHint = append([]byte{}, data[2:]...)
+ return nil
+ }
+
+ if publicKeyLength := int(data[0]); len(data) != publicKeyLength+1 {
+ return errBufferTooSmall
+ }
+
+ m.PublicKey = append([]byte{}, data[1:]...)
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go
new file mode 100644
index 0000000..c65d42a
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go
@@ -0,0 +1,27 @@
+package handshake
+
+// MessageFinished is a DTLS Handshake Message
+// this message is the first one protected with the just
+// negotiated algorithms, keys, and secrets. Recipients of Finished
+// messages MUST verify that the contents are correct.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.9
+type MessageFinished struct {
+ VerifyData []byte
+}
+
+// Type returns the Handshake Type
+func (m MessageFinished) Type() Type {
+ return TypeFinished
+}
+
+// Marshal encodes the Handshake
+func (m *MessageFinished) Marshal() ([]byte, error) {
+ return append([]byte{}, m.VerifyData...), nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageFinished) Unmarshal(data []byte) error {
+ m.VerifyData = append([]byte{}, data...)
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go
new file mode 100644
index 0000000..ef834dc
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go
@@ -0,0 +1,62 @@
+package handshake
+
+import (
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// MessageHelloVerifyRequest is as follows:
+//
+// struct {
+// ProtocolVersion server_version;
+// opaque cookie<0..2^8-1>;
+// } HelloVerifyRequest;
+//
+// The HelloVerifyRequest message type is hello_verify_request(3).
+//
+// When the client sends its ClientHello message to the server, the server
+// MAY respond with a HelloVerifyRequest message. This message contains
+// a stateless cookie generated using the technique of [PHOTURIS]. The
+// client MUST retransmit the ClientHello with the cookie added.
+//
+// https://tools.ietf.org/html/rfc6347#section-4.2.1
+type MessageHelloVerifyRequest struct {
+ Version protocol.Version
+ Cookie []byte
+}
+
+// Type returns the Handshake Type
+func (m MessageHelloVerifyRequest) Type() Type {
+ return TypeHelloVerifyRequest
+}
+
+// Marshal encodes the Handshake
+func (m *MessageHelloVerifyRequest) Marshal() ([]byte, error) {
+ if len(m.Cookie) > 255 {
+ return nil, errCookieTooLong
+ }
+
+ out := make([]byte, 3+len(m.Cookie))
+ out[0] = m.Version.Major
+ out[1] = m.Version.Minor
+ out[2] = byte(len(m.Cookie))
+ copy(out[3:], m.Cookie)
+
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageHelloVerifyRequest) Unmarshal(data []byte) error {
+ if len(data) < 3 {
+ return errBufferTooSmall
+ }
+ m.Version.Major = data[0]
+ m.Version.Minor = data[1]
+ cookieLength := data[2]
+ if len(data) < (int(cookieLength) + 3) {
+ return errBufferTooSmall
+ }
+ m.Cookie = make([]byte, cookieLength)
+
+ copy(m.Cookie, data[3:3+cookieLength])
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go
new file mode 100644
index 0000000..c4b181f
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go
@@ -0,0 +1,106 @@
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+)
+
+// MessageServerHello is sent in response to a ClientHello
+// message when it was able to find an acceptable set of algorithms.
+// If it cannot find such a match, it will respond with a handshake
+// failure alert.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.1.3
+type MessageServerHello struct {
+ Version protocol.Version
+ Random Random
+
+ CipherSuiteID *uint16
+ CompressionMethod *protocol.CompressionMethod
+ Extensions []extension.Extension
+}
+
+const messageServerHelloVariableWidthStart = 2 + RandomLength
+
+// Type returns the Handshake Type
+func (m MessageServerHello) Type() Type {
+ return TypeServerHello
+}
+
+// Marshal encodes the Handshake
+func (m *MessageServerHello) Marshal() ([]byte, error) {
+ if m.CipherSuiteID == nil {
+ return nil, errCipherSuiteUnset
+ } else if m.CompressionMethod == nil {
+ return nil, errCompressionMethodUnset
+ }
+
+ out := make([]byte, messageServerHelloVariableWidthStart)
+ out[0] = m.Version.Major
+ out[1] = m.Version.Minor
+
+ rand := m.Random.MarshalFixed()
+ copy(out[2:], rand[:])
+
+ out = append(out, 0x00) // SessionID
+
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], *m.CipherSuiteID)
+
+ out = append(out, byte(m.CompressionMethod.ID))
+
+ extensions, err := extension.Marshal(m.Extensions)
+ if err != nil {
+ return nil, err
+ }
+
+ return append(out, extensions...), nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageServerHello) Unmarshal(data []byte) error {
+ if len(data) < 2+RandomLength {
+ return errBufferTooSmall
+ }
+
+ m.Version.Major = data[0]
+ m.Version.Minor = data[1]
+
+ var random [RandomLength]byte
+ copy(random[:], data[2:])
+ m.Random.UnmarshalFixed(random)
+
+ currOffset := messageServerHelloVariableWidthStart
+ currOffset += int(data[currOffset]) + 1 // SessionID
+ if len(data) < (currOffset + 2) {
+ return errBufferTooSmall
+ }
+
+ m.CipherSuiteID = new(uint16)
+ *m.CipherSuiteID = binary.BigEndian.Uint16(data[currOffset:])
+ currOffset += 2
+
+ if len(data) < currOffset {
+ return errBufferTooSmall
+ }
+ if compressionMethod, ok := protocol.CompressionMethods()[protocol.CompressionMethodID(data[currOffset])]; ok {
+ m.CompressionMethod = compressionMethod
+ currOffset++
+ } else {
+ return errInvalidCompressionMethod
+ }
+
+ if len(data) <= currOffset {
+ m.Extensions = []extension.Extension{}
+ return nil
+ }
+
+ extensions, err := extension.Unmarshal(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.Extensions = extensions
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go
new file mode 100644
index 0000000..a004802
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go
@@ -0,0 +1,22 @@
+package handshake
+
+// MessageServerHelloDone is final non-encrypted message from server
+// this communicates server has sent all its handshake messages and next
+// should be MessageFinished
+type MessageServerHelloDone struct {
+}
+
+// Type returns the Handshake Type
+func (m MessageServerHelloDone) Type() Type {
+ return TypeServerHelloDone
+}
+
+// Marshal encodes the Handshake
+func (m *MessageServerHelloDone) Marshal() ([]byte, error) {
+ return []byte{}, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageServerHelloDone) Unmarshal(data []byte) error {
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go
new file mode 100644
index 0000000..4148fe0
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go
@@ -0,0 +1,119 @@
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+)
+
+// MessageServerKeyExchange supports ECDH and PSK
+type MessageServerKeyExchange struct {
+ IdentityHint []byte
+
+ EllipticCurveType elliptic.CurveType
+ NamedCurve elliptic.Curve
+ PublicKey []byte
+ HashAlgorithm hash.Algorithm
+ SignatureAlgorithm signature.Algorithm
+ Signature []byte
+}
+
+// Type returns the Handshake Type
+func (m MessageServerKeyExchange) Type() Type {
+ return TypeServerKeyExchange
+}
+
+// Marshal encodes the Handshake
+func (m *MessageServerKeyExchange) Marshal() ([]byte, error) {
+ if m.IdentityHint != nil {
+ out := append([]byte{0x00, 0x00}, m.IdentityHint...)
+ binary.BigEndian.PutUint16(out, uint16(len(out)-2))
+ return out, nil
+ }
+
+ out := []byte{byte(m.EllipticCurveType), 0x00, 0x00}
+ binary.BigEndian.PutUint16(out[1:], uint16(m.NamedCurve))
+
+ out = append(out, byte(len(m.PublicKey)))
+ out = append(out, m.PublicKey...)
+
+ if m.HashAlgorithm == hash.None && m.SignatureAlgorithm == signature.Anonymous && len(m.Signature) == 0 {
+ return out, nil
+ }
+
+ out = append(out, []byte{byte(m.HashAlgorithm), byte(m.SignatureAlgorithm), 0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.Signature)))
+ out = append(out, m.Signature...)
+
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageServerKeyExchange) Unmarshal(data []byte) error {
+ if len(data) < 2 {
+ return errBufferTooSmall
+ }
+
+ // If parsed as PSK return early and only populate PSK Identity Hint
+ if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) {
+ m.IdentityHint = append([]byte{}, data[2:]...)
+ return nil
+ }
+
+ if _, ok := elliptic.CurveTypes()[elliptic.CurveType(data[0])]; ok {
+ m.EllipticCurveType = elliptic.CurveType(data[0])
+ } else {
+ return errInvalidEllipticCurveType
+ }
+
+ if len(data[1:]) < 2 {
+ return errBufferTooSmall
+ }
+ m.NamedCurve = elliptic.Curve(binary.BigEndian.Uint16(data[1:3]))
+ if _, ok := elliptic.Curves()[m.NamedCurve]; !ok {
+ return errInvalidNamedCurve
+ }
+ if len(data) < 4 {
+ return errBufferTooSmall
+ }
+
+ publicKeyLength := int(data[3])
+ offset := 4 + publicKeyLength
+ if len(data) < offset {
+ return errBufferTooSmall
+ }
+ m.PublicKey = append([]byte{}, data[4:offset]...)
+
+ // Anon connection doesn't contains hashAlgorithm, signatureAlgorithm, signature
+ if len(data) == offset {
+ return nil
+ } else if len(data) <= offset {
+ return errBufferTooSmall
+ }
+
+ m.HashAlgorithm = hash.Algorithm(data[offset])
+ if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok {
+ return errInvalidHashAlgorithm
+ }
+ offset++
+ if len(data) <= offset {
+ return errBufferTooSmall
+ }
+ m.SignatureAlgorithm = signature.Algorithm(data[offset])
+ if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok {
+ return errInvalidSignatureAlgorithm
+ }
+ offset++
+ if len(data) < offset+2 {
+ return errBufferTooSmall
+ }
+ signatureLength := int(binary.BigEndian.Uint16(data[offset:]))
+ offset += 2
+ if len(data) < offset+signatureLength {
+ return errBufferTooSmall
+ }
+ m.Signature = append([]byte{}, data[offset:offset+signatureLength]...)
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go
new file mode 100644
index 0000000..0ade936
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go
@@ -0,0 +1,49 @@
+package handshake
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "time"
+)
+
+// Consts for Random in Handshake
+const (
+ RandomBytesLength = 28
+ RandomLength = RandomBytesLength + 4
+)
+
+// Random value that is used in ClientHello and ServerHello
+//
+// https://tools.ietf.org/html/rfc4346#section-7.4.1.2
+type Random struct {
+ GMTUnixTime time.Time
+ RandomBytes [RandomBytesLength]byte
+}
+
+// MarshalFixed encodes the Handshake
+func (r *Random) MarshalFixed() [RandomLength]byte {
+ var out [RandomLength]byte
+
+ binary.BigEndian.PutUint32(out[0:], uint32(r.GMTUnixTime.Unix()))
+ copy(out[4:], r.RandomBytes[:])
+
+ return out
+}
+
+// UnmarshalFixed populates the message from encoded data
+func (r *Random) UnmarshalFixed(data [RandomLength]byte) {
+ r.GMTUnixTime = time.Unix(int64(binary.BigEndian.Uint32(data[0:])), 0)
+ copy(r.RandomBytes[:], data[4:])
+}
+
+// Populate fills the handshakeRandom with random values
+// may be called multiple times
+func (r *Random) Populate() error {
+ r.GMTUnixTime = time.Now()
+
+ tmp := make([]byte, RandomBytesLength)
+ _, err := rand.Read(tmp)
+ copy(r.RandomBytes[:], tmp)
+
+ return err
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go
new file mode 100644
index 0000000..7033d40
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go
@@ -0,0 +1,16 @@
+// Package recordlayer implements the TLS Record Layer https://tools.ietf.org/html/rfc5246#section-6
+package recordlayer
+
+import (
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var (
+ errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errInvalidPacketLength = &protocol.TemporaryError{Err: errors.New("packet length and declared length do not match")} //nolint:goerr113
+ errSequenceNumberOverflow = &protocol.InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
+ errUnsupportedProtocolVersion = &protocol.FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
+ errInvalidContentType = &protocol.TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
+)
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go
new file mode 100644
index 0000000..65047d7
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go
@@ -0,0 +1,61 @@
+package recordlayer
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/internal/util"
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// Header implements a TLS RecordLayer header
+type Header struct {
+ ContentType protocol.ContentType
+ ContentLen uint16
+ Version protocol.Version
+ Epoch uint16
+ SequenceNumber uint64 // uint48 in spec
+}
+
+// RecordLayer enums
+const (
+ HeaderSize = 13
+ MaxSequenceNumber = 0x0000FFFFFFFFFFFF
+)
+
+// Marshal encodes a TLS RecordLayer Header to binary
+func (h *Header) Marshal() ([]byte, error) {
+ if h.SequenceNumber > MaxSequenceNumber {
+ return nil, errSequenceNumberOverflow
+ }
+
+ out := make([]byte, HeaderSize)
+ out[0] = byte(h.ContentType)
+ out[1] = h.Version.Major
+ out[2] = h.Version.Minor
+ binary.BigEndian.PutUint16(out[3:], h.Epoch)
+ util.PutBigEndianUint48(out[5:], h.SequenceNumber)
+ binary.BigEndian.PutUint16(out[HeaderSize-2:], h.ContentLen)
+ return out, nil
+}
+
+// Unmarshal populates a TLS RecordLayer Header from binary
+func (h *Header) Unmarshal(data []byte) error {
+ if len(data) < HeaderSize {
+ return errBufferTooSmall
+ }
+ h.ContentType = protocol.ContentType(data[0])
+ h.Version.Major = data[1]
+ h.Version.Minor = data[2]
+ h.Epoch = binary.BigEndian.Uint16(data[3:])
+
+ // SequenceNumber is stored as uint48, make into uint64
+ seqCopy := make([]byte, 8)
+ copy(seqCopy[2:], data[5:11])
+ h.SequenceNumber = binary.BigEndian.Uint64(seqCopy)
+
+ if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
+ return errUnsupportedProtocolVersion
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go
new file mode 100644
index 0000000..67e5a72
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go
@@ -0,0 +1,99 @@
+package recordlayer
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+)
+
+// RecordLayer which handles all data transport.
+// The record layer is assumed to sit directly on top of some
+// reliable transport such as TCP. The record layer can carry four types of content:
+//
+// 1. Handshake messages—used for algorithm negotiation and key establishment.
+// 2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message.
+// 3. Alert messages—used to signal that errors have occurred
+// 4. Application layer data
+//
+// The DTLS record layer is extremely similar to that of TLS 1.1. The
+// only change is the inclusion of an explicit sequence number in the
+// record. This sequence number allows the recipient to correctly
+// verify the TLS MAC.
+//
+// https://tools.ietf.org/html/rfc4347#section-4.1
+type RecordLayer struct {
+ Header Header
+ Content protocol.Content
+}
+
+// Marshal encodes the RecordLayer to binary
+func (r *RecordLayer) Marshal() ([]byte, error) {
+ contentRaw, err := r.Content.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ r.Header.ContentLen = uint16(len(contentRaw))
+ r.Header.ContentType = r.Content.ContentType()
+
+ headerRaw, err := r.Header.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(headerRaw, contentRaw...), nil
+}
+
+// Unmarshal populates the RecordLayer from binary
+func (r *RecordLayer) Unmarshal(data []byte) error {
+ if len(data) < HeaderSize {
+ return errBufferTooSmall
+ }
+ if err := r.Header.Unmarshal(data); err != nil {
+ return err
+ }
+
+ switch protocol.ContentType(data[0]) {
+ case protocol.ContentTypeChangeCipherSpec:
+ r.Content = &protocol.ChangeCipherSpec{}
+ case protocol.ContentTypeAlert:
+ r.Content = &alert.Alert{}
+ case protocol.ContentTypeHandshake:
+ r.Content = &handshake.Handshake{}
+ case protocol.ContentTypeApplicationData:
+ r.Content = &protocol.ApplicationData{}
+ default:
+ return errInvalidContentType
+ }
+
+ return r.Content.Unmarshal(data[HeaderSize:])
+}
+
+// UnpackDatagram extracts all RecordLayer messages from a single datagram.
+// Note that as with TLS, multiple handshake messages may be placed in
+// the same DTLS record, provided that there is room and that they are
+// part of the same flight. Thus, there are two acceptable ways to pack
+// two DTLS messages into the same datagram: in the same record or in
+// separate records.
+// https://tools.ietf.org/html/rfc6347#section-4.2.3
+func UnpackDatagram(buf []byte) ([][]byte, error) {
+ out := [][]byte{}
+
+ for offset := 0; len(buf) != offset; {
+ if len(buf)-offset <= HeaderSize {
+ return nil, errInvalidPacketLength
+ }
+
+ pktLen := (HeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:])))
+ if offset+pktLen > len(buf) {
+ return nil, errInvalidPacketLength
+ }
+
+ out = append(out, buf[offset:offset+pktLen])
+ offset += pktLen
+ }
+
+ return out, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go
new file mode 100644
index 0000000..d5ddb1d
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go
@@ -0,0 +1,21 @@
+// Package protocol provides the DTLS wire format
+package protocol
+
+// Version enums
+var (
+ Version1_0 = Version{Major: 0xfe, Minor: 0xff} //nolint:gochecknoglobals
+ Version1_2 = Version{Major: 0xfe, Minor: 0xfd} //nolint:gochecknoglobals
+)
+
+// Version is the minor/major value in the RecordLayer
+// and ClientHello/ServerHello
+//
+// https://tools.ietf.org/html/rfc4346#section-6.2.1
+type Version struct {
+ Major, Minor uint8
+}
+
+// Equal determines if two protocol versions are equal
+func (v Version) Equal(x Version) bool {
+ return v.Major == x.Major && v.Minor == x.Minor
+}
diff --git a/vendor/github.com/pion/dtls/v2/renovate.json b/vendor/github.com/pion/dtls/v2/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/dtls/v2/resume.go b/vendor/github.com/pion/dtls/v2/resume.go
new file mode 100644
index 0000000..40e55e4
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/resume.go
@@ -0,0 +1,19 @@
+package dtls
+
+import (
+ "context"
+ "net"
+)
+
+// Resume imports an already established dtls connection using a specific dtls state
+func Resume(state *State, conn net.Conn, config *Config) (*Conn, error) {
+ if err := state.initCipherSuite(); err != nil {
+ return nil, err
+ }
+ c, err := createConn(context.Background(), conn, config, state.isClient, state)
+ if err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
diff --git a/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go b/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go
new file mode 100644
index 0000000..1c3ae55
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go
@@ -0,0 +1,14 @@
+package dtls
+
+import "github.com/pion/dtls/v2/pkg/protocol/extension"
+
+// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing
+// https://tools.ietf.org/html/rfc5764#section-4.1.2
+type SRTPProtectionProfile = extension.SRTPProtectionProfile
+
+const (
+ SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_80 // nolint
+ SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_32 // nolint
+ SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_128_GCM // nolint
+ SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_256_GCM // nolint
+)
diff --git a/vendor/github.com/pion/dtls/v2/state.go b/vendor/github.com/pion/dtls/v2/state.go
new file mode 100644
index 0000000..53fca6a
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/state.go
@@ -0,0 +1,194 @@
+package dtls
+
+import (
+ "bytes"
+ "encoding/gob"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/transport/replaydetector"
+)
+
+// State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
+type State struct {
+ localEpoch, remoteEpoch atomic.Value
+ localSequenceNumber []uint64 // uint48
+ localRandom, remoteRandom handshake.Random
+ masterSecret []byte
+ cipherSuite CipherSuite // nil if a cipherSuite hasn't been chosen
+
+ srtpProtectionProfile SRTPProtectionProfile // Negotiated SRTPProtectionProfile
+ PeerCertificates [][]byte
+ IdentityHint []byte
+
+ isClient bool
+
+ preMasterSecret []byte
+ extendedMasterSecret bool
+
+ namedCurve elliptic.Curve
+ localKeypair *elliptic.Keypair
+ cookie []byte
+ handshakeSendSequence int
+ handshakeRecvSequence int
+ serverName string
+ remoteRequestedCertificate bool // Did we get a CertificateRequest
+ localCertificatesVerify []byte // cache CertificateVerify
+ localVerifyData []byte // cached VerifyData
+ localKeySignature []byte // cached keySignature
+ peerCertificatesVerified bool
+
+ replayDetector []replaydetector.ReplayDetector
+}
+
+type serializedState struct {
+ LocalEpoch uint16
+ RemoteEpoch uint16
+ LocalRandom [handshake.RandomLength]byte
+ RemoteRandom [handshake.RandomLength]byte
+ CipherSuiteID uint16
+ MasterSecret []byte
+ SequenceNumber uint64
+ SRTPProtectionProfile uint16
+ PeerCertificates [][]byte
+ IdentityHint []byte
+ IsClient bool
+}
+
+func (s *State) clone() *State {
+ serialized := s.serialize()
+ state := &State{}
+ state.deserialize(*serialized)
+
+ return state
+}
+
+func (s *State) serialize() *serializedState {
+ // Marshal random values
+ localRnd := s.localRandom.MarshalFixed()
+ remoteRnd := s.remoteRandom.MarshalFixed()
+
+ epoch := s.localEpoch.Load().(uint16)
+ return &serializedState{
+ LocalEpoch: epoch,
+ RemoteEpoch: s.remoteEpoch.Load().(uint16),
+ CipherSuiteID: uint16(s.cipherSuite.ID()),
+ MasterSecret: s.masterSecret,
+ SequenceNumber: atomic.LoadUint64(&s.localSequenceNumber[epoch]),
+ LocalRandom: localRnd,
+ RemoteRandom: remoteRnd,
+ SRTPProtectionProfile: uint16(s.srtpProtectionProfile),
+ PeerCertificates: s.PeerCertificates,
+ IdentityHint: s.IdentityHint,
+ IsClient: s.isClient,
+ }
+}
+
+func (s *State) deserialize(serialized serializedState) {
+ // Set epoch values
+ epoch := serialized.LocalEpoch
+ s.localEpoch.Store(serialized.LocalEpoch)
+ s.remoteEpoch.Store(serialized.RemoteEpoch)
+
+ for len(s.localSequenceNumber) <= int(epoch) {
+ s.localSequenceNumber = append(s.localSequenceNumber, uint64(0))
+ }
+
+ // Set random values
+ localRandom := &handshake.Random{}
+ localRandom.UnmarshalFixed(serialized.LocalRandom)
+ s.localRandom = *localRandom
+
+ remoteRandom := &handshake.Random{}
+ remoteRandom.UnmarshalFixed(serialized.RemoteRandom)
+ s.remoteRandom = *remoteRandom
+
+ s.isClient = serialized.IsClient
+
+ // Set master secret
+ s.masterSecret = serialized.MasterSecret
+
+ // Set cipher suite
+ s.cipherSuite = cipherSuiteForID(CipherSuiteID(serialized.CipherSuiteID), nil)
+
+ atomic.StoreUint64(&s.localSequenceNumber[epoch], serialized.SequenceNumber)
+ s.srtpProtectionProfile = SRTPProtectionProfile(serialized.SRTPProtectionProfile)
+
+ // Set remote certificate
+ s.PeerCertificates = serialized.PeerCertificates
+ s.IdentityHint = serialized.IdentityHint
+}
+
+func (s *State) initCipherSuite() error {
+ if s.cipherSuite.IsInitialized() {
+ return nil
+ }
+
+ localRandom := s.localRandom.MarshalFixed()
+ remoteRandom := s.remoteRandom.MarshalFixed()
+
+ var err error
+ if s.isClient {
+ err = s.cipherSuite.Init(s.masterSecret, localRandom[:], remoteRandom[:], true)
+ } else {
+ err = s.cipherSuite.Init(s.masterSecret, remoteRandom[:], localRandom[:], false)
+ }
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// MarshalBinary is a binary.BinaryMarshaler.MarshalBinary implementation
+func (s *State) MarshalBinary() ([]byte, error) {
+ serialized := s.serialize()
+
+ var buf bytes.Buffer
+ enc := gob.NewEncoder(&buf)
+ if err := enc.Encode(*serialized); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+// UnmarshalBinary is a binary.BinaryUnmarshaler.UnmarshalBinary implementation
+func (s *State) UnmarshalBinary(data []byte) error {
+ enc := gob.NewDecoder(bytes.NewBuffer(data))
+ var serialized serializedState
+ if err := enc.Decode(&serialized); err != nil {
+ return err
+ }
+
+ s.deserialize(serialized)
+ if err := s.initCipherSuite(); err != nil {
+ return err
+ }
+ return nil
+}
+
+// ExportKeyingMaterial returns length bytes of exported key material in a new
+// slice as defined in RFC 5705.
+// This allows protocols to use DTLS for key establishment, but
+// then use some of the keying material for their own purposes
+func (s *State) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
+ if s.localEpoch.Load().(uint16) == 0 {
+ return nil, errHandshakeInProgress
+ } else if len(context) != 0 {
+ return nil, errContextUnsupported
+ } else if _, ok := invalidKeyingLabels()[label]; ok {
+ return nil, errReservedExportKeyingMaterial
+ }
+
+ localRandom := s.localRandom.MarshalFixed()
+ remoteRandom := s.remoteRandom.MarshalFixed()
+
+ seed := []byte(label)
+ if s.isClient {
+ seed = append(append(seed, localRandom[:]...), remoteRandom[:]...)
+ } else {
+ seed = append(append(seed, remoteRandom[:]...), localRandom[:]...)
+ }
+ return prf.PHash(s.masterSecret, seed, length, s.cipherSuite.HashFunc())
+}
diff --git a/vendor/github.com/pion/dtls/v2/util.go b/vendor/github.com/pion/dtls/v2/util.go
new file mode 100644
index 0000000..745182d
--- /dev/null
+++ b/vendor/github.com/pion/dtls/v2/util.go
@@ -0,0 +1,38 @@
+package dtls
+
+func findMatchingSRTPProfile(a, b []SRTPProtectionProfile) (SRTPProtectionProfile, bool) {
+ for _, aProfile := range a {
+ for _, bProfile := range b {
+ if aProfile == bProfile {
+ return aProfile, true
+ }
+ }
+ }
+ return 0, false
+}
+
+func findMatchingCipherSuite(a, b []CipherSuite) (CipherSuite, bool) { //nolint
+ for _, aSuite := range a {
+ for _, bSuite := range b {
+ if aSuite.ID() == bSuite.ID() {
+ return aSuite, true
+ }
+ }
+ }
+ return nil, false
+}
+
+func splitBytes(bytes []byte, splitLen int) [][]byte {
+ splitBytes := make([][]byte, 0)
+ numBytes := len(bytes)
+ for i := 0; i < numBytes; i += splitLen {
+ j := i + splitLen
+ if j > numBytes {
+ j = numBytes
+ }
+
+ splitBytes = append(splitBytes, bytes[i:j])
+ }
+
+ return splitBytes
+}
diff --git a/vendor/github.com/pion/ice/v2/.gitignore b/vendor/github.com/pion/ice/v2/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/ice/v2/.golangci.yml b/vendor/github.com/pion/ice/v2/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/ice/v2/LICENSE b/vendor/github.com/pion/ice/v2/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/ice/v2/README.md b/vendor/github.com/pion/ice/v2/README.md
new file mode 100644
index 0000000..cbb55fd
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/README.md
@@ -0,0 +1,67 @@
+<h1 align="center">
+ <br>
+ Pion ICE
+ <br>
+</h1>
+<h4 align="center">A Go implementation of ICE</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-ice-gray.svg?longCache=true&colorB=brightgreen" alt="Pion transport"></a>
+ <a href="http://gophers.slack.com/messages/pion"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/ice"><img src="https://travis-ci.org/pion/ice.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/ice"><img src="https://godoc.org/github.com/pion/ice?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/ice"><img src="https://codecov.io/gh/pion/ice/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/ice"><img src="https://goreportcard.com/badge/github.com/pion/ice" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michael MacDonald](https://github.com/mjmac) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *Original Author*
+* [Konstantin Itskov](https://github.com/trivigy) - *Original Author*
+* [Luke Curley](https://github.com/kixelated)
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Adam Kiss](https://github.com/masterada)
+* [Aleksandr Razumov](https://github.com/ernado)
+* [Yutaka Takeda](https://github.com/enobufs)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Robert Eperjesi](https://github.com/epes)
+* [Sebastian Waisbrot](https://github.com/seppo0010)
+* [Zizheng Tai](https://github.com/ZizhengTai)
+* [Aaron France](https://github.com/AeroNotix)
+* [Chao Yuan](https://github.com/yuanchao0310)
+* [Jason Maldonis](https://github.com/jjmaldonis)
+* [Nevio Vesic](https://github.com/0x19)
+* [David Hamilton](https://github.com/dihamilton)
+* [adwpc](https://github.com/adwpc)
+* [Ori Bernstein](https://eigenstate.org)
+* [Sam Lancia](https://github.com/nerd2)
+* [Lander Noterman](https://github.com/LanderN)
+* [BUPTCZQ](https://github.com/buptczq)
+* [Henry](https://github.com/cryptix)
+* [Jerko Steiner](https://github.com/jeremija)
+* [Sidney San Martín](https://github.com/s4y)
+* [JooYoung Lim](https://github.com/DevRockstarZ)
+* [Kory Miller](https://github.com/korymiller1489)
+* [ZHENK](https://github.com/scorpionknifes)
+* [Assad Obaid](https://github.com/assadobaid)
+* [Antoine Baché](https://github.com/Antonito)
+* [Will Forcey](https://github.com/wawesomeNOGUI)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/ice/v2/agent.go b/vendor/github.com/pion/ice/v2/agent.go
new file mode 100644
index 0000000..9f1ac6a
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/agent.go
@@ -0,0 +1,1233 @@
+// Package ice implements the Interactive Connectivity Establishment (ICE)
+// protocol defined in rfc5245.
+package ice
+
+import (
+ "context"
+ "net"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/mdns"
+ "github.com/pion/stun"
+ "github.com/pion/transport/packetio"
+ "github.com/pion/transport/vnet"
+ "golang.org/x/net/proxy"
+)
+
+type bindingRequest struct {
+ timestamp time.Time
+ transactionID [stun.TransactionIDSize]byte
+ destination net.Addr
+ isUseCandidate bool
+}
+
+// Agent represents the ICE agent
+type Agent struct {
+ chanTask chan task
+ afterRunFn []func(ctx context.Context)
+ muAfterRun sync.Mutex
+
+ onConnectionStateChangeHdlr atomic.Value // func(ConnectionState)
+ onSelectedCandidatePairChangeHdlr atomic.Value // func(Candidate, Candidate)
+ onCandidateHdlr atomic.Value // func(Candidate)
+
+ // State owned by the taskLoop
+ onConnected chan struct{}
+ onConnectedOnce sync.Once
+
+ // force candidate to be contacted immediately (instead of waiting for task ticker)
+ forceCandidateContact chan bool
+
+ tieBreaker uint64
+ lite bool
+
+ connectionState ConnectionState
+ gatheringState GatheringState
+
+ mDNSMode MulticastDNSMode
+ mDNSName string
+ mDNSConn *mdns.Conn
+
+ muHaveStarted sync.Mutex
+ startedCh <-chan struct{}
+ startedFn func()
+ isControlling bool
+
+ maxBindingRequests uint16
+
+ hostAcceptanceMinWait time.Duration
+ srflxAcceptanceMinWait time.Duration
+ prflxAcceptanceMinWait time.Duration
+ relayAcceptanceMinWait time.Duration
+
+ portmin uint16
+ portmax uint16
+
+ candidateTypes []CandidateType
+
+ // How long connectivity checks can fail before the ICE Agent
+ // goes to disconnected
+ disconnectedTimeout time.Duration
+
+ // How long connectivity checks can fail before the ICE Agent
+ // goes to failed
+ failedTimeout time.Duration
+
+ // How often should we send keepalive packets?
+ // 0 means never
+ keepaliveInterval time.Duration
+
+ // How often should we run our internal taskLoop to check for state changes when connecting
+ checkInterval time.Duration
+
+ localUfrag string
+ localPwd string
+ localCandidates map[NetworkType][]Candidate
+
+ remoteUfrag string
+ remotePwd string
+ remoteCandidates map[NetworkType][]Candidate
+
+ checklist []*candidatePair
+ selector pairCandidateSelector
+
+ selectedPair atomic.Value // *candidatePair
+
+ urls []*URL
+ networkTypes []NetworkType
+
+ buffer *packetio.Buffer
+
+ // LRU of outbound Binding request Transaction IDs
+ pendingBindingRequests []bindingRequest
+
+ // 1:1 D-NAT IP address mapping
+ extIPMapper *externalIPMapper
+
+ // State for closing
+ done chan struct{}
+ err atomicError
+
+ gatherCandidateCancel func()
+
+ chanCandidate chan Candidate
+ chanCandidatePair chan *candidatePair
+ chanState chan ConnectionState
+
+ loggerFactory logging.LoggerFactory
+ log logging.LeveledLogger
+
+ net *vnet.Net
+ tcpMux TCPMux
+
+ interfaceFilter func(string) bool
+
+ insecureSkipVerify bool
+
+ proxyDialer proxy.Dialer
+}
+
+type task struct {
+ fn func(context.Context, *Agent)
+ done chan struct{}
+}
+
+// afterRun registers function to be run after the task.
+func (a *Agent) afterRun(f func(context.Context)) {
+ a.muAfterRun.Lock()
+ a.afterRunFn = append(a.afterRunFn, f)
+ a.muAfterRun.Unlock()
+}
+
+func (a *Agent) getAfterRunFn() []func(context.Context) {
+ a.muAfterRun.Lock()
+ defer a.muAfterRun.Unlock()
+ fns := a.afterRunFn
+ a.afterRunFn = nil
+ return fns
+}
+
+func (a *Agent) ok() error {
+ select {
+ case <-a.done:
+ return a.getErr()
+ default:
+ }
+ return nil
+}
+
+func (a *Agent) getErr() error {
+ if err := a.err.Load(); err != nil {
+ return err
+ }
+ return ErrClosed
+}
+
+// Run task in serial. Blocking tasks must be cancelable by context.
+func (a *Agent) run(ctx context.Context, t func(context.Context, *Agent)) error {
+ if err := a.ok(); err != nil {
+ return err
+ }
+ done := make(chan struct{})
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case a.chanTask <- task{t, done}:
+ <-done
+ return nil
+ }
+}
+
+// taskLoop handles registered tasks and agent close.
+func (a *Agent) taskLoop() {
+ after := func() {
+ for {
+ // Get and run func registered by afterRun().
+ fns := a.getAfterRunFn()
+ if len(fns) == 0 {
+ break
+ }
+ for _, fn := range fns {
+ fn(a.context())
+ }
+ }
+ }
+ defer func() {
+ a.deleteAllCandidates()
+ a.startedFn()
+
+ if err := a.buffer.Close(); err != nil {
+ a.log.Warnf("failed to close buffer: %v", err)
+ }
+
+ a.closeMulticastConn()
+ a.updateConnectionState(ConnectionStateClosed)
+
+ after()
+
+ close(a.chanState)
+ close(a.chanCandidate)
+ close(a.chanCandidatePair)
+ }()
+
+ for {
+ select {
+ case <-a.done:
+ return
+ case t := <-a.chanTask:
+ t.fn(a.context(), a)
+ close(t.done)
+ after()
+ }
+ }
+}
+
+// NewAgent creates a new Agent
+func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
+ var err error
+ if config.PortMax < config.PortMin {
+ return nil, ErrPort
+ }
+
+ mDNSName := config.MulticastDNSHostName
+ if mDNSName == "" {
+ if mDNSName, err = generateMulticastDNSName(); err != nil {
+ return nil, err
+ }
+ }
+
+ if !strings.HasSuffix(mDNSName, ".local") || len(strings.Split(mDNSName, ".")) != 2 {
+ return nil, ErrInvalidMulticastDNSHostName
+ }
+
+ mDNSMode := config.MulticastDNSMode
+ if mDNSMode == 0 {
+ mDNSMode = MulticastDNSModeQueryOnly
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+ log := loggerFactory.NewLogger("ice")
+
+ var mDNSConn *mdns.Conn
+ mDNSConn, mDNSMode, err = createMulticastDNS(mDNSMode, mDNSName, log)
+ // Opportunistic mDNS: If we can't open the connection, that's ok: we
+ // can continue without it.
+ if err != nil {
+ log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err)
+ }
+ closeMDNSConn := func() {
+ if mDNSConn != nil {
+ if mdnsCloseErr := mDNSConn.Close(); mdnsCloseErr != nil {
+ log.Warnf("Failed to close mDNS: %v", mdnsCloseErr)
+ }
+ }
+ }
+
+ startedCtx, startedFn := context.WithCancel(context.Background())
+
+ a := &Agent{
+ chanTask: make(chan task),
+ chanState: make(chan ConnectionState),
+ chanCandidate: make(chan Candidate),
+ chanCandidatePair: make(chan *candidatePair),
+ tieBreaker: globalMathRandomGenerator.Uint64(),
+ lite: config.Lite,
+ gatheringState: GatheringStateNew,
+ connectionState: ConnectionStateNew,
+ localCandidates: make(map[NetworkType][]Candidate),
+ remoteCandidates: make(map[NetworkType][]Candidate),
+ urls: config.Urls,
+ networkTypes: config.NetworkTypes,
+ onConnected: make(chan struct{}),
+ buffer: packetio.NewBuffer(),
+ done: make(chan struct{}),
+ startedCh: startedCtx.Done(),
+ startedFn: startedFn,
+ portmin: config.PortMin,
+ portmax: config.PortMax,
+ loggerFactory: loggerFactory,
+ log: log,
+ net: config.Net,
+ proxyDialer: config.ProxyDialer,
+
+ mDNSMode: mDNSMode,
+ mDNSName: mDNSName,
+ mDNSConn: mDNSConn,
+
+ gatherCandidateCancel: func() {},
+
+ forceCandidateContact: make(chan bool, 1),
+
+ interfaceFilter: config.InterfaceFilter,
+
+ insecureSkipVerify: config.InsecureSkipVerify,
+ }
+
+ a.tcpMux = config.TCPMux
+ if a.tcpMux == nil {
+ a.tcpMux = newInvalidTCPMux()
+ }
+
+ if a.net == nil {
+ a.net = vnet.NewNet(nil)
+ } else if a.net.IsVirtual() {
+ a.log.Warn("vnet is enabled")
+ if a.mDNSMode != MulticastDNSModeDisabled {
+ a.log.Warn("vnet does not support mDNS yet")
+ }
+ }
+
+ config.initWithDefaults(a)
+
+ // Make sure the buffer doesn't grow indefinitely.
+ // NOTE: We actually won't get anywhere close to this limit.
+ // SRTP will constantly read from the endpoint and drop packets if it's full.
+ a.buffer.SetLimitSize(maxBufferSize)
+
+ if a.lite && (len(a.candidateTypes) != 1 || a.candidateTypes[0] != CandidateTypeHost) {
+ closeMDNSConn()
+ return nil, ErrLiteUsingNonHostCandidates
+ }
+
+ if config.Urls != nil && len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) {
+ closeMDNSConn()
+ return nil, ErrUselessUrlsProvided
+ }
+
+ if err = config.initExtIPMapping(a); err != nil {
+ closeMDNSConn()
+ return nil, err
+ }
+
+ go a.taskLoop()
+ a.startOnConnectionStateChangeRoutine()
+
+ // Restart is also used to initialize the agent for the first time
+ if err := a.Restart(config.LocalUfrag, config.LocalPwd); err != nil {
+ closeMDNSConn()
+ _ = a.Close()
+ return nil, err
+ }
+
+ return a, nil
+}
+
+// OnConnectionStateChange sets a handler that is fired when the connection state changes
+func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error {
+ a.onConnectionStateChangeHdlr.Store(f)
+ return nil
+}
+
+// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate
+// pair is selected
+func (a *Agent) OnSelectedCandidatePairChange(f func(Candidate, Candidate)) error {
+ a.onSelectedCandidatePairChangeHdlr.Store(f)
+ return nil
+}
+
+// OnCandidate sets a handler that is fired when new candidates gathered. When
+// the gathering process complete the last candidate is nil.
+func (a *Agent) OnCandidate(f func(Candidate)) error {
+ a.onCandidateHdlr.Store(f)
+ return nil
+}
+
+func (a *Agent) onSelectedCandidatePairChange(p *candidatePair) {
+ if h, ok := a.onSelectedCandidatePairChangeHdlr.Load().(func(Candidate, Candidate)); ok {
+ h(p.local, p.remote)
+ }
+}
+
+func (a *Agent) onCandidate(c Candidate) {
+ if onCandidateHdlr, ok := a.onCandidateHdlr.Load().(func(Candidate)); ok {
+ onCandidateHdlr(c)
+ }
+}
+
+func (a *Agent) onConnectionStateChange(s ConnectionState) {
+ if hdlr, ok := a.onConnectionStateChangeHdlr.Load().(func(ConnectionState)); ok {
+ hdlr(s)
+ }
+}
+
+func (a *Agent) startOnConnectionStateChangeRoutine() {
+ go func() {
+ for {
+ // CandidatePair and ConnectionState are usually changed at once.
+ // Blocking one by the other one causes deadlock.
+ p, isOpen := <-a.chanCandidatePair
+ if !isOpen {
+ return
+ }
+ a.onSelectedCandidatePairChange(p)
+ }
+ }()
+ go func() {
+ for {
+ select {
+ case s, isOpen := <-a.chanState:
+ if !isOpen {
+ for c := range a.chanCandidate {
+ a.onCandidate(c)
+ }
+ return
+ }
+ a.onConnectionStateChange(s)
+
+ case c, isOpen := <-a.chanCandidate:
+ if !isOpen {
+ for s := range a.chanState {
+ a.onConnectionStateChange(s)
+ }
+ return
+ }
+ a.onCandidate(c)
+ }
+ }
+ }()
+}
+
+func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remotePwd string) error {
+ a.muHaveStarted.Lock()
+ defer a.muHaveStarted.Unlock()
+ select {
+ case <-a.startedCh:
+ return ErrMultipleStart
+ default:
+ }
+ if err := a.SetRemoteCredentials(remoteUfrag, remotePwd); err != nil {
+ return err
+ }
+
+ a.log.Debugf("Started agent: isControlling? %t, remoteUfrag: %q, remotePwd: %q", isControlling, remoteUfrag, remotePwd)
+
+ return a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ agent.isControlling = isControlling
+ agent.remoteUfrag = remoteUfrag
+ agent.remotePwd = remotePwd
+
+ if isControlling {
+ a.selector = &controllingSelector{agent: a, log: a.log}
+ } else {
+ a.selector = &controlledSelector{agent: a, log: a.log}
+ }
+
+ if a.lite {
+ a.selector = &liteSelector{pairCandidateSelector: a.selector}
+ }
+
+ a.selector.Start()
+ a.startedFn()
+
+ agent.updateConnectionState(ConnectionStateChecking)
+
+ a.requestConnectivityCheck()
+ go a.connectivityChecks()
+ })
+}
+
+func (a *Agent) connectivityChecks() {
+ lastConnectionState := ConnectionState(0)
+ checkingDuration := time.Time{}
+
+ contact := func() {
+ if err := a.run(a.context(), func(ctx context.Context, a *Agent) {
+ defer func() {
+ lastConnectionState = a.connectionState
+ }()
+
+ switch a.connectionState {
+ case ConnectionStateFailed:
+ // The connection is currently failed so don't send any checks
+ // In the future it may be restarted though
+ return
+ case ConnectionStateChecking:
+ // We have just entered checking for the first time so update our checking timer
+ if lastConnectionState != a.connectionState {
+ checkingDuration = time.Now()
+ }
+
+ // We have been in checking longer then Disconnect+Failed timeout, set the connection to Failed
+ if time.Since(checkingDuration) > a.disconnectedTimeout+a.failedTimeout {
+ a.updateConnectionState(ConnectionStateFailed)
+ return
+ }
+ }
+
+ a.selector.ContactCandidates()
+ }); err != nil {
+ a.log.Warnf("taskLoop failed: %v", err)
+ }
+ }
+
+ for {
+ interval := defaultKeepaliveInterval
+
+ updateInterval := func(x time.Duration) {
+ if x != 0 && (interval == 0 || interval > x) {
+ interval = x
+ }
+ }
+
+ switch lastConnectionState {
+ case ConnectionStateNew, ConnectionStateChecking: // While connecting, check candidates more frequently
+ updateInterval(a.checkInterval)
+ case ConnectionStateConnected, ConnectionStateDisconnected:
+ updateInterval(a.keepaliveInterval)
+ default:
+ }
+ // Ensure we run our task loop as quickly as the minimum of our various configured timeouts
+ updateInterval(a.disconnectedTimeout)
+ updateInterval(a.failedTimeout)
+
+ t := time.NewTimer(interval)
+ select {
+ case <-a.forceCandidateContact:
+ t.Stop()
+ contact()
+ case <-t.C:
+ contact()
+ case <-a.done:
+ t.Stop()
+ return
+ }
+ }
+}
+
+func (a *Agent) updateConnectionState(newState ConnectionState) {
+ if a.connectionState != newState {
+ // Connection has gone to failed, release all gathered candidates
+ if newState == ConnectionStateFailed {
+ a.deleteAllCandidates()
+ }
+
+ a.log.Infof("Setting new connection state: %s", newState)
+ a.connectionState = newState
+
+ // Call handler after finishing current task since we may be holding the agent lock
+ // and the handler may also require it
+ a.afterRun(func(ctx context.Context) {
+ a.chanState <- newState
+ })
+ }
+}
+
+func (a *Agent) setSelectedPair(p *candidatePair) {
+ a.log.Tracef("Set selected candidate pair: %s", p)
+
+ if p == nil {
+ var nilPair *candidatePair
+ a.selectedPair.Store(nilPair)
+ return
+ }
+
+ p.nominated = true
+ a.selectedPair.Store(p)
+
+ a.updateConnectionState(ConnectionStateConnected)
+
+ // Notify when the selected pair changes
+ if p != nil {
+ a.afterRun(func(ctx context.Context) {
+ select {
+ case a.chanCandidatePair <- p:
+ case <-ctx.Done():
+ }
+ })
+ }
+
+ // Signal connected
+ a.onConnectedOnce.Do(func() { close(a.onConnected) })
+}
+
+func (a *Agent) pingAllCandidates() {
+ a.log.Trace("pinging all candidates")
+
+ if len(a.checklist) == 0 {
+ a.log.Warn("pingAllCandidates called with no candidate pairs. Connection is not possible yet.")
+ }
+
+ for _, p := range a.checklist {
+ if p.state == CandidatePairStateWaiting {
+ p.state = CandidatePairStateInProgress
+ } else if p.state != CandidatePairStateInProgress {
+ continue
+ }
+
+ if p.bindingRequestCount > a.maxBindingRequests {
+ a.log.Tracef("max requests reached for pair %s, marking it as failed\n", p)
+ p.state = CandidatePairStateFailed
+ } else {
+ a.selector.PingCandidate(p.local, p.remote)
+ p.bindingRequestCount++
+ }
+ }
+}
+
+func (a *Agent) getBestAvailableCandidatePair() *candidatePair {
+ var best *candidatePair
+ for _, p := range a.checklist {
+ if p.state == CandidatePairStateFailed {
+ continue
+ }
+
+ if best == nil {
+ best = p
+ } else if best.Priority() < p.Priority() {
+ best = p
+ }
+ }
+ return best
+}
+
+func (a *Agent) getBestValidCandidatePair() *candidatePair {
+ var best *candidatePair
+ for _, p := range a.checklist {
+ if p.state != CandidatePairStateSucceeded {
+ continue
+ }
+
+ if best == nil {
+ best = p
+ } else if best.Priority() < p.Priority() {
+ best = p
+ }
+ }
+ return best
+}
+
+func (a *Agent) addPair(local, remote Candidate) *candidatePair {
+ p := newCandidatePair(local, remote, a.isControlling)
+ a.checklist = append(a.checklist, p)
+ return p
+}
+
+func (a *Agent) findPair(local, remote Candidate) *candidatePair {
+ for _, p := range a.checklist {
+ if p.local.Equal(local) && p.remote.Equal(remote) {
+ return p
+ }
+ }
+ return nil
+}
+
+// validateSelectedPair checks if the selected pair is (still) valid
+// Note: the caller should hold the agent lock.
+func (a *Agent) validateSelectedPair() bool {
+ selectedPair := a.getSelectedPair()
+ if selectedPair == nil {
+ return false
+ }
+
+ disconnectedTime := time.Since(selectedPair.remote.LastReceived())
+
+ // Only allow transitions to failed if a.failedTimeout is non-zero
+ totalTimeToFailure := a.failedTimeout
+ if totalTimeToFailure != 0 {
+ totalTimeToFailure += a.disconnectedTimeout
+ }
+
+ switch {
+ case totalTimeToFailure != 0 && disconnectedTime > totalTimeToFailure:
+ a.updateConnectionState(ConnectionStateFailed)
+ case a.disconnectedTimeout != 0 && disconnectedTime > a.disconnectedTimeout:
+ a.updateConnectionState(ConnectionStateDisconnected)
+ default:
+ a.updateConnectionState(ConnectionStateConnected)
+ }
+
+ return true
+}
+
+// checkKeepalive sends STUN Binding Indications to the selected pair
+// if no packet has been sent on that pair in the last keepaliveInterval
+// Note: the caller should hold the agent lock.
+func (a *Agent) checkKeepalive() {
+ selectedPair := a.getSelectedPair()
+ if selectedPair == nil {
+ return
+ }
+
+ if (a.keepaliveInterval != 0) &&
+ ((time.Since(selectedPair.local.LastSent()) > a.keepaliveInterval) ||
+ (time.Since(selectedPair.remote.LastReceived()) > a.keepaliveInterval)) {
+ // we use binding request instead of indication to support refresh consent schemas
+ // see https://tools.ietf.org/html/rfc7675
+ a.selector.PingCandidate(selectedPair.local, selectedPair.remote)
+ }
+}
+
+// AddRemoteCandidate adds a new remote candidate
+func (a *Agent) AddRemoteCandidate(c Candidate) error {
+ if c == nil {
+ return nil
+ }
+
+ // cannot check for network yet because it might not be applied
+ // when mDNS hostame is used.
+ if c.TCPType() == TCPTypeActive {
+ // TCP Candidates with tcptype active will probe server passive ones, so
+ // no need to do anything with them.
+ a.log.Infof("Ignoring remote candidate with tcpType active: %s", c)
+ return nil
+ }
+
+ // If we have a mDNS Candidate lets fully resolve it before adding it locally
+ if c.Type() == CandidateTypeHost && strings.HasSuffix(c.Address(), ".local") {
+ if a.mDNSMode == MulticastDNSModeDisabled {
+ a.log.Warnf("remote mDNS candidate added, but mDNS is disabled: (%s)", c.Address())
+ return nil
+ }
+
+ hostCandidate, ok := c.(*CandidateHost)
+ if !ok {
+ return ErrAddressParseFailed
+ }
+
+ go a.resolveAndAddMulticastCandidate(hostCandidate)
+ return nil
+ }
+
+ go func() {
+ if err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ agent.addRemoteCandidate(c)
+ }); err != nil {
+ a.log.Warnf("Failed to add remote candidate %s: %v", c.Address(), err)
+ return
+ }
+ }()
+ return nil
+}
+
+func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) {
+ if a.mDNSConn == nil {
+ return
+ }
+ _, src, err := a.mDNSConn.Query(c.context(), c.Address())
+ if err != nil {
+ a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err)
+ return
+ }
+
+ ip, _, _, _ := parseAddr(src) //nolint:dogsled
+ if ip == nil {
+ a.log.Warnf("Failed to discover mDNS candidate %s: failed to parse IP", c.Address())
+ return
+ }
+
+ if err = c.setIP(ip); err != nil {
+ a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err)
+ return
+ }
+
+ if err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ agent.addRemoteCandidate(c)
+ }); err != nil {
+ a.log.Warnf("Failed to add mDNS candidate %s: %v", c.Address(), err)
+ return
+ }
+}
+
+func (a *Agent) requestConnectivityCheck() {
+ select {
+ case a.forceCandidateContact <- true:
+ default:
+ }
+}
+
+// addRemoteCandidate assumes you are holding the lock (must be execute using a.run)
+func (a *Agent) addRemoteCandidate(c Candidate) {
+ set := a.remoteCandidates[c.NetworkType()]
+
+ for _, candidate := range set {
+ if candidate.Equal(c) {
+ return
+ }
+ }
+
+ set = append(set, c)
+ a.remoteCandidates[c.NetworkType()] = set
+
+ if localCandidates, ok := a.localCandidates[c.NetworkType()]; ok {
+ for _, localCandidate := range localCandidates {
+ a.addPair(localCandidate, c)
+ }
+ }
+
+ a.requestConnectivityCheck()
+}
+
+func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net.PacketConn) error {
+ return a.run(ctx, func(ctx context.Context, agent *Agent) {
+ c.start(a, candidateConn, a.startedCh)
+
+ set := a.localCandidates[c.NetworkType()]
+ for _, candidate := range set {
+ if candidate.Equal(c) {
+ if err := c.close(); err != nil {
+ a.log.Warnf("Failed to close duplicate candidate: %v", err)
+ }
+ return
+ }
+ }
+
+ set = append(set, c)
+ a.localCandidates[c.NetworkType()] = set
+
+ if remoteCandidates, ok := a.remoteCandidates[c.NetworkType()]; ok {
+ for _, remoteCandidate := range remoteCandidates {
+ a.addPair(c, remoteCandidate)
+ }
+ }
+
+ a.requestConnectivityCheck()
+
+ a.chanCandidate <- c
+ })
+}
+
+// GetLocalCandidates returns the local candidates
+func (a *Agent) GetLocalCandidates() ([]Candidate, error) {
+ var res []Candidate
+
+ err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ var candidates []Candidate
+ for _, set := range agent.localCandidates {
+ candidates = append(candidates, set...)
+ }
+ res = candidates
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+// GetLocalUserCredentials returns the local user credentials
+func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) {
+ valSet := make(chan struct{})
+ err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ frag = agent.localUfrag
+ pwd = agent.localPwd
+ close(valSet)
+ })
+
+ if err == nil {
+ <-valSet
+ }
+ return
+}
+
+// GetRemoteUserCredentials returns the remote user credentials
+func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) {
+ valSet := make(chan struct{})
+ err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ frag = agent.remoteUfrag
+ pwd = agent.remotePwd
+ close(valSet)
+ })
+
+ if err == nil {
+ <-valSet
+ }
+ return
+}
+
+// Close cleans up the Agent
+func (a *Agent) Close() error {
+ if err := a.ok(); err != nil {
+ return err
+ }
+
+ done := make(chan struct{})
+
+ a.afterRun(func(context.Context) {
+ close(done)
+ })
+
+ a.gatherCandidateCancel()
+ a.err.Store(ErrClosed)
+
+ a.tcpMux.RemoveConnByUfrag(a.localUfrag)
+
+ close(a.done)
+
+ <-done
+ return nil
+}
+
+// Remove all candidates. This closes any listening sockets
+// and removes both the local and remote candidate lists.
+//
+// This is used for restarts, failures and on close
+func (a *Agent) deleteAllCandidates() {
+ for net, cs := range a.localCandidates {
+ for _, c := range cs {
+ if err := c.close(); err != nil {
+ a.log.Warnf("Failed to close candidate %s: %v", c, err)
+ }
+ }
+ delete(a.localCandidates, net)
+ }
+ for net, cs := range a.remoteCandidates {
+ for _, c := range cs {
+ if err := c.close(); err != nil {
+ a.log.Warnf("Failed to close candidate %s: %v", c, err)
+ }
+ }
+ delete(a.remoteCandidates, net)
+ }
+}
+
+func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Candidate {
+ ip, port, _, ok := parseAddr(addr)
+ if !ok {
+ a.log.Warnf("Error parsing addr: %s", addr)
+ return nil
+ }
+
+ set := a.remoteCandidates[networkType]
+ for _, c := range set {
+ if c.Address() == ip.String() && c.Port() == port {
+ return c
+ }
+ }
+ return nil
+}
+
+func (a *Agent) sendBindingRequest(m *stun.Message, local, remote Candidate) {
+ a.log.Tracef("ping STUN from %s to %s\n", local.String(), remote.String())
+
+ a.invalidatePendingBindingRequests(time.Now())
+ a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{
+ timestamp: time.Now(),
+ transactionID: m.TransactionID,
+ destination: remote.addr(),
+ isUseCandidate: m.Contains(stun.AttrUseCandidate),
+ })
+
+ a.sendSTUN(m, local, remote)
+}
+
+func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) {
+ base := remote
+
+ ip, port, _, ok := parseAddr(base.addr())
+ if !ok {
+ a.log.Warnf("Error parsing addr: %s", base.addr())
+ return
+ }
+
+ if out, err := stun.Build(m, stun.BindingSuccess,
+ &stun.XORMappedAddress{
+ IP: ip,
+ Port: port,
+ },
+ stun.NewShortTermIntegrity(a.localPwd),
+ stun.Fingerprint,
+ ); err != nil {
+ a.log.Warnf("Failed to handle inbound ICE from: %s to: %s error: %s", local, remote, err)
+ } else {
+ a.sendSTUN(out, local, remote)
+ }
+}
+
+/* Removes pending binding requests that are over maxBindingRequestTimeout old
+
+ Let HTO be the transaction timeout, which SHOULD be 2*RTT if
+ RTT is known or 500 ms otherwise.
+ https://tools.ietf.org/html/rfc8445#appendix-B.1
+*/
+func (a *Agent) invalidatePendingBindingRequests(filterTime time.Time) {
+ initialSize := len(a.pendingBindingRequests)
+
+ temp := a.pendingBindingRequests[:0]
+ for _, bindingRequest := range a.pendingBindingRequests {
+ if filterTime.Sub(bindingRequest.timestamp) < maxBindingRequestTimeout {
+ temp = append(temp, bindingRequest)
+ }
+ }
+
+ a.pendingBindingRequests = temp
+ if bindRequestsRemoved := initialSize - len(a.pendingBindingRequests); bindRequestsRemoved > 0 {
+ a.log.Tracef("Discarded %d binding requests because they expired", bindRequestsRemoved)
+ }
+}
+
+// Assert that the passed TransactionID is in our pendingBindingRequests and returns the destination
+// If the bindingRequest was valid remove it from our pending cache
+func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bool, *bindingRequest) {
+ a.invalidatePendingBindingRequests(time.Now())
+ for i := range a.pendingBindingRequests {
+ if a.pendingBindingRequests[i].transactionID == id {
+ validBindingRequest := a.pendingBindingRequests[i]
+ a.pendingBindingRequests = append(a.pendingBindingRequests[:i], a.pendingBindingRequests[i+1:]...)
+ return true, &validBindingRequest
+ }
+ }
+ return false, nil
+}
+
+// handleInbound processes STUN traffic from a remote candidate
+func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) { //nolint:gocognit
+ var err error
+ if m == nil || local == nil {
+ return
+ }
+
+ if m.Type.Method != stun.MethodBinding ||
+ !(m.Type.Class == stun.ClassSuccessResponse ||
+ m.Type.Class == stun.ClassRequest ||
+ m.Type.Class == stun.ClassIndication) {
+ a.log.Tracef("unhandled STUN from %s to %s class(%s) method(%s)", remote, local, m.Type.Class, m.Type.Method)
+ return
+ }
+
+ if a.isControlling {
+ if m.Contains(stun.AttrICEControlling) {
+ a.log.Debug("inbound isControlling && a.isControlling == true")
+ return
+ } else if m.Contains(stun.AttrUseCandidate) {
+ a.log.Debug("useCandidate && a.isControlling == true")
+ return
+ }
+ } else {
+ if m.Contains(stun.AttrICEControlled) {
+ a.log.Debug("inbound isControlled && a.isControlling == false")
+ return
+ }
+ }
+
+ remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote)
+ if m.Type.Class == stun.ClassSuccessResponse {
+ if err = assertInboundMessageIntegrity(m, []byte(a.remotePwd)); err != nil {
+ a.log.Warnf("discard message from (%s), %v", remote, err)
+ return
+ }
+
+ if remoteCandidate == nil {
+ a.log.Warnf("discard success message from (%s), no such remote", remote)
+ return
+ }
+
+ a.selector.HandleSuccessResponse(m, local, remoteCandidate, remote)
+ } else if m.Type.Class == stun.ClassRequest {
+ if err = assertInboundUsername(m, a.localUfrag+":"+a.remoteUfrag); err != nil {
+ a.log.Warnf("discard message from (%s), %v", remote, err)
+ return
+ } else if err = assertInboundMessageIntegrity(m, []byte(a.localPwd)); err != nil {
+ a.log.Warnf("discard message from (%s), %v", remote, err)
+ return
+ }
+
+ if remoteCandidate == nil {
+ ip, port, networkType, ok := parseAddr(remote)
+ if !ok {
+ a.log.Errorf("Failed to create parse remote net.Addr when creating remote prflx candidate")
+ return
+ }
+
+ prflxCandidateConfig := CandidatePeerReflexiveConfig{
+ Network: networkType.String(),
+ Address: ip.String(),
+ Port: port,
+ Component: local.Component(),
+ RelAddr: "",
+ RelPort: 0,
+ }
+
+ prflxCandidate, err := NewCandidatePeerReflexive(&prflxCandidateConfig)
+ if err != nil {
+ a.log.Errorf("Failed to create new remote prflx candidate (%s)", err)
+ return
+ }
+ remoteCandidate = prflxCandidate
+
+ a.log.Debugf("adding a new peer-reflexive candidate: %s ", remote)
+ a.addRemoteCandidate(remoteCandidate)
+ }
+
+ a.log.Tracef("inbound STUN (Request) from %s to %s", remote.String(), local.String())
+
+ a.selector.HandleBindingRequest(m, local, remoteCandidate)
+ }
+
+ if remoteCandidate != nil {
+ remoteCandidate.seen(false)
+ }
+}
+
+// validateNonSTUNTraffic processes non STUN traffic from a remote candidate,
+// and returns true if it is an actual remote candidate
+func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) bool {
+ var isValidCandidate uint64
+ if err := a.run(local.context(), func(ctx context.Context, agent *Agent) {
+ remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote)
+ if remoteCandidate != nil {
+ remoteCandidate.seen(false)
+ atomic.AddUint64(&isValidCandidate, 1)
+ }
+ }); err != nil {
+ a.log.Warnf("failed to validate remote candidate: %v", err)
+ }
+
+ return atomic.LoadUint64(&isValidCandidate) == 1
+}
+
+func (a *Agent) getSelectedPair() *candidatePair {
+ selectedPair := a.selectedPair.Load()
+
+ if selectedPair == nil {
+ return nil
+ }
+
+ return selectedPair.(*candidatePair)
+}
+
+func (a *Agent) closeMulticastConn() {
+ if a.mDNSConn != nil {
+ if err := a.mDNSConn.Close(); err != nil {
+ a.log.Warnf("failed to close mDNS Conn: %v", err)
+ }
+ }
+}
+
+// SetRemoteCredentials sets the credentials of the remote agent
+func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error {
+ switch {
+ case remoteUfrag == "":
+ return ErrRemoteUfragEmpty
+ case remotePwd == "":
+ return ErrRemotePwdEmpty
+ }
+
+ return a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ agent.remoteUfrag = remoteUfrag
+ agent.remotePwd = remotePwd
+ })
+}
+
+// Restart restarts the ICE Agent with the provided ufrag/pwd
+// If no ufrag/pwd is provided the Agent will generate one itself
+//
+// Restart must only be called when GatheringState is GatheringStateComplete
+// a user must then call GatherCandidates explicitly to start generating new ones
+func (a *Agent) Restart(ufrag, pwd string) error {
+ if ufrag == "" {
+ var err error
+ ufrag, err = generateUFrag()
+ if err != nil {
+ return err
+ }
+ }
+ if pwd == "" {
+ var err error
+ pwd, err = generatePwd()
+ if err != nil {
+ return err
+ }
+ }
+
+ if len([]rune(ufrag))*8 < 24 {
+ return ErrLocalUfragInsufficientBits
+ }
+ if len([]rune(pwd))*8 < 128 {
+ return ErrLocalPwdInsufficientBits
+ }
+
+ var err error
+ if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ if agent.gatheringState == GatheringStateGathering {
+ err = ErrRestartWhenGathering
+ return
+ }
+
+ // Clear all agent needed to take back to fresh state
+ agent.localUfrag = ufrag
+ agent.localPwd = pwd
+ agent.remoteUfrag = ""
+ agent.remotePwd = ""
+ a.gatheringState = GatheringStateNew
+ a.checklist = make([]*candidatePair, 0)
+ a.pendingBindingRequests = make([]bindingRequest, 0)
+ a.setSelectedPair(nil)
+ a.deleteAllCandidates()
+ if a.selector != nil {
+ a.selector.Start()
+ }
+
+ // Restart is used by NewAgent. Accept/Connect should be used to move to checking
+ // for new Agents
+ if a.connectionState != ConnectionStateNew {
+ a.updateConnectionState(ConnectionStateChecking)
+ }
+ }); runErr != nil {
+ return runErr
+ }
+ return err
+}
+
+func (a *Agent) setGatheringState(newState GatheringState) error {
+ done := make(chan struct{})
+ if err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ if a.gatheringState != newState && newState == GatheringStateComplete {
+ a.chanCandidate <- nil
+ }
+
+ a.gatheringState = newState
+ close(done)
+ }); err != nil {
+ return err
+ }
+
+ <-done
+ return nil
+}
diff --git a/vendor/github.com/pion/ice/v2/agent_config.go b/vendor/github.com/pion/ice/v2/agent_config.go
new file mode 100644
index 0000000..e09ad76
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/agent_config.go
@@ -0,0 +1,252 @@
+package ice
+
+import (
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/transport/vnet"
+ "golang.org/x/net/proxy"
+)
+
+const (
+ // defaultCheckInterval is the interval at which the agent performs candidate checks in the connecting phase
+ defaultCheckInterval = 200 * time.Millisecond
+
+ // keepaliveInterval used to keep candidates alive
+ defaultKeepaliveInterval = 2 * time.Second
+
+ // defaultDisconnectedTimeout is the default time till an Agent transitions disconnected
+ defaultDisconnectedTimeout = 5 * time.Second
+
+ // defaultFailedTimeout is the default time till an Agent transitions to failed after disconnected
+ defaultFailedTimeout = 25 * time.Second
+
+ // wait time before nominating a host candidate
+ defaultHostAcceptanceMinWait = 0
+
+ // wait time before nominating a srflx candidate
+ defaultSrflxAcceptanceMinWait = 500 * time.Millisecond
+
+ // wait time before nominating a prflx candidate
+ defaultPrflxAcceptanceMinWait = 1000 * time.Millisecond
+
+ // wait time before nominating a relay candidate
+ defaultRelayAcceptanceMinWait = 2000 * time.Millisecond
+
+ // max binding request before considering a pair failed
+ defaultMaxBindingRequests = 7
+
+ // the number of bytes that can be buffered before we start to error
+ maxBufferSize = 1000 * 1000 // 1MB
+
+ // wait time before binding requests can be deleted
+ maxBindingRequestTimeout = 4000 * time.Millisecond
+)
+
+func defaultCandidateTypes() []CandidateType {
+ return []CandidateType{CandidateTypeHost, CandidateTypeServerReflexive, CandidateTypeRelay}
+}
+
+// AgentConfig collects the arguments to ice.Agent construction into
+// a single structure, for future-proofness of the interface
+type AgentConfig struct {
+ Urls []*URL
+
+ // PortMin and PortMax are optional. Leave them 0 for the default UDP port allocation strategy.
+ PortMin uint16
+ PortMax uint16
+
+ // LocalUfrag and LocalPwd values used to perform connectivity
+ // checks. The values MUST be unguessable, with at least 128 bits of
+ // random number generator output used to generate the password, and
+ // at least 24 bits of output to generate the username fragment.
+ LocalUfrag string
+ LocalPwd string
+
+ // MulticastDNSMode controls mDNS behavior for the ICE agent
+ MulticastDNSMode MulticastDNSMode
+
+ // MulticastDNSHostName controls the hostname for this agent. If none is specified a random one will be generated
+ MulticastDNSHostName string
+
+ // DisconnectedTimeout defaults to 5 seconds when this property is nil.
+ // If the duration is 0, the ICE Agent will never go to disconnected
+ DisconnectedTimeout *time.Duration
+
+ // FailedTimeout defaults to 25 seconds when this property is nil.
+ // If the duration is 0, we will never go to failed.
+ FailedTimeout *time.Duration
+
+ // KeepaliveInterval determines how often should we send ICE
+ // keepalives (should be less then connectiontimeout above)
+ // when this is nil, it defaults to 10 seconds.
+ // A keepalive interval of 0 means we never send keepalive packets
+ KeepaliveInterval *time.Duration
+
+ // NetworkTypes is an optional configuration for disabling or enabling
+ // support for specific network types.
+ NetworkTypes []NetworkType
+
+ // CandidateTypes is an optional configuration for disabling or enabling
+ // support for specific candidate types.
+ CandidateTypes []CandidateType
+
+ LoggerFactory logging.LoggerFactory
+
+ // checkInterval controls how often our internal task loop runs when
+ // in the connecting state. Only useful for testing.
+ checkInterval time.Duration
+
+ // MaxBindingRequests is the max amount of binding requests the agent will send
+ // over a candidate pair for validation or nomination, if after MaxBindingRequests
+ // the candidate is yet to answer a binding request or a nomination we set the pair as failed
+ MaxBindingRequests *uint16
+
+ // Lite agents do not perform connectivity check and only provide host candidates.
+ Lite bool
+
+ // NAT1To1IPCandidateType is used along with NAT1To1IPs to specify which candidate type
+ // the 1:1 NAT IP addresses should be mapped to.
+ // If unspecified or CandidateTypeHost, NAT1To1IPs are used to replace host candidate IPs.
+ // If CandidateTypeServerReflexive, it will insert a srflx candidate (as if it was dervied
+ // from a STUN server) with its port number being the one for the actual host candidate.
+ // Other values will result in an error.
+ NAT1To1IPCandidateType CandidateType
+
+ // NAT1To1IPs contains a list of public IP addresses that are to be used as a host
+ // candidate or srflx candidate. This is used typically for servers that are behind
+ // 1:1 D-NAT (e.g. AWS EC2 instances) and to eliminate the need of server reflexisive
+ // candidate gathering.
+ NAT1To1IPs []string
+
+ // HostAcceptanceMinWait specify a minimum wait time before selecting host candidates
+ HostAcceptanceMinWait *time.Duration
+ // HostAcceptanceMinWait specify a minimum wait time before selecting srflx candidates
+ SrflxAcceptanceMinWait *time.Duration
+ // HostAcceptanceMinWait specify a minimum wait time before selecting prflx candidates
+ PrflxAcceptanceMinWait *time.Duration
+ // HostAcceptanceMinWait specify a minimum wait time before selecting relay candidates
+ RelayAcceptanceMinWait *time.Duration
+
+ // Net is the our abstracted network interface for internal development purpose only
+ // (see github.com/pion/transport/vnet)
+ Net *vnet.Net
+
+ // InterfaceFilter is a function that you can use in order to whitelist or blacklist
+ // the interfaces which are used to gather ICE candidates.
+ InterfaceFilter func(string) bool
+
+ // InsecureSkipVerify controls if self-signed certificates are accepted when connecting
+ // to TURN servers via TLS or DTLS
+ InsecureSkipVerify bool
+
+ // TCPMux will be used for multiplexing incoming TCP connections for ICE TCP.
+ // Currently only passive candidates are supported. This functionality is
+ // experimental and the API might change in the future.
+ TCPMux TCPMux
+
+ // Proxy Dialer is a dialer that should be implemented by the user based on golang.org/x/net/proxy
+ // dial interface in order to support corporate proxies
+ ProxyDialer proxy.Dialer
+}
+
+// initWithDefaults populates an agent and falls back to defaults if fields are unset
+func (config *AgentConfig) initWithDefaults(a *Agent) {
+ if config.MaxBindingRequests == nil {
+ a.maxBindingRequests = defaultMaxBindingRequests
+ } else {
+ a.maxBindingRequests = *config.MaxBindingRequests
+ }
+
+ if config.HostAcceptanceMinWait == nil {
+ a.hostAcceptanceMinWait = defaultHostAcceptanceMinWait
+ } else {
+ a.hostAcceptanceMinWait = *config.HostAcceptanceMinWait
+ }
+
+ if config.SrflxAcceptanceMinWait == nil {
+ a.srflxAcceptanceMinWait = defaultSrflxAcceptanceMinWait
+ } else {
+ a.srflxAcceptanceMinWait = *config.SrflxAcceptanceMinWait
+ }
+
+ if config.PrflxAcceptanceMinWait == nil {
+ a.prflxAcceptanceMinWait = defaultPrflxAcceptanceMinWait
+ } else {
+ a.prflxAcceptanceMinWait = *config.PrflxAcceptanceMinWait
+ }
+
+ if config.RelayAcceptanceMinWait == nil {
+ a.relayAcceptanceMinWait = defaultRelayAcceptanceMinWait
+ } else {
+ a.relayAcceptanceMinWait = *config.RelayAcceptanceMinWait
+ }
+
+ if config.DisconnectedTimeout == nil {
+ a.disconnectedTimeout = defaultDisconnectedTimeout
+ } else {
+ a.disconnectedTimeout = *config.DisconnectedTimeout
+ }
+
+ if config.FailedTimeout == nil {
+ a.failedTimeout = defaultFailedTimeout
+ } else {
+ a.failedTimeout = *config.FailedTimeout
+ }
+
+ if config.KeepaliveInterval == nil {
+ a.keepaliveInterval = defaultKeepaliveInterval
+ } else {
+ a.keepaliveInterval = *config.KeepaliveInterval
+ }
+
+ if config.checkInterval == 0 {
+ a.checkInterval = defaultCheckInterval
+ } else {
+ a.checkInterval = config.checkInterval
+ }
+
+ if config.CandidateTypes == nil || len(config.CandidateTypes) == 0 {
+ a.candidateTypes = defaultCandidateTypes()
+ } else {
+ a.candidateTypes = config.CandidateTypes
+ }
+}
+
+func (config *AgentConfig) initExtIPMapping(a *Agent) error {
+ var err error
+ a.extIPMapper, err = newExternalIPMapper(config.NAT1To1IPCandidateType, config.NAT1To1IPs)
+ if err != nil {
+ return err
+ }
+ if a.extIPMapper == nil {
+ return nil // this may happen when config.NAT1To1IPs is an empty array
+ }
+ if a.extIPMapper.candidateType == CandidateTypeHost {
+ if a.mDNSMode == MulticastDNSModeQueryAndGather {
+ return ErrMulticastDNSWithNAT1To1IPMapping
+ }
+ candiHostEnabled := false
+ for _, candiType := range a.candidateTypes {
+ if candiType == CandidateTypeHost {
+ candiHostEnabled = true
+ break
+ }
+ }
+ if !candiHostEnabled {
+ return ErrIneffectiveNAT1To1IPMappingHost
+ }
+ } else if a.extIPMapper.candidateType == CandidateTypeServerReflexive {
+ candiSrflxEnabled := false
+ for _, candiType := range a.candidateTypes {
+ if candiType == CandidateTypeServerReflexive {
+ candiSrflxEnabled = true
+ break
+ }
+ }
+ if !candiSrflxEnabled {
+ return ErrIneffectiveNAT1To1IPMappingSrflx
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/ice/v2/agent_stats.go b/vendor/github.com/pion/ice/v2/agent_stats.go
new file mode 100644
index 0000000..18d9ed8
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/agent_stats.go
@@ -0,0 +1,113 @@
+package ice
+
+import (
+ "context"
+ "time"
+)
+
+// GetCandidatePairsStats returns a list of candidate pair stats
+func (a *Agent) GetCandidatePairsStats() []CandidatePairStats {
+ var res []CandidatePairStats
+ err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ result := make([]CandidatePairStats, 0, len(agent.checklist))
+ for _, cp := range agent.checklist {
+ stat := CandidatePairStats{
+ Timestamp: time.Now(),
+ LocalCandidateID: cp.local.ID(),
+ RemoteCandidateID: cp.remote.ID(),
+ State: cp.state,
+ Nominated: cp.nominated,
+ // PacketsSent uint32
+ // PacketsReceived uint32
+ // BytesSent uint64
+ // BytesReceived uint64
+ // LastPacketSentTimestamp time.Time
+ // LastPacketReceivedTimestamp time.Time
+ // FirstRequestTimestamp time.Time
+ // LastRequestTimestamp time.Time
+ // LastResponseTimestamp time.Time
+ // TotalRoundTripTime float64
+ // CurrentRoundTripTime float64
+ // AvailableOutgoingBitrate float64
+ // AvailableIncomingBitrate float64
+ // CircuitBreakerTriggerCount uint32
+ // RequestsReceived uint64
+ // RequestsSent uint64
+ // ResponsesReceived uint64
+ // ResponsesSent uint64
+ // RetransmissionsReceived uint64
+ // RetransmissionsSent uint64
+ // ConsentRequestsSent uint64
+ // ConsentExpiredTimestamp time.Time
+ }
+ result = append(result, stat)
+ }
+ res = result
+ })
+ if err != nil {
+ a.log.Errorf("error getting candidate pairs stats %v", err)
+ return []CandidatePairStats{}
+ }
+ return res
+}
+
+// GetLocalCandidatesStats returns a list of local candidates stats
+func (a *Agent) GetLocalCandidatesStats() []CandidateStats {
+ var res []CandidateStats
+ err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ result := make([]CandidateStats, 0, len(agent.localCandidates))
+ for networkType, localCandidates := range agent.localCandidates {
+ for _, c := range localCandidates {
+ stat := CandidateStats{
+ Timestamp: time.Now(),
+ ID: c.ID(),
+ NetworkType: networkType,
+ IP: c.Address(),
+ Port: c.Port(),
+ CandidateType: c.Type(),
+ Priority: c.Priority(),
+ // URL string
+ RelayProtocol: "udp",
+ // Deleted bool
+ }
+ result = append(result, stat)
+ }
+ }
+ res = result
+ })
+ if err != nil {
+ a.log.Errorf("error getting candidate pairs stats %v", err)
+ return []CandidateStats{}
+ }
+ return res
+}
+
+// GetRemoteCandidatesStats returns a list of remote candidates stats
+func (a *Agent) GetRemoteCandidatesStats() []CandidateStats {
+ var res []CandidateStats
+ err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ result := make([]CandidateStats, 0, len(agent.remoteCandidates))
+ for networkType, localCandidates := range agent.remoteCandidates {
+ for _, c := range localCandidates {
+ stat := CandidateStats{
+ Timestamp: time.Now(),
+ ID: c.ID(),
+ NetworkType: networkType,
+ IP: c.Address(),
+ Port: c.Port(),
+ CandidateType: c.Type(),
+ Priority: c.Priority(),
+ // URL string
+ RelayProtocol: "udp",
+ }
+ result = append(result, stat)
+ }
+ }
+ res = result
+ })
+ if err != nil {
+ a.log.Errorf("error getting candidate pairs stats %v", err)
+ return []CandidateStats{}
+ }
+ return res
+}
diff --git a/vendor/github.com/pion/ice/v2/candidate.go b/vendor/github.com/pion/ice/v2/candidate.go
new file mode 100644
index 0000000..701f8f8
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidate.go
@@ -0,0 +1,68 @@
+package ice
+
+import (
+ "context"
+ "net"
+ "time"
+)
+
+const (
+ receiveMTU = 8192
+ defaultLocalPreference = 65535
+
+ // ComponentRTP indicates that the candidate is used for RTP
+ ComponentRTP uint16 = 1
+ // ComponentRTCP indicates that the candidate is used for RTCP
+ ComponentRTCP
+)
+
+// Candidate represents an ICE candidate
+type Candidate interface {
+ // An arbitrary string used in the freezing algorithm to
+ // group similar candidates. It is the same for two candidates that
+ // have the same type, base IP address, protocol (UDP, TCP, etc.),
+ // and STUN or TURN server.
+ Foundation() string
+
+ // ID is a unique identifier for just this candidate
+ // Unlike the foundation this is different for each candidate
+ ID() string
+
+ // A component is a piece of a data stream.
+ // An example is one for RTP, and one for RTCP
+ Component() uint16
+ SetComponent(uint16)
+
+ // The last time this candidate received traffic
+ LastReceived() time.Time
+
+ // The last time this candidate sent traffic
+ LastSent() time.Time
+
+ NetworkType() NetworkType
+ Address() string
+ Port() int
+
+ Priority() uint32
+
+ // A transport address related to a
+ // candidate, which is useful for diagnostics and other purposes
+ RelatedAddress() *CandidateRelatedAddress
+
+ String() string
+ Type() CandidateType
+ TCPType() TCPType
+
+ Equal(other Candidate) bool
+
+ Marshal() string
+
+ addr() net.Addr
+ agent() *Agent
+ context() context.Context
+
+ close() error
+ seen(outbound bool)
+ start(a *Agent, conn net.PacketConn, initializedCh <-chan struct{})
+ writeTo(raw []byte, dst Candidate) (int, error)
+}
diff --git a/vendor/github.com/pion/ice/v2/candidate_base.go b/vendor/github.com/pion/ice/v2/candidate_base.go
new file mode 100644
index 0000000..2f16a8a
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidate_base.go
@@ -0,0 +1,496 @@
+package ice
+
+import (
+ "context"
+ "fmt"
+ "hash/crc32"
+ "net"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+)
+
+type candidateBase struct {
+ id string
+ networkType NetworkType
+ candidateType CandidateType
+
+ component uint16
+ address string
+ port int
+ relatedAddress *CandidateRelatedAddress
+ tcpType TCPType
+
+ resolvedAddr net.Addr
+
+ lastSent atomic.Value
+ lastReceived atomic.Value
+ conn net.PacketConn
+
+ currAgent *Agent
+ closeCh chan struct{}
+ closedCh chan struct{}
+
+ foundationOverride string
+ priorityOverride uint32
+}
+
+// Done implements context.Context
+func (c *candidateBase) Done() <-chan struct{} {
+ return c.closeCh
+}
+
+// Err implements context.Context
+func (c *candidateBase) Err() error {
+ select {
+ case <-c.closedCh:
+ return ErrRunCanceled
+ default:
+ return nil
+ }
+}
+
+// Deadline implements context.Context
+func (c *candidateBase) Deadline() (deadline time.Time, ok bool) {
+ return time.Time{}, false
+}
+
+// Value implements context.Context
+func (c *candidateBase) Value(key interface{}) interface{} {
+ return nil
+}
+
+// ID returns Candidate ID
+func (c *candidateBase) ID() string {
+ return c.id
+}
+
+func (c *candidateBase) Foundation() string {
+ if c.foundationOverride != "" {
+ return c.foundationOverride
+ }
+
+ return fmt.Sprintf("%d", crc32.ChecksumIEEE([]byte(c.Type().String()+c.address+c.networkType.String())))
+}
+
+// Address returns Candidate Address
+func (c *candidateBase) Address() string {
+ return c.address
+}
+
+// Port returns Candidate Port
+func (c *candidateBase) Port() int {
+ return c.port
+}
+
+// Type returns candidate type
+func (c *candidateBase) Type() CandidateType {
+ return c.candidateType
+}
+
+// NetworkType returns candidate NetworkType
+func (c *candidateBase) NetworkType() NetworkType {
+ return c.networkType
+}
+
+// Component returns candidate component
+func (c *candidateBase) Component() uint16 {
+ return c.component
+}
+
+func (c *candidateBase) SetComponent(component uint16) {
+ c.component = component
+}
+
+// LocalPreference returns the local preference for this candidate
+func (c *candidateBase) LocalPreference() uint16 {
+ if c.NetworkType().IsTCP() {
+ // RFC 6544, section 4.2
+ //
+ // In Section 4.1.2.1 of [RFC5245], a recommended formula for UDP ICE
+ // candidate prioritization is defined. For TCP candidates, the same
+ // formula and candidate type preferences SHOULD be used, and the
+ // RECOMMENDED type preferences for the new candidate types defined in
+ // this document (see Section 5) are 105 for NAT-assisted candidates and
+ // 75 for UDP-tunneled candidates.
+ //
+ // (...)
+ //
+ // With TCP candidates, the local preference part of the recommended
+ // priority formula is updated to also include the directionality
+ // (active, passive, or simultaneous-open) of the TCP connection. The
+ // RECOMMENDED local preference is then defined as:
+ //
+ // local preference = (2^13) * direction-pref + other-pref
+ //
+ // The direction-pref MUST be between 0 and 7 (both inclusive), with 7
+ // being the most preferred. The other-pref MUST be between 0 and 8191
+ // (both inclusive), with 8191 being the most preferred. It is
+ // RECOMMENDED that the host, UDP-tunneled, and relayed TCP candidates
+ // have the direction-pref assigned as follows: 6 for active, 4 for
+ // passive, and 2 for S-O. For the NAT-assisted and server reflexive
+ // candidates, the RECOMMENDED values are: 6 for S-O, 4 for active, and
+ // 2 for passive.
+ //
+ // (...)
+ //
+ // If any two candidates have the same type-preference and direction-
+ // pref, they MUST have a unique other-pref. With this specification,
+ // this usually only happens with multi-homed hosts, in which case
+ // other-pref is the preference for the particular IP address from which
+ // the candidate was obtained. When there is only a single IP address,
+ // this value SHOULD be set to the maximum allowed value (8191).
+ var otherPref uint16 = 8191
+
+ directionPref := func() uint16 {
+ switch c.Type() {
+ case CandidateTypeHost, CandidateTypeRelay:
+ switch c.tcpType {
+ case TCPTypeActive:
+ return 6
+ case TCPTypePassive:
+ return 4
+ case TCPTypeSimultaneousOpen:
+ return 2
+ case TCPTypeUnspecified:
+ return 0
+ }
+ case CandidateTypePeerReflexive, CandidateTypeServerReflexive:
+ switch c.tcpType {
+ case TCPTypeSimultaneousOpen:
+ return 6
+ case TCPTypeActive:
+ return 4
+ case TCPTypePassive:
+ return 2
+ case TCPTypeUnspecified:
+ return 0
+ }
+ case CandidateTypeUnspecified:
+ return 0
+ }
+ return 0
+ }()
+
+ return (1<<13)*directionPref + otherPref
+ }
+
+ return defaultLocalPreference
+}
+
+// RelatedAddress returns *CandidateRelatedAddress
+func (c *candidateBase) RelatedAddress() *CandidateRelatedAddress {
+ return c.relatedAddress
+}
+
+func (c *candidateBase) TCPType() TCPType {
+ return c.tcpType
+}
+
+// start runs the candidate using the provided connection
+func (c *candidateBase) start(a *Agent, conn net.PacketConn, initializedCh <-chan struct{}) {
+ if c.conn != nil {
+ c.agent().log.Warn("Can't start already started candidateBase")
+ return
+ }
+ c.currAgent = a
+ c.conn = conn
+ c.closeCh = make(chan struct{})
+ c.closedCh = make(chan struct{})
+
+ go c.recvLoop(initializedCh)
+}
+
+func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) {
+ defer func() {
+ close(c.closedCh)
+ }()
+
+ select {
+ case <-initializedCh:
+ case <-c.closeCh:
+ return
+ }
+
+ log := c.agent().log
+ buffer := make([]byte, receiveMTU)
+ for {
+ n, srcAddr, err := c.conn.ReadFrom(buffer)
+ if err != nil {
+ return
+ }
+
+ handleInboundCandidateMsg(c, c, buffer[:n], srcAddr, log)
+ }
+}
+
+func handleInboundCandidateMsg(ctx context.Context, c Candidate, buffer []byte, srcAddr net.Addr, log logging.LeveledLogger) {
+ if stun.IsMessage(buffer) {
+ m := &stun.Message{
+ Raw: make([]byte, len(buffer)),
+ }
+ // Explicitly copy raw buffer so Message can own the memory.
+ copy(m.Raw, buffer)
+ if err := m.Decode(); err != nil {
+ log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err)
+ return
+ }
+ err := c.agent().run(ctx, func(ctx context.Context, agent *Agent) {
+ agent.handleInbound(m, c, srcAddr)
+ })
+ if err != nil {
+ log.Warnf("Failed to handle message: %v", err)
+ }
+
+ return
+ }
+
+ if !c.agent().validateNonSTUNTraffic(c, srcAddr) {
+ log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr())
+ return
+ }
+
+ // NOTE This will return packetio.ErrFull if the buffer ever manages to fill up.
+ if _, err := c.agent().buffer.Write(buffer); err != nil {
+ log.Warnf("failed to write packet")
+ }
+}
+
+// close stops the recvLoop
+func (c *candidateBase) close() error {
+ // If conn has never been started will be nil
+ if c.Done() == nil {
+ return nil
+ }
+
+ // Assert that conn has not already been closed
+ select {
+ case <-c.Done():
+ return nil
+ default:
+ }
+
+ var firstErr error
+
+ // Unblock recvLoop
+ close(c.closeCh)
+ if err := c.conn.SetDeadline(time.Now()); err != nil {
+ firstErr = err
+ }
+
+ // Close the conn
+ if err := c.conn.Close(); err != nil && firstErr == nil {
+ firstErr = err
+ }
+
+ if firstErr != nil {
+ return firstErr
+ }
+
+ // Wait until the recvLoop is closed
+ <-c.closedCh
+
+ return nil
+}
+
+func (c *candidateBase) writeTo(raw []byte, dst Candidate) (int, error) {
+ n, err := c.conn.WriteTo(raw, dst.addr())
+ if err != nil {
+ c.agent().log.Warnf("%s: %v", errSendPacket, err)
+ return n, nil
+ }
+ c.seen(true)
+ return n, nil
+}
+
+// Priority computes the priority for this ICE Candidate
+func (c *candidateBase) Priority() uint32 {
+ if c.priorityOverride != 0 {
+ return c.priorityOverride
+ }
+
+ // The local preference MUST be an integer from 0 (lowest preference) to
+ // 65535 (highest preference) inclusive. When there is only a single IP
+ // address, this value SHOULD be set to 65535. If there are multiple
+ // candidates for a particular component for a particular data stream
+ // that have the same type, the local preference MUST be unique for each
+ // one.
+ return (1<<24)*uint32(c.Type().Preference()) +
+ (1<<8)*uint32(c.LocalPreference()) +
+ uint32(256-c.Component())
+}
+
+// Equal is used to compare two candidateBases
+func (c *candidateBase) Equal(other Candidate) bool {
+ return c.NetworkType() == other.NetworkType() &&
+ c.Type() == other.Type() &&
+ c.Address() == other.Address() &&
+ c.Port() == other.Port() &&
+ c.TCPType() == other.TCPType() &&
+ c.RelatedAddress().Equal(other.RelatedAddress())
+}
+
+// String makes the candidateBase printable
+func (c *candidateBase) String() string {
+ return fmt.Sprintf("%s %s %s:%d%s", c.NetworkType(), c.Type(), c.Address(), c.Port(), c.relatedAddress)
+}
+
+// LastReceived returns a time.Time indicating the last time
+// this candidate was received
+func (c *candidateBase) LastReceived() time.Time {
+ lastReceived := c.lastReceived.Load()
+ if lastReceived == nil {
+ return time.Time{}
+ }
+ return lastReceived.(time.Time)
+}
+
+func (c *candidateBase) setLastReceived(t time.Time) {
+ c.lastReceived.Store(t)
+}
+
+// LastSent returns a time.Time indicating the last time
+// this candidate was sent
+func (c *candidateBase) LastSent() time.Time {
+ lastSent := c.lastSent.Load()
+ if lastSent == nil {
+ return time.Time{}
+ }
+ return lastSent.(time.Time)
+}
+
+func (c *candidateBase) setLastSent(t time.Time) {
+ c.lastSent.Store(t)
+}
+
+func (c *candidateBase) seen(outbound bool) {
+ if outbound {
+ c.setLastSent(time.Now())
+ } else {
+ c.setLastReceived(time.Now())
+ }
+}
+
+func (c *candidateBase) addr() net.Addr {
+ return c.resolvedAddr
+}
+
+func (c *candidateBase) agent() *Agent {
+ return c.currAgent
+}
+
+func (c *candidateBase) context() context.Context {
+ return c
+}
+
+// Marshal returns the string representation of the ICECandidate
+func (c *candidateBase) Marshal() string {
+ val := fmt.Sprintf("%s %d %s %d %s %d typ %s",
+ c.Foundation(),
+ c.Component(),
+ c.NetworkType().NetworkShort(),
+ c.Priority(),
+ c.Address(),
+ c.Port(),
+ c.Type())
+
+ if c.tcpType != TCPTypeUnspecified {
+ val += fmt.Sprintf(" tcptype %s", c.tcpType.String())
+ }
+
+ if c.RelatedAddress() != nil {
+ val = fmt.Sprintf("%s raddr %s rport %d",
+ val,
+ c.RelatedAddress().Address,
+ c.RelatedAddress().Port)
+ }
+
+ return val
+}
+
+// UnmarshalCandidate creates a Candidate from its string representation
+func UnmarshalCandidate(raw string) (Candidate, error) {
+ split := strings.Fields(raw)
+ if len(split) < 8 {
+ return nil, fmt.Errorf("%w (%d)", errAttributeTooShortICECandidate, len(split))
+ }
+
+ // Foundation
+ foundation := split[0]
+
+ // Component
+ rawComponent, err := strconv.ParseUint(split[1], 10, 16)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errParseComponent, err)
+ }
+ component := uint16(rawComponent)
+
+ // Protocol
+ protocol := split[2]
+
+ // Priority
+ priorityRaw, err := strconv.ParseUint(split[3], 10, 32)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errParsePriority, err)
+ }
+ priority := uint32(priorityRaw)
+
+ // Address
+ address := split[4]
+
+ // Port
+ rawPort, err := strconv.ParseUint(split[5], 10, 16)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errParsePort, err)
+ }
+ port := int(rawPort)
+ typ := split[7]
+
+ relatedAddress := ""
+ relatedPort := 0
+ tcpType := TCPTypeUnspecified
+
+ if len(split) > 8 {
+ split = split[8:]
+
+ if split[0] == "raddr" {
+ if len(split) < 4 {
+ return nil, fmt.Errorf("%w: incorrect length", errParseRelatedAddr)
+ }
+
+ // RelatedAddress
+ relatedAddress = split[1]
+
+ // RelatedPort
+ rawRelatedPort, parseErr := strconv.ParseUint(split[3], 10, 16)
+ if parseErr != nil {
+ return nil, fmt.Errorf("%w: %v", errParsePort, parseErr)
+ }
+ relatedPort = int(rawRelatedPort)
+ } else if split[0] == "tcptype" {
+ if len(split) < 2 {
+ return nil, fmt.Errorf("%w: incorrect length", errParseTypType)
+ }
+
+ tcpType = NewTCPType(split[1])
+ }
+ }
+
+ switch typ {
+ case "host":
+ return NewCandidateHost(&CandidateHostConfig{"", protocol, address, port, component, priority, foundation, tcpType})
+ case "srflx":
+ return NewCandidateServerReflexive(&CandidateServerReflexiveConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort})
+ case "prflx":
+ return NewCandidatePeerReflexive(&CandidatePeerReflexiveConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort})
+ case "relay":
+ return NewCandidateRelay(&CandidateRelayConfig{"", protocol, address, port, component, priority, foundation, relatedAddress, relatedPort, nil})
+ default:
+ }
+
+ return nil, fmt.Errorf("%w (%s)", errUnknownCandidateTyp, typ)
+}
diff --git a/vendor/github.com/pion/ice/v2/candidate_host.go b/vendor/github.com/pion/ice/v2/candidate_host.go
new file mode 100644
index 0000000..b03dbdb
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidate_host.go
@@ -0,0 +1,76 @@
+package ice
+
+import (
+ "net"
+ "strings"
+)
+
+// CandidateHost is a candidate of type host
+type CandidateHost struct {
+ candidateBase
+
+ network string
+}
+
+// CandidateHostConfig is the config required to create a new CandidateHost
+type CandidateHostConfig struct {
+ CandidateID string
+ Network string
+ Address string
+ Port int
+ Component uint16
+ Priority uint32
+ Foundation string
+ TCPType TCPType
+}
+
+// NewCandidateHost creates a new host candidate
+func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) {
+ candidateID := config.CandidateID
+
+ if candidateID == "" {
+ candidateID = globalCandidateIDGenerator.Generate()
+ }
+
+ c := &CandidateHost{
+ candidateBase: candidateBase{
+ id: candidateID,
+ address: config.Address,
+ candidateType: CandidateTypeHost,
+ component: config.Component,
+ port: config.Port,
+ tcpType: config.TCPType,
+ foundationOverride: config.Foundation,
+ priorityOverride: config.Priority,
+ },
+ network: config.Network,
+ }
+
+ if !strings.HasSuffix(config.Address, ".local") {
+ ip := net.ParseIP(config.Address)
+ if ip == nil {
+ return nil, ErrAddressParseFailed
+ }
+
+ if err := c.setIP(ip); err != nil {
+ return nil, err
+ }
+ } else {
+ // Until mDNS candidate is resolved assume it is UDPv4
+ c.candidateBase.networkType = NetworkTypeUDP4
+ }
+
+ return c, nil
+}
+
+func (c *CandidateHost) setIP(ip net.IP) error {
+ networkType, err := determineNetworkType(c.network, ip)
+ if err != nil {
+ return err
+ }
+
+ c.candidateBase.networkType = networkType
+ c.candidateBase.resolvedAddr = createAddr(networkType, ip, c.port)
+
+ return nil
+}
diff --git a/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go b/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go
new file mode 100644
index 0000000..0b330d1
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go
@@ -0,0 +1,60 @@
+// Package ice ...
+//nolint:dupl
+package ice
+
+import "net"
+
+// CandidatePeerReflexive ...
+type CandidatePeerReflexive struct {
+ candidateBase
+}
+
+// CandidatePeerReflexiveConfig is the config required to create a new CandidatePeerReflexive
+type CandidatePeerReflexiveConfig struct {
+ CandidateID string
+ Network string
+ Address string
+ Port int
+ Component uint16
+ Priority uint32
+ Foundation string
+ RelAddr string
+ RelPort int
+}
+
+// NewCandidatePeerReflexive creates a new peer reflective candidate
+func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*CandidatePeerReflexive, error) {
+ ip := net.ParseIP(config.Address)
+ if ip == nil {
+ return nil, ErrAddressParseFailed
+ }
+
+ networkType, err := determineNetworkType(config.Network, ip)
+ if err != nil {
+ return nil, err
+ }
+
+ candidateID := config.CandidateID
+ candidateIDGenerator := newCandidateIDGenerator()
+ if candidateID == "" {
+ candidateID = candidateIDGenerator.Generate()
+ }
+
+ return &CandidatePeerReflexive{
+ candidateBase: candidateBase{
+ id: candidateID,
+ networkType: networkType,
+ candidateType: CandidateTypePeerReflexive,
+ address: config.Address,
+ port: config.Port,
+ resolvedAddr: createAddr(networkType, ip, config.Port),
+ component: config.Component,
+ foundationOverride: config.Foundation,
+ priorityOverride: config.Priority,
+ relatedAddress: &CandidateRelatedAddress{
+ Address: config.RelAddr,
+ Port: config.RelPort,
+ },
+ },
+ }, nil
+}
diff --git a/vendor/github.com/pion/ice/v2/candidate_relay.go b/vendor/github.com/pion/ice/v2/candidate_relay.go
new file mode 100644
index 0000000..44762f7
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidate_relay.go
@@ -0,0 +1,73 @@
+package ice
+
+import (
+ "net"
+)
+
+// CandidateRelay ...
+type CandidateRelay struct {
+ candidateBase
+
+ onClose func() error
+}
+
+// CandidateRelayConfig is the config required to create a new CandidateRelay
+type CandidateRelayConfig struct {
+ CandidateID string
+ Network string
+ Address string
+ Port int
+ Component uint16
+ Priority uint32
+ Foundation string
+ RelAddr string
+ RelPort int
+ OnClose func() error
+}
+
+// NewCandidateRelay creates a new relay candidate
+func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) {
+ candidateID := config.CandidateID
+
+ if candidateID == "" {
+ candidateID = globalCandidateIDGenerator.Generate()
+ }
+
+ ip := net.ParseIP(config.Address)
+ if ip == nil {
+ return nil, ErrAddressParseFailed
+ }
+
+ networkType, err := determineNetworkType(config.Network, ip)
+ if err != nil {
+ return nil, err
+ }
+
+ return &CandidateRelay{
+ candidateBase: candidateBase{
+ id: candidateID,
+ networkType: networkType,
+ candidateType: CandidateTypeRelay,
+ address: config.Address,
+ port: config.Port,
+ resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port},
+ component: config.Component,
+ foundationOverride: config.Foundation,
+ priorityOverride: config.Priority,
+ relatedAddress: &CandidateRelatedAddress{
+ Address: config.RelAddr,
+ Port: config.RelPort,
+ },
+ },
+ onClose: config.OnClose,
+ }, nil
+}
+
+func (c *CandidateRelay) close() error {
+ err := c.candidateBase.close()
+ if c.onClose != nil {
+ err = c.onClose()
+ c.onClose = nil
+ }
+ return err
+}
diff --git a/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go b/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go
new file mode 100644
index 0000000..125a537
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go
@@ -0,0 +1,59 @@
+// Package ice ...
+//nolint:dupl
+package ice
+
+import "net"
+
+// CandidateServerReflexive ...
+type CandidateServerReflexive struct {
+ candidateBase
+}
+
+// CandidateServerReflexiveConfig is the config required to create a new CandidateServerReflexive
+type CandidateServerReflexiveConfig struct {
+ CandidateID string
+ Network string
+ Address string
+ Port int
+ Component uint16
+ Priority uint32
+ Foundation string
+ RelAddr string
+ RelPort int
+}
+
+// NewCandidateServerReflexive creates a new server reflective candidate
+func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*CandidateServerReflexive, error) {
+ ip := net.ParseIP(config.Address)
+ if ip == nil {
+ return nil, ErrAddressParseFailed
+ }
+
+ networkType, err := determineNetworkType(config.Network, ip)
+ if err != nil {
+ return nil, err
+ }
+
+ candidateID := config.CandidateID
+ if candidateID == "" {
+ candidateID = globalCandidateIDGenerator.Generate()
+ }
+
+ return &CandidateServerReflexive{
+ candidateBase: candidateBase{
+ id: candidateID,
+ networkType: networkType,
+ candidateType: CandidateTypeServerReflexive,
+ address: config.Address,
+ port: config.Port,
+ resolvedAddr: &net.UDPAddr{IP: ip, Port: config.Port},
+ component: config.Component,
+ foundationOverride: config.Foundation,
+ priorityOverride: config.Priority,
+ relatedAddress: &CandidateRelatedAddress{
+ Address: config.RelAddr,
+ Port: config.RelPort,
+ },
+ },
+ }, nil
+}
diff --git a/vendor/github.com/pion/ice/v2/candidatepair.go b/vendor/github.com/pion/ice/v2/candidatepair.go
new file mode 100644
index 0000000..49dec37
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidatepair.go
@@ -0,0 +1,98 @@
+package ice
+
+import (
+ "fmt"
+
+ "github.com/pion/stun"
+)
+
+func newCandidatePair(local, remote Candidate, controlling bool) *candidatePair {
+ return &candidatePair{
+ iceRoleControlling: controlling,
+ remote: remote,
+ local: local,
+ state: CandidatePairStateWaiting,
+ }
+}
+
+// candidatePair represents a combination of a local and remote candidate
+type candidatePair struct {
+ iceRoleControlling bool
+ remote Candidate
+ local Candidate
+ bindingRequestCount uint16
+ state CandidatePairState
+ nominated bool
+}
+
+func (p *candidatePair) String() string {
+ if p == nil {
+ return ""
+ }
+
+ return fmt.Sprintf("prio %d (local, prio %d) %s <-> %s (remote, prio %d)",
+ p.Priority(), p.local.Priority(), p.local, p.remote, p.remote.Priority())
+}
+
+func (p *candidatePair) Equal(other *candidatePair) bool {
+ if p == nil && other == nil {
+ return true
+ }
+ if p == nil || other == nil {
+ return false
+ }
+ return p.local.Equal(other.local) && p.remote.Equal(other.remote)
+}
+
+// RFC 5245 - 5.7.2. Computing Pair Priority and Ordering Pairs
+// Let G be the priority for the candidate provided by the controlling
+// agent. Let D be the priority for the candidate provided by the
+// controlled agent.
+// pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0)
+func (p *candidatePair) Priority() uint64 {
+ var g uint32
+ var d uint32
+ if p.iceRoleControlling {
+ g = p.local.Priority()
+ d = p.remote.Priority()
+ } else {
+ g = p.remote.Priority()
+ d = p.local.Priority()
+ }
+
+ // Just implement these here rather
+ // than fooling around with the math package
+ min := func(x, y uint32) uint64 {
+ if x < y {
+ return uint64(x)
+ }
+ return uint64(y)
+ }
+ max := func(x, y uint32) uint64 {
+ if x > y {
+ return uint64(x)
+ }
+ return uint64(y)
+ }
+ cmp := func(x, y uint32) uint64 {
+ if x > y {
+ return uint64(1)
+ }
+ return uint64(0)
+ }
+
+ // 1<<32 overflows uint32; and if both g && d are
+ // maxUint32, this result would overflow uint64
+ return (1<<32-1)*min(g, d) + 2*max(g, d) + cmp(g, d)
+}
+
+func (p *candidatePair) Write(b []byte) (int, error) {
+ return p.local.writeTo(b, p.remote)
+}
+
+func (a *Agent) sendSTUN(msg *stun.Message, local, remote Candidate) {
+ _, err := local.writeTo(msg.Raw, remote)
+ if err != nil {
+ a.log.Tracef("failed to send STUN message: %s", err)
+ }
+}
diff --git a/vendor/github.com/pion/ice/v2/candidatepair_state.go b/vendor/github.com/pion/ice/v2/candidatepair_state.go
new file mode 100644
index 0000000..28c7187
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidatepair_state.go
@@ -0,0 +1,37 @@
+package ice
+
+// CandidatePairState represent the ICE candidate pair state
+type CandidatePairState int
+
+const (
+ // CandidatePairStateWaiting means a check has not been performed for
+ // this pair
+ CandidatePairStateWaiting = iota + 1
+
+ // CandidatePairStateInProgress means a check has been sent for this pair,
+ // but the transaction is in progress.
+ CandidatePairStateInProgress
+
+ // CandidatePairStateFailed means a check for this pair was already done
+ // and failed, either never producing any response or producing an unrecoverable
+ // failure response.
+ CandidatePairStateFailed
+
+ // CandidatePairStateSucceeded means a check for this pair was already
+ // done and produced a successful result.
+ CandidatePairStateSucceeded
+)
+
+func (c CandidatePairState) String() string {
+ switch c {
+ case CandidatePairStateWaiting:
+ return "waiting"
+ case CandidatePairStateInProgress:
+ return "in-progress"
+ case CandidatePairStateFailed:
+ return "failed"
+ case CandidatePairStateSucceeded:
+ return "succeeded"
+ }
+ return "Unknown candidate pair state"
+}
diff --git a/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go b/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go
new file mode 100644
index 0000000..18cf318
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go
@@ -0,0 +1,30 @@
+package ice
+
+import "fmt"
+
+// CandidateRelatedAddress convey transport addresses related to the
+// candidate, useful for diagnostics and other purposes.
+type CandidateRelatedAddress struct {
+ Address string
+ Port int
+}
+
+// String makes CandidateRelatedAddress printable
+func (c *CandidateRelatedAddress) String() string {
+ if c == nil {
+ return ""
+ }
+
+ return fmt.Sprintf(" related %s:%d", c.Address, c.Port)
+}
+
+// Equal allows comparing two CandidateRelatedAddresses.
+// The CandidateRelatedAddress are allowed to be nil.
+func (c *CandidateRelatedAddress) Equal(other *CandidateRelatedAddress) bool {
+ if c == nil && other == nil {
+ return true
+ }
+ return c != nil && other != nil &&
+ c.Address == other.Address &&
+ c.Port == other.Port
+}
diff --git a/vendor/github.com/pion/ice/v2/candidatetype.go b/vendor/github.com/pion/ice/v2/candidatetype.go
new file mode 100644
index 0000000..376c408
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/candidatetype.go
@@ -0,0 +1,62 @@
+package ice
+
+// CandidateType represents the type of candidate
+type CandidateType byte
+
+// CandidateType enum
+const (
+ CandidateTypeUnspecified CandidateType = iota
+ CandidateTypeHost
+ CandidateTypeServerReflexive
+ CandidateTypePeerReflexive
+ CandidateTypeRelay
+)
+
+// String makes CandidateType printable
+func (c CandidateType) String() string {
+ switch c {
+ case CandidateTypeHost:
+ return "host"
+ case CandidateTypeServerReflexive:
+ return "srflx"
+ case CandidateTypePeerReflexive:
+ return "prflx"
+ case CandidateTypeRelay:
+ return "relay"
+ case CandidateTypeUnspecified:
+ return "Unknown candidate type"
+ }
+ return "Unknown candidate type"
+}
+
+// Preference returns the preference weight of a CandidateType
+//
+// 4.1.2.2. Guidelines for Choosing Type and Local Preferences
+// The RECOMMENDED values are 126 for host candidates, 100
+// for server reflexive candidates, 110 for peer reflexive candidates,
+// and 0 for relayed candidates.
+func (c CandidateType) Preference() uint16 {
+ switch c {
+ case CandidateTypeHost:
+ return 126
+ case CandidateTypePeerReflexive:
+ return 110
+ case CandidateTypeServerReflexive:
+ return 100
+ case CandidateTypeRelay, CandidateTypeUnspecified:
+ return 0
+ }
+ return 0
+}
+
+func containsCandidateType(candidateType CandidateType, candidateTypeList []CandidateType) bool {
+ if candidateTypeList == nil {
+ return false
+ }
+ for _, ct := range candidateTypeList {
+ if ct == candidateType {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/pion/ice/v2/codecov.yml b/vendor/github.com/pion/ice/v2/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/ice/v2/context.go b/vendor/github.com/pion/ice/v2/context.go
new file mode 100644
index 0000000..627d81e
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/context.go
@@ -0,0 +1,37 @@
+package ice
+
+import (
+ "context"
+ "time"
+)
+
+func (a *Agent) context() context.Context {
+ return agentContext(a.done)
+}
+
+type agentContext chan struct{}
+
+// Done implements context.Context
+func (a agentContext) Done() <-chan struct{} {
+ return (chan struct{})(a)
+}
+
+// Err implements context.Context
+func (a agentContext) Err() error {
+ select {
+ case <-(chan struct{})(a):
+ return ErrRunCanceled
+ default:
+ return nil
+ }
+}
+
+// Deadline implements context.Context
+func (a agentContext) Deadline() (deadline time.Time, ok bool) {
+ return time.Time{}, false
+}
+
+// Value implements context.Context
+func (a agentContext) Value(key interface{}) interface{} {
+ return nil
+}
diff --git a/vendor/github.com/pion/ice/v2/errors.go b/vendor/github.com/pion/ice/v2/errors.go
new file mode 100644
index 0000000..e7dd625
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/errors.go
@@ -0,0 +1,132 @@
+package ice
+
+import "errors"
+
+var (
+ // ErrUnknownType indicates an error with Unknown info.
+ ErrUnknownType = errors.New("Unknown")
+
+ // ErrSchemeType indicates the scheme type could not be parsed.
+ ErrSchemeType = errors.New("unknown scheme type")
+
+ // ErrSTUNQuery indicates query arguments are provided in a STUN URL.
+ ErrSTUNQuery = errors.New("queries not supported in stun address")
+
+ // ErrInvalidQuery indicates an malformed query is provided.
+ ErrInvalidQuery = errors.New("invalid query")
+
+ // ErrHost indicates malformed hostname is provided.
+ ErrHost = errors.New("invalid hostname")
+
+ // ErrPort indicates malformed port is provided.
+ ErrPort = errors.New("invalid port")
+
+ // ErrLocalUfragInsufficientBits indicates local username fragment insufficient bits are provided.
+ // Have to be at least 24 bits long
+ ErrLocalUfragInsufficientBits = errors.New("local username fragment is less than 24 bits long")
+
+ // ErrLocalPwdInsufficientBits indicates local passoword insufficient bits are provided.
+ // Have to be at least 128 bits long
+ ErrLocalPwdInsufficientBits = errors.New("local password is less than 128 bits long")
+
+ // ErrProtoType indicates an unsupported transport type was provided.
+ ErrProtoType = errors.New("invalid transport protocol type")
+
+ // ErrClosed indicates the agent is closed
+ ErrClosed = errors.New("the agent is closed")
+
+ // ErrNoCandidatePairs indicates agent does not have a valid candidate pair
+ ErrNoCandidatePairs = errors.New("no candidate pairs available")
+
+ // ErrCanceledByCaller indicates agent connection was canceled by the caller
+ ErrCanceledByCaller = errors.New("connecting canceled by caller")
+
+ // ErrMultipleStart indicates agent was started twice
+ ErrMultipleStart = errors.New("attempted to start agent twice")
+
+ // ErrRemoteUfragEmpty indicates agent was started with an empty remote ufrag
+ ErrRemoteUfragEmpty = errors.New("remote ufrag is empty")
+
+ // ErrRemotePwdEmpty indicates agent was started with an empty remote pwd
+ ErrRemotePwdEmpty = errors.New("remote pwd is empty")
+
+ // ErrNoOnCandidateHandler indicates agent was started without OnCandidate
+ ErrNoOnCandidateHandler = errors.New("no OnCandidate provided")
+
+ // ErrMultipleGatherAttempted indicates GatherCandidates has been called multiple times
+ ErrMultipleGatherAttempted = errors.New("attempting to gather candidates during gathering state")
+
+ // ErrUsernameEmpty indicates agent was give TURN URL with an empty Username
+ ErrUsernameEmpty = errors.New("username is empty")
+
+ // ErrPasswordEmpty indicates agent was give TURN URL with an empty Password
+ ErrPasswordEmpty = errors.New("password is empty")
+
+ // ErrAddressParseFailed indicates we were unable to parse a candidate address
+ ErrAddressParseFailed = errors.New("failed to parse address")
+
+ // ErrLiteUsingNonHostCandidates indicates non host candidates were selected for a lite agent
+ ErrLiteUsingNonHostCandidates = errors.New("lite agents must only use host candidates")
+
+ // ErrUselessUrlsProvided indicates that one or more URL was provided to the agent but no host
+ // candidate required them
+ ErrUselessUrlsProvided = errors.New("agent does not need URL with selected candidate types")
+
+ // ErrUnsupportedNAT1To1IPCandidateType indicates that the specified NAT1To1IPCandidateType is
+ // unsupported
+ ErrUnsupportedNAT1To1IPCandidateType = errors.New("unsupported 1:1 NAT IP candidate type")
+
+ // ErrInvalidNAT1To1IPMapping indicates that the given 1:1 NAT IP mapping is invalid
+ ErrInvalidNAT1To1IPMapping = errors.New("invalid 1:1 NAT IP mapping")
+
+ // ErrExternalMappedIPNotFound in NAT1To1IPMapping
+ ErrExternalMappedIPNotFound = errors.New("external mapped IP not found")
+
+ // ErrMulticastDNSWithNAT1To1IPMapping indicates that the mDNS gathering cannot be used along
+ // with 1:1 NAT IP mapping for host candidate.
+ ErrMulticastDNSWithNAT1To1IPMapping = errors.New("mDNS gathering cannot be used with 1:1 NAT IP mapping for host candidate")
+
+ // ErrIneffectiveNAT1To1IPMappingHost indicates that 1:1 NAT IP mapping for host candidate is
+ // requested, but the host candidate type is disabled.
+ ErrIneffectiveNAT1To1IPMappingHost = errors.New("1:1 NAT IP mapping for host candidate ineffective")
+
+ // ErrIneffectiveNAT1To1IPMappingSrflx indicates that 1:1 NAT IP mapping for srflx candidate is
+ // requested, but the srflx candidate type is disabled.
+ ErrIneffectiveNAT1To1IPMappingSrflx = errors.New("1:1 NAT IP mapping for srflx candidate ineffective")
+
+ // ErrInvalidMulticastDNSHostName indicates an invalid MulticastDNSHostName
+ ErrInvalidMulticastDNSHostName = errors.New("invalid mDNS HostName, must end with .local and can only contain a single '.'")
+
+ // ErrRestartWhenGathering indicates Restart was called when Agent is in GatheringStateGathering
+ ErrRestartWhenGathering = errors.New("ICE Agent can not be restarted when gathering")
+
+ // ErrRunCanceled indicates a run operation was canceled by its individual done
+ ErrRunCanceled = errors.New("run was canceled by done")
+
+ // ErrTCPMuxNotInitialized indicates TCPMux is not initialized and that invalidTCPMux is used.
+ ErrTCPMuxNotInitialized = errors.New("TCPMux is not initialized")
+
+ // ErrTCPRemoteAddrAlreadyExists indicates we already have the connection with same remote addr.
+ ErrTCPRemoteAddrAlreadyExists = errors.New("conn with same remote addr already exists")
+
+ errSendPacket = errors.New("failed to send packet")
+ errAttributeTooShortICECandidate = errors.New("attribute not long enough to be ICE candidate")
+ errParseComponent = errors.New("could not parse component")
+ errParsePriority = errors.New("could not parse priority")
+ errParsePort = errors.New("could not parse port")
+ errParseRelatedAddr = errors.New("could not parse related addresses")
+ errParseTypType = errors.New("could not parse typtype")
+ errUnknownCandidateTyp = errors.New("unknown candidate typ")
+ errGetXorMappedAddrResponse = errors.New("failed to get XOR-MAPPED-ADDRESS response")
+ errConnectionAddrAlreadyExist = errors.New("connection with same remote address already exists")
+ errReadingStreamingPacket = errors.New("error reading streaming packet")
+ errWriting = errors.New("error writing to")
+ errClosingConnection = errors.New("error closing connection")
+ errDetermineNetworkType = errors.New("unable to determine networkType")
+ errMissingProtocolScheme = errors.New("missing protocol scheme")
+ errTooManyColonsAddr = errors.New("too many colons in address")
+ errRead = errors.New("unexpected error trying to read")
+ errUnknownRole = errors.New("unknown role")
+ errMismatchUsername = errors.New("username mismatch")
+ errICEWriteSTUNMessage = errors.New("the ICE conn can't write STUN messages")
+)
diff --git a/vendor/github.com/pion/ice/v2/external_ip_mapper.go b/vendor/github.com/pion/ice/v2/external_ip_mapper.go
new file mode 100644
index 0000000..5310cc0
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/external_ip_mapper.go
@@ -0,0 +1,143 @@
+package ice
+
+import (
+ "net"
+ "strings"
+)
+
+func validateIPString(ipStr string) (net.IP, bool, error) {
+ ip := net.ParseIP(ipStr)
+ if ip == nil {
+ return nil, false, ErrInvalidNAT1To1IPMapping
+ }
+ return ip, (ip.To4() != nil), nil
+}
+
+// ipMapping holds the mapping of local and external IP address for a particular IP family
+type ipMapping struct {
+ ipSole net.IP // when non-nil, this is the sole external IP for one local IP assumed
+ ipMap map[string]net.IP // local-to-external IP mapping (k: local, v: external)
+}
+
+func (m *ipMapping) setSoleIP(ip net.IP) error {
+ if m.ipSole != nil || len(m.ipMap) > 0 {
+ return ErrInvalidNAT1To1IPMapping
+ }
+
+ m.ipSole = ip
+
+ return nil
+}
+
+func (m *ipMapping) addIPMapping(locIP, extIP net.IP) error {
+ if m.ipSole != nil {
+ return ErrInvalidNAT1To1IPMapping
+ }
+
+ locIPStr := locIP.String()
+
+ // check if dup of local IP
+ if _, ok := m.ipMap[locIPStr]; ok {
+ return ErrInvalidNAT1To1IPMapping
+ }
+
+ m.ipMap[locIPStr] = extIP
+
+ return nil
+}
+
+func (m *ipMapping) findExternalIP(locIP net.IP) (net.IP, error) {
+ if m.ipSole != nil {
+ return m.ipSole, nil
+ }
+
+ extIP, ok := m.ipMap[locIP.String()]
+ if !ok {
+ return nil, ErrExternalMappedIPNotFound
+ }
+
+ return extIP, nil
+}
+
+type externalIPMapper struct {
+ ipv4Mapping ipMapping
+ ipv6Mapping ipMapping
+ candidateType CandidateType
+}
+
+func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIPMapper, error) { //nolint:gocognit
+ if len(ips) == 0 {
+ return nil, nil
+ }
+ if candidateType == CandidateTypeUnspecified {
+ candidateType = CandidateTypeHost // defaults to host
+ } else if candidateType != CandidateTypeHost && candidateType != CandidateTypeServerReflexive {
+ return nil, ErrUnsupportedNAT1To1IPCandidateType
+ }
+
+ m := &externalIPMapper{
+ ipv4Mapping: ipMapping{ipMap: map[string]net.IP{}},
+ ipv6Mapping: ipMapping{ipMap: map[string]net.IP{}},
+ candidateType: candidateType,
+ }
+
+ for _, extIPStr := range ips {
+ ipPair := strings.Split(extIPStr, "/")
+ if len(ipPair) == 0 || len(ipPair) > 2 {
+ return nil, ErrInvalidNAT1To1IPMapping
+ }
+
+ extIP, isExtIPv4, err := validateIPString(ipPair[0])
+ if err != nil {
+ return nil, err
+ }
+ if len(ipPair) == 1 {
+ if isExtIPv4 {
+ if err := m.ipv4Mapping.setSoleIP(extIP); err != nil {
+ return nil, err
+ }
+ } else {
+ if err := m.ipv6Mapping.setSoleIP(extIP); err != nil {
+ return nil, err
+ }
+ }
+ } else {
+ locIP, isLocIPv4, err := validateIPString(ipPair[1])
+ if err != nil {
+ return nil, err
+ }
+ if isExtIPv4 {
+ if !isLocIPv4 {
+ return nil, ErrInvalidNAT1To1IPMapping
+ }
+
+ if err := m.ipv4Mapping.addIPMapping(locIP, extIP); err != nil {
+ return nil, err
+ }
+ } else {
+ if isLocIPv4 {
+ return nil, ErrInvalidNAT1To1IPMapping
+ }
+
+ if err := m.ipv6Mapping.addIPMapping(locIP, extIP); err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+
+ return m, nil
+}
+
+func (m *externalIPMapper) findExternalIP(localIPStr string) (net.IP, error) {
+ locIP, isLocIPv4, err := validateIPString(localIPStr)
+ if err != nil {
+ return nil, err
+ }
+
+ if isLocIPv4 {
+ return m.ipv4Mapping.findExternalIP(locIP)
+ }
+
+ return m.ipv6Mapping.findExternalIP(locIP)
+}
diff --git a/vendor/github.com/pion/ice/v2/gather.go b/vendor/github.com/pion/ice/v2/gather.go
new file mode 100644
index 0000000..3bc3d77
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/gather.go
@@ -0,0 +1,497 @@
+package ice
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "reflect"
+ "sync"
+ "time"
+
+ "github.com/pion/dtls/v2"
+ "github.com/pion/logging"
+ "github.com/pion/turn/v2"
+)
+
+const (
+ stunGatherTimeout = time.Second * 5
+)
+
+type closeable interface {
+ Close() error
+}
+
+// Close a net.Conn and log if we have a failure
+func closeConnAndLog(c closeable, log logging.LeveledLogger, msg string) {
+ if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) {
+ log.Warnf("Conn is not allocated (%s)", msg)
+ return
+ }
+
+ log.Warnf(msg)
+ if err := c.Close(); err != nil {
+ log.Warnf("Failed to close conn: %v", err)
+ }
+}
+
+// fakePacketConn wraps a net.Conn and emulates net.PacketConn
+type fakePacketConn struct {
+ nextConn net.Conn
+}
+
+func (f *fakePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
+ n, err = f.nextConn.Read(p)
+ addr = f.nextConn.RemoteAddr()
+ return
+}
+func (f *fakePacketConn) Close() error { return f.nextConn.Close() }
+func (f *fakePacketConn) LocalAddr() net.Addr { return f.nextConn.LocalAddr() }
+func (f *fakePacketConn) SetDeadline(t time.Time) error { return f.nextConn.SetDeadline(t) }
+func (f *fakePacketConn) SetReadDeadline(t time.Time) error { return f.nextConn.SetReadDeadline(t) }
+func (f *fakePacketConn) SetWriteDeadline(t time.Time) error { return f.nextConn.SetWriteDeadline(t) }
+func (f *fakePacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
+ return f.nextConn.Write(p)
+}
+
+// GatherCandidates initiates the trickle based gathering process.
+func (a *Agent) GatherCandidates() error {
+ var gatherErr error
+
+ if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ if a.gatheringState != GatheringStateNew {
+ gatherErr = ErrMultipleGatherAttempted
+ return
+ } else if a.onCandidateHdlr.Load() == nil {
+ gatherErr = ErrNoOnCandidateHandler
+ return
+ }
+
+ a.gatherCandidateCancel() // Cancel previous gathering routine
+ ctx, cancel := context.WithCancel(ctx)
+ a.gatherCandidateCancel = cancel
+
+ go a.gatherCandidates(ctx)
+ }); runErr != nil {
+ return runErr
+ }
+ return gatherErr
+}
+
+func (a *Agent) gatherCandidates(ctx context.Context) {
+ if err := a.setGatheringState(GatheringStateGathering); err != nil {
+ a.log.Warnf("failed to set gatheringState to GatheringStateGathering: %v", err)
+ return
+ }
+
+ var wg sync.WaitGroup
+ for _, t := range a.candidateTypes {
+ switch t {
+ case CandidateTypeHost:
+ wg.Add(1)
+ go func() {
+ a.gatherCandidatesLocal(ctx, a.networkTypes)
+ wg.Done()
+ }()
+ case CandidateTypeServerReflexive:
+ wg.Add(1)
+ go func() {
+ a.gatherCandidatesSrflx(ctx, a.urls, a.networkTypes)
+ wg.Done()
+ }()
+ if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeServerReflexive {
+ wg.Add(1)
+ go func() {
+ a.gatherCandidatesSrflxMapped(ctx, a.networkTypes)
+ wg.Done()
+ }()
+ }
+ case CandidateTypeRelay:
+ wg.Add(1)
+ go func() {
+ a.gatherCandidatesRelay(ctx, a.urls)
+ wg.Done()
+ }()
+ case CandidateTypePeerReflexive, CandidateTypeUnspecified:
+ }
+ }
+ // Block until all STUN and TURN URLs have been gathered (or timed out)
+ wg.Wait()
+
+ if err := a.setGatheringState(GatheringStateComplete); err != nil {
+ a.log.Warnf("failed to set gatheringState to GatheringStateComplete: %v", err)
+ }
+}
+
+func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []NetworkType) { //nolint:gocognit
+ networks := map[string]struct{}{}
+ for _, networkType := range networkTypes {
+ if networkType.IsTCP() {
+ networks[tcp] = struct{}{}
+ } else {
+ networks[udp] = struct{}{}
+ }
+ }
+
+ localIPs, err := localInterfaces(a.net, a.interfaceFilter, networkTypes)
+ if err != nil {
+ a.log.Warnf("failed to iterate local interfaces, host candidates will not be gathered %s", err)
+ return
+ }
+
+ for _, ip := range localIPs {
+ mappedIP := ip
+ if a.mDNSMode != MulticastDNSModeQueryAndGather && a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost {
+ if _mappedIP, err := a.extIPMapper.findExternalIP(ip.String()); err == nil {
+ mappedIP = _mappedIP
+ } else {
+ a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s\n", ip.String())
+ }
+ }
+
+ address := mappedIP.String()
+ if a.mDNSMode == MulticastDNSModeQueryAndGather {
+ address = a.mDNSName
+ }
+
+ for network := range networks {
+ var port int
+ var conn net.PacketConn
+ var err error
+
+ var tcpType TCPType
+ switch network {
+ case tcp:
+ // Handle ICE TCP passive mode
+
+ a.log.Debugf("GetConn by ufrag: %s\n", a.localUfrag)
+ conn, err = a.tcpMux.GetConnByUfrag(a.localUfrag)
+ if err != nil {
+ if !errors.Is(err, ErrTCPMuxNotInitialized) {
+ a.log.Warnf("error getting tcp conn by ufrag: %s %s %s\n", network, ip, a.localUfrag)
+ }
+ continue
+ }
+ port = conn.LocalAddr().(*net.TCPAddr).Port
+ tcpType = TCPTypePassive
+ // is there a way to verify that the listen address is even
+ // accessible from the current interface.
+ case udp:
+ conn, err = listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: ip, Port: 0})
+ if err != nil {
+ a.log.Warnf("could not listen %s %s\n", network, ip)
+ continue
+ }
+
+ port = conn.LocalAddr().(*net.UDPAddr).Port
+ }
+ hostConfig := CandidateHostConfig{
+ Network: network,
+ Address: address,
+ Port: port,
+ Component: ComponentRTP,
+ TCPType: tcpType,
+ }
+
+ c, err := NewCandidateHost(&hostConfig)
+ if err != nil {
+ closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create host candidate: %s %s %d: %v\n", network, mappedIP, port, err))
+ continue
+ }
+
+ if a.mDNSMode == MulticastDNSModeQueryAndGather {
+ if err = c.setIP(ip); err != nil {
+ closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create host candidate: %s %s %d: %v\n", network, mappedIP, port, err))
+ continue
+ }
+ }
+
+ if err := a.addCandidate(ctx, c, conn); err != nil {
+ if closeErr := c.close(); closeErr != nil {
+ a.log.Warnf("Failed to close candidate: %v", closeErr)
+ }
+ a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err)
+ }
+ }
+ }
+}
+
+func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes []NetworkType) {
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ for _, networkType := range networkTypes {
+ if networkType.IsTCP() {
+ continue
+ }
+
+ network := networkType.String()
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ conn, err := listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0})
+ if err != nil {
+ a.log.Warnf("Failed to listen %s: %v\n", network, err)
+ return
+ }
+
+ laddr := conn.LocalAddr().(*net.UDPAddr)
+ mappedIP, err := a.extIPMapper.findExternalIP(laddr.IP.String())
+ if err != nil {
+ closeConnAndLog(conn, a.log, fmt.Sprintf("1:1 NAT mapping is enabled but no external IP is found for %s\n", laddr.IP.String()))
+ return
+ }
+
+ srflxConfig := CandidateServerReflexiveConfig{
+ Network: network,
+ Address: mappedIP.String(),
+ Port: laddr.Port,
+ Component: ComponentRTP,
+ RelAddr: laddr.IP.String(),
+ RelPort: laddr.Port,
+ }
+ c, err := NewCandidateServerReflexive(&srflxConfig)
+ if err != nil {
+ closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create server reflexive candidate: %s %s %d: %v\n",
+ network,
+ mappedIP.String(),
+ laddr.Port,
+ err))
+ return
+ }
+
+ if err := a.addCandidate(ctx, c, conn); err != nil {
+ if closeErr := c.close(); closeErr != nil {
+ a.log.Warnf("Failed to close candidate: %v", closeErr)
+ }
+ a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err)
+ }
+ }()
+ }
+}
+
+func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*URL, networkTypes []NetworkType) {
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ for _, networkType := range networkTypes {
+ if networkType.IsTCP() {
+ continue
+ }
+
+ for i := range urls {
+ wg.Add(1)
+ go func(url URL, network string) {
+ defer wg.Done()
+ hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port)
+ serverAddr, err := a.net.ResolveUDPAddr(network, hostPort)
+ if err != nil {
+ a.log.Warnf("failed to resolve stun host: %s: %v", hostPort, err)
+ return
+ }
+
+ conn, err := listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0})
+ if err != nil {
+ closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to listen for %s: %v\n", serverAddr.String(), err))
+ return
+ }
+
+ xoraddr, err := getXORMappedAddr(conn, serverAddr, stunGatherTimeout)
+ if err != nil {
+ closeConnAndLog(conn, a.log, fmt.Sprintf("could not get server reflexive address %s %s: %v\n", network, url, err))
+ return
+ }
+
+ ip := xoraddr.IP
+ port := xoraddr.Port
+
+ laddr := conn.LocalAddr().(*net.UDPAddr)
+ srflxConfig := CandidateServerReflexiveConfig{
+ Network: network,
+ Address: ip.String(),
+ Port: port,
+ Component: ComponentRTP,
+ RelAddr: laddr.IP.String(),
+ RelPort: laddr.Port,
+ }
+ c, err := NewCandidateServerReflexive(&srflxConfig)
+ if err != nil {
+ closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create server reflexive candidate: %s %s %d: %v\n", network, ip, port, err))
+ return
+ }
+
+ if err := a.addCandidate(ctx, c, conn); err != nil {
+ if closeErr := c.close(); closeErr != nil {
+ a.log.Warnf("Failed to close candidate: %v", closeErr)
+ }
+ a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err)
+ }
+ }(*urls[i], networkType.String())
+ }
+ }
+}
+
+func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*URL) { //nolint:gocognit
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ network := NetworkTypeUDP4.String()
+ for i := range urls {
+ switch {
+ case urls[i].Scheme != SchemeTypeTURN && urls[i].Scheme != SchemeTypeTURNS:
+ continue
+ case urls[i].Username == "":
+ a.log.Errorf("Failed to gather relay candidates: %v", ErrUsernameEmpty)
+ return
+ case urls[i].Password == "":
+ a.log.Errorf("Failed to gather relay candidates: %v", ErrPasswordEmpty)
+ return
+ }
+
+ wg.Add(1)
+ go func(url URL) {
+ defer wg.Done()
+ TURNServerAddr := fmt.Sprintf("%s:%d", url.Host, url.Port)
+ var (
+ locConn net.PacketConn
+ err error
+ RelAddr string
+ RelPort int
+ )
+
+ switch {
+ case url.Proto == ProtoTypeUDP && url.Scheme == SchemeTypeTURN:
+ if locConn, err = a.net.ListenPacket(network, "0.0.0.0:0"); err != nil {
+ a.log.Warnf("Failed to listen %s: %v\n", network, err)
+ return
+ }
+
+ RelAddr = locConn.LocalAddr().(*net.UDPAddr).IP.String()
+ RelPort = locConn.LocalAddr().(*net.UDPAddr).Port
+ case a.proxyDialer != nil && url.Proto == ProtoTypeTCP &&
+ (url.Scheme == SchemeTypeTURN || url.Scheme == SchemeTypeTURNS):
+ conn, connectErr := a.proxyDialer.Dial(NetworkTypeTCP4.String(), TURNServerAddr)
+ if connectErr != nil {
+ a.log.Warnf("Failed to Dial TCP Addr %s via proxy dialer: %v\n", TURNServerAddr, connectErr)
+ return
+ }
+
+ RelAddr = conn.LocalAddr().(*net.TCPAddr).IP.String()
+ RelPort = conn.LocalAddr().(*net.TCPAddr).Port
+ locConn = turn.NewSTUNConn(conn)
+
+ case url.Proto == ProtoTypeTCP && url.Scheme == SchemeTypeTURN:
+ tcpAddr, connectErr := net.ResolveTCPAddr(NetworkTypeTCP4.String(), TURNServerAddr)
+ if connectErr != nil {
+ a.log.Warnf("Failed to resolve TCP Addr %s: %v\n", TURNServerAddr, connectErr)
+ return
+ }
+
+ conn, connectErr := net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr)
+ if connectErr != nil {
+ a.log.Warnf("Failed to Dial TCP Addr %s: %v\n", TURNServerAddr, connectErr)
+ return
+ }
+
+ RelAddr = conn.LocalAddr().(*net.TCPAddr).IP.String()
+ RelPort = conn.LocalAddr().(*net.TCPAddr).Port
+ locConn = turn.NewSTUNConn(conn)
+ case url.Proto == ProtoTypeUDP && url.Scheme == SchemeTypeTURNS:
+ udpAddr, connectErr := net.ResolveUDPAddr(network, TURNServerAddr)
+ if connectErr != nil {
+ a.log.Warnf("Failed to resolve UDP Addr %s: %v\n", TURNServerAddr, connectErr)
+ return
+ }
+
+ conn, connectErr := dtls.Dial(network, udpAddr, &dtls.Config{
+ InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec
+ })
+ if connectErr != nil {
+ a.log.Warnf("Failed to Dial DTLS Addr %s: %v\n", TURNServerAddr, connectErr)
+ return
+ }
+
+ RelAddr = conn.LocalAddr().(*net.UDPAddr).IP.String()
+ RelPort = conn.LocalAddr().(*net.UDPAddr).Port
+ locConn = &fakePacketConn{conn}
+ case url.Proto == ProtoTypeTCP && url.Scheme == SchemeTypeTURNS:
+ conn, connectErr := tls.Dial(NetworkTypeTCP4.String(), TURNServerAddr, &tls.Config{
+ InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec
+ })
+ if connectErr != nil {
+ a.log.Warnf("Failed to Dial TLS Addr %s: %v\n", TURNServerAddr, connectErr)
+ return
+ }
+ RelAddr = conn.LocalAddr().(*net.TCPAddr).IP.String()
+ RelPort = conn.LocalAddr().(*net.TCPAddr).Port
+ locConn = turn.NewSTUNConn(conn)
+ default:
+ a.log.Warnf("Unable to handle URL in gatherCandidatesRelay %v\n", url)
+ return
+ }
+
+ client, err := turn.NewClient(&turn.ClientConfig{
+ TURNServerAddr: TURNServerAddr,
+ Conn: locConn,
+ Username: url.Username,
+ Password: url.Password,
+ LoggerFactory: a.loggerFactory,
+ Net: a.net,
+ })
+ if err != nil {
+ closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to build new turn.Client %s %s\n", TURNServerAddr, err))
+ return
+ }
+
+ if err = client.Listen(); err != nil {
+ client.Close()
+ closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to listen on turn.Client %s %s\n", TURNServerAddr, err))
+ return
+ }
+
+ relayConn, err := client.Allocate()
+ if err != nil {
+ client.Close()
+ closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to allocate on turn.Client %s %s\n", TURNServerAddr, err))
+ return
+ }
+
+ raddr := relayConn.LocalAddr().(*net.UDPAddr)
+ relayConfig := CandidateRelayConfig{
+ Network: network,
+ Component: ComponentRTP,
+ Address: raddr.IP.String(),
+ Port: raddr.Port,
+ RelAddr: RelAddr,
+ RelPort: RelPort,
+ OnClose: func() error {
+ client.Close()
+ return locConn.Close()
+ },
+ }
+ relayConnClose := func() {
+ if relayConErr := relayConn.Close(); relayConErr != nil {
+ a.log.Warnf("Failed to close relay %v", relayConErr)
+ }
+ }
+ candidate, err := NewCandidateRelay(&relayConfig)
+ if err != nil {
+ relayConnClose()
+
+ client.Close()
+ closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to create relay candidate: %s %s: %v\n", network, raddr.String(), err))
+ return
+ }
+
+ if err := a.addCandidate(ctx, candidate, relayConn); err != nil {
+ relayConnClose()
+
+ if closeErr := candidate.close(); closeErr != nil {
+ a.log.Warnf("Failed to close candidate: %v", closeErr)
+ }
+ a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err)
+ }
+ }(*urls[i])
+ }
+}
diff --git a/vendor/github.com/pion/ice/v2/go.mod b/vendor/github.com/pion/ice/v2/go.mod
new file mode 100644
index 0000000..410c2e3
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/go.mod
@@ -0,0 +1,16 @@
+module github.com/pion/ice/v2
+
+go 1.13
+
+require (
+ github.com/google/uuid v1.1.5
+ github.com/pion/dtls/v2 v2.0.4
+ github.com/pion/logging v0.2.2
+ github.com/pion/mdns v0.0.4
+ github.com/pion/randutil v0.1.0
+ github.com/pion/stun v0.3.5
+ github.com/pion/transport v0.12.1
+ github.com/pion/turn/v2 v2.0.5
+ github.com/stretchr/testify v1.6.1
+ golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7
+)
diff --git a/vendor/github.com/pion/ice/v2/go.sum b/vendor/github.com/pion/ice/v2/go.sum
new file mode 100644
index 0000000..7e8981a
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/go.sum
@@ -0,0 +1,60 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
+github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
+github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
+github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
+github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
+github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
+github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
+github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM=
+github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
+github.com/pion/transport v0.12.1 h1:6v8lxQGVZpwSICEZjhl/CCv6aErINZlrm3O5ncFXj/c=
+github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
+github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
+github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
+github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
+github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
+golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/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-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/ice/v2/ice.go b/vendor/github.com/pion/ice/v2/ice.go
new file mode 100644
index 0000000..d7094f6
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/ice.go
@@ -0,0 +1,76 @@
+package ice
+
+// ConnectionState is an enum showing the state of a ICE Connection
+type ConnectionState int
+
+// List of supported States
+const (
+ // ConnectionStateNew ICE agent is gathering addresses
+ ConnectionStateNew = iota + 1
+
+ // ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match
+ ConnectionStateChecking
+
+ // ConnectionStateConnected ICE agent has a pairing, but is still checking other pairs
+ ConnectionStateConnected
+
+ // ConnectionStateCompleted ICE agent has finished
+ ConnectionStateCompleted
+
+ // ConnectionStateFailed ICE agent never could successfully connect
+ ConnectionStateFailed
+
+ // ConnectionStateDisconnected ICE agent connected successfully, but has entered a failed state
+ ConnectionStateDisconnected
+
+ // ConnectionStateClosed ICE agent has finished and is no longer handling requests
+ ConnectionStateClosed
+)
+
+func (c ConnectionState) String() string {
+ switch c {
+ case ConnectionStateNew:
+ return "New"
+ case ConnectionStateChecking:
+ return "Checking"
+ case ConnectionStateConnected:
+ return "Connected"
+ case ConnectionStateCompleted:
+ return "Completed"
+ case ConnectionStateFailed:
+ return "Failed"
+ case ConnectionStateDisconnected:
+ return "Disconnected"
+ case ConnectionStateClosed:
+ return "Closed"
+ default:
+ return "Invalid"
+ }
+}
+
+// GatheringState describes the state of the candidate gathering process
+type GatheringState int
+
+const (
+ // GatheringStateNew indicates candidate gatering is not yet started
+ GatheringStateNew GatheringState = iota + 1
+
+ // GatheringStateGathering indicates candidate gatering is ongoing
+ GatheringStateGathering
+
+ // GatheringStateComplete indicates candidate gatering has been completed
+ GatheringStateComplete
+)
+
+func (t GatheringState) String() string {
+ switch t {
+ case GatheringStateNew:
+ return "new"
+ case GatheringStateGathering:
+ return "gathering"
+ case GatheringStateComplete:
+ return "complete"
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/ice/v2/icecontrol.go b/vendor/github.com/pion/ice/v2/icecontrol.go
new file mode 100644
index 0000000..ede2e09
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/icecontrol.go
@@ -0,0 +1,87 @@
+package ice
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/stun"
+)
+
+// tiebreaker is common helper for ICE-{CONTROLLED,CONTROLLING}
+// and represents the so-called tiebreaker number.
+type tiebreaker uint64
+
+const tiebreakerSize = 8 // 64 bit
+
+// AddToAs adds tiebreaker value to m as t attribute.
+func (a tiebreaker) AddToAs(m *stun.Message, t stun.AttrType) error {
+ v := make([]byte, tiebreakerSize)
+ binary.BigEndian.PutUint64(v, uint64(a))
+ m.Add(t, v)
+ return nil
+}
+
+// GetFromAs decodes tiebreaker value in message getting it as for t type.
+func (a *tiebreaker) GetFromAs(m *stun.Message, t stun.AttrType) error {
+ v, err := m.Get(t)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(t, len(v), tiebreakerSize); err != nil {
+ return err
+ }
+ *a = tiebreaker(binary.BigEndian.Uint64(v))
+ return nil
+}
+
+// AttrControlled represents ICE-CONTROLLED attribute.
+type AttrControlled uint64
+
+// AddTo adds ICE-CONTROLLED to message.
+func (c AttrControlled) AddTo(m *stun.Message) error {
+ return tiebreaker(c).AddToAs(m, stun.AttrICEControlled)
+}
+
+// GetFrom decodes ICE-CONTROLLED from message.
+func (c *AttrControlled) GetFrom(m *stun.Message) error {
+ return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlled)
+}
+
+// AttrControlling represents ICE-CONTROLLING attribute.
+type AttrControlling uint64
+
+// AddTo adds ICE-CONTROLLING to message.
+func (c AttrControlling) AddTo(m *stun.Message) error {
+ return tiebreaker(c).AddToAs(m, stun.AttrICEControlling)
+}
+
+// GetFrom decodes ICE-CONTROLLING from message.
+func (c *AttrControlling) GetFrom(m *stun.Message) error {
+ return (*tiebreaker)(c).GetFromAs(m, stun.AttrICEControlling)
+}
+
+// AttrControl is helper that wraps ICE-{CONTROLLED,CONTROLLING}.
+type AttrControl struct {
+ Role Role
+ Tiebreaker uint64
+}
+
+// AddTo adds ICE-CONTROLLED or ICE-CONTROLLING attribute depending on Role.
+func (c AttrControl) AddTo(m *stun.Message) error {
+ if c.Role == Controlling {
+ return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlling)
+ }
+ return tiebreaker(c.Tiebreaker).AddToAs(m, stun.AttrICEControlled)
+}
+
+// GetFrom decodes Role and Tiebreaker value from message.
+func (c *AttrControl) GetFrom(m *stun.Message) error {
+ if m.Contains(stun.AttrICEControlling) {
+ c.Role = Controlling
+ return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlling)
+ }
+ if m.Contains(stun.AttrICEControlled) {
+ c.Role = Controlled
+ return (*tiebreaker)(&c.Tiebreaker).GetFromAs(m, stun.AttrICEControlled)
+ }
+ return stun.ErrAttributeNotFound
+}
diff --git a/vendor/github.com/pion/ice/v2/mdns.go b/vendor/github.com/pion/ice/v2/mdns.go
new file mode 100644
index 0000000..5a431d1
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/mdns.go
@@ -0,0 +1,63 @@
+package ice
+
+import (
+ "net"
+
+ "github.com/google/uuid"
+ "github.com/pion/logging"
+ "github.com/pion/mdns"
+ "golang.org/x/net/ipv4"
+)
+
+// MulticastDNSMode represents the different Multicast modes ICE can run in
+type MulticastDNSMode byte
+
+// MulticastDNSMode enum
+const (
+ // MulticastDNSModeDisabled means remote mDNS candidates will be discarded, and local host candidates will use IPs
+ MulticastDNSModeDisabled MulticastDNSMode = iota + 1
+
+ // MulticastDNSModeQueryOnly means remote mDNS candidates will be accepted, and local host candidates will use IPs
+ MulticastDNSModeQueryOnly
+
+ // MulticastDNSModeQueryAndGather means remote mDNS candidates will be accepted, and local host candidates will use mDNS
+ MulticastDNSModeQueryAndGather
+)
+
+func generateMulticastDNSName() (string, error) {
+ // https://tools.ietf.org/id/draft-ietf-rtcweb-mdns-ice-candidates-02.html#gathering
+ // The unique name MUST consist of a version 4 UUID as defined in [RFC4122], followed by “.local”.
+ u, err := uuid.NewRandom()
+ return u.String() + ".local", err
+}
+
+func createMulticastDNS(mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger) (*mdns.Conn, MulticastDNSMode, error) {
+ if mDNSMode == MulticastDNSModeDisabled {
+ return nil, mDNSMode, nil
+ }
+
+ addr, mdnsErr := net.ResolveUDPAddr("udp4", mdns.DefaultAddress)
+ if mdnsErr != nil {
+ return nil, mDNSMode, mdnsErr
+ }
+
+ l, mdnsErr := net.ListenUDP("udp4", addr)
+ if mdnsErr != nil {
+ // If ICE fails to start MulticastDNS server just warn the user and continue
+ log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode: (%s)", mdnsErr)
+ return nil, MulticastDNSModeDisabled, nil
+ }
+
+ switch mDNSMode {
+ case MulticastDNSModeQueryOnly:
+ conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{})
+ return conn, mDNSMode, err
+ case MulticastDNSModeQueryAndGather:
+ conn, err := mdns.Server(ipv4.NewPacketConn(l), &mdns.Config{
+ LocalNames: []string{mDNSName},
+ })
+ return conn, mDNSMode, err
+ default:
+ return nil, mDNSMode, nil
+ }
+}
diff --git a/vendor/github.com/pion/ice/v2/networktype.go b/vendor/github.com/pion/ice/v2/networktype.go
new file mode 100644
index 0000000..462ff2d
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/networktype.go
@@ -0,0 +1,130 @@
+package ice
+
+import (
+ "fmt"
+ "net"
+ "strings"
+)
+
+const (
+ udp = "udp"
+ tcp = "tcp"
+)
+
+func supportedNetworkTypes() []NetworkType {
+ return []NetworkType{
+ NetworkTypeUDP4,
+ NetworkTypeUDP6,
+ NetworkTypeTCP4,
+ NetworkTypeTCP6,
+ }
+}
+
+// NetworkType represents the type of network
+type NetworkType int
+
+const (
+ // NetworkTypeUDP4 indicates UDP over IPv4.
+ NetworkTypeUDP4 NetworkType = iota + 1
+
+ // NetworkTypeUDP6 indicates UDP over IPv6.
+ NetworkTypeUDP6
+
+ // NetworkTypeTCP4 indicates TCP over IPv4.
+ NetworkTypeTCP4
+
+ // NetworkTypeTCP6 indicates TCP over IPv6.
+ NetworkTypeTCP6
+)
+
+func (t NetworkType) String() string {
+ switch t {
+ case NetworkTypeUDP4:
+ return "udp4"
+ case NetworkTypeUDP6:
+ return "udp6"
+ case NetworkTypeTCP4:
+ return "tcp4"
+ case NetworkTypeTCP6:
+ return "tcp6"
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// IsUDP returns true when network is UDP4 or UDP6.
+func (t NetworkType) IsUDP() bool {
+ return t == NetworkTypeUDP4 || t == NetworkTypeUDP6
+}
+
+// IsTCP returns true when network is TCP4 or TCP6.
+func (t NetworkType) IsTCP() bool {
+ return t == NetworkTypeTCP4 || t == NetworkTypeTCP6
+}
+
+// NetworkShort returns the short network description
+func (t NetworkType) NetworkShort() string {
+ switch t {
+ case NetworkTypeUDP4, NetworkTypeUDP6:
+ return udp
+ case NetworkTypeTCP4, NetworkTypeTCP6:
+ return tcp
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// IsReliable returns true if the network is reliable
+func (t NetworkType) IsReliable() bool {
+ switch t {
+ case NetworkTypeUDP4, NetworkTypeUDP6:
+ return false
+ case NetworkTypeTCP4, NetworkTypeTCP6:
+ return true
+ }
+ return false
+}
+
+// IsIPv4 returns whether the network type is IPv4 or not.
+func (t NetworkType) IsIPv4() bool {
+ switch t {
+ case NetworkTypeUDP4, NetworkTypeTCP4:
+ return true
+ case NetworkTypeUDP6, NetworkTypeTCP6:
+ return false
+ }
+ return false
+}
+
+// IsIPv6 returns whether the network type is IPv6 or not.
+func (t NetworkType) IsIPv6() bool {
+ switch t {
+ case NetworkTypeUDP4, NetworkTypeTCP4:
+ return false
+ case NetworkTypeUDP6, NetworkTypeTCP6:
+ return true
+ }
+ return false
+}
+
+// determineNetworkType determines the type of network based on
+// the short network string and an IP address.
+func determineNetworkType(network string, ip net.IP) (NetworkType, error) {
+ ipv4 := ip.To4() != nil
+
+ switch {
+ case strings.HasPrefix(strings.ToLower(network), udp):
+ if ipv4 {
+ return NetworkTypeUDP4, nil
+ }
+ return NetworkTypeUDP6, nil
+
+ case strings.HasPrefix(strings.ToLower(network), tcp):
+ if ipv4 {
+ return NetworkTypeTCP4, nil
+ }
+ return NetworkTypeTCP6, nil
+ }
+
+ return NetworkType(0), fmt.Errorf("%w from %s %s", errDetermineNetworkType, network, ip)
+}
diff --git a/vendor/github.com/pion/ice/v2/priority.go b/vendor/github.com/pion/ice/v2/priority.go
new file mode 100644
index 0000000..4218299
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/priority.go
@@ -0,0 +1,33 @@
+package ice
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/stun"
+)
+
+// PriorityAttr represents PRIORITY attribute.
+type PriorityAttr uint32
+
+const prioritySize = 4 // 32 bit
+
+// AddTo adds PRIORITY attribute to message.
+func (p PriorityAttr) AddTo(m *stun.Message) error {
+ v := make([]byte, prioritySize)
+ binary.BigEndian.PutUint32(v, uint32(p))
+ m.Add(stun.AttrPriority, v)
+ return nil
+}
+
+// GetFrom decodes PRIORITY attribute from message.
+func (p *PriorityAttr) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrPriority)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(stun.AttrPriority, len(v), prioritySize); err != nil {
+ return err
+ }
+ *p = PriorityAttr(binary.BigEndian.Uint32(v))
+ return nil
+}
diff --git a/vendor/github.com/pion/ice/v2/rand.go b/vendor/github.com/pion/ice/v2/rand.go
new file mode 100644
index 0000000..918783e
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/rand.go
@@ -0,0 +1,53 @@
+package ice
+
+import "github.com/pion/randutil"
+
+const (
+ runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ runesDigit = "0123456789"
+ runesCandidateIDFoundation = runesAlpha + runesDigit + "+/"
+
+ lenUFrag = 16
+ lenPwd = 32
+)
+
+// Seeding random generator each time limits number of generated sequence to 31-bits,
+// and causes collision on low time accuracy environments.
+// Use global random generator seeded by crypto grade random.
+var (
+ globalMathRandomGenerator = randutil.NewMathRandomGenerator() //nolint:gochecknoglobals
+ globalCandidateIDGenerator = candidateIDGenerator{globalMathRandomGenerator} //nolint:gochecknoglobals
+)
+
+// candidateIDGenerator is a random candidate ID generator.
+// Candidate ID is used in SDP and always shared to the other peer.
+// It doesn't require cryptographic random.
+type candidateIDGenerator struct {
+ randutil.MathRandomGenerator
+}
+
+func newCandidateIDGenerator() *candidateIDGenerator {
+ return &candidateIDGenerator{
+ randutil.NewMathRandomGenerator(),
+ }
+}
+
+func (g *candidateIDGenerator) Generate() string {
+ // https://tools.ietf.org/html/rfc5245#section-15.1
+ // candidate-id = "candidate" ":" foundation
+ // foundation = 1*32ice-char
+ // ice-char = ALPHA / DIGIT / "+" / "/"
+ return "candidate:" + g.MathRandomGenerator.GenerateString(32, runesCandidateIDFoundation)
+}
+
+// generatePwd generates ICE pwd.
+// This internally uses generateCryptoRandomString.
+func generatePwd() (string, error) {
+ return randutil.GenerateCryptoRandomString(lenPwd, runesAlpha)
+}
+
+// generateUFrag generates ICE user fragment.
+// This internally uses generateCryptoRandomString.
+func generateUFrag() (string, error) {
+ return randutil.GenerateCryptoRandomString(lenUFrag, runesAlpha)
+}
diff --git a/vendor/github.com/pion/ice/v2/renovate.json b/vendor/github.com/pion/ice/v2/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/ice/v2/role.go b/vendor/github.com/pion/ice/v2/role.go
new file mode 100644
index 0000000..7a8bc06
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/role.go
@@ -0,0 +1,43 @@
+package ice
+
+import (
+ "fmt"
+)
+
+// Role represents ICE agent role, which can be controlling or controlled.
+type Role byte
+
+// Possible ICE agent roles.
+const (
+ Controlling Role = iota
+ Controlled
+)
+
+// UnmarshalText implements TextUnmarshaler.
+func (r *Role) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "controlling":
+ *r = Controlling
+ case "controlled":
+ *r = Controlled
+ default:
+ return fmt.Errorf("%w %q", errUnknownRole, text)
+ }
+ return nil
+}
+
+// MarshalText implements TextMarshaler.
+func (r Role) MarshalText() (text []byte, err error) {
+ return []byte(r.String()), nil
+}
+
+func (r Role) String() string {
+ switch r {
+ case Controlling:
+ return "controlling"
+ case Controlled:
+ return "controlled"
+ default:
+ return "unknown"
+ }
+}
diff --git a/vendor/github.com/pion/ice/v2/selection.go b/vendor/github.com/pion/ice/v2/selection.go
new file mode 100644
index 0000000..e0cfb10
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/selection.go
@@ -0,0 +1,287 @@
+package ice
+
+import (
+ "net"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+)
+
+type pairCandidateSelector interface {
+ Start()
+ ContactCandidates()
+ PingCandidate(local, remote Candidate)
+ HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr)
+ HandleBindingRequest(m *stun.Message, local, remote Candidate)
+}
+
+type controllingSelector struct {
+ startTime time.Time
+ agent *Agent
+ nominatedPair *candidatePair
+ log logging.LeveledLogger
+}
+
+func (s *controllingSelector) Start() {
+ s.startTime = time.Now()
+ s.nominatedPair = nil
+}
+
+func (s *controllingSelector) isNominatable(c Candidate) bool {
+ switch {
+ case c.Type() == CandidateTypeHost:
+ return time.Since(s.startTime).Nanoseconds() > s.agent.hostAcceptanceMinWait.Nanoseconds()
+ case c.Type() == CandidateTypeServerReflexive:
+ return time.Since(s.startTime).Nanoseconds() > s.agent.srflxAcceptanceMinWait.Nanoseconds()
+ case c.Type() == CandidateTypePeerReflexive:
+ return time.Since(s.startTime).Nanoseconds() > s.agent.prflxAcceptanceMinWait.Nanoseconds()
+ case c.Type() == CandidateTypeRelay:
+ return time.Since(s.startTime).Nanoseconds() > s.agent.relayAcceptanceMinWait.Nanoseconds()
+ }
+
+ s.log.Errorf("isNominatable invalid candidate type %s", c.Type().String())
+ return false
+}
+
+func (s *controllingSelector) ContactCandidates() {
+ switch {
+ case s.agent.getSelectedPair() != nil:
+ if s.agent.validateSelectedPair() {
+ s.log.Trace("checking keepalive")
+ s.agent.checkKeepalive()
+ }
+ case s.nominatedPair != nil:
+ s.nominatePair(s.nominatedPair)
+ default:
+ p := s.agent.getBestValidCandidatePair()
+ if p != nil && s.isNominatable(p.local) && s.isNominatable(p.remote) {
+ s.log.Tracef("Nominatable pair found, nominating (%s, %s)", p.local.String(), p.remote.String())
+ p.nominated = true
+ s.nominatedPair = p
+ s.nominatePair(p)
+ return
+ }
+ s.agent.pingAllCandidates()
+ }
+}
+
+func (s *controllingSelector) nominatePair(pair *candidatePair) {
+ // The controlling agent MUST include the USE-CANDIDATE attribute in
+ // order to nominate a candidate pair (Section 8.1.1). The controlled
+ // agent MUST NOT include the USE-CANDIDATE attribute in a Binding
+ // request.
+ msg, err := stun.Build(stun.BindingRequest, stun.TransactionID,
+ stun.NewUsername(s.agent.remoteUfrag+":"+s.agent.localUfrag),
+ UseCandidate(),
+ AttrControlling(s.agent.tieBreaker),
+ PriorityAttr(pair.local.Priority()),
+ stun.NewShortTermIntegrity(s.agent.remotePwd),
+ stun.Fingerprint,
+ )
+ if err != nil {
+ s.log.Error(err.Error())
+ return
+ }
+
+ s.log.Tracef("ping STUN (nominate candidate pair) from %s to %s\n", pair.local.String(), pair.remote.String())
+ s.agent.sendBindingRequest(msg, pair.local, pair.remote)
+}
+
+func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) {
+ s.agent.sendBindingSuccess(m, local, remote)
+
+ p := s.agent.findPair(local, remote)
+
+ if p == nil {
+ s.agent.addPair(local, remote)
+ return
+ }
+
+ if p.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil {
+ bestPair := s.agent.getBestAvailableCandidatePair()
+ if bestPair == nil {
+ s.log.Tracef("No best pair available\n")
+ } else if bestPair.Equal(p) && s.isNominatable(p.local) && s.isNominatable(p.remote) {
+ s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated\n",
+ p.local.String(), p.remote.String())
+ s.nominatedPair = p
+ s.nominatePair(p)
+ }
+ }
+}
+
+func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) {
+ ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID)
+ if !ok {
+ s.log.Warnf("discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID)
+ return
+ }
+
+ transactionAddr := pendingRequest.destination
+
+ // Assert that NAT is not symmetric
+ // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
+ if !addrEqual(transactionAddr, remoteAddr) {
+ s.log.Debugf("discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote)
+ return
+ }
+
+ s.log.Tracef("inbound STUN (SuccessResponse) from %s to %s", remote.String(), local.String())
+ p := s.agent.findPair(local, remote)
+
+ if p == nil {
+ // This shouldn't happen
+ s.log.Error("Success response from invalid candidate pair")
+ return
+ }
+
+ p.state = CandidatePairStateSucceeded
+ s.log.Tracef("Found valid candidate pair: %s", p)
+ if pendingRequest.isUseCandidate && s.agent.getSelectedPair() == nil {
+ s.agent.setSelectedPair(p)
+ }
+}
+
+func (s *controllingSelector) PingCandidate(local, remote Candidate) {
+ msg, err := stun.Build(stun.BindingRequest, stun.TransactionID,
+ stun.NewUsername(s.agent.remoteUfrag+":"+s.agent.localUfrag),
+ AttrControlling(s.agent.tieBreaker),
+ PriorityAttr(local.Priority()),
+ stun.NewShortTermIntegrity(s.agent.remotePwd),
+ stun.Fingerprint,
+ )
+ if err != nil {
+ s.log.Error(err.Error())
+ return
+ }
+
+ s.agent.sendBindingRequest(msg, local, remote)
+}
+
+type controlledSelector struct {
+ agent *Agent
+ log logging.LeveledLogger
+}
+
+func (s *controlledSelector) Start() {
+}
+
+func (s *controlledSelector) ContactCandidates() {
+ if s.agent.getSelectedPair() != nil {
+ if s.agent.validateSelectedPair() {
+ s.log.Trace("checking keepalive")
+ s.agent.checkKeepalive()
+ }
+ } else {
+ s.agent.pingAllCandidates()
+ }
+}
+
+func (s *controlledSelector) PingCandidate(local, remote Candidate) {
+ msg, err := stun.Build(stun.BindingRequest, stun.TransactionID,
+ stun.NewUsername(s.agent.remoteUfrag+":"+s.agent.localUfrag),
+ AttrControlled(s.agent.tieBreaker),
+ PriorityAttr(local.Priority()),
+ stun.NewShortTermIntegrity(s.agent.remotePwd),
+ stun.Fingerprint,
+ )
+ if err != nil {
+ s.log.Error(err.Error())
+ return
+ }
+
+ s.agent.sendBindingRequest(msg, local, remote)
+}
+
+func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) {
+ // nolint:godox
+ // TODO according to the standard we should specifically answer a failed nomination:
+ // https://tools.ietf.org/html/rfc8445#section-7.3.1.5
+ // If the controlled agent does not accept the request from the
+ // controlling agent, the controlled agent MUST reject the nomination
+ // request with an appropriate error code response (e.g., 400)
+ // [RFC5389].
+
+ ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID)
+ if !ok {
+ s.log.Warnf("discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID)
+ return
+ }
+
+ transactionAddr := pendingRequest.destination
+
+ // Assert that NAT is not symmetric
+ // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1
+ if !addrEqual(transactionAddr, remoteAddr) {
+ s.log.Debugf("discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote)
+ return
+ }
+
+ s.log.Tracef("inbound STUN (SuccessResponse) from %s to %s", remote.String(), local.String())
+
+ p := s.agent.findPair(local, remote)
+ if p == nil {
+ // This shouldn't happen
+ s.log.Error("Success response from invalid candidate pair")
+ return
+ }
+
+ p.state = CandidatePairStateSucceeded
+ s.log.Tracef("Found valid candidate pair: %s", p)
+}
+
+func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) {
+ useCandidate := m.Contains(stun.AttrUseCandidate)
+
+ p := s.agent.findPair(local, remote)
+
+ if p == nil {
+ p = s.agent.addPair(local, remote)
+ }
+
+ if useCandidate {
+ // https://tools.ietf.org/html/rfc8445#section-7.3.1.5
+
+ if p.state == CandidatePairStateSucceeded {
+ // If the state of this pair is Succeeded, it means that the check
+ // previously sent by this pair produced a successful response and
+ // generated a valid pair (Section 7.2.5.3.2). The agent sets the
+ // nominated flag value of the valid pair to true.
+ if selectedPair := s.agent.getSelectedPair(); selectedPair == nil {
+ s.agent.setSelectedPair(p)
+ }
+ s.agent.sendBindingSuccess(m, local, remote)
+ } else {
+ // If the received Binding request triggered a new check to be
+ // enqueued in the triggered-check queue (Section 7.3.1.4), once the
+ // check is sent and if it generates a successful response, and
+ // generates a valid pair, the agent sets the nominated flag of the
+ // pair to true. If the request fails (Section 7.2.5.2), the agent
+ // MUST remove the candidate pair from the valid list, set the
+ // candidate pair state to Failed, and set the checklist state to
+ // Failed.
+ s.PingCandidate(local, remote)
+ }
+ } else {
+ s.agent.sendBindingSuccess(m, local, remote)
+ s.PingCandidate(local, remote)
+ }
+}
+
+type liteSelector struct {
+ pairCandidateSelector
+}
+
+// A lite selector should not contact candidates
+func (s *liteSelector) ContactCandidates() {
+ if _, ok := s.pairCandidateSelector.(*controllingSelector); ok {
+ // nolint:godox
+ // pion/ice#96
+ // TODO: implement lite controlling agent. For now falling back to full agent.
+ // This only happens if both peers are lite. See RFC 8445 S6.1.1 and S6.2
+ s.pairCandidateSelector.ContactCandidates()
+ } else if v, ok := s.pairCandidateSelector.(*controlledSelector); ok {
+ v.agent.validateSelectedPair()
+ }
+}
diff --git a/vendor/github.com/pion/ice/v2/stats.go b/vendor/github.com/pion/ice/v2/stats.go
new file mode 100644
index 0000000..f59d89f
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/stats.go
@@ -0,0 +1,177 @@
+package ice
+
+import (
+ "time"
+)
+
+// CandidatePairStats contains ICE candidate pair statistics
+type CandidatePairStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp time.Time
+
+ // LocalCandidateID is the ID of the local candidate
+ LocalCandidateID string
+
+ // RemoteCandidateID is the ID of the remote candidate
+ RemoteCandidateID string
+
+ // State represents the state of the checklist for the local and remote
+ // candidates in a pair.
+ State CandidatePairState
+
+ // Nominated is true when this valid pair that should be used for media
+ // if it is the highest-priority one amongst those whose nominated flag is set
+ Nominated bool
+
+ // PacketsSent represents the total number of packets sent on this candidate pair.
+ PacketsSent uint32
+
+ // PacketsReceived represents the total number of packets received on this candidate pair.
+ PacketsReceived uint32
+
+ // BytesSent represents the total number of payload bytes sent on this candidate pair
+ // not including headers or padding.
+ BytesSent uint64
+
+ // BytesReceived represents the total number of payload bytes received on this candidate pair
+ // not including headers or padding.
+ BytesReceived uint64
+
+ // LastPacketSentTimestamp represents the timestamp at which the last packet was
+ // sent on this particular candidate pair, excluding STUN packets.
+ LastPacketSentTimestamp time.Time
+
+ // LastPacketReceivedTimestamp represents the timestamp at which the last packet
+ // was received on this particular candidate pair, excluding STUN packets.
+ LastPacketReceivedTimestamp time.Time
+
+ // FirstRequestTimestamp represents the timestamp at which the first STUN request
+ // was sent on this particular candidate pair.
+ FirstRequestTimestamp time.Time
+
+ // LastRequestTimestamp represents the timestamp at which the last STUN request
+ // was sent on this particular candidate pair. The average interval between two
+ // consecutive connectivity checks sent can be calculated with
+ // (LastRequestTimestamp - FirstRequestTimestamp) / RequestsSent.
+ LastRequestTimestamp time.Time
+
+ // LastResponseTimestamp represents the timestamp at which the last STUN response
+ // was received on this particular candidate pair.
+ LastResponseTimestamp time.Time
+
+ // TotalRoundTripTime represents the sum of all round trip time measurements
+ // in seconds since the beginning of the session, based on STUN connectivity
+ // check responses (ResponsesReceived), including those that reply to requests
+ // that are sent in order to verify consent. The average round trip time can
+ // be computed from TotalRoundTripTime by dividing it by ResponsesReceived.
+ TotalRoundTripTime float64
+
+ // CurrentRoundTripTime represents the latest round trip time measured in seconds,
+ // computed from both STUN connectivity checks, including those that are sent
+ // for consent verification.
+ CurrentRoundTripTime float64
+
+ // AvailableOutgoingBitrate is calculated by the underlying congestion control
+ // by combining the available bitrate for all the outgoing RTP streams using
+ // this candidate pair. The bitrate measurement does not count the size of the
+ // IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
+ // in RFC 3890, i.e., it is measured in bits per second and the bitrate is calculated
+ // over a 1 second window.
+ AvailableOutgoingBitrate float64
+
+ // AvailableIncomingBitrate is calculated by the underlying congestion control
+ // by combining the available bitrate for all the incoming RTP streams using
+ // this candidate pair. The bitrate measurement does not count the size of the
+ // IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
+ // in RFC 3890, i.e., it is measured in bits per second and the bitrate is
+ // calculated over a 1 second window.
+ AvailableIncomingBitrate float64
+
+ // CircuitBreakerTriggerCount represents the number of times the circuit breaker
+ // is triggered for this particular 5-tuple, ceasing transmission.
+ CircuitBreakerTriggerCount uint32
+
+ // RequestsReceived represents the total number of connectivity check requests
+ // received (including retransmissions). It is impossible for the receiver to
+ // tell whether the request was sent in order to check connectivity or check
+ // consent, so all connectivity checks requests are counted here.
+ RequestsReceived uint64
+
+ // RequestsSent represents the total number of connectivity check requests
+ // sent (not including retransmissions).
+ RequestsSent uint64
+
+ // ResponsesReceived represents the total number of connectivity check responses received.
+ ResponsesReceived uint64
+
+ // ResponsesSent epresents the total number of connectivity check responses sent.
+ // Since we cannot distinguish connectivity check requests and consent requests,
+ // all responses are counted.
+ ResponsesSent uint64
+
+ // RetransmissionsReceived represents the total number of connectivity check
+ // request retransmissions received.
+ RetransmissionsReceived uint64
+
+ // RetransmissionsSent represents the total number of connectivity check
+ // request retransmissions sent.
+ RetransmissionsSent uint64
+
+ // ConsentRequestsSent represents the total number of consent requests sent.
+ ConsentRequestsSent uint64
+
+ // ConsentExpiredTimestamp represents the timestamp at which the latest valid
+ // STUN binding response expired.
+ ConsentExpiredTimestamp time.Time
+}
+
+// CandidateStats contains ICE candidate statistics related to the ICETransport objects.
+type CandidateStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp time.Time
+
+ // ID is the candidate ID
+ ID string
+
+ // NetworkType represents the type of network interface used by the base of a
+ // local candidate (the address the ICE agent sends from). Only present for
+ // local candidates; it's not possible to know what type of network interface
+ // a remote candidate is using.
+ //
+ // Note:
+ // This stat only tells you about the network interface used by the first "hop";
+ // it's possible that a connection will be bottlenecked by another type of network.
+ // For example, when using Wi-Fi tethering, the networkType of the relevant candidate
+ // would be "wifi", even when the next hop is over a cellular connection.
+ NetworkType NetworkType
+
+ // IP is the IP address of the candidate, allowing for IPv4 addresses and
+ // IPv6 addresses, but fully qualified domain names (FQDNs) are not allowed.
+ IP string
+
+ // Port is the port number of the candidate.
+ Port int
+
+ // CandidateType is the "Type" field of the ICECandidate.
+ CandidateType CandidateType
+
+ // Priority is the "Priority" field of the ICECandidate.
+ Priority uint32
+
+ // URL is the URL of the TURN or STUN server indicated in the that translated
+ // this IP address. It is the URL address surfaced in an PeerConnectionICEEvent.
+ URL string
+
+ // RelayProtocol is the protocol used by the endpoint to communicate with the
+ // TURN server. This is only present for local candidates. Valid values for
+ // the TURN URL protocol is one of udp, tcp, or tls.
+ RelayProtocol string
+
+ // Deleted is true if the candidate has been deleted/freed. For host candidates,
+ // this means that any network resources (typically a socket) associated with the
+ // candidate have been released. For TURN candidates, this means the TURN allocation
+ // is no longer active.
+ //
+ // Only defined for local candidates. For remote candidates, this property is not applicable.
+ Deleted bool
+}
diff --git a/vendor/github.com/pion/ice/v2/stun.go b/vendor/github.com/pion/ice/v2/stun.go
new file mode 100644
index 0000000..bef7c87
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/stun.go
@@ -0,0 +1,24 @@
+package ice
+
+import (
+ "fmt"
+
+ "github.com/pion/stun"
+)
+
+func assertInboundUsername(m *stun.Message, expectedUsername string) error {
+ var username stun.Username
+ if err := username.GetFrom(m); err != nil {
+ return err
+ }
+ if string(username) != expectedUsername {
+ return fmt.Errorf("%w expected(%x) actual(%x)", errMismatchUsername, expectedUsername, string(username))
+ }
+
+ return nil
+}
+
+func assertInboundMessageIntegrity(m *stun.Message, key []byte) error {
+ messageIntegrityAttr := stun.MessageIntegrity(key)
+ return messageIntegrityAttr.Check(m)
+}
diff --git a/vendor/github.com/pion/ice/v2/tcp_mux.go b/vendor/github.com/pion/ice/v2/tcp_mux.go
new file mode 100644
index 0000000..1a9a797
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/tcp_mux.go
@@ -0,0 +1,295 @@
+package ice
+
+import (
+ "encoding/binary"
+ "io"
+ "net"
+ "strings"
+ "sync"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+)
+
+// TCPMux is allows grouping multiple TCP net.Conns and using them like UDP
+// net.PacketConns. The main implementation of this is TCPMuxDefault, and this
+// interface exists to:
+// 1. prevent SEGV panics when TCPMuxDefault is not initialized by using the
+// invalidTCPMux implementation, and
+// 2. allow mocking in tests.
+type TCPMux interface {
+ io.Closer
+ GetConnByUfrag(ufrag string) (net.PacketConn, error)
+ RemoveConnByUfrag(ufrag string)
+}
+
+// invalidTCPMux is an implementation of TCPMux that always returns ErroTCPMuxNotInitialized.
+type invalidTCPMux struct {
+}
+
+func newInvalidTCPMux() *invalidTCPMux {
+ return &invalidTCPMux{}
+}
+
+// Close implements TCPMux interface.
+func (m *invalidTCPMux) Close() error {
+ return ErrTCPMuxNotInitialized
+}
+
+// GetConnByUfrag implements TCPMux interface.
+func (m *invalidTCPMux) GetConnByUfrag(ufrag string) (net.PacketConn, error) {
+ return nil, ErrTCPMuxNotInitialized
+}
+
+// RemoveConnByUfrag implements TCPMux interface.
+func (m *invalidTCPMux) RemoveConnByUfrag(ufrag string) {}
+
+// TCPMuxDefault muxes TCP net.Conns into net.PacketConns and groups them by
+// Ufrag. It is a default implementation of TCPMux interface.
+type TCPMuxDefault struct {
+ params *TCPMuxParams
+ closed bool
+
+ // conns is a map of all tcpPacketConns indexed by ufrag
+ conns map[string]*tcpPacketConn
+
+ mu sync.Mutex
+ wg sync.WaitGroup
+}
+
+// TCPMuxParams are parameters for TCPMux.
+type TCPMuxParams struct {
+ Listener net.Listener
+ Logger logging.LeveledLogger
+ ReadBufferSize int
+}
+
+// NewTCPMuxDefault creates a new instance of TCPMuxDefault.
+func NewTCPMuxDefault(params TCPMuxParams) *TCPMuxDefault {
+ if params.Logger == nil {
+ params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice")
+ }
+
+ m := &TCPMuxDefault{
+ params: &params,
+
+ conns: map[string]*tcpPacketConn{},
+ }
+
+ m.wg.Add(1)
+ go func() {
+ defer m.wg.Done()
+ m.start()
+ }()
+
+ return m
+}
+
+func (m *TCPMuxDefault) start() {
+ m.params.Logger.Infof("Listening TCP on %s\n", m.params.Listener.Addr())
+ for {
+ conn, err := m.params.Listener.Accept()
+ if err != nil {
+ m.params.Logger.Infof("Error accepting connection: %s\n", err)
+ return
+ }
+
+ m.params.Logger.Debugf("Accepted connection from: %s to %s", conn.RemoteAddr(), conn.LocalAddr())
+
+ m.wg.Add(1)
+ go func() {
+ defer m.wg.Done()
+ m.handleConn(conn)
+ }()
+ }
+}
+
+// LocalAddr returns the listening address of this TCPMuxDefault.
+func (m *TCPMuxDefault) LocalAddr() net.Addr {
+ return m.params.Listener.Addr()
+}
+
+// GetConnByUfrag retrieves an existing or creates a new net.PacketConn.
+func (m *TCPMuxDefault) GetConnByUfrag(ufrag string) (net.PacketConn, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if m.closed {
+ return nil, io.ErrClosedPipe
+ }
+
+ conn, ok := m.conns[ufrag]
+
+ if ok {
+ return conn, nil
+ // return nil, fmt.Errorf("duplicate ufrag %v", ufrag)
+ }
+
+ conn = m.createConn(ufrag, m.LocalAddr())
+
+ return conn, nil
+}
+
+func (m *TCPMuxDefault) createConn(ufrag string, localAddr net.Addr) *tcpPacketConn {
+ conn := newTCPPacketConn(tcpPacketParams{
+ ReadBuffer: m.params.ReadBufferSize,
+ LocalAddr: localAddr,
+ Logger: m.params.Logger,
+ })
+ m.conns[ufrag] = conn
+
+ m.wg.Add(1)
+ go func() {
+ defer m.wg.Done()
+ <-conn.CloseChannel()
+ m.RemoveConnByUfrag(ufrag)
+ }()
+
+ return conn
+}
+
+func (m *TCPMuxDefault) closeAndLogError(closer io.Closer) {
+ err := closer.Close()
+ if err != nil {
+ m.params.Logger.Warnf("Error closing connection: %s", err)
+ }
+}
+
+func (m *TCPMuxDefault) handleConn(conn net.Conn) {
+ buf := make([]byte, receiveMTU)
+
+ n, err := readStreamingPacket(conn, buf)
+ if err != nil {
+ m.params.Logger.Warnf("Error reading first packet: %s", err)
+ return
+ }
+
+ buf = buf[:n]
+
+ msg := &stun.Message{
+ Raw: make([]byte, len(buf)),
+ }
+ // Explicitly copy raw buffer so Message can own the memory.
+ copy(msg.Raw, buf)
+ if err = msg.Decode(); err != nil {
+ m.closeAndLogError(conn)
+ m.params.Logger.Warnf("Failed to handle decode ICE from %s to %s: %v\n", conn.RemoteAddr(), conn.LocalAddr(), err)
+ return
+ }
+
+ if m == nil || msg.Type.Method != stun.MethodBinding { // not a stun
+ m.closeAndLogError(conn)
+ m.params.Logger.Warnf("Not a STUN message from %s to %s\n", conn.RemoteAddr(), conn.LocalAddr())
+ return
+ }
+
+ for _, attr := range msg.Attributes {
+ m.params.Logger.Debugf("msg attr: %s\n", attr.String())
+ }
+
+ attr, err := msg.Get(stun.AttrUsername)
+ if err != nil {
+ m.closeAndLogError(conn)
+ m.params.Logger.Warnf("No Username attribute in STUN message from %s to %s\n", conn.RemoteAddr(), conn.LocalAddr())
+ return
+ }
+
+ ufrag := strings.Split(string(attr), ":")[0]
+ m.params.Logger.Debugf("Ufrag: %s\n", ufrag)
+
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ packetConn, ok := m.conns[ufrag]
+ if !ok {
+ packetConn = m.createConn(ufrag, conn.LocalAddr())
+ }
+
+ if err := packetConn.AddConn(conn, buf); err != nil {
+ m.closeAndLogError(conn)
+ m.params.Logger.Warnf("Error adding conn to tcpPacketConn from %s to %s: %s\n", conn.RemoteAddr(), conn.LocalAddr(), err)
+ return
+ }
+}
+
+// Close closes the listener and waits for all goroutines to exit.
+func (m *TCPMuxDefault) Close() error {
+ m.mu.Lock()
+ m.closed = true
+
+ for _, conn := range m.conns {
+ m.closeAndLogError(conn)
+ }
+ m.conns = map[string]*tcpPacketConn{}
+
+ err := m.params.Listener.Close()
+
+ m.mu.Unlock()
+
+ m.wg.Wait()
+
+ return err
+}
+
+// RemoveConnByUfrag closes and removes a net.PacketConn by Ufrag.
+func (m *TCPMuxDefault) RemoveConnByUfrag(ufrag string) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+
+ if conn, ok := m.conns[ufrag]; ok {
+ m.closeAndLogError(conn)
+ delete(m.conns, ufrag)
+ }
+}
+
+const streamingPacketHeaderLen = 2
+
+// readStreamingPacket reads 1 packet from stream
+// read packet bytes https://tools.ietf.org/html/rfc4571#section-2
+// 2-byte length header prepends each packet:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// -----------------------------------------------------------------
+// | LENGTH | RTP or RTCP packet ... |
+// -----------------------------------------------------------------
+func readStreamingPacket(conn net.Conn, buf []byte) (int, error) {
+ header := make([]byte, streamingPacketHeaderLen)
+ var bytesRead, n int
+ var err error
+
+ for bytesRead < streamingPacketHeaderLen {
+ if n, err = conn.Read(header[bytesRead:streamingPacketHeaderLen]); err != nil {
+ return 0, err
+ }
+ bytesRead += n
+ }
+
+ length := int(binary.BigEndian.Uint16(header))
+
+ if length > cap(buf) {
+ return length, io.ErrShortBuffer
+ }
+
+ bytesRead = 0
+ for bytesRead < length {
+ if n, err = conn.Read(buf[bytesRead:length]); err != nil {
+ return 0, err
+ }
+ bytesRead += n
+ }
+
+ return bytesRead, nil
+}
+
+func writeStreamingPacket(conn net.Conn, buf []byte) (int, error) {
+ bufferCopy := make([]byte, streamingPacketHeaderLen+len(buf))
+ binary.BigEndian.PutUint16(bufferCopy, uint16(len(buf)))
+ copy(bufferCopy[2:], buf)
+
+ n, err := conn.Write(bufferCopy)
+ if err != nil {
+ return 0, err
+ }
+
+ return n - streamingPacketHeaderLen, nil
+}
diff --git a/vendor/github.com/pion/ice/v2/tcp_packet_conn.go b/vendor/github.com/pion/ice/v2/tcp_packet_conn.go
new file mode 100644
index 0000000..dc4eaf0
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/tcp_packet_conn.go
@@ -0,0 +1,240 @@
+package ice
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+type tcpPacketConn struct {
+ params *tcpPacketParams
+
+ // conns is a map of net.Conns indexed by remote net.Addr.String()
+ conns map[string]net.Conn
+
+ recvChan chan streamingPacket
+
+ mu sync.Mutex
+ wg sync.WaitGroup
+ closedChan chan struct{}
+ closeOnce sync.Once
+}
+
+type streamingPacket struct {
+ Data []byte
+ RAddr net.Addr
+ Err error
+}
+
+type tcpPacketParams struct {
+ ReadBuffer int
+ LocalAddr net.Addr
+ Logger logging.LeveledLogger
+}
+
+func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn {
+ p := &tcpPacketConn{
+ params: &params,
+
+ conns: map[string]net.Conn{},
+
+ recvChan: make(chan streamingPacket, params.ReadBuffer),
+ closedChan: make(chan struct{}),
+ }
+
+ return p
+}
+
+func (t *tcpPacketConn) AddConn(conn net.Conn, firstPacketData []byte) error {
+ t.params.Logger.Infof("AddConn: %s %s", conn.RemoteAddr().Network(), conn.RemoteAddr())
+
+ t.mu.Lock()
+ defer t.mu.Unlock()
+
+ select {
+ case <-t.closedChan:
+ return io.ErrClosedPipe
+ default:
+ }
+
+ if _, ok := t.conns[conn.RemoteAddr().String()]; ok {
+ return fmt.Errorf("%w: %s", errConnectionAddrAlreadyExist, conn.RemoteAddr().String())
+ }
+
+ t.conns[conn.RemoteAddr().String()] = conn
+
+ t.wg.Add(1)
+ go func() {
+ if firstPacketData != nil {
+ t.recvChan <- streamingPacket{firstPacketData, conn.RemoteAddr(), nil}
+ }
+ defer t.wg.Done()
+ t.startReading(conn)
+ }()
+
+ return nil
+}
+
+func (t *tcpPacketConn) startReading(conn net.Conn) {
+ buf := make([]byte, receiveMTU)
+
+ for {
+ n, err := readStreamingPacket(conn, buf)
+ // t.params.Logger.Infof("readStreamingPacket read %d bytes", n)
+ if err != nil {
+ t.params.Logger.Infof("%w: %s\n", errReadingStreamingPacket, err)
+ t.handleRecv(streamingPacket{nil, conn.RemoteAddr(), err})
+ t.removeConn(conn)
+ return
+ }
+
+ data := make([]byte, n)
+ copy(data, buf[:n])
+
+ // t.params.Logger.Infof("Writing read streaming packet to recvChan: %d bytes", len(data))
+ t.handleRecv(streamingPacket{data, conn.RemoteAddr(), nil})
+ }
+}
+
+func (t *tcpPacketConn) handleRecv(pkt streamingPacket) {
+ t.mu.Lock()
+
+ recvChan := t.recvChan
+ if t.isClosed() {
+ recvChan = nil
+ }
+
+ t.mu.Unlock()
+
+ select {
+ case recvChan <- pkt:
+ case <-t.closedChan:
+ }
+}
+
+func (t *tcpPacketConn) isClosed() bool {
+ select {
+ case <-t.closedChan:
+ return true
+ default:
+ return false
+ }
+}
+
+// WriteTo is for passive and s-o candidates.
+func (t *tcpPacketConn) ReadFrom(b []byte) (n int, raddr net.Addr, err error) {
+ pkt, ok := <-t.recvChan
+
+ if !ok {
+ return 0, nil, io.ErrClosedPipe
+ }
+
+ if pkt.Err != nil {
+ return 0, pkt.RAddr, pkt.Err
+ }
+
+ if cap(b) < len(pkt.Data) {
+ return 0, pkt.RAddr, io.ErrShortBuffer
+ }
+
+ n = len(pkt.Data)
+ copy(b, pkt.Data[:n])
+ return n, pkt.RAddr, err
+}
+
+// WriteTo is for active and s-o candidates.
+func (t *tcpPacketConn) WriteTo(buf []byte, raddr net.Addr) (n int, err error) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+
+ conn, ok := t.conns[raddr.String()]
+ if !ok {
+ return 0, io.ErrClosedPipe
+ // conn, err := net.DialTCP(tcp, nil, raddr.(*net.TCPAddr))
+
+ // if err != nil {
+ // t.params.Logger.Tracef("DialTCP error: %s", err)
+ // return 0, err
+ // }
+
+ // go t.startReading(conn)
+ // t.conns[raddr.String()] = conn
+ }
+
+ n, err = writeStreamingPacket(conn, buf)
+ if err != nil {
+ t.params.Logger.Tracef("%w %s\n", errWriting, raddr)
+ return n, err
+ }
+
+ return n, err
+}
+
+func (t *tcpPacketConn) closeAndLogError(closer io.Closer) {
+ err := closer.Close()
+ if err != nil {
+ t.params.Logger.Warnf("%w: %s", errClosingConnection, err)
+ }
+}
+
+func (t *tcpPacketConn) removeConn(conn net.Conn) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+
+ t.closeAndLogError(conn)
+
+ delete(t.conns, conn.RemoteAddr().String())
+}
+
+func (t *tcpPacketConn) Close() error {
+ t.mu.Lock()
+
+ var shouldCloseRecvChan bool
+ t.closeOnce.Do(func() {
+ close(t.closedChan)
+ shouldCloseRecvChan = true
+ })
+
+ for _, conn := range t.conns {
+ t.closeAndLogError(conn)
+ delete(t.conns, conn.RemoteAddr().String())
+ }
+
+ t.mu.Unlock()
+
+ t.wg.Wait()
+
+ if shouldCloseRecvChan {
+ close(t.recvChan)
+ }
+
+ return nil
+}
+
+func (t *tcpPacketConn) LocalAddr() net.Addr {
+ return t.params.LocalAddr
+}
+
+func (t *tcpPacketConn) SetDeadline(tm time.Time) error {
+ return nil
+}
+
+func (t *tcpPacketConn) SetReadDeadline(tm time.Time) error {
+ return nil
+}
+
+func (t *tcpPacketConn) SetWriteDeadline(tm time.Time) error {
+ return nil
+}
+
+func (t *tcpPacketConn) CloseChannel() <-chan struct{} {
+ return t.closedChan
+}
+
+func (t *tcpPacketConn) String() string {
+ return fmt.Sprintf("tcpPacketConn{LocalAddr: %s}", t.params.LocalAddr)
+}
diff --git a/vendor/github.com/pion/ice/v2/tcptype.go b/vendor/github.com/pion/ice/v2/tcptype.go
new file mode 100644
index 0000000..6700fe5
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/tcptype.go
@@ -0,0 +1,48 @@
+package ice
+
+import "strings"
+
+// TCPType is the type of ICE TCP candidate as described in
+// ttps://tools.ietf.org/html/rfc6544#section-4.5
+type TCPType int
+
+const (
+ // TCPTypeUnspecified is the default value. For example UDP candidates do not
+ // need this field.
+ TCPTypeUnspecified TCPType = iota
+ // TCPTypeActive is active TCP candidate, which initiates TCP connections.
+ TCPTypeActive
+ // TCPTypePassive is passive TCP candidate, only accepts TCP connections.
+ TCPTypePassive
+ // TCPTypeSimultaneousOpen is like active and passive at the same time.
+ TCPTypeSimultaneousOpen
+)
+
+// NewTCPType creates a new TCPType from string.
+func NewTCPType(value string) TCPType {
+ switch strings.ToLower(value) {
+ case "active":
+ return TCPTypeActive
+ case "passive":
+ return TCPTypePassive
+ case "so":
+ return TCPTypeSimultaneousOpen
+ default:
+ return TCPTypeUnspecified
+ }
+}
+
+func (t TCPType) String() string {
+ switch t {
+ case TCPTypeUnspecified:
+ return ""
+ case TCPTypeActive:
+ return "active"
+ case TCPTypePassive:
+ return "passive"
+ case TCPTypeSimultaneousOpen:
+ return "so"
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/ice/v2/transport.go b/vendor/github.com/pion/ice/v2/transport.go
new file mode 100644
index 0000000..d1c82ff
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/transport.go
@@ -0,0 +1,145 @@
+package ice
+
+import (
+ "context"
+ "net"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/stun"
+)
+
+// Dial connects to the remote agent, acting as the controlling ice agent.
+// Dial blocks until at least one ice candidate pair has successfully connected.
+func (a *Agent) Dial(ctx context.Context, remoteUfrag, remotePwd string) (*Conn, error) {
+ return a.connect(ctx, true, remoteUfrag, remotePwd)
+}
+
+// Accept connects to the remote agent, acting as the controlled ice agent.
+// Accept blocks until at least one ice candidate pair has successfully connected.
+func (a *Agent) Accept(ctx context.Context, remoteUfrag, remotePwd string) (*Conn, error) {
+ return a.connect(ctx, false, remoteUfrag, remotePwd)
+}
+
+// Conn represents the ICE connection.
+// At the moment the lifetime of the Conn is equal to the Agent.
+type Conn struct {
+ bytesReceived uint64
+ bytesSent uint64
+ agent *Agent
+}
+
+// BytesSent returns the number of bytes sent
+func (c *Conn) BytesSent() uint64 {
+ return atomic.LoadUint64(&c.bytesSent)
+}
+
+// BytesReceived returns the number of bytes received
+func (c *Conn) BytesReceived() uint64 {
+ return atomic.LoadUint64(&c.bytesReceived)
+}
+
+func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, remotePwd string) (*Conn, error) {
+ err := a.ok()
+ if err != nil {
+ return nil, err
+ }
+ err = a.startConnectivityChecks(isControlling, remoteUfrag, remotePwd)
+ if err != nil {
+ return nil, err
+ }
+
+ // block until pair selected
+ select {
+ case <-a.done:
+ return nil, a.getErr()
+ case <-ctx.Done():
+ return nil, ErrCanceledByCaller
+ case <-a.onConnected:
+ }
+
+ return &Conn{
+ agent: a,
+ }, nil
+}
+
+// Read implements the Conn Read method.
+func (c *Conn) Read(p []byte) (int, error) {
+ err := c.agent.ok()
+ if err != nil {
+ return 0, err
+ }
+
+ n, err := c.agent.buffer.Read(p)
+ atomic.AddUint64(&c.bytesReceived, uint64(n))
+ return n, err
+}
+
+// Write implements the Conn Write method.
+func (c *Conn) Write(p []byte) (int, error) {
+ err := c.agent.ok()
+ if err != nil {
+ return 0, err
+ }
+
+ if stun.IsMessage(p) {
+ return 0, errICEWriteSTUNMessage
+ }
+
+ pair := c.agent.getSelectedPair()
+ if pair == nil {
+ if err = c.agent.run(c.agent.context(), func(ctx context.Context, a *Agent) {
+ pair = a.getBestValidCandidatePair()
+ }); err != nil {
+ return 0, err
+ }
+
+ if pair == nil {
+ return 0, err
+ }
+ }
+
+ atomic.AddUint64(&c.bytesSent, uint64(len(p)))
+ return pair.Write(p)
+}
+
+// Close implements the Conn Close method. It is used to close
+// the connection. Any calls to Read and Write will be unblocked and return an error.
+func (c *Conn) Close() error {
+ return c.agent.Close()
+}
+
+// LocalAddr returns the local address of the current selected pair or nil if there is none.
+func (c *Conn) LocalAddr() net.Addr {
+ pair := c.agent.getSelectedPair()
+ if pair == nil {
+ return nil
+ }
+
+ return pair.local.addr()
+}
+
+// RemoteAddr returns the remote address of the current selected pair or nil if there is none.
+func (c *Conn) RemoteAddr() net.Addr {
+ pair := c.agent.getSelectedPair()
+ if pair == nil {
+ return nil
+ }
+
+ return pair.remote.addr()
+}
+
+// SetDeadline is a stub
+func (c *Conn) SetDeadline(t time.Time) error {
+ return nil
+}
+
+// SetReadDeadline is a stub
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ return nil
+}
+
+// SetWriteDeadline is a stub
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ return nil
+}
diff --git a/vendor/github.com/pion/ice/v2/url.go b/vendor/github.com/pion/ice/v2/url.go
new file mode 100644
index 0000000..390591e
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/url.go
@@ -0,0 +1,225 @@
+package ice
+
+import (
+ "net"
+ "net/url"
+ "strconv"
+)
+
+// SchemeType indicates the type of server used in the ice.URL structure.
+type SchemeType int
+
+// Unknown defines default public constant to use for "enum" like struct
+// comparisons when no value was defined.
+const Unknown = iota
+
+const (
+ // SchemeTypeSTUN indicates the URL represents a STUN server.
+ SchemeTypeSTUN SchemeType = iota + 1
+
+ // SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server.
+ SchemeTypeSTUNS
+
+ // SchemeTypeTURN indicates the URL represents a TURN server.
+ SchemeTypeTURN
+
+ // SchemeTypeTURNS indicates the URL represents a TURNS (secure) server.
+ SchemeTypeTURNS
+)
+
+// NewSchemeType defines a procedure for creating a new SchemeType from a raw
+// string naming the scheme type.
+func NewSchemeType(raw string) SchemeType {
+ switch raw {
+ case "stun":
+ return SchemeTypeSTUN
+ case "stuns":
+ return SchemeTypeSTUNS
+ case "turn":
+ return SchemeTypeTURN
+ case "turns":
+ return SchemeTypeTURNS
+ default:
+ return SchemeType(Unknown)
+ }
+}
+
+func (t SchemeType) String() string {
+ switch t {
+ case SchemeTypeSTUN:
+ return "stun"
+ case SchemeTypeSTUNS:
+ return "stuns"
+ case SchemeTypeTURN:
+ return "turn"
+ case SchemeTypeTURNS:
+ return "turns"
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// ProtoType indicates the transport protocol type that is used in the ice.URL
+// structure.
+type ProtoType int
+
+const (
+ // ProtoTypeUDP indicates the URL uses a UDP transport.
+ ProtoTypeUDP ProtoType = iota + 1
+
+ // ProtoTypeTCP indicates the URL uses a TCP transport.
+ ProtoTypeTCP
+)
+
+// NewProtoType defines a procedure for creating a new ProtoType from a raw
+// string naming the transport protocol type.
+func NewProtoType(raw string) ProtoType {
+ switch raw {
+ case "udp":
+ return ProtoTypeUDP
+ case "tcp":
+ return ProtoTypeTCP
+ default:
+ return ProtoType(Unknown)
+ }
+}
+
+func (t ProtoType) String() string {
+ switch t {
+ case ProtoTypeUDP:
+ return "udp"
+ case ProtoTypeTCP:
+ return "tcp"
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// URL represents a STUN (rfc7064) or TURN (rfc7065) URL
+type URL struct {
+ Scheme SchemeType
+ Host string
+ Port int
+ Username string
+ Password string
+ Proto ProtoType
+}
+
+// ParseURL parses a STUN or TURN urls following the ABNF syntax described in
+// https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065
+// respectively.
+func ParseURL(raw string) (*URL, error) { //nolint:gocognit
+ rawParts, err := url.Parse(raw)
+ if err != nil {
+ return nil, err
+ }
+
+ var u URL
+ u.Scheme = NewSchemeType(rawParts.Scheme)
+ if u.Scheme == SchemeType(Unknown) {
+ return nil, ErrSchemeType
+ }
+
+ var rawPort string
+ if u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque); err != nil {
+ if e, ok := err.(*net.AddrError); ok {
+ if e.Err == "missing port in address" {
+ nextRawURL := u.Scheme.String() + ":" + rawParts.Opaque
+ switch {
+ case u.Scheme == SchemeTypeSTUN || u.Scheme == SchemeTypeTURN:
+ nextRawURL += ":3478"
+ if rawParts.RawQuery != "" {
+ nextRawURL += "?" + rawParts.RawQuery
+ }
+ return ParseURL(nextRawURL)
+ case u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS:
+ nextRawURL += ":5349"
+ if rawParts.RawQuery != "" {
+ nextRawURL += "?" + rawParts.RawQuery
+ }
+ return ParseURL(nextRawURL)
+ }
+ }
+ }
+ return nil, err
+ }
+
+ if u.Host == "" {
+ return nil, ErrHost
+ }
+
+ if u.Port, err = strconv.Atoi(rawPort); err != nil {
+ return nil, ErrPort
+ }
+
+ switch u.Scheme {
+ case SchemeTypeSTUN:
+ qArgs, err := url.ParseQuery(rawParts.RawQuery)
+ if err != nil || len(qArgs) > 0 {
+ return nil, ErrSTUNQuery
+ }
+ u.Proto = ProtoTypeUDP
+ case SchemeTypeSTUNS:
+ qArgs, err := url.ParseQuery(rawParts.RawQuery)
+ if err != nil || len(qArgs) > 0 {
+ return nil, ErrSTUNQuery
+ }
+ u.Proto = ProtoTypeTCP
+ case SchemeTypeTURN:
+ proto, err := parseProto(rawParts.RawQuery)
+ if err != nil {
+ return nil, err
+ }
+
+ u.Proto = proto
+ if u.Proto == ProtoType(Unknown) {
+ u.Proto = ProtoTypeUDP
+ }
+ case SchemeTypeTURNS:
+ proto, err := parseProto(rawParts.RawQuery)
+ if err != nil {
+ return nil, err
+ }
+
+ u.Proto = proto
+ if u.Proto == ProtoType(Unknown) {
+ u.Proto = ProtoTypeTCP
+ }
+ }
+
+ return &u, nil
+}
+
+func parseProto(raw string) (ProtoType, error) {
+ qArgs, err := url.ParseQuery(raw)
+ if err != nil || len(qArgs) > 1 {
+ return ProtoType(Unknown), ErrInvalidQuery
+ }
+
+ var proto ProtoType
+ if rawProto := qArgs.Get("transport"); rawProto != "" {
+ if proto = NewProtoType(rawProto); proto == ProtoType(0) {
+ return ProtoType(Unknown), ErrProtoType
+ }
+ return proto, nil
+ }
+
+ if len(qArgs) > 0 {
+ return ProtoType(Unknown), ErrInvalidQuery
+ }
+
+ return proto, nil
+}
+
+func (u URL) String() string {
+ rawURL := u.Scheme.String() + ":" + net.JoinHostPort(u.Host, strconv.Itoa(u.Port))
+ if u.Scheme == SchemeTypeTURN || u.Scheme == SchemeTypeTURNS {
+ rawURL += "?transport=" + u.Proto.String()
+ }
+ return rawURL
+}
+
+// IsSecure returns whether the this URL's scheme describes secure scheme or not.
+func (u URL) IsSecure() bool {
+ return u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS
+}
diff --git a/vendor/github.com/pion/ice/v2/usecandidate.go b/vendor/github.com/pion/ice/v2/usecandidate.go
new file mode 100644
index 0000000..f168c08
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/usecandidate.go
@@ -0,0 +1,23 @@
+package ice
+
+import "github.com/pion/stun"
+
+// UseCandidateAttr represents USE-CANDIDATE attribute.
+type UseCandidateAttr struct{}
+
+// AddTo adds USE-CANDIDATE attribute to message.
+func (UseCandidateAttr) AddTo(m *stun.Message) error {
+ m.Add(stun.AttrUseCandidate, nil)
+ return nil
+}
+
+// IsSet returns true if USE-CANDIDATE attribute is set.
+func (UseCandidateAttr) IsSet(m *stun.Message) bool {
+ _, err := m.Get(stun.AttrUseCandidate)
+ return err == nil
+}
+
+// UseCandidate is shorthand for UseCandidateAttr.
+func UseCandidate() UseCandidateAttr {
+ return UseCandidateAttr{}
+}
diff --git a/vendor/github.com/pion/ice/v2/util.go b/vendor/github.com/pion/ice/v2/util.go
new file mode 100644
index 0000000..7eb13c8
--- /dev/null
+++ b/vendor/github.com/pion/ice/v2/util.go
@@ -0,0 +1,233 @@
+package ice
+
+import (
+ "fmt"
+ "net"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+ "github.com/pion/transport/vnet"
+)
+
+type atomicError struct{ v atomic.Value }
+
+func (a *atomicError) Store(err error) {
+ a.v.Store(struct{ error }{err})
+}
+
+func (a *atomicError) Load() error {
+ err, _ := a.v.Load().(struct{ error })
+ return err.error
+}
+
+// The conditions of invalidation written below are defined in
+// https://tools.ietf.org/html/rfc8445#section-5.1.1.1
+func isSupportedIPv6(ip net.IP) bool {
+ if len(ip) != net.IPv6len ||
+ isZeros(ip[0:12]) || // !(IPv4-compatible IPv6)
+ ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 || // !(IPv6 site-local unicast)
+ ip.IsLinkLocalUnicast() ||
+ ip.IsLinkLocalMulticast() {
+ return false
+ }
+ return true
+}
+
+func isZeros(ip net.IP) bool {
+ for i := 0; i < len(ip); i++ {
+ if ip[i] != 0 {
+ return false
+ }
+ }
+ return true
+}
+
+func parseAddr(in net.Addr) (net.IP, int, NetworkType, bool) {
+ switch addr := in.(type) {
+ case *net.UDPAddr:
+ return addr.IP, addr.Port, NetworkTypeUDP4, true
+ case *net.TCPAddr:
+ return addr.IP, addr.Port, NetworkTypeTCP4, true
+ }
+ return nil, 0, 0, false
+}
+
+func createAddr(network NetworkType, ip net.IP, port int) net.Addr {
+ switch {
+ case network.IsTCP():
+ return &net.TCPAddr{IP: ip, Port: port}
+ default:
+ return &net.UDPAddr{IP: ip, Port: port}
+ }
+}
+
+func addrEqual(a, b net.Addr) bool {
+ aIP, aPort, aType, aOk := parseAddr(a)
+ if !aOk {
+ return false
+ }
+
+ bIP, bPort, bType, bOk := parseAddr(b)
+ if !bOk {
+ return false
+ }
+
+ return aType == bType && aIP.Equal(bIP) && aPort == bPort
+}
+
+// getXORMappedAddr initiates a stun requests to serverAddr using conn, reads the response and returns
+// the XORMappedAddress returned by the stun server.
+//
+// Adapted from stun v0.2.
+func getXORMappedAddr(conn net.PacketConn, serverAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) {
+ if deadline > 0 {
+ if err := conn.SetReadDeadline(time.Now().Add(deadline)); err != nil {
+ return nil, err
+ }
+ }
+ defer func() {
+ if deadline > 0 {
+ _ = conn.SetReadDeadline(time.Time{})
+ }
+ }()
+ resp, err := stunRequest(
+ func(p []byte) (int, error) {
+ n, _, errr := conn.ReadFrom(p)
+ return n, errr
+ },
+ func(b []byte) (int, error) {
+ return conn.WriteTo(b, serverAddr)
+ },
+ )
+ if err != nil {
+ return nil, err
+ }
+ var addr stun.XORMappedAddress
+ if err = addr.GetFrom(resp); err != nil {
+ return nil, fmt.Errorf("%w: %v", errGetXorMappedAddrResponse, err)
+ }
+ return &addr, nil
+}
+
+func stunRequest(read func([]byte) (int, error), write func([]byte) (int, error)) (*stun.Message, error) {
+ req, err := stun.Build(stun.BindingRequest, stun.TransactionID)
+ if err != nil {
+ return nil, err
+ }
+ if _, err = write(req.Raw); err != nil {
+ return nil, err
+ }
+ const maxMessageSize = 1280
+ bs := make([]byte, maxMessageSize)
+ n, err := read(bs)
+ if err != nil {
+ return nil, err
+ }
+ res := &stun.Message{Raw: bs[:n]}
+ if err := res.Decode(); err != nil {
+ return nil, err
+ }
+ return res, nil
+}
+
+func localInterfaces(vnet *vnet.Net, interfaceFilter func(string) bool, networkTypes []NetworkType) ([]net.IP, error) { //nolint:gocognit
+ ips := []net.IP{}
+ ifaces, err := vnet.Interfaces()
+ if err != nil {
+ return ips, err
+ }
+
+ var IPv4Requested, IPv6Requested bool
+ for _, typ := range networkTypes {
+ if typ.IsIPv4() {
+ IPv4Requested = true
+ }
+
+ if typ.IsIPv6() {
+ IPv6Requested = true
+ }
+ }
+
+ for _, iface := range ifaces {
+ if iface.Flags&net.FlagUp == 0 {
+ continue // interface down
+ }
+ if iface.Flags&net.FlagLoopback != 0 {
+ continue // loopback interface
+ }
+
+ if interfaceFilter != nil && !interfaceFilter(iface.Name) {
+ continue
+ }
+
+ addrs, err := iface.Addrs()
+ if err != nil {
+ continue
+ }
+
+ for _, addr := range addrs {
+ var ip net.IP
+ switch addr := addr.(type) {
+ case *net.IPNet:
+ ip = addr.IP
+ case *net.IPAddr:
+ ip = addr.IP
+ }
+ if ip == nil || ip.IsLoopback() {
+ continue
+ }
+
+ if ipv4 := ip.To4(); ipv4 == nil {
+ if !IPv6Requested {
+ continue
+ } else if !isSupportedIPv6(ip) {
+ continue
+ }
+ } else if !IPv4Requested {
+ continue
+ }
+
+ ips = append(ips, ip)
+ }
+ }
+ return ips, nil
+}
+
+func listenUDPInPortRange(vnet *vnet.Net, log logging.LeveledLogger, portMax, portMin int, network string, laddr *net.UDPAddr) (vnet.UDPPacketConn, error) {
+ if (laddr.Port != 0) || ((portMin == 0) && (portMax == 0)) {
+ return vnet.ListenUDP(network, laddr)
+ }
+ var i, j int
+ i = portMin
+ if i == 0 {
+ i = 1
+ }
+ j = portMax
+ if j == 0 {
+ j = 0xFFFF
+ }
+ if i > j {
+ return nil, ErrPort
+ }
+
+ portStart := globalMathRandomGenerator.Intn(j-i+1) + i
+ portCurrent := portStart
+ for {
+ laddr = &net.UDPAddr{IP: laddr.IP, Port: portCurrent}
+ c, e := vnet.ListenUDP(network, laddr)
+ if e == nil {
+ return c, e
+ }
+ log.Debugf("failed to listen %s: %v", laddr.String(), e)
+ portCurrent++
+ if portCurrent > j {
+ portCurrent = i
+ }
+ if portCurrent == portStart {
+ break
+ }
+ }
+ return nil, ErrPort
+}
diff --git a/vendor/github.com/pion/interceptor/.gitignore b/vendor/github.com/pion/interceptor/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/interceptor/.golangci.yml b/vendor/github.com/pion/interceptor/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/interceptor/LICENSE b/vendor/github.com/pion/interceptor/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/interceptor/README.md b/vendor/github.com/pion/interceptor/README.md
new file mode 100644
index 0000000..007c340
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/README.md
@@ -0,0 +1,81 @@
+<h1 align="center">
+ <br>
+ Pion Interceptor
+ <br>
+</h1>
+<h4 align="center">RTCP and RTCP processors for building real time communications</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-interceptor-gray.svg?longCache=true&colorB=brightgreen" alt="Pion Interceptor"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://pkg.go.dev/github.com/pion/interceptor"><img src="https://godoc.org/github.com/pion/interceptor?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/interceptor"><img src="https://codecov.io/gh/pion/interceptor/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/interceptor"><img src="https://goreportcard.com/badge/github.com/pion/interceptor" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+Interceptor is a framework for building RTP/RTCP communication software. This framework defines
+a interface that each interceptor must satisfy. These interceptors are then run sequentially. We
+also then provide common interceptors that will be useful for building RTC software.
+
+This package was built for [pion/webrtc](https://github.com/pion/webrtc), but we designed it to be consumable
+by anyone. With the following tenets in mind.
+
+* Useful defaults. Each interceptor will be configured to give you a good default experience.
+* Unblock unique use cases. New use cases are what is driving WebRTC, we want to empower them.
+* Encourage modification. Add your own interceptors without forking. Mixing with the ones we provide.
+* Empower learning. This code base should be useful to read and learn even if you aren't using Pion.
+
+#### Current Interceptors
+* NACK Generator/Responder
+
+#### Planned Interceptors
+* [Sender and Receiver Reports](https://tools.ietf.org/html/rfc3550#section-6.4)
+ - Bandwidth Estimation from Receiver Reports
+* [Transport Wide Congestion Control Feedback](https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01)
+ - [NADA](https://tools.ietf.org/html/rfc8698)
+ - [Google Congestion Control](https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02)
+* JitterBuffer, re-order packets and wait for arrival
+* [FlexFec](https://tools.ietf.org/html/draft-ietf-payload-flexible-fec-scheme-20)
+* [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) compliant statistics generation
+
+### Interceptor Public API
+The public interface is defined in [interceptor.go](https://github.com/pion/interceptor/blob/master/interceptor.go).
+The methods you need to satisy are broken up into 4 groups.
+
+* `BindRTCPWriter` and `BindRTCPReader` allow you to inspect/modify RTCP traffic.
+* `BindLocalStream` and `BindRemoteStream` notify you of a new SSRC stream and allow you to inspect/modify.
+* `UnbindLocalStream` and `UnbindRemoteStream` notify you when a SSRC stream has been removed
+* `Close` called when the interceptor is closed.
+
+Interceptors also pass Attributes between each other. These are a collection of key/value pairs and are useful for storing metadata
+or caching.
+
+[noop.go](https://github.com/pion/interceptor/blob/master/noop.go) is an interceptor that satisfies this interface, but does nothing.
+You can embed this interceptor as a starting point so you only need to define exactly what you need.
+
+[chain.go]( https://github.com/pion/interceptor/blob/master/chain.go) is used to combine multiple interceptors into one. They are called
+sequentially as the packet moves through them.
+
+### Examples
+The [examples](https://github.com/pion/interceptor/blob/master/examples) directory provides some basic examples. If you need more please file an issue!
+You should also look in [pion/webrtc](https://github.com/pion/webrtc) for real world examples.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Adam Kiss](https://github.com/masterada) - *Original Author*
+* [Sean DuBois](https://github.com/sean-der) - *Original Author*
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Alessandro Ros](https://github.com/aler9)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/interceptor/chain.go b/vendor/github.com/pion/interceptor/chain.go
new file mode 100644
index 0000000..d53c307
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/chain.go
@@ -0,0 +1,75 @@
+package interceptor
+
+// Chain is an interceptor that runs all child interceptors in order.
+type Chain struct {
+ interceptors []Interceptor
+}
+
+// NewChain returns a new Chain interceptor.
+func NewChain(interceptors []Interceptor) *Chain {
+ return &Chain{interceptors: interceptors}
+}
+
+// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
+// change in the future. The returned method will be called once per packet batch.
+func (i *Chain) BindRTCPReader(reader RTCPReader) RTCPReader {
+ for _, interceptor := range i.interceptors {
+ reader = interceptor.BindRTCPReader(reader)
+ }
+
+ return reader
+}
+
+// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
+// will be called once per packet batch.
+func (i *Chain) BindRTCPWriter(writer RTCPWriter) RTCPWriter {
+ for _, interceptor := range i.interceptors {
+ writer = interceptor.BindRTCPWriter(writer)
+ }
+
+ return writer
+}
+
+// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
+// will be called once per rtp packet.
+func (i *Chain) BindLocalStream(ctx *StreamInfo, writer RTPWriter) RTPWriter {
+ for _, interceptor := range i.interceptors {
+ writer = interceptor.BindLocalStream(ctx, writer)
+ }
+
+ return writer
+}
+
+// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (i *Chain) UnbindLocalStream(ctx *StreamInfo) {
+ for _, interceptor := range i.interceptors {
+ interceptor.UnbindLocalStream(ctx)
+ }
+}
+
+// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// will be called once per rtp packet.
+func (i *Chain) BindRemoteStream(ctx *StreamInfo, reader RTPReader) RTPReader {
+ for _, interceptor := range i.interceptors {
+ reader = interceptor.BindRemoteStream(ctx, reader)
+ }
+
+ return reader
+}
+
+// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (i *Chain) UnbindRemoteStream(ctx *StreamInfo) {
+ for _, interceptor := range i.interceptors {
+ interceptor.UnbindRemoteStream(ctx)
+ }
+}
+
+// Close closes the Interceptor, cleaning up any data if necessary.
+func (i *Chain) Close() error {
+ var errs []error
+ for _, interceptor := range i.interceptors {
+ errs = append(errs, interceptor.Close())
+ }
+
+ return flattenErrs(errs)
+}
diff --git a/vendor/github.com/pion/interceptor/codecov.yml b/vendor/github.com/pion/interceptor/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/interceptor/errors.go b/vendor/github.com/pion/interceptor/errors.go
new file mode 100644
index 0000000..45e1252
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/errors.go
@@ -0,0 +1,51 @@
+package interceptor
+
+import (
+ "errors"
+ "strings"
+)
+
+func flattenErrs(errs []error) error {
+ errs2 := []error{}
+ for _, e := range errs {
+ if e != nil {
+ errs2 = append(errs2, e)
+ }
+ }
+ if len(errs2) == 0 {
+ return nil
+ }
+ return multiError(errs2)
+}
+
+type multiError []error
+
+func (me multiError) Error() string {
+ var errstrings []string
+
+ for _, err := range me {
+ if err != nil {
+ errstrings = append(errstrings, err.Error())
+ }
+ }
+
+ if len(errstrings) == 0 {
+ return "multiError must contain multiple error but is empty"
+ }
+
+ return strings.Join(errstrings, "\n")
+}
+
+func (me multiError) Is(err error) bool {
+ for _, e := range me {
+ if errors.Is(e, err) {
+ return true
+ }
+ if me2, ok := e.(multiError); ok {
+ if me2.Is(err) {
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/pion/interceptor/go.mod b/vendor/github.com/pion/interceptor/go.mod
new file mode 100644
index 0000000..1370bc9
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/go.mod
@@ -0,0 +1,10 @@
+module github.com/pion/interceptor
+
+go 1.15
+
+require (
+ github.com/pion/logging v0.2.2
+ github.com/pion/rtcp v1.2.6
+ github.com/pion/rtp v1.6.2
+ github.com/stretchr/testify v1.7.0
+)
diff --git a/vendor/github.com/pion/interceptor/go.sum b/vendor/github.com/pion/interceptor/go.sum
new file mode 100644
index 0000000..104d018
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/go.sum
@@ -0,0 +1,21 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
+github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
+github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
+github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/interceptor/interceptor.go b/vendor/github.com/pion/interceptor/interceptor.go
new file mode 100644
index 0000000..2d1b97e
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/interceptor.go
@@ -0,0 +1,108 @@
+// Package interceptor contains the Interceptor interface, with some useful interceptors that should be safe to use
+// in most cases.
+package interceptor
+
+import (
+ "io"
+
+ "github.com/pion/rtcp"
+ "github.com/pion/rtp"
+)
+
+// Interceptor can be used to add functionality to you PeerConnections by modifying any incoming/outgoing rtp/rtcp
+// packets, or sending your own packets as needed.
+type Interceptor interface {
+
+ // BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
+ // change in the future. The returned method will be called once per packet batch.
+ BindRTCPReader(reader RTCPReader) RTCPReader
+
+ // BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
+ // will be called once per packet batch.
+ BindRTCPWriter(writer RTCPWriter) RTCPWriter
+
+ // BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
+ // will be called once per rtp packet.
+ BindLocalStream(info *StreamInfo, writer RTPWriter) RTPWriter
+
+ // UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+ UnbindLocalStream(info *StreamInfo)
+
+ // BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+ // will be called once per rtp packet.
+ BindRemoteStream(info *StreamInfo, reader RTPReader) RTPReader
+
+ // UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+ UnbindRemoteStream(info *StreamInfo)
+
+ io.Closer
+}
+
+// RTPWriter is used by Interceptor.BindLocalStream.
+type RTPWriter interface {
+ // Write a rtp packet
+ Write(header *rtp.Header, payload []byte, attributes Attributes) (int, error)
+}
+
+// RTPReader is used by Interceptor.BindRemoteStream.
+type RTPReader interface {
+ // Read a rtp packet
+ Read([]byte, Attributes) (int, Attributes, error)
+}
+
+// RTCPWriter is used by Interceptor.BindRTCPWriter.
+type RTCPWriter interface {
+ // Write a batch of rtcp packets
+ Write(pkts []rtcp.Packet, attributes Attributes) (int, error)
+}
+
+// RTCPReader is used by Interceptor.BindRTCPReader.
+type RTCPReader interface {
+ // Read a batch of rtcp packets
+ Read([]byte, Attributes) (int, Attributes, error)
+}
+
+// Attributes are a generic key/value store used by interceptors
+type Attributes map[interface{}]interface{}
+
+// RTPWriterFunc is an adapter for RTPWrite interface
+type RTPWriterFunc func(header *rtp.Header, payload []byte, attributes Attributes) (int, error)
+
+// RTPReaderFunc is an adapter for RTPReader interface
+type RTPReaderFunc func([]byte, Attributes) (int, Attributes, error)
+
+// RTCPWriterFunc is an adapter for RTCPWriter interface
+type RTCPWriterFunc func(pkts []rtcp.Packet, attributes Attributes) (int, error)
+
+// RTCPReaderFunc is an adapter for RTCPReader interface
+type RTCPReaderFunc func([]byte, Attributes) (int, Attributes, error)
+
+// Write a rtp packet
+func (f RTPWriterFunc) Write(header *rtp.Header, payload []byte, attributes Attributes) (int, error) {
+ return f(header, payload, attributes)
+}
+
+// Read a rtp packet
+func (f RTPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) {
+ return f(b, a)
+}
+
+// Write a batch of rtcp packets
+func (f RTCPWriterFunc) Write(pkts []rtcp.Packet, attributes Attributes) (int, error) {
+ return f(pkts, attributes)
+}
+
+// Read a batch of rtcp packets
+func (f RTCPReaderFunc) Read(b []byte, a Attributes) (int, Attributes, error) {
+ return f(b, a)
+}
+
+// Get returns the attribute associated with key.
+func (a Attributes) Get(key interface{}) interface{} {
+ return a[key]
+}
+
+// Set sets the attribute associated with key to the given value.
+func (a Attributes) Set(key interface{}, val interface{}) {
+ a[key] = val
+}
diff --git a/vendor/github.com/pion/interceptor/noop.go b/vendor/github.com/pion/interceptor/noop.go
new file mode 100644
index 0000000..2dc4e8e
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/noop.go
@@ -0,0 +1,40 @@
+package interceptor
+
+// NoOp is an Interceptor that does not modify any packets. It can embedded in other interceptors, so it's
+// possible to implement only a subset of the methods.
+type NoOp struct{}
+
+// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
+// change in the future. The returned method will be called once per packet batch.
+func (i *NoOp) BindRTCPReader(reader RTCPReader) RTCPReader {
+ return reader
+}
+
+// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
+// will be called once per packet batch.
+func (i *NoOp) BindRTCPWriter(writer RTCPWriter) RTCPWriter {
+ return writer
+}
+
+// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
+// will be called once per rtp packet.
+func (i *NoOp) BindLocalStream(_ *StreamInfo, writer RTPWriter) RTPWriter {
+ return writer
+}
+
+// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (i *NoOp) UnbindLocalStream(_ *StreamInfo) {}
+
+// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// will be called once per rtp packet.
+func (i *NoOp) BindRemoteStream(_ *StreamInfo, reader RTPReader) RTPReader {
+ return reader
+}
+
+// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (i *NoOp) UnbindRemoteStream(_ *StreamInfo) {}
+
+// Close closes the Interceptor, cleaning up any data if necessary.
+func (i *NoOp) Close() error {
+ return nil
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/errors.go b/vendor/github.com/pion/interceptor/pkg/nack/errors.go
new file mode 100644
index 0000000..bbfc773
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/errors.go
@@ -0,0 +1,6 @@
+package nack
+
+import "errors"
+
+// ErrInvalidSize is returned by newReceiveLog/newSendBuffer, when an incorrect buffer size is supplied.
+var ErrInvalidSize = errors.New("invalid buffer size")
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go b/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
new file mode 100644
index 0000000..447a949
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
@@ -0,0 +1,162 @@
+package nack
+
+import (
+ "math/rand"
+ "sync"
+ "time"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+ "github.com/pion/rtcp"
+ "github.com/pion/rtp"
+)
+
+// GeneratorInterceptor interceptor generates nack feedback messages.
+type GeneratorInterceptor struct {
+ interceptor.NoOp
+ size uint16
+ skipLastN uint16
+ interval time.Duration
+ m sync.Mutex
+ wg sync.WaitGroup
+ close chan struct{}
+ log logging.LeveledLogger
+
+ receiveLogs map[uint32]*receiveLog
+ receiveLogsMu sync.Mutex
+}
+
+// NewGeneratorInterceptor returns a new GeneratorInterceptor interceptor
+func NewGeneratorInterceptor(opts ...GeneratorOption) (*GeneratorInterceptor, error) {
+ r := &GeneratorInterceptor{
+ size: 8192,
+ skipLastN: 0,
+ interval: time.Millisecond * 100,
+ receiveLogs: map[uint32]*receiveLog{},
+ close: make(chan struct{}),
+ log: logging.NewDefaultLoggerFactory().NewLogger("nack_generator"),
+ }
+
+ for _, opt := range opts {
+ if err := opt(r); err != nil {
+ return nil, err
+ }
+ }
+
+ if _, err := newReceiveLog(r.size); err != nil {
+ return nil, err
+ }
+
+ return r, nil
+}
+
+// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
+// will be called once per packet batch.
+func (n *GeneratorInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
+ n.m.Lock()
+ defer n.m.Unlock()
+
+ if n.isClosed() {
+ return writer
+ }
+
+ n.wg.Add(1)
+
+ go n.loop(writer)
+
+ return writer
+}
+
+// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// will be called once per rtp packet.
+func (n *GeneratorInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+ if !streamSupportNack(info) {
+ return reader
+ }
+
+ // error is already checked in NewGeneratorInterceptor
+ receiveLog, _ := newReceiveLog(n.size)
+ n.receiveLogsMu.Lock()
+ n.receiveLogs[info.SSRC] = receiveLog
+ n.receiveLogsMu.Unlock()
+
+ return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
+ i, attr, err := reader.Read(b, a)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ pkt := rtp.Packet{}
+ if err = pkt.Unmarshal(b[:i]); err != nil {
+ return 0, nil, err
+ }
+ receiveLog.add(pkt.Header.SequenceNumber)
+
+ return i, attr, nil
+ })
+}
+
+// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (n *GeneratorInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
+ n.receiveLogsMu.Lock()
+ delete(n.receiveLogs, info.SSRC)
+ n.receiveLogsMu.Unlock()
+}
+
+// Close closes the interceptor
+func (n *GeneratorInterceptor) Close() error {
+ defer n.wg.Wait()
+ n.m.Lock()
+ defer n.m.Unlock()
+
+ if !n.isClosed() {
+ close(n.close)
+ }
+
+ return nil
+}
+
+func (n *GeneratorInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
+ defer n.wg.Done()
+
+ senderSSRC := rand.Uint32() // #nosec
+
+ ticker := time.NewTicker(n.interval)
+ for {
+ select {
+ case <-ticker.C:
+ func() {
+ n.receiveLogsMu.Lock()
+ defer n.receiveLogsMu.Unlock()
+
+ for ssrc, receiveLog := range n.receiveLogs {
+ missing := receiveLog.missingSeqNumbers(n.skipLastN)
+ if len(missing) == 0 {
+ continue
+ }
+
+ nack := &rtcp.TransportLayerNack{
+ SenderSSRC: senderSSRC,
+ MediaSSRC: ssrc,
+ Nacks: rtcp.NackPairsFromSequenceNumbers(missing),
+ }
+
+ if _, err := rtcpWriter.Write([]rtcp.Packet{nack}, interceptor.Attributes{}); err != nil {
+ n.log.Warnf("failed sending nack: %+v", err)
+ }
+ }
+ }()
+ case <-n.close:
+ return
+ }
+ }
+}
+
+func (n *GeneratorInterceptor) isClosed() bool {
+ select {
+ case <-n.close:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go b/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
new file mode 100644
index 0000000..092f5db
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
@@ -0,0 +1,44 @@
+package nack
+
+import (
+ "time"
+
+ "github.com/pion/logging"
+)
+
+// GeneratorOption can be used to configure GeneratorInterceptor
+type GeneratorOption func(r *GeneratorInterceptor) error
+
+// GeneratorSize sets the size of the interceptor.
+// Size must be one of: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
+func GeneratorSize(size uint16) GeneratorOption {
+ return func(r *GeneratorInterceptor) error {
+ r.size = size
+ return nil
+ }
+}
+
+// GeneratorSkipLastN sets the number of packets (n-1 packets before the last received packets) to ignore when generating
+// nack requests.
+func GeneratorSkipLastN(skipLastN uint16) GeneratorOption {
+ return func(r *GeneratorInterceptor) error {
+ r.skipLastN = skipLastN
+ return nil
+ }
+}
+
+// GeneratorLog sets a logger for the interceptor
+func GeneratorLog(log logging.LeveledLogger) GeneratorOption {
+ return func(r *GeneratorInterceptor) error {
+ r.log = log
+ return nil
+ }
+}
+
+// GeneratorInterval sets the nack send interval for the interceptor
+func GeneratorInterval(interval time.Duration) GeneratorOption {
+ return func(r *GeneratorInterceptor) error {
+ r.interval = interval
+ return nil
+ }
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/nack.go b/vendor/github.com/pion/interceptor/pkg/nack/nack.go
new file mode 100644
index 0000000..a658e7f
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/nack.go
@@ -0,0 +1,14 @@
+// Package nack provides interceptors to implement sending and receiving negative acknowledgements
+package nack
+
+import "github.com/pion/interceptor"
+
+func streamSupportNack(info *interceptor.StreamInfo) bool {
+ for _, fb := range info.RTCPFeedback {
+ if fb.Type == "nack" && fb.Parameter == "" {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go b/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go
new file mode 100644
index 0000000..8107f59
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go
@@ -0,0 +1,134 @@
+package nack
+
+import (
+ "fmt"
+ "sync"
+)
+
+type receiveLog struct {
+ packets []uint64
+ size uint16
+ end uint16
+ started bool
+ lastConsecutive uint16
+ m sync.RWMutex
+}
+
+func newReceiveLog(size uint16) (*receiveLog, error) {
+ allowedSizes := make([]uint16, 0)
+ correctSize := false
+ for i := 6; i < 16; i++ {
+ if size == 1<<i {
+ correctSize = true
+ break
+ }
+ allowedSizes = append(allowedSizes, 1<<i)
+ }
+
+ if !correctSize {
+ return nil, fmt.Errorf("%w: %d is not a valid size, allowed sizes: %v", ErrInvalidSize, size, allowedSizes)
+ }
+
+ return &receiveLog{
+ packets: make([]uint64, size/64),
+ size: size,
+ }, nil
+}
+
+func (s *receiveLog) add(seq uint16) {
+ s.m.Lock()
+ defer s.m.Unlock()
+
+ if !s.started {
+ s.setReceived(seq)
+ s.end = seq
+ s.started = true
+ s.lastConsecutive = seq
+ return
+ }
+
+ diff := seq - s.end
+ switch {
+ case diff == 0:
+ return
+ case diff < uint16SizeHalf:
+ // this means a positive diff, in other words seq > end (with counting for rollovers)
+ for i := s.end + 1; i != seq; i++ {
+ // clear packets between end and seq (these may contain packets from a "size" ago)
+ s.delReceived(i)
+ }
+ s.end = seq
+
+ if s.lastConsecutive+1 == seq {
+ s.lastConsecutive = seq
+ } else if seq-s.lastConsecutive > s.size {
+ s.lastConsecutive = seq - s.size
+ s.fixLastConsecutive() // there might be valid packets at the beginning of the buffer now
+ }
+ case s.lastConsecutive+1 == seq:
+ // negative diff, seq < end (with counting for rollovers)
+ s.lastConsecutive = seq
+ s.fixLastConsecutive() // there might be other valid packets after seq
+ }
+
+ s.setReceived(seq)
+}
+
+func (s *receiveLog) get(seq uint16) bool {
+ s.m.RLock()
+ defer s.m.RUnlock()
+
+ diff := s.end - seq
+ if diff >= uint16SizeHalf {
+ return false
+ }
+
+ if diff >= s.size {
+ return false
+ }
+
+ return s.getReceived(seq)
+}
+
+func (s *receiveLog) missingSeqNumbers(skipLastN uint16) []uint16 {
+ s.m.RLock()
+ defer s.m.RUnlock()
+
+ until := s.end - skipLastN
+ if until-s.lastConsecutive >= uint16SizeHalf {
+ // until < s.lastConsecutive (counting for rollover)
+ return nil
+ }
+
+ missingPacketSeqNums := make([]uint16, 0)
+ for i := s.lastConsecutive + 1; i != until+1; i++ {
+ if !s.getReceived(i) {
+ missingPacketSeqNums = append(missingPacketSeqNums, i)
+ }
+ }
+
+ return missingPacketSeqNums
+}
+
+func (s *receiveLog) setReceived(seq uint16) {
+ pos := seq % s.size
+ s.packets[pos/64] |= 1 << (pos % 64)
+}
+
+func (s *receiveLog) delReceived(seq uint16) {
+ pos := seq % s.size
+ s.packets[pos/64] &^= 1 << (pos % 64)
+}
+
+func (s *receiveLog) getReceived(seq uint16) bool {
+ pos := seq % s.size
+ return (s.packets[pos/64] & (1 << (pos % 64))) != 0
+}
+
+func (s *receiveLog) fixLastConsecutive() {
+ i := s.lastConsecutive + 1
+ for ; i != s.end+1 && s.getReceived(i); i++ {
+ // find all consecutive packets
+ }
+ s.lastConsecutive = i - 1
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
new file mode 100644
index 0000000..121657e
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
@@ -0,0 +1,119 @@
+package nack
+
+import (
+ "sync"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+ "github.com/pion/rtcp"
+ "github.com/pion/rtp"
+)
+
+// ResponderInterceptor responds to nack feedback messages
+type ResponderInterceptor struct {
+ interceptor.NoOp
+ size uint16
+ log logging.LeveledLogger
+
+ streams map[uint32]*localStream
+ streamsMu sync.Mutex
+}
+
+type localStream struct {
+ sendBuffer *sendBuffer
+ rtpWriter interceptor.RTPWriter
+}
+
+// NewResponderInterceptor returns a new GeneratorInterceptor interceptor
+func NewResponderInterceptor(opts ...ResponderOption) (*ResponderInterceptor, error) {
+ r := &ResponderInterceptor{
+ size: 8192,
+ log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"),
+ streams: map[uint32]*localStream{},
+ }
+
+ for _, opt := range opts {
+ if err := opt(r); err != nil {
+ return nil, err
+ }
+ }
+
+ if _, err := newSendBuffer(r.size); err != nil {
+ return nil, err
+ }
+
+ return r, nil
+}
+
+// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
+// change in the future. The returned method will be called once per packet batch.
+func (n *ResponderInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
+ return interceptor.RTCPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
+ i, attr, err := reader.Read(b, a)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ pkts, err := rtcp.Unmarshal(b[:i])
+ if err != nil {
+ return 0, nil, err
+ }
+ for _, rtcpPacket := range pkts {
+ nack, ok := rtcpPacket.(*rtcp.TransportLayerNack)
+ if !ok {
+ continue
+ }
+
+ go n.resendPackets(nack)
+ }
+
+ return i, attr, err
+ })
+}
+
+// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
+// will be called once per rtp packet.
+func (n *ResponderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
+ if !streamSupportNack(info) {
+ return writer
+ }
+
+ // error is already checked in NewGeneratorInterceptor
+ sendBuffer, _ := newSendBuffer(n.size)
+ n.streamsMu.Lock()
+ n.streams[info.SSRC] = &localStream{sendBuffer: sendBuffer, rtpWriter: writer}
+ n.streamsMu.Unlock()
+
+ return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
+ sendBuffer.add(&rtp.Packet{Header: *header, Payload: payload})
+ return writer.Write(header, payload, attributes)
+ })
+}
+
+// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (n *ResponderInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
+ n.streamsMu.Lock()
+ delete(n.streams, info.SSRC)
+ n.streamsMu.Unlock()
+}
+
+func (n *ResponderInterceptor) resendPackets(nack *rtcp.TransportLayerNack) {
+ n.streamsMu.Lock()
+ stream, ok := n.streams[nack.MediaSSRC]
+ n.streamsMu.Unlock()
+ if !ok {
+ return
+ }
+
+ for i := range nack.Nacks {
+ nack.Nacks[i].Range(func(seq uint16) bool {
+ if p := stream.sendBuffer.get(seq); p != nil {
+ if _, err := stream.rtpWriter.Write(&p.Header, p.Payload, interceptor.Attributes{}); err != nil {
+ n.log.Warnf("failed resending nacked packet: %+v", err)
+ }
+ }
+
+ return true
+ })
+ }
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go b/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
new file mode 100644
index 0000000..7ad52c8
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
@@ -0,0 +1,23 @@
+package nack
+
+import "github.com/pion/logging"
+
+// ResponderOption can be used to configure ResponderInterceptor
+type ResponderOption func(s *ResponderInterceptor) error
+
+// ResponderSize sets the size of the interceptor.
+// Size must be one of: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
+func ResponderSize(size uint16) ResponderOption {
+ return func(r *ResponderInterceptor) error {
+ r.size = size
+ return nil
+ }
+}
+
+// ResponderLog sets a logger for the interceptor
+func ResponderLog(log logging.LeveledLogger) ResponderOption {
+ return func(r *ResponderInterceptor) error {
+ r.log = log
+ return nil
+ }
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go b/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
new file mode 100644
index 0000000..cf3f020
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
@@ -0,0 +1,74 @@
+package nack
+
+import (
+ "fmt"
+
+ "github.com/pion/rtp"
+)
+
+const (
+ uint16SizeHalf = 1 << 15
+)
+
+type sendBuffer struct {
+ packets []*rtp.Packet
+ size uint16
+ lastAdded uint16
+ started bool
+}
+
+func newSendBuffer(size uint16) (*sendBuffer, error) {
+ allowedSizes := make([]uint16, 0)
+ correctSize := false
+ for i := 0; i < 16; i++ {
+ if size == 1<<i {
+ correctSize = true
+ break
+ }
+ allowedSizes = append(allowedSizes, 1<<i)
+ }
+
+ if !correctSize {
+ return nil, fmt.Errorf("%w: %d is not a valid size, allowed sizes: %v", ErrInvalidSize, size, allowedSizes)
+ }
+
+ return &sendBuffer{
+ packets: make([]*rtp.Packet, size),
+ size: size,
+ }, nil
+}
+
+func (s *sendBuffer) add(packet *rtp.Packet) {
+ seq := packet.SequenceNumber
+ if !s.started {
+ s.packets[seq%s.size] = packet
+ s.lastAdded = seq
+ s.started = true
+ return
+ }
+
+ diff := seq - s.lastAdded
+ if diff == 0 {
+ return
+ } else if diff < uint16SizeHalf {
+ for i := s.lastAdded + 1; i != seq; i++ {
+ s.packets[i%s.size] = nil
+ }
+ }
+
+ s.packets[seq%s.size] = packet
+ s.lastAdded = seq
+}
+
+func (s *sendBuffer) get(seq uint16) *rtp.Packet {
+ diff := s.lastAdded - seq
+ if diff >= uint16SizeHalf {
+ return nil
+ }
+
+ if diff >= s.size {
+ return nil
+ }
+
+ return s.packets[seq%s.size]
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
new file mode 100644
index 0000000..5235b99
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
@@ -0,0 +1,166 @@
+package report
+
+import (
+ "sync"
+ "time"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+ "github.com/pion/rtcp"
+ "github.com/pion/rtp"
+)
+
+// ReceiverInterceptor interceptor generates receiver reports.
+type ReceiverInterceptor struct {
+ interceptor.NoOp
+ interval time.Duration
+ now func() time.Time
+ streams sync.Map
+ log logging.LeveledLogger
+ m sync.Mutex
+ wg sync.WaitGroup
+ close chan struct{}
+}
+
+// NewReceiverInterceptor returns a new ReceiverInterceptor interceptor.
+func NewReceiverInterceptor(opts ...ReceiverOption) (*ReceiverInterceptor, error) {
+ r := &ReceiverInterceptor{
+ interval: 1 * time.Second,
+ now: time.Now,
+ log: logging.NewDefaultLoggerFactory().NewLogger("receiver_interceptor"),
+ close: make(chan struct{}),
+ }
+
+ for _, opt := range opts {
+ if err := opt(r); err != nil {
+ return nil, err
+ }
+ }
+
+ return r, nil
+}
+
+func (r *ReceiverInterceptor) isClosed() bool {
+ select {
+ case <-r.close:
+ return true
+ default:
+ return false
+ }
+}
+
+// Close closes the interceptor.
+func (r *ReceiverInterceptor) Close() error {
+ defer r.wg.Wait()
+ r.m.Lock()
+ defer r.m.Unlock()
+
+ if !r.isClosed() {
+ close(r.close)
+ }
+
+ return nil
+}
+
+// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
+// will be called once per packet batch.
+func (r *ReceiverInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
+ r.m.Lock()
+ defer r.m.Unlock()
+
+ if r.isClosed() {
+ return writer
+ }
+
+ r.wg.Add(1)
+
+ go r.loop(writer)
+
+ return writer
+}
+
+func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
+ defer r.wg.Done()
+
+ ticker := time.NewTicker(r.interval)
+ for {
+ select {
+ case <-ticker.C:
+ now := r.now()
+ r.streams.Range(func(key, value interface{}) bool {
+ stream := value.(*receiverStream)
+
+ var pkts []rtcp.Packet
+
+ pkts = append(pkts, stream.generateReport(now))
+
+ if _, err := rtcpWriter.Write(pkts, interceptor.Attributes{}); err != nil {
+ r.log.Warnf("failed sending: %+v", err)
+ }
+
+ return true
+ })
+
+ case <-r.close:
+ return
+ }
+ }
+}
+
+// BindRemoteStream lets you modify any incoming RTP packets. It is called once for per RemoteStream. The returned method
+// will be called once per rtp packet.
+func (r *ReceiverInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reader interceptor.RTPReader) interceptor.RTPReader {
+ stream := newReceiverStream(info.SSRC, info.ClockRate)
+ r.streams.Store(info.SSRC, stream)
+
+ return interceptor.RTPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
+ i, attr, err := reader.Read(b, a)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ pkt := rtp.Packet{}
+ if err = pkt.Unmarshal(b[:i]); err != nil {
+ return 0, nil, err
+ }
+
+ stream.processRTP(r.now(), &pkt)
+
+ return i, attr, nil
+ })
+}
+
+// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track.
+func (r *ReceiverInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) {
+ r.streams.Delete(info.SSRC)
+}
+
+// BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might
+// change in the future. The returned method will be called once per packet batch.
+func (r *ReceiverInterceptor) BindRTCPReader(reader interceptor.RTCPReader) interceptor.RTCPReader {
+ return interceptor.RTCPReaderFunc(func(b []byte, a interceptor.Attributes) (int, interceptor.Attributes, error) {
+ i, attr, err := reader.Read(b, a)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ pkts, err := rtcp.Unmarshal(b[:i])
+ if err != nil {
+ return 0, nil, err
+ }
+
+ for _, pkt := range pkts {
+ if sr, ok := (pkt).(*rtcp.SenderReport); ok {
+ value, ok := r.streams.Load(sr.SSRC)
+ if !ok {
+ continue
+ }
+
+ stream := value.(*receiverStream)
+ stream.processSenderReport(r.now(), sr)
+ }
+ }
+
+ return i, attr, nil
+ })
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go
new file mode 100644
index 0000000..0467dc5
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go
@@ -0,0 +1,34 @@
+package report
+
+import (
+ "time"
+
+ "github.com/pion/logging"
+)
+
+// ReceiverOption can be used to configure ReceiverInterceptor.
+type ReceiverOption func(r *ReceiverInterceptor) error
+
+// ReceiverLog sets a logger for the interceptor.
+func ReceiverLog(log logging.LeveledLogger) ReceiverOption {
+ return func(r *ReceiverInterceptor) error {
+ r.log = log
+ return nil
+ }
+}
+
+// ReceiverInterval sets send interval for the interceptor.
+func ReceiverInterval(interval time.Duration) ReceiverOption {
+ return func(r *ReceiverInterceptor) error {
+ r.interval = interval
+ return nil
+ }
+}
+
+// ReceiverNow sets an alternative for the time.Now function.
+func ReceiverNow(f func() time.Time) ReceiverOption {
+ return func(r *ReceiverInterceptor) error {
+ r.now = f
+ return nil
+ }
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
new file mode 100644
index 0000000..569715d
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
@@ -0,0 +1,159 @@
+package report
+
+import (
+ "math/rand"
+ "sync"
+ "time"
+
+ "github.com/pion/rtcp"
+ "github.com/pion/rtp"
+)
+
+type receiverStream struct {
+ ssrc uint32
+ receiverSSRC uint32
+ clockRate float64
+
+ m sync.Mutex
+ size uint16
+ packets []uint64
+ started bool
+ seqnumCycles uint16
+ lastSeqnum uint16
+ lastReportSeqnum uint16
+ lastRTPTimeRTP uint32
+ lastRTPTimeTime time.Time
+ jitter float64
+ lastSenderReport uint32
+ lastSenderReportTime time.Time
+ totalLost uint32
+}
+
+func newReceiverStream(ssrc uint32, clockRate uint32) *receiverStream {
+ receiverSSRC := rand.Uint32() // #nosec
+ return &receiverStream{
+ ssrc: ssrc,
+ receiverSSRC: receiverSSRC,
+ clockRate: float64(clockRate),
+ size: 128,
+ packets: make([]uint64, 128),
+ }
+}
+
+func (stream *receiverStream) processRTP(now time.Time, pkt *rtp.Packet) {
+ stream.m.Lock()
+ defer stream.m.Unlock()
+
+ if !stream.started { // first frame
+ stream.started = true
+ stream.setReceived(pkt.SequenceNumber)
+ stream.lastSeqnum = pkt.SequenceNumber
+ stream.lastReportSeqnum = pkt.SequenceNumber - 1
+ stream.lastRTPTimeRTP = pkt.Timestamp
+ stream.lastRTPTimeTime = now
+ } else { // following frames
+ stream.setReceived(pkt.SequenceNumber)
+
+ diff := int32(pkt.SequenceNumber) - int32(stream.lastSeqnum)
+ if diff > 0 || diff < -0x0FFF {
+ // overflow
+ if diff < -0x0FFF {
+ stream.seqnumCycles++
+ }
+
+ // set missing packets as missing
+ for i := stream.lastSeqnum + 1; i != pkt.SequenceNumber; i++ {
+ stream.delReceived(i)
+ }
+
+ stream.lastSeqnum = pkt.SequenceNumber
+ }
+
+ // compute jitter
+ // https://tools.ietf.org/html/rfc3550#page-39
+ D := now.Sub(stream.lastRTPTimeTime).Seconds()*stream.clockRate -
+ (float64(pkt.Timestamp) - float64(stream.lastRTPTimeRTP))
+ if D < 0 {
+ D = -D
+ }
+ stream.jitter += (D - stream.jitter) / 16
+ stream.lastRTPTimeRTP = pkt.Timestamp
+ stream.lastRTPTimeTime = now
+ }
+}
+
+func (stream *receiverStream) setReceived(seq uint16) {
+ pos := seq % stream.size
+ stream.packets[pos/64] |= 1 << (pos % 64)
+}
+
+func (stream *receiverStream) delReceived(seq uint16) {
+ pos := seq % stream.size
+ stream.packets[pos/64] &^= 1 << (pos % 64)
+}
+
+func (stream *receiverStream) getReceived(seq uint16) bool {
+ pos := seq % stream.size
+ return (stream.packets[pos/64] & (1 << (pos % 64))) != 0
+}
+
+func (stream *receiverStream) processSenderReport(now time.Time, sr *rtcp.SenderReport) {
+ stream.m.Lock()
+ defer stream.m.Unlock()
+
+ stream.lastSenderReport = uint32(sr.NTPTime >> 16)
+ stream.lastSenderReportTime = now
+}
+
+func (stream *receiverStream) generateReport(now time.Time) *rtcp.ReceiverReport {
+ stream.m.Lock()
+ defer stream.m.Unlock()
+
+ totalSinceReport := stream.lastSeqnum - stream.lastReportSeqnum
+ totalLostSinceReport := func() uint32 {
+ if stream.lastSeqnum == stream.lastReportSeqnum {
+ return 0
+ }
+
+ ret := uint32(0)
+ for i := stream.lastReportSeqnum + 1; i != stream.lastSeqnum; i++ {
+ if !stream.getReceived(i) {
+ ret++
+ }
+ }
+ return ret
+ }()
+ stream.totalLost += totalLostSinceReport
+
+ // allow up to 24 bits
+ if totalLostSinceReport > 0xFFFFFF {
+ totalLostSinceReport = 0xFFFFFF
+ }
+ if stream.totalLost > 0xFFFFFF {
+ stream.totalLost = 0xFFFFFF
+ }
+
+ r := &rtcp.ReceiverReport{
+ SSRC: stream.receiverSSRC,
+ Reports: []rtcp.ReceptionReport{
+ {
+ SSRC: stream.ssrc,
+ LastSequenceNumber: uint32(stream.seqnumCycles)<<16 | uint32(stream.lastSeqnum),
+ LastSenderReport: stream.lastSenderReport,
+ FractionLost: uint8(float64(totalLostSinceReport*256) / float64(totalSinceReport)),
+ TotalLost: stream.totalLost,
+ Delay: func() uint32 {
+ if stream.lastSenderReportTime.IsZero() {
+ return 0
+ }
+ return uint32(now.Sub(stream.lastSenderReportTime).Seconds() * 65536)
+ }(),
+ Jitter: uint32(stream.jitter),
+ },
+ },
+ }
+
+ stream.lastReportSeqnum = stream.lastSeqnum
+
+ return r
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/report.go b/vendor/github.com/pion/interceptor/pkg/report/report.go
new file mode 100644
index 0000000..0a3034c
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/report/report.go
@@ -0,0 +1,2 @@
+// Package report provides interceptors to implement sending sender and receiver reports.
+package report
diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go b/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
new file mode 100644
index 0000000..e56fd8c
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
@@ -0,0 +1,139 @@
+package report
+
+import (
+ "sync"
+ "time"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+ "github.com/pion/rtcp"
+ "github.com/pion/rtp"
+)
+
+func ntpTime(t time.Time) uint64 {
+ // seconds since 1st January 1900
+ s := (float64(t.UnixNano()) / 1000000000) + 2208988800
+
+ // higher 32 bits are the integer part, lower 32 bits are the fractional part
+ integerPart := uint32(s)
+ fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF)
+ return uint64(integerPart)<<32 | uint64(fractionalPart)
+}
+
+// SenderInterceptor interceptor generates sender reports.
+type SenderInterceptor struct {
+ interceptor.NoOp
+ interval time.Duration
+ now func() time.Time
+ streams sync.Map
+ log logging.LeveledLogger
+ m sync.Mutex
+ wg sync.WaitGroup
+ close chan struct{}
+}
+
+// NewSenderInterceptor returns a new SenderInterceptor interceptor.
+func NewSenderInterceptor(opts ...SenderOption) (*SenderInterceptor, error) {
+ s := &SenderInterceptor{
+ interval: 1 * time.Second,
+ now: time.Now,
+ log: logging.NewDefaultLoggerFactory().NewLogger("sender_interceptor"),
+ close: make(chan struct{}),
+ }
+
+ for _, opt := range opts {
+ if err := opt(s); err != nil {
+ return nil, err
+ }
+ }
+
+ return s, nil
+}
+
+func (s *SenderInterceptor) isClosed() bool {
+ select {
+ case <-s.close:
+ return true
+ default:
+ return false
+ }
+}
+
+// Close closes the interceptor.
+func (s *SenderInterceptor) Close() error {
+ defer s.wg.Wait()
+ s.m.Lock()
+ defer s.m.Unlock()
+
+ if !s.isClosed() {
+ close(s.close)
+ }
+
+ return nil
+}
+
+// BindRTCPWriter lets you modify any outgoing RTCP packets. It is called once per PeerConnection. The returned method
+// will be called once per packet batch.
+func (s *SenderInterceptor) BindRTCPWriter(writer interceptor.RTCPWriter) interceptor.RTCPWriter {
+ s.m.Lock()
+ defer s.m.Unlock()
+
+ if s.isClosed() {
+ return writer
+ }
+
+ s.wg.Add(1)
+
+ go s.loop(writer)
+
+ return writer
+}
+
+func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) {
+ defer s.wg.Done()
+
+ ticker := time.NewTicker(s.interval)
+ for {
+ select {
+ case <-ticker.C:
+ now := s.now()
+ s.streams.Range(func(key, value interface{}) bool {
+ ssrc := key.(uint32)
+ stream := value.(*senderStream)
+
+ stream.m.Lock()
+ defer stream.m.Unlock()
+
+ sr := &rtcp.SenderReport{
+ SSRC: ssrc,
+ NTPTime: ntpTime(now),
+ RTPTime: stream.lastRTPTimeRTP + uint32(now.Sub(stream.lastRTPTimeTime).Seconds()*stream.clockRate),
+ PacketCount: stream.packetCount,
+ OctetCount: stream.octetCount,
+ }
+
+ if _, err := rtcpWriter.Write([]rtcp.Packet{sr}, interceptor.Attributes{}); err != nil {
+ s.log.Warnf("failed sending: %+v", err)
+ }
+
+ return true
+ })
+
+ case <-s.close:
+ return
+ }
+ }
+}
+
+// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
+// will be called once per rtp packet.
+func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter {
+ stream := newSenderStream(info.ClockRate)
+ s.streams.Store(info.SSRC, stream)
+
+ return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, a interceptor.Attributes) (int, error) {
+ stream.processRTP(s.now(), header, payload)
+
+ return writer.Write(header, payload, a)
+ })
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_option.go b/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
new file mode 100644
index 0000000..4cb161a
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
@@ -0,0 +1,34 @@
+package report
+
+import (
+ "time"
+
+ "github.com/pion/logging"
+)
+
+// SenderOption can be used to configure SenderInterceptor.
+type SenderOption func(r *SenderInterceptor) error
+
+// SenderLog sets a logger for the interceptor.
+func SenderLog(log logging.LeveledLogger) SenderOption {
+ return func(r *SenderInterceptor) error {
+ r.log = log
+ return nil
+ }
+}
+
+// SenderInterval sets send interval for the interceptor.
+func SenderInterval(interval time.Duration) SenderOption {
+ return func(r *SenderInterceptor) error {
+ r.interval = interval
+ return nil
+ }
+}
+
+// SenderNow sets an alternative for the time.Now function.
+func SenderNow(f func() time.Time) SenderOption {
+ return func(r *SenderInterceptor) error {
+ r.now = f
+ return nil
+ }
+}
diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go b/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
new file mode 100644
index 0000000..851d70e
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
@@ -0,0 +1,37 @@
+package report
+
+import (
+ "sync"
+ "time"
+
+ "github.com/pion/rtp"
+)
+
+type senderStream struct {
+ clockRate float64
+ m sync.Mutex
+
+ // data from rtp packets
+ lastRTPTimeRTP uint32
+ lastRTPTimeTime time.Time
+ packetCount uint32
+ octetCount uint32
+}
+
+func newSenderStream(clockRate uint32) *senderStream {
+ return &senderStream{
+ clockRate: float64(clockRate),
+ }
+}
+
+func (stream *senderStream) processRTP(now time.Time, header *rtp.Header, payload []byte) {
+ stream.m.Lock()
+ defer stream.m.Unlock()
+
+ // always update time to minimize errors
+ stream.lastRTPTimeRTP = header.Timestamp
+ stream.lastRTPTimeTime = now
+
+ stream.packetCount++
+ stream.octetCount += uint32(len(payload))
+}
diff --git a/vendor/github.com/pion/interceptor/registry.go b/vendor/github.com/pion/interceptor/registry.go
new file mode 100644
index 0000000..d3eed3b
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/registry.go
@@ -0,0 +1,20 @@
+package interceptor
+
+// Registry is a collector for interceptors.
+type Registry struct {
+ interceptors []Interceptor
+}
+
+// Add adds a new Interceptor to the registry.
+func (i *Registry) Add(icpr Interceptor) {
+ i.interceptors = append(i.interceptors, icpr)
+}
+
+// Build constructs a single Interceptor from a InterceptorRegistry
+func (i *Registry) Build() Interceptor {
+ if len(i.interceptors) == 0 {
+ return &NoOp{}
+ }
+
+ return NewChain(i.interceptors)
+}
diff --git a/vendor/github.com/pion/interceptor/renovate.json b/vendor/github.com/pion/interceptor/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/interceptor/streaminfo.go b/vendor/github.com/pion/interceptor/streaminfo.go
new file mode 100644
index 0000000..956fa53
--- /dev/null
+++ b/vendor/github.com/pion/interceptor/streaminfo.go
@@ -0,0 +1,34 @@
+package interceptor
+
+// RTPHeaderExtension represents a negotiated RFC5285 RTP header extension.
+type RTPHeaderExtension struct {
+ URI string
+ ID int
+}
+
+// StreamInfo is the Context passed when a StreamLocal or StreamRemote has been Binded or Unbinded
+type StreamInfo struct {
+ ID string
+ Attributes Attributes
+ SSRC uint32
+ PayloadType uint8
+ RTPHeaderExtensions []RTPHeaderExtension
+ MimeType string
+ ClockRate uint32
+ Channels uint16
+ SDPFmtpLine string
+ RTCPFeedback []RTCPFeedback
+}
+
+// RTCPFeedback signals the connection to use additional RTCP packet types.
+// https://draft.ortc.org/#dom-rtcrtcpfeedback
+type RTCPFeedback struct {
+ // Type is the type of feedback.
+ // see: https://draft.ortc.org/#dom-rtcrtcpfeedback
+ // valid: ack, ccm, nack, goog-remb, transport-cc
+ Type string
+
+ // The parameter value depends on the type.
+ // For example, type="nack" parameter="pli" will send Picture Loss Indicator packets.
+ Parameter string
+}
diff --git a/vendor/github.com/pion/logging/.golangci.yml b/vendor/github.com/pion/logging/.golangci.yml
new file mode 100644
index 0000000..ffb0058
--- /dev/null
+++ b/vendor/github.com/pion/logging/.golangci.yml
@@ -0,0 +1,13 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+
+linters:
+ enable-all: true
+
+issues:
+ exclude-use-default: false
+ max-per-linter: 0
+ max-same-issues: 50
diff --git a/vendor/github.com/pion/logging/.travis.yml b/vendor/github.com/pion/logging/.travis.yml
new file mode 100644
index 0000000..b96a1ed
--- /dev/null
+++ b/vendor/github.com/pion/logging/.travis.yml
@@ -0,0 +1,19 @@
+language: go
+
+go:
+ - "1.x" # use the latest Go release
+
+env:
+ - GO111MODULE=on
+
+before_script:
+ - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
+
+script:
+ - golangci-lint run ./...
+# - rm -rf examples # Remove examples, no test coverage for them
+ - go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./...
+ - bash <(curl -s https://codecov.io/bash)
+ - bash .github/assert-contributors.sh
+ - bash .github/lint-disallowed-functions-in-library.sh
+ - bash .github/lint-commit-message.sh
diff --git a/vendor/github.com/pion/logging/LICENSE b/vendor/github.com/pion/logging/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/logging/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/logging/README.md b/vendor/github.com/pion/logging/README.md
new file mode 100644
index 0000000..c15471d
--- /dev/null
+++ b/vendor/github.com/pion/logging/README.md
@@ -0,0 +1,41 @@
+<h1 align="center">
+ <br>
+ Pion Logging
+ <br>
+</h1>
+<h4 align="center">The Pion logging library</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-logging-gray.svg?longCache=true&colorB=brightgreen" alt="Pion transport"></a>
+ <a href="http://gophers.slack.com/messages/pion"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/logging"><img src="https://travis-ci.org/pion/logging.svg?branch=master" alt="Build Status"></a>
+ <a href="https://godoc.org/github.com/pion/logging"><img src="https://godoc.org/github.com/pion/logging?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/logging"><img src="https://codecov.io/gh/pion/logging/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/logging"><img src="https://goreportcard.com/badge/github.com/pion/logging" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michael MacDonald](https://github.com/mjmac) - *Original Author*
+* [Woodrow Douglass](https://github.com/wdouglass) - *Test coverage*
+* [Michiel De Backker](https://github.com/backkem) - *Docs*
+* [Hugo Arregui](https://github.com/hugoArregui) - *Custom Logs*
+* [Justin Okamoto](https://github.com/justinokamoto) - *Disabled Logs Update*
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/logging/go.mod b/vendor/github.com/pion/logging/go.mod
new file mode 100644
index 0000000..a1b849b
--- /dev/null
+++ b/vendor/github.com/pion/logging/go.mod
@@ -0,0 +1,3 @@
+module github.com/pion/logging
+
+go 1.12
diff --git a/vendor/github.com/pion/logging/go.sum b/vendor/github.com/pion/logging/go.sum
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/github.com/pion/logging/go.sum
diff --git a/vendor/github.com/pion/logging/logger.go b/vendor/github.com/pion/logging/logger.go
new file mode 100644
index 0000000..35f6505
--- /dev/null
+++ b/vendor/github.com/pion/logging/logger.go
@@ -0,0 +1,228 @@
+package logging
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "strings"
+ "sync"
+)
+
+// Use this abstraction to ensure thread-safe access to the logger's io.Writer
+// (which could change at runtime)
+type loggerWriter struct {
+ sync.RWMutex
+ output io.Writer
+}
+
+func (lw *loggerWriter) SetOutput(output io.Writer) {
+ lw.Lock()
+ defer lw.Unlock()
+ lw.output = output
+}
+
+func (lw *loggerWriter) Write(data []byte) (int, error) {
+ lw.RLock()
+ defer lw.RUnlock()
+ return lw.output.Write(data)
+}
+
+// DefaultLeveledLogger encapsulates functionality for providing logging at
+// user-defined levels
+type DefaultLeveledLogger struct {
+ level LogLevel
+ writer *loggerWriter
+ trace *log.Logger
+ debug *log.Logger
+ info *log.Logger
+ warn *log.Logger
+ err *log.Logger
+}
+
+// WithTraceLogger is a chainable configuration function which sets the
+// Trace-level logger
+func (ll *DefaultLeveledLogger) WithTraceLogger(log *log.Logger) *DefaultLeveledLogger {
+ ll.trace = log
+ return ll
+}
+
+// WithDebugLogger is a chainable configuration function which sets the
+// Debug-level logger
+func (ll *DefaultLeveledLogger) WithDebugLogger(log *log.Logger) *DefaultLeveledLogger {
+ ll.debug = log
+ return ll
+}
+
+// WithInfoLogger is a chainable configuration function which sets the
+// Info-level logger
+func (ll *DefaultLeveledLogger) WithInfoLogger(log *log.Logger) *DefaultLeveledLogger {
+ ll.info = log
+ return ll
+}
+
+// WithWarnLogger is a chainable configuration function which sets the
+// Warn-level logger
+func (ll *DefaultLeveledLogger) WithWarnLogger(log *log.Logger) *DefaultLeveledLogger {
+ ll.warn = log
+ return ll
+}
+
+// WithErrorLogger is a chainable configuration function which sets the
+// Error-level logger
+func (ll *DefaultLeveledLogger) WithErrorLogger(log *log.Logger) *DefaultLeveledLogger {
+ ll.err = log
+ return ll
+}
+
+// WithOutput is a chainable configuration function which sets the logger's
+// logging output to the supplied io.Writer
+func (ll *DefaultLeveledLogger) WithOutput(output io.Writer) *DefaultLeveledLogger {
+ ll.writer.SetOutput(output)
+ return ll
+}
+
+func (ll *DefaultLeveledLogger) logf(logger *log.Logger, level LogLevel, format string, args ...interface{}) {
+ if ll.level.Get() < level {
+ return
+ }
+
+ callDepth := 3 // this frame + wrapper func + caller
+ msg := fmt.Sprintf(format, args...)
+ if err := logger.Output(callDepth, msg); err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to log: %s", err)
+ }
+}
+
+// SetLevel sets the logger's logging level
+func (ll *DefaultLeveledLogger) SetLevel(newLevel LogLevel) {
+ ll.level.Set(newLevel)
+}
+
+// Trace emits the preformatted message if the logger is at or below LogLevelTrace
+func (ll *DefaultLeveledLogger) Trace(msg string) {
+ ll.logf(ll.trace, LogLevelTrace, msg)
+}
+
+// Tracef formats and emits a message if the logger is at or below LogLevelTrace
+func (ll *DefaultLeveledLogger) Tracef(format string, args ...interface{}) {
+ ll.logf(ll.trace, LogLevelTrace, format, args...)
+}
+
+// Debug emits the preformatted message if the logger is at or below LogLevelDebug
+func (ll *DefaultLeveledLogger) Debug(msg string) {
+ ll.logf(ll.debug, LogLevelDebug, msg)
+}
+
+// Debugf formats and emits a message if the logger is at or below LogLevelDebug
+func (ll *DefaultLeveledLogger) Debugf(format string, args ...interface{}) {
+ ll.logf(ll.debug, LogLevelDebug, format, args...)
+}
+
+// Info emits the preformatted message if the logger is at or below LogLevelInfo
+func (ll *DefaultLeveledLogger) Info(msg string) {
+ ll.logf(ll.info, LogLevelInfo, msg)
+}
+
+// Infof formats and emits a message if the logger is at or below LogLevelInfo
+func (ll *DefaultLeveledLogger) Infof(format string, args ...interface{}) {
+ ll.logf(ll.info, LogLevelInfo, format, args...)
+}
+
+// Warn emits the preformatted message if the logger is at or below LogLevelWarn
+func (ll *DefaultLeveledLogger) Warn(msg string) {
+ ll.logf(ll.warn, LogLevelWarn, msg)
+}
+
+// Warnf formats and emits a message if the logger is at or below LogLevelWarn
+func (ll *DefaultLeveledLogger) Warnf(format string, args ...interface{}) {
+ ll.logf(ll.warn, LogLevelWarn, format, args...)
+}
+
+// Error emits the preformatted message if the logger is at or below LogLevelError
+func (ll *DefaultLeveledLogger) Error(msg string) {
+ ll.logf(ll.err, LogLevelError, msg)
+}
+
+// Errorf formats and emits a message if the logger is at or below LogLevelError
+func (ll *DefaultLeveledLogger) Errorf(format string, args ...interface{}) {
+ ll.logf(ll.err, LogLevelError, format, args...)
+}
+
+// NewDefaultLeveledLoggerForScope returns a configured LeveledLogger
+func NewDefaultLeveledLoggerForScope(scope string, level LogLevel, writer io.Writer) *DefaultLeveledLogger {
+ if writer == nil {
+ writer = os.Stdout
+ }
+ logger := &DefaultLeveledLogger{
+ writer: &loggerWriter{output: writer},
+ level: level,
+ }
+ return logger.
+ WithTraceLogger(log.New(logger.writer, fmt.Sprintf("%s TRACE: ", scope), log.Lmicroseconds|log.Lshortfile)).
+ WithDebugLogger(log.New(logger.writer, fmt.Sprintf("%s DEBUG: ", scope), log.Lmicroseconds|log.Lshortfile)).
+ WithInfoLogger(log.New(logger.writer, fmt.Sprintf("%s INFO: ", scope), log.LstdFlags)).
+ WithWarnLogger(log.New(logger.writer, fmt.Sprintf("%s WARNING: ", scope), log.LstdFlags)).
+ WithErrorLogger(log.New(logger.writer, fmt.Sprintf("%s ERROR: ", scope), log.LstdFlags))
+}
+
+// DefaultLoggerFactory define levels by scopes and creates new DefaultLeveledLogger
+type DefaultLoggerFactory struct {
+ Writer io.Writer
+ DefaultLogLevel LogLevel
+ ScopeLevels map[string]LogLevel
+}
+
+// NewDefaultLoggerFactory creates a new DefaultLoggerFactory
+func NewDefaultLoggerFactory() *DefaultLoggerFactory {
+ factory := DefaultLoggerFactory{}
+ factory.DefaultLogLevel = LogLevelError
+ factory.ScopeLevels = make(map[string]LogLevel)
+ factory.Writer = os.Stdout
+
+ logLevels := map[string]LogLevel{
+ "DISABLE": LogLevelDisabled,
+ "ERROR": LogLevelError,
+ "WARN": LogLevelWarn,
+ "INFO": LogLevelInfo,
+ "DEBUG": LogLevelDebug,
+ "TRACE": LogLevelTrace,
+ }
+
+ for name, level := range logLevels {
+ env := os.Getenv(fmt.Sprintf("PION_LOG_%s", name))
+
+ if env == "" {
+ env = os.Getenv(fmt.Sprintf("PIONS_LOG_%s", name))
+ }
+
+ if env == "" {
+ continue
+ }
+
+ if strings.ToLower(env) == "all" {
+ factory.DefaultLogLevel = level
+ continue
+ }
+
+ scopes := strings.Split(strings.ToLower(env), ",")
+ for _, scope := range scopes {
+ factory.ScopeLevels[scope] = level
+ }
+ }
+
+ return &factory
+}
+
+// NewLogger returns a configured LeveledLogger for the given , argsscope
+func (f *DefaultLoggerFactory) NewLogger(scope string) LeveledLogger {
+ logLevel := f.DefaultLogLevel
+ if f.ScopeLevels != nil {
+ scopeLevel, found := f.ScopeLevels[scope]
+
+ if found {
+ logLevel = scopeLevel
+ }
+ }
+ return NewDefaultLeveledLoggerForScope(scope, logLevel, f.Writer)
+}
diff --git a/vendor/github.com/pion/logging/scoped.go b/vendor/github.com/pion/logging/scoped.go
new file mode 100644
index 0000000..678bab4
--- /dev/null
+++ b/vendor/github.com/pion/logging/scoped.go
@@ -0,0 +1,72 @@
+package logging
+
+import (
+ "sync/atomic"
+)
+
+// LogLevel represents the level at which the logger will emit log messages
+type LogLevel int32
+
+// Set updates the LogLevel to the supplied value
+func (ll *LogLevel) Set(newLevel LogLevel) {
+ atomic.StoreInt32((*int32)(ll), int32(newLevel))
+}
+
+// Get retrieves the current LogLevel value
+func (ll *LogLevel) Get() LogLevel {
+ return LogLevel(atomic.LoadInt32((*int32)(ll)))
+}
+
+func (ll LogLevel) String() string {
+ switch ll {
+ case LogLevelDisabled:
+ return "Disabled"
+ case LogLevelError:
+ return "Error"
+ case LogLevelWarn:
+ return "Warn"
+ case LogLevelInfo:
+ return "Info"
+ case LogLevelDebug:
+ return "Debug"
+ case LogLevelTrace:
+ return "Trace"
+ default:
+ return "UNKNOWN"
+ }
+}
+
+const (
+ // LogLevelDisabled completely disables logging of any events
+ LogLevelDisabled LogLevel = iota
+ // LogLevelError is for fatal errors which should be handled by user code,
+ // but are logged to ensure that they are seen
+ LogLevelError
+ // LogLevelWarn is for logging abnormal, but non-fatal library operation
+ LogLevelWarn
+ // LogLevelInfo is for logging normal library operation (e.g. state transitions, etc.)
+ LogLevelInfo
+ // LogLevelDebug is for logging low-level library information (e.g. internal operations)
+ LogLevelDebug
+ // LogLevelTrace is for logging very low-level library information (e.g. network traces)
+ LogLevelTrace
+)
+
+// LeveledLogger is the basic pion Logger interface
+type LeveledLogger interface {
+ Trace(msg string)
+ Tracef(format string, args ...interface{})
+ Debug(msg string)
+ Debugf(format string, args ...interface{})
+ Info(msg string)
+ Infof(format string, args ...interface{})
+ Warn(msg string)
+ Warnf(format string, args ...interface{})
+ Error(msg string)
+ Errorf(format string, args ...interface{})
+}
+
+// LoggerFactory is the basic pion LoggerFactory interface
+type LoggerFactory interface {
+ NewLogger(scope string) LeveledLogger
+}
diff --git a/vendor/github.com/pion/mdns/.golangci.yml b/vendor/github.com/pion/mdns/.golangci.yml
new file mode 100644
index 0000000..fb2ff86
--- /dev/null
+++ b/vendor/github.com/pion/mdns/.golangci.yml
@@ -0,0 +1,13 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+
+linters:
+ enable-all: true
+ disable:
+ - funlen
+
+issues:
+ exclude-use-default: false
diff --git a/vendor/github.com/pion/mdns/.travis.yml b/vendor/github.com/pion/mdns/.travis.yml
new file mode 100644
index 0000000..5efedaf
--- /dev/null
+++ b/vendor/github.com/pion/mdns/.travis.yml
@@ -0,0 +1,19 @@
+language: go
+
+go:
+ - "1.x" # use the latest Go release
+
+env:
+ - GO111MODULE=on
+
+before_script:
+ - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.18.0
+
+script:
+ - golangci-lint run ./...
+ - rm -rf examples # Remove examples, no test coverage for them
+ - go test -coverpkg=$(go list ./... | tr '\n' ',') -coverprofile=cover.out -v -race -covermode=atomic ./...
+ - bash <(curl -s https://codecov.io/bash)
+ - bash .github/assert-contributors.sh
+ - bash .github/lint-disallowed-functions-in-library.sh
+ - bash .github/lint-commit-message.sh
diff --git a/vendor/github.com/pion/mdns/LICENSE b/vendor/github.com/pion/mdns/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/mdns/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/mdns/README.md b/vendor/github.com/pion/mdns/README.md
new file mode 100644
index 0000000..0be76a7
--- /dev/null
+++ b/vendor/github.com/pion/mdns/README.md
@@ -0,0 +1,63 @@
+<h1 align="center">
+ <br>
+ Pion mDNS
+ <br>
+</h1>
+<h4 align="center">A Go implementation of mDNS</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-mdns-gray.svg?longCache=true&colorB=brightgreen" alt="Pion mDNS"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/mdns"><img src="https://travis-ci.org/pion/mdns.svg?branch=master" alt="Build Status"></a>
+ <a href="https://godoc.org/github.com/pion/mdns"><img src="https://godoc.org/github.com/pion/mdns?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/mdns"><img src="https://codecov.io/gh/pion/mdns/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/mdns"><img src="https://goreportcard.com/badge/github.com/pion/mdns" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+Go mDNS implementation. The original user is Pion WebRTC, but we would love to see it work for everyone.
+
+### Running Server
+For a mDNS server that responds to queries for `pion-test.local`
+```sh
+go run examples/listen/main.go
+```
+
+
+### Running Client
+To query using Pion you can run the `query` example
+```sh
+go run examples/query/main.go
+```
+
+You can use the macOS client
+```
+dns-sd -q pion-test.local
+```
+
+Or the avahi client
+```
+avahi-resolve -a pion-test.local
+```
+
+### References
+https://tools.ietf.org/html/rfc6762
+https://tools.ietf.org/id/draft-ietf-rtcweb-mdns-ice-candidates-02.html
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Konstantin Itskov](https://github.com/trivigy) - Contributor
+* [Hugo Arregui](https://github.com/hugoArregui)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/mdns/config.go b/vendor/github.com/pion/mdns/config.go
new file mode 100644
index 0000000..ebfa4e5
--- /dev/null
+++ b/vendor/github.com/pion/mdns/config.go
@@ -0,0 +1,27 @@
+package mdns
+
+import (
+ "time"
+
+ "github.com/pion/logging"
+)
+
+const (
+ // DefaultAddress is the default used by mDNS
+ // and in most cases should be the address that the
+ // net.Conn passed to Server is bound to
+ DefaultAddress = "224.0.0.0:5353"
+)
+
+// Config is used to configure a mDNS client or server.
+type Config struct {
+ // QueryInterval controls how ofter we sends Queries until we
+ // get a response for the requested name
+ QueryInterval time.Duration
+
+ // LocalNames are the names that we will generate answers for
+ // when we get questions
+ LocalNames []string
+
+ LoggerFactory logging.LoggerFactory
+}
diff --git a/vendor/github.com/pion/mdns/conn.go b/vendor/github.com/pion/mdns/conn.go
new file mode 100644
index 0000000..a8aafb2
--- /dev/null
+++ b/vendor/github.com/pion/mdns/conn.go
@@ -0,0 +1,316 @@
+package mdns
+
+import (
+ "context"
+ "math/big"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+ "golang.org/x/net/dns/dnsmessage"
+ "golang.org/x/net/ipv4"
+)
+
+// Conn represents a mDNS Server
+type Conn struct {
+ mu sync.RWMutex
+ log logging.LeveledLogger
+
+ socket *ipv4.PacketConn
+ dstAddr *net.UDPAddr
+
+ queryInterval time.Duration
+ localNames []string
+ queries []query
+
+ closed chan interface{}
+}
+
+type query struct {
+ nameWithSuffix string
+ queryResultChan chan queryResult
+}
+
+type queryResult struct {
+ answer dnsmessage.ResourceHeader
+ addr net.Addr
+}
+
+const (
+ inboundBufferSize = 512
+ defaultQueryInterval = time.Second
+ destinationAddress = "224.0.0.251:5353"
+ maxMessageRecords = 3
+ responseTTL = 120
+)
+
+// Server establishes a mDNS connection over an existing conn
+func Server(conn *ipv4.PacketConn, config *Config) (*Conn, error) {
+ if config == nil {
+ return nil, errNilConfig
+ }
+
+ ifaces, err := net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+
+ joinErrCount := 0
+ for i := range ifaces {
+ if err = conn.JoinGroup(&ifaces[i], &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251)}); err != nil {
+ joinErrCount++
+ }
+ }
+ if joinErrCount >= len(ifaces) {
+ return nil, errJoiningMulticastGroup
+ }
+
+ dstAddr, err := net.ResolveUDPAddr("udp", destinationAddress)
+ if err != nil {
+ return nil, err
+
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ localNames := []string{}
+ for _, l := range config.LocalNames {
+ localNames = append(localNames, l+".")
+ }
+
+ c := &Conn{
+ queryInterval: defaultQueryInterval,
+ queries: []query{},
+ socket: conn,
+ dstAddr: dstAddr,
+ localNames: localNames,
+ log: loggerFactory.NewLogger("mdns"),
+ closed: make(chan interface{}),
+ }
+ if config.QueryInterval != 0 {
+ c.queryInterval = config.QueryInterval
+ }
+
+ go c.start()
+ return c, nil
+}
+
+// Close closes the mDNS Conn
+func (c *Conn) Close() error {
+ select {
+ case <-c.closed:
+ return nil
+ default:
+ }
+
+ if err := c.socket.Close(); err != nil {
+ return err
+ }
+
+ <-c.closed
+ return nil
+}
+
+// Query sends mDNS Queries for the following name until
+// either the Context is canceled/expires or we get a result
+func (c *Conn) Query(ctx context.Context, name string) (dnsmessage.ResourceHeader, net.Addr, error) {
+ select {
+ case <-c.closed:
+ return dnsmessage.ResourceHeader{}, nil, errConnectionClosed
+ default:
+ }
+
+ nameWithSuffix := name + "."
+
+ queryChan := make(chan queryResult, 1)
+ c.mu.Lock()
+ c.queries = append(c.queries, query{nameWithSuffix, queryChan})
+ ticker := time.NewTicker(c.queryInterval)
+ c.mu.Unlock()
+
+ c.sendQuestion(nameWithSuffix)
+ for {
+ select {
+ case <-ticker.C:
+ c.sendQuestion(nameWithSuffix)
+ case <-c.closed:
+ return dnsmessage.ResourceHeader{}, nil, errConnectionClosed
+ case res := <-queryChan:
+ return res.answer, res.addr, nil
+ case <-ctx.Done():
+ return dnsmessage.ResourceHeader{}, nil, errContextElapsed
+ }
+ }
+}
+
+func ipToBytes(ip net.IP) (out [4]byte) {
+ rawIP := ip.To4()
+ if rawIP == nil {
+ return
+ }
+
+ ipInt := big.NewInt(0)
+ ipInt.SetBytes(rawIP)
+ copy(out[:], ipInt.Bytes())
+ return
+}
+
+func interfaceForRemote(remote string) (net.IP, error) {
+ conn, err := net.Dial("udp", remote)
+ if err != nil {
+ return nil, err
+ }
+
+ localAddr := conn.LocalAddr().(*net.UDPAddr)
+ if err := conn.Close(); err != nil {
+ return nil, err
+ }
+
+ return localAddr.IP, nil
+}
+
+func (c *Conn) sendQuestion(name string) {
+ packedName, err := dnsmessage.NewName(name)
+ if err != nil {
+ c.log.Warnf("Failed to construct mDNS packet %v", err)
+ return
+ }
+
+ msg := dnsmessage.Message{
+ Header: dnsmessage.Header{},
+ Questions: []dnsmessage.Question{
+ {
+ Type: dnsmessage.TypeA,
+ Class: dnsmessage.ClassINET,
+ Name: packedName,
+ },
+ },
+ }
+
+ rawQuery, err := msg.Pack()
+ if err != nil {
+ c.log.Warnf("Failed to construct mDNS packet %v", err)
+ return
+ }
+
+ if _, err := c.socket.WriteTo(rawQuery, nil, c.dstAddr); err != nil {
+ c.log.Warnf("Failed to send mDNS packet %v", err)
+ return
+ }
+}
+
+func (c *Conn) sendAnswer(name string, dst net.IP) {
+ packedName, err := dnsmessage.NewName(name)
+ if err != nil {
+ c.log.Warnf("Failed to construct mDNS packet %v", err)
+ return
+ }
+
+ msg := dnsmessage.Message{
+ Header: dnsmessage.Header{
+ Response: true,
+ Authoritative: true,
+ },
+ Answers: []dnsmessage.Resource{
+ {
+ Header: dnsmessage.ResourceHeader{
+ Type: dnsmessage.TypeA,
+ Class: dnsmessage.ClassINET,
+ Name: packedName,
+ TTL: responseTTL,
+ },
+ Body: &dnsmessage.AResource{
+ A: ipToBytes(dst),
+ },
+ },
+ },
+ }
+
+ rawAnswer, err := msg.Pack()
+ if err != nil {
+ c.log.Warnf("Failed to construct mDNS packet %v", err)
+ return
+ }
+
+ if _, err := c.socket.WriteTo(rawAnswer, nil, c.dstAddr); err != nil {
+ c.log.Warnf("Failed to send mDNS packet %v", err)
+ return
+ }
+}
+
+func (c *Conn) start() {
+ defer func() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ close(c.closed)
+ }()
+
+ b := make([]byte, inboundBufferSize)
+ p := dnsmessage.Parser{}
+
+ for {
+ n, _, src, err := c.socket.ReadFrom(b)
+ if err != nil {
+ return
+ }
+
+ func() {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+
+ if _, err := p.Start(b[:n]); err != nil {
+ c.log.Warnf("Failed to parse mDNS packet %v", err)
+ return
+ }
+
+ for i := 0; i <= maxMessageRecords; i++ {
+ q, err := p.Question()
+ if err == dnsmessage.ErrSectionDone {
+ break
+ } else if err != nil {
+ c.log.Warnf("Failed to parse mDNS packet %v", err)
+ return
+ }
+
+ for _, localName := range c.localNames {
+ if localName == q.Name.String() {
+
+ localAddress, err := interfaceForRemote(src.String())
+ if err != nil {
+ c.log.Warnf("Failed to get local interface to communicate with %s: %v", src.String(), err)
+ continue
+ }
+
+ c.sendAnswer(q.Name.String(), localAddress)
+ }
+ }
+ }
+
+ for i := 0; i <= maxMessageRecords; i++ {
+ a, err := p.AnswerHeader()
+ if err == dnsmessage.ErrSectionDone {
+ return
+ }
+ if err != nil {
+ c.log.Warnf("Failed to parse mDNS packet %v", err)
+ return
+ }
+
+ if a.Type != dnsmessage.TypeA && a.Type != dnsmessage.TypeAAAA {
+ continue
+ }
+
+ for i := len(c.queries) - 1; i >= 0; i-- {
+ if c.queries[i].nameWithSuffix == a.Name.String() {
+ c.queries[i].queryResultChan <- queryResult{a, src}
+ c.queries = append(c.queries[:i], c.queries[i+1:]...)
+ }
+ }
+ }
+ }()
+ }
+}
diff --git a/vendor/github.com/pion/mdns/errors.go b/vendor/github.com/pion/mdns/errors.go
new file mode 100644
index 0000000..2f8dc92
--- /dev/null
+++ b/vendor/github.com/pion/mdns/errors.go
@@ -0,0 +1,10 @@
+package mdns
+
+import "errors"
+
+var (
+ errJoiningMulticastGroup = errors.New("mDNS: failed to join multicast group")
+ errConnectionClosed = errors.New("mDNS: connection is closed")
+ errContextElapsed = errors.New("mDNS: context has elapsed")
+ errNilConfig = errors.New("mDNS: config must not be nil")
+)
diff --git a/vendor/github.com/pion/mdns/go.mod b/vendor/github.com/pion/mdns/go.mod
new file mode 100644
index 0000000..8359ed9
--- /dev/null
+++ b/vendor/github.com/pion/mdns/go.mod
@@ -0,0 +1,9 @@
+module github.com/pion/mdns
+
+go 1.12
+
+require (
+ github.com/pion/logging v0.2.2
+ github.com/pion/transport v0.8.10
+ golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933
+)
diff --git a/vendor/github.com/pion/mdns/go.sum b/vendor/github.com/pion/mdns/go.sum
new file mode 100644
index 0000000..047fa8b
--- /dev/null
+++ b/vendor/github.com/pion/mdns/go.sum
@@ -0,0 +1,16 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/transport v0.8.10 h1:lTiobMEw2PG6BH/mgIVqTV2mBp/mPT+IJLaN8ZxgdHk=
+github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 h1:e6HwijUxhDe+hPNjZQQn9bA5PW3vNmnN64U2ZW759Lk=
+golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+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=
diff --git a/vendor/github.com/pion/mdns/renovate.json b/vendor/github.com/pion/mdns/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/mdns/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/randutil/.travis.yml b/vendor/github.com/pion/randutil/.travis.yml
new file mode 100644
index 0000000..f04a896
--- /dev/null
+++ b/vendor/github.com/pion/randutil/.travis.yml
@@ -0,0 +1,142 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+# If this repository should have package specific CI config,
+# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
+#
+# If you want to update the shared CI config, send a PR to
+# https://github.com/pion/.goassets instead of this repository.
+#
+
+dist: bionic
+language: go
+
+
+branches:
+ only:
+ - master
+
+env:
+ global:
+ - GO111MODULE=on
+ - GOLANGCI_LINT_VERSION=1.19.1
+
+cache:
+ directories:
+ - ${HOME}/.cache/go-build
+ - ${GOPATH}/pkg/mod
+ npm: true
+ yarn: true
+
+_lint_job: &lint_job
+ env: CACHE_NAME=lint
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ install: skip
+ before_script:
+ - |
+ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
+ | bash -s - -b $GOPATH/bin v${GOLANGCI_LINT_VERSION}
+ script:
+ - bash .github/assert-contributors.sh
+ - bash .github/lint-disallowed-functions-in-library.sh
+ - bash .github/lint-commit-message.sh
+ - bash .github/lint-filename.sh
+ - golangci-lint run ./...
+_test_job: &test_job
+ env: CACHE_NAME=test
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ - go mod download
+ install:
+ - go build ./...
+ script:
+ # If you want to specify repository specific test packages rule,
+ # add `TEST_PACKAGES=$(command to list test target packages)` to .github/.ci.conf
+ - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+ - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
+ - |
+ go test \
+ -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
+ ${TEST_EXTRA_ARGS:-} \
+ -v -race ${testpkgs}
+ - if [ -n "${TEST_HOOK}" ]; then ${TEST_HOOK}; fi
+ after_success:
+ - travis_retry bash <(curl -s https://codecov.io/bash) -c -F go
+_test_i386_job: &test_i386_job
+ env: CACHE_NAME=test386
+ services: docker
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ script:
+ - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+ - |
+ docker run \
+ -u $(id -u):$(id -g) \
+ -e "GO111MODULE=on" \
+ -e "CGO_ENABLED=0" \
+ -v ${PWD}:/go/src/github.com/pion/$(basename ${PWD}) \
+ -v ${HOME}/gopath/pkg/mod:/go/pkg/mod \
+ -v ${HOME}/.cache/go-build:/.cache/go-build \
+ -w /go/src/github.com/pion/$(basename ${PWD}) \
+ -it i386/golang:${GO_VERSION}-alpine \
+ /usr/local/go/bin/go test \
+ ${TEST_EXTRA_ARGS:-} \
+ -v ${testpkgs}
+_test_wasm_job: &test_wasm_job
+ env: CACHE_NAME=wasm
+ language: node_js
+ node_js: 12
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ - if ${SKIP_WASM_TEST:-false}; then exit 0; fi
+ install:
+ # Manually download and install Go instead of using gimme.
+ # It looks like gimme Go causes some errors on go-test for Wasm.
+ - curl -sSfL https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz | tar -C ~ -xzf -
+ - export GOROOT=${HOME}/go
+ - export PATH=${GOROOT}/bin:${PATH}
+ - yarn install
+ - export GO_JS_WASM_EXEC=${GO_JS_WASM_EXEC:-${GOROOT}/misc/wasm/go_js_wasm_exec}
+ script:
+ - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+ - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
+ - |
+ GOOS=js GOARCH=wasm go test \
+ -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
+ -exec="${GO_JS_WASM_EXEC}" \
+ -v ${testpkgs}
+ after_success:
+ - travis_retry bash <(curl -s https://codecov.io/bash) -c -F wasm
+
+jobs:
+ include:
+ - <<: *lint_job
+ name: Lint 1.14
+ go: 1.14
+ - <<: *test_job
+ name: Test 1.13
+ go: 1.13
+ - <<: *test_job
+ name: Test 1.14
+ go: 1.14
+ - <<: *test_i386_job
+ name: Test i386 1.13
+ env: GO_VERSION=1.13
+ go: 1.14 # Go version for host environment only for `go list`.
+ # All tests are done on the version specified by GO_VERSION.
+ - <<: *test_i386_job
+ name: Test i386 1.14
+ env: GO_VERSION=1.14
+ go: 1.14 # Go version for host environment only for `go list`.
+ # All tests are done on the version specified by GO_VERSION.
+ - <<: *test_wasm_job
+ name: Test WASM 1.13
+ env: GO_VERSION=1.13
+ - <<: *test_wasm_job
+ name: Test WASM 1.14
+ env: GO_VERSION=1.14
+
+notifications:
+ email: false
diff --git a/vendor/github.com/pion/randutil/LICENSE b/vendor/github.com/pion/randutil/LICENSE
new file mode 100644
index 0000000..5b5a394
--- /dev/null
+++ b/vendor/github.com/pion/randutil/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Pion
+
+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.
diff --git a/vendor/github.com/pion/randutil/README.md b/vendor/github.com/pion/randutil/README.md
new file mode 100644
index 0000000..94baf77
--- /dev/null
+++ b/vendor/github.com/pion/randutil/README.md
@@ -0,0 +1,14 @@
+# randutil
+Helper library for cryptographic and mathmatical randoms
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Atsushi Watanabe](https://github.com/at-wat) - *Original Author*
diff --git a/vendor/github.com/pion/randutil/codecov.yml b/vendor/github.com/pion/randutil/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/randutil/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/randutil/crypto.go b/vendor/github.com/pion/randutil/crypto.go
new file mode 100644
index 0000000..d10df98
--- /dev/null
+++ b/vendor/github.com/pion/randutil/crypto.go
@@ -0,0 +1,30 @@
+package randutil
+
+import (
+ crand "crypto/rand"
+ "encoding/binary"
+ "math/big"
+)
+
+// GenerateCryptoRandomString generates a random string for cryptographic usage.
+func GenerateCryptoRandomString(n int, runes string) (string, error) {
+ letters := []rune(runes)
+ b := make([]rune, n)
+ for i := range b {
+ v, err := crand.Int(crand.Reader, big.NewInt(int64(len(letters))))
+ if err != nil {
+ return "", err
+ }
+ b[i] = letters[v.Int64()]
+ }
+ return string(b), nil
+}
+
+// CryptoUint64 returns cryptographic random uint64.
+func CryptoUint64() (uint64, error) {
+ var v uint64
+ if err := binary.Read(crand.Reader, binary.LittleEndian, &v); err != nil {
+ return 0, err
+ }
+ return v, nil
+}
diff --git a/vendor/github.com/pion/randutil/go.mod b/vendor/github.com/pion/randutil/go.mod
new file mode 100644
index 0000000..2a824e0
--- /dev/null
+++ b/vendor/github.com/pion/randutil/go.mod
@@ -0,0 +1,3 @@
+module github.com/pion/randutil
+
+go 1.14
diff --git a/vendor/github.com/pion/randutil/math.go b/vendor/github.com/pion/randutil/math.go
new file mode 100644
index 0000000..fbbf460
--- /dev/null
+++ b/vendor/github.com/pion/randutil/math.go
@@ -0,0 +1,72 @@
+package randutil
+
+import (
+ mrand "math/rand" // used for non-crypto unique ID and random port selection
+ "sync"
+ "time"
+)
+
+// MathRandomGenerator is a random generator for non-crypto usage.
+type MathRandomGenerator interface {
+ // Intn returns random integer within [0:n).
+ Intn(n int) int
+
+ // Uint32 returns random 32-bit unsigned integer.
+ Uint32() uint32
+
+ // Uint64 returns random 64-bit unsigned integer.
+ Uint64() uint64
+
+ // GenerateString returns ranom string using given set of runes.
+ // It can be used for generating unique ID to avoid name collision.
+ //
+ // Caution: DO NOT use this for cryptographic usage.
+ GenerateString(n int, runes string) string
+}
+
+type mathRandomGenerator struct {
+ r *mrand.Rand
+ mu sync.Mutex
+}
+
+// NewMathRandomGenerator creates new mathmatical random generator.
+// Random generator is seeded by crypto random.
+func NewMathRandomGenerator() MathRandomGenerator {
+ seed, err := CryptoUint64()
+ if err != nil {
+ // crypto/rand is unavailable. Fallback to seed by time.
+ seed = uint64(time.Now().UnixNano())
+ }
+
+ return &mathRandomGenerator{r: mrand.New(mrand.NewSource(int64(seed)))}
+}
+
+func (g *mathRandomGenerator) Intn(n int) int {
+ g.mu.Lock()
+ v := g.r.Intn(n)
+ g.mu.Unlock()
+ return v
+}
+
+func (g *mathRandomGenerator) Uint32() uint32 {
+ g.mu.Lock()
+ v := g.r.Uint32()
+ g.mu.Unlock()
+ return v
+}
+
+func (g *mathRandomGenerator) Uint64() uint64 {
+ g.mu.Lock()
+ v := g.r.Uint64()
+ g.mu.Unlock()
+ return v
+}
+
+func (g *mathRandomGenerator) GenerateString(n int, runes string) string {
+ letters := []rune(runes)
+ b := make([]rune, n)
+ for i := range b {
+ b[i] = letters[g.Intn(len(letters))]
+ }
+ return string(b)
+}
diff --git a/vendor/github.com/pion/randutil/renovate.json b/vendor/github.com/pion/randutil/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/randutil/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/rtcp/.gitignore b/vendor/github.com/pion/rtcp/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/rtcp/.golangci.yml b/vendor/github.com/pion/rtcp/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/rtcp/LICENSE b/vendor/github.com/pion/rtcp/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/rtcp/README.md b/vendor/github.com/pion/rtcp/README.md
new file mode 100644
index 0000000..a054dae
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/README.md
@@ -0,0 +1,49 @@
+<h1 align="center">
+ <br>
+ Pion RTCP
+ <br>
+</h1>
+<h4 align="center">A Go implementation of RTCP</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-rtcp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion RTCP"></a>
+ <a href="https://sourcegraph.com/github.com/pion/rtcp?badge"><img src="https://sourcegraph.com/github.com/pion/rtcp/-/badge.svg" alt="Sourcegraph Widget"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/rtcp"><img src="https://travis-ci.org/pion/rtcp.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/rtcp"><img src="https://godoc.org/github.com/pion/rtcp?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/rtcp"><img src="https://codecov.io/gh/pion/rtcp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/rtcp"><img src="https://goreportcard.com/badge/github.com/pion/rtcp" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Max Hawkins](https://github.com/maxhawkins) - *Original Author*
+* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
+* [Sean DuBois](https://github.com/Sean-Der) - *Linter fixes*
+* [adwpc](https://github.com/adwpc)
+* [Luke Curley](https://github.com/kixelated)
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Juliusz Chroboczek](https://github.com/jech)
+* [Gabor Pongracz](https://github.com/pongraczgabor87)
+* [Simone Gotti](https://github.com/sgotti)
+* [lllf](https://github.com/LittleLightLittleFire)
+* [cnderrauber](https://github.com/cnderrauber)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/rtcp/codecov.yml b/vendor/github.com/pion/rtcp/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/rtcp/compound_packet.go b/vendor/github.com/pion/rtcp/compound_packet.go
new file mode 100644
index 0000000..2c74279
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/compound_packet.go
@@ -0,0 +1,136 @@
+package rtcp
+
+// A CompoundPacket is a collection of RTCP packets transmitted as a single packet with
+// the underlying protocol (for example UDP).
+//
+// To maximize the resolution of receiption statistics, the first Packet in a CompoundPacket
+// must always be either a SenderReport or a ReceiverReport. This is true even if no data
+// has been sent or received, in which case an empty ReceiverReport must be sent, and even
+// if the only other RTCP packet in the compound packet is a Goodbye.
+//
+// Next, a SourceDescription containing a CNAME item must be included in each CompoundPacket
+// to identify the source and to begin associating media for purposes such as lip-sync.
+//
+// Other RTCP packet types may follow in any order. Packet types may appear more than once.
+type CompoundPacket []Packet
+
+var _ Packet = (*CompoundPacket)(nil) // assert is a Packet
+
+// Validate returns an error if this is not an RFC-compliant CompoundPacket.
+func (c CompoundPacket) Validate() error {
+ if len(c) == 0 {
+ return errEmptyCompound
+ }
+
+ // SenderReport and ReceiverReport are the only types that
+ // are allowed to be the first packet in a compound datagram
+ switch c[0].(type) {
+ case *SenderReport, *ReceiverReport:
+ // ok
+ default:
+ return errBadFirstPacket
+ }
+
+ for _, pkt := range c[1:] {
+ switch p := pkt.(type) {
+ // If the number of RecetpionReports exceeds 31 additional ReceiverReports
+ // can be included here.
+ case *ReceiverReport:
+ continue
+
+ // A SourceDescription containing a CNAME must be included in every
+ // CompoundPacket.
+ case *SourceDescription:
+ var hasCNAME bool
+ for _, c := range p.Chunks {
+ for _, it := range c.Items {
+ if it.Type == SDESCNAME {
+ hasCNAME = true
+ }
+ }
+ }
+
+ if !hasCNAME {
+ return errMissingCNAME
+ }
+
+ return nil
+
+ // Other packets are not permitted before the CNAME
+ default:
+ return errPacketBeforeCNAME
+ }
+ }
+
+ // CNAME never reached
+ return errMissingCNAME
+}
+
+// CNAME returns the CNAME that *must* be present in every CompoundPacket
+func (c CompoundPacket) CNAME() (string, error) {
+ var err error
+
+ if len(c) < 1 {
+ return "", errEmptyCompound
+ }
+
+ for _, pkt := range c[1:] {
+ sdes, ok := pkt.(*SourceDescription)
+ if ok {
+ for _, c := range sdes.Chunks {
+ for _, it := range c.Items {
+ if it.Type == SDESCNAME {
+ return it.Text, err
+ }
+ }
+ }
+ } else {
+ _, ok := pkt.(*ReceiverReport)
+ if !ok {
+ err = errPacketBeforeCNAME
+ }
+ }
+ }
+ return "", errMissingCNAME
+}
+
+// Marshal encodes the CompoundPacket as binary.
+func (c CompoundPacket) Marshal() ([]byte, error) {
+ if err := c.Validate(); err != nil {
+ return nil, err
+ }
+
+ p := []Packet(c)
+ return Marshal(p)
+}
+
+// Unmarshal decodes a CompoundPacket from binary.
+func (c *CompoundPacket) Unmarshal(rawData []byte) error {
+ out := make(CompoundPacket, 0)
+ for len(rawData) != 0 {
+ p, processed, err := unmarshal(rawData)
+ if err != nil {
+ return err
+ }
+
+ out = append(out, p)
+ rawData = rawData[processed:]
+ }
+ *c = out
+
+ if err := c.Validate(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// DestinationSSRC returns the synchronization sources associated with this
+// CompoundPacket's reception report.
+func (c CompoundPacket) DestinationSSRC() []uint32 {
+ if len(c) == 0 {
+ return nil
+ }
+
+ return c[0].DestinationSSRC()
+}
diff --git a/vendor/github.com/pion/rtcp/doc.go b/vendor/github.com/pion/rtcp/doc.go
new file mode 100644
index 0000000..fa948df
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/doc.go
@@ -0,0 +1,39 @@
+/*
+Package rtcp implements encoding and decoding of RTCP packets according to RFCs 3550 and 5506.
+
+RTCP is a sister protocol of the Real-time Transport Protocol (RTP). Its basic functionality
+and packet structure is defined in RFC 3550. RTCP provides out-of-band statistics and control
+information for an RTP session. It partners with RTP in the delivery and packaging of multimedia data,
+but does not transport any media data itself.
+
+The primary function of RTCP is to provide feedback on the quality of service (QoS)
+in media distribution by periodically sending statistics information such as transmitted octet
+and packet counts, packet loss, packet delay variation, and round-trip delay time to participants
+in a streaming multimedia session. An application may use this information to control quality of
+service parameters, perhaps by limiting flow, or using a different codec.
+
+Decoding RTCP packets:
+
+ pkt, err := rtcp.Unmarshal(rtcpData)
+ // ...
+
+ switch p := pkt.(type) {
+ case *rtcp.CompoundPacket:
+ ...
+ case *rtcp.PictureLossIndication:
+ ...
+ default:
+ ...
+ }
+
+Encoding RTCP packets:
+
+ pkt := &rtcp.PictureLossIndication{
+ SenderSSRC: senderSSRC,
+ MediaSSRC: mediaSSRC
+ }
+ pliData, err := pkt.Marshal()
+ // ...
+
+*/
+package rtcp
diff --git a/vendor/github.com/pion/rtcp/errors.go b/vendor/github.com/pion/rtcp/errors.go
new file mode 100644
index 0000000..d1b00c5
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/errors.go
@@ -0,0 +1,30 @@
+package rtcp
+
+import "errors"
+
+var (
+ errWrongMarshalSize = errors.New("rtcp: wrong marshal size")
+ errInvalidTotalLost = errors.New("rtcp: invalid total lost count")
+ errInvalidHeader = errors.New("rtcp: invalid header")
+ errEmptyCompound = errors.New("rtcp: empty compound packet")
+ errBadFirstPacket = errors.New("rtcp: first packet in compound must be SR or RR")
+ errMissingCNAME = errors.New("rtcp: compound missing SourceDescription with CNAME")
+ errPacketBeforeCNAME = errors.New("rtcp: feedback packet seen before CNAME")
+ errTooManyReports = errors.New("rtcp: too many reports")
+ errTooManyChunks = errors.New("rtcp: too many chunks")
+ errTooManySources = errors.New("rtcp: too many sources")
+ errPacketTooShort = errors.New("rtcp: packet too short")
+ errWrongType = errors.New("rtcp: wrong packet type")
+ errSDESTextTooLong = errors.New("rtcp: sdes must be < 255 octets long")
+ errSDESMissingType = errors.New("rtcp: sdes item missing type")
+ errReasonTooLong = errors.New("rtcp: reason must be < 255 octets long")
+ errBadVersion = errors.New("rtcp: invalid packet version")
+ errWrongPadding = errors.New("rtcp: invalid padding value")
+ errWrongFeedbackType = errors.New("rtcp: wrong feedback message type")
+ errWrongPayloadType = errors.New("rtcp: wrong payload type")
+ errHeaderTooSmall = errors.New("rtcp: header length is too small")
+ errSSRCMustBeZero = errors.New("rtcp: media SSRC must be 0")
+ errMissingREMBidentifier = errors.New("missing REMB identifier")
+ errSSRCNumAndLengthMismatch = errors.New("SSRC num and length do not match")
+ errInvalidSizeOrStartIndex = errors.New("invalid size or startIndex")
+)
diff --git a/vendor/github.com/pion/rtcp/full_intra_request.go b/vendor/github.com/pion/rtcp/full_intra_request.go
new file mode 100644
index 0000000..74ca928
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/full_intra_request.go
@@ -0,0 +1,107 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// A FIREntry is a (SSRC, seqno) pair, as carried by FullIntraRequest.
+type FIREntry struct {
+ SSRC uint32
+ SequenceNumber uint8
+}
+
+// The FullIntraRequest packet is used to reliably request an Intra frame
+// in a video stream. See RFC 5104 Section 3.5.1. This is not for loss
+// recovery, which should use PictureLossIndication (PLI) instead.
+type FullIntraRequest struct {
+ SenderSSRC uint32
+ MediaSSRC uint32
+
+ FIR []FIREntry
+}
+
+const (
+ firOffset = 8
+)
+
+var _ Packet = (*FullIntraRequest)(nil)
+
+// Marshal encodes the FullIntraRequest
+func (p FullIntraRequest) Marshal() ([]byte, error) {
+ rawPacket := make([]byte, firOffset+(len(p.FIR)*8))
+ binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC)
+ binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC)
+ for i, fir := range p.FIR {
+ binary.BigEndian.PutUint32(rawPacket[firOffset+8*i:], fir.SSRC)
+ rawPacket[firOffset+8*i+4] = fir.SequenceNumber
+ }
+ h := p.Header()
+ hData, err := h.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(hData, rawPacket...), nil
+}
+
+// Unmarshal decodes the TransportLayerNack
+func (p *FullIntraRequest) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) < (headerLength + ssrcLength) {
+ return errPacketTooShort
+ }
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if len(rawPacket) < (headerLength + int(4*h.Length)) {
+ return errPacketTooShort
+ }
+
+ if h.Type != TypePayloadSpecificFeedback || h.Count != FormatFIR {
+ return errWrongType
+ }
+
+ p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
+ p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
+ for i := headerLength + firOffset; i < (headerLength + int(h.Length*4)); i += 8 {
+ p.FIR = append(p.FIR, FIREntry{
+ binary.BigEndian.Uint32(rawPacket[i:]),
+ rawPacket[i+4],
+ })
+ }
+ return nil
+}
+
+// Header returns the Header associated with this packet.
+func (p *FullIntraRequest) Header() Header {
+ return Header{
+ Count: FormatFIR,
+ Type: TypePayloadSpecificFeedback,
+ Length: uint16((p.len() / 4) - 1),
+ }
+}
+
+func (p *FullIntraRequest) len() int {
+ return headerLength + firOffset + len(p.FIR)*8
+}
+
+func (p *FullIntraRequest) String() string {
+ out := fmt.Sprintf("FullIntraRequest %x %x",
+ p.SenderSSRC, p.MediaSSRC)
+ for _, e := range p.FIR {
+ out += fmt.Sprintf(" (%x %v)", e.SSRC, e.SequenceNumber)
+ }
+ return out
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (p *FullIntraRequest) DestinationSSRC() []uint32 {
+ ssrcs := make([]uint32, 0, len(p.FIR))
+ for _, entry := range p.FIR {
+ ssrcs = append(ssrcs, entry.SSRC)
+ }
+ return ssrcs
+}
diff --git a/vendor/github.com/pion/rtcp/fuzz.go b/vendor/github.com/pion/rtcp/fuzz.go
new file mode 100644
index 0000000..2ea4fb1
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/fuzz.go
@@ -0,0 +1,51 @@
+// +build gofuzz
+
+package rtcp
+
+import (
+ "bytes"
+ "io"
+)
+
+// Fuzz implements a randomized fuzz test of the rtcp
+// parser using go-fuzz.
+//
+// To run the fuzzer, first download go-fuzz:
+// `go get github.com/dvyukov/go-fuzz/...`
+//
+// Then build the testing package:
+// `go-fuzz-build github.com/pion/webrtc`
+//
+// And run the fuzzer on the corpus:
+// ```
+// mkdir workdir
+//
+// # optionally add a starter corpus of valid rtcp packets.
+// # the corpus should be as compact and diverse as possible.
+// cp -r ~/my-rtcp-packets workdir/corpus
+//
+// go-fuzz -bin=ase-fuzz.zip -workdir=workdir
+// ````
+func Fuzz(data []byte) int {
+ r := NewReader(bytes.NewReader(data))
+ for {
+ _, data, err := r.ReadPacket()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return 0
+ }
+
+ packet, err := Unmarshal(data)
+ if err != nil {
+ return 0
+ }
+
+ if _, err := packet.Marshal(); err != nil {
+ return 0
+ }
+ }
+
+ return 1
+}
diff --git a/vendor/github.com/pion/rtcp/go.mod b/vendor/github.com/pion/rtcp/go.mod
new file mode 100644
index 0000000..28b6e9d
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/go.mod
@@ -0,0 +1,5 @@
+module github.com/pion/rtcp
+
+go 1.13
+
+require github.com/stretchr/testify v1.6.1
diff --git a/vendor/github.com/pion/rtcp/go.sum b/vendor/github.com/pion/rtcp/go.sum
new file mode 100644
index 0000000..afe7890
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/go.sum
@@ -0,0 +1,11 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/rtcp/goodbye.go b/vendor/github.com/pion/rtcp/goodbye.go
new file mode 100644
index 0000000..b8718b3
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/goodbye.go
@@ -0,0 +1,146 @@
+package rtcp
+
+import (
+ "encoding/binary"
+)
+
+// The Goodbye packet indicates that one or more sources are no longer active.
+type Goodbye struct {
+ // The SSRC/CSRC identifiers that are no longer active
+ Sources []uint32
+ // Optional text indicating the reason for leaving, e.g., "camera malfunction" or "RTP loop detected"
+ Reason string
+}
+
+var _ Packet = (*Goodbye)(nil) // assert is a Packet
+
+// Marshal encodes the Goodbye packet in binary
+func (g Goodbye) Marshal() ([]byte, error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V=2|P| SC | PT=BYE=203 | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SSRC/CSRC |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * : ... :
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * (opt) | length | reason for leaving ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ rawPacket := make([]byte, g.len())
+ packetBody := rawPacket[headerLength:]
+
+ if len(g.Sources) > countMax {
+ return nil, errTooManySources
+ }
+
+ for i, s := range g.Sources {
+ binary.BigEndian.PutUint32(packetBody[i*ssrcLength:], s)
+ }
+
+ if g.Reason != "" {
+ reason := []byte(g.Reason)
+
+ if len(reason) > sdesMaxOctetCount {
+ return nil, errReasonTooLong
+ }
+
+ reasonOffset := len(g.Sources) * ssrcLength
+ packetBody[reasonOffset] = uint8(len(reason))
+ copy(packetBody[reasonOffset+1:], reason)
+ }
+
+ hData, err := g.Header().Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(rawPacket, hData)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the Goodbye packet from binary
+func (g *Goodbye) Unmarshal(rawPacket []byte) error {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V=2|P| SC | PT=BYE=203 | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SSRC/CSRC |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * : ... :
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * (opt) | length | reason for leaving ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ var header Header
+ if err := header.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if header.Type != TypeGoodbye {
+ return errWrongType
+ }
+
+ if getPadding(len(rawPacket)) != 0 {
+ return errPacketTooShort
+ }
+
+ g.Sources = make([]uint32, header.Count)
+
+ reasonOffset := int(headerLength + header.Count*ssrcLength)
+ if reasonOffset > len(rawPacket) {
+ return errPacketTooShort
+ }
+
+ for i := 0; i < int(header.Count); i++ {
+ offset := headerLength + i*ssrcLength
+
+ g.Sources[i] = binary.BigEndian.Uint32(rawPacket[offset:])
+ }
+
+ if reasonOffset < len(rawPacket) {
+ reasonLen := int(rawPacket[reasonOffset])
+ reasonEnd := reasonOffset + 1 + reasonLen
+
+ if reasonEnd > len(rawPacket) {
+ return errPacketTooShort
+ }
+
+ g.Reason = string(rawPacket[reasonOffset+1 : reasonEnd])
+ }
+
+ return nil
+}
+
+// Header returns the Header associated with this packet.
+func (g *Goodbye) Header() Header {
+ return Header{
+ Padding: false,
+ Count: uint8(len(g.Sources)),
+ Type: TypeGoodbye,
+ Length: uint16((g.len() / 4) - 1),
+ }
+}
+
+func (g *Goodbye) len() int {
+ srcsLength := len(g.Sources) * ssrcLength
+ reasonLength := len(g.Reason) + 1
+
+ l := headerLength + srcsLength + reasonLength
+
+ // align to 32-bit boundary
+ return l + getPadding(l)
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (g *Goodbye) DestinationSSRC() []uint32 {
+ out := make([]uint32, len(g.Sources))
+ copy(out, g.Sources)
+ return out
+}
diff --git a/vendor/github.com/pion/rtcp/header.go b/vendor/github.com/pion/rtcp/header.go
new file mode 100644
index 0000000..055ca18
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/header.go
@@ -0,0 +1,140 @@
+package rtcp
+
+import (
+ "encoding/binary"
+)
+
+// PacketType specifies the type of an RTCP packet
+type PacketType uint8
+
+// RTCP packet types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-4
+const (
+ TypeSenderReport PacketType = 200 // RFC 3550, 6.4.1
+ TypeReceiverReport PacketType = 201 // RFC 3550, 6.4.2
+ TypeSourceDescription PacketType = 202 // RFC 3550, 6.5
+ TypeGoodbye PacketType = 203 // RFC 3550, 6.6
+ TypeApplicationDefined PacketType = 204 // RFC 3550, 6.7 (unimplemented)
+ TypeTransportSpecificFeedback PacketType = 205 // RFC 4585, 6051
+ TypePayloadSpecificFeedback PacketType = 206 // RFC 4585, 6.3
+
+)
+
+// Transport and Payload specific feedback messages overload the count field to act as a message type. those are listed here
+const (
+ FormatSLI uint8 = 2
+ FormatPLI uint8 = 1
+ FormatFIR uint8 = 4
+ FormatTLN uint8 = 1
+ FormatRRR uint8 = 5
+ FormatREMB uint8 = 15
+
+ //https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
+ FormatTCC uint8 = 15
+)
+
+func (p PacketType) String() string {
+ switch p {
+ case TypeSenderReport:
+ return "SR"
+ case TypeReceiverReport:
+ return "RR"
+ case TypeSourceDescription:
+ return "SDES"
+ case TypeGoodbye:
+ return "BYE"
+ case TypeApplicationDefined:
+ return "APP"
+ case TypeTransportSpecificFeedback:
+ return "TSFB"
+ case TypePayloadSpecificFeedback:
+ return "PSFB"
+ default:
+ return string(p)
+ }
+}
+
+const rtpVersion = 2
+
+// A Header is the common header shared by all RTCP packets
+type Header struct {
+ // If the padding bit is set, this individual RTCP packet contains
+ // some additional padding octets at the end which are not part of
+ // the control information but are included in the length field.
+ Padding bool
+ // The number of reception reports, sources contained or FMT in this packet (depending on the Type)
+ Count uint8
+ // The RTCP packet type for this packet
+ Type PacketType
+ // The length of this RTCP packet in 32-bit words minus one,
+ // including the header and any padding.
+ Length uint16
+}
+
+const (
+ headerLength = 4
+ versionShift = 6
+ versionMask = 0x3
+ paddingShift = 5
+ paddingMask = 0x1
+ countShift = 0
+ countMask = 0x1f
+ countMax = (1 << 5) - 1
+)
+
+// Marshal encodes the Header in binary
+func (h Header) Marshal() ([]byte, error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V=2|P| RC | PT=SR=200 | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ rawPacket := make([]byte, headerLength)
+
+ rawPacket[0] |= rtpVersion << versionShift
+
+ if h.Padding {
+ rawPacket[0] |= 1 << paddingShift
+ }
+
+ if h.Count > 31 {
+ return nil, errInvalidHeader
+ }
+ rawPacket[0] |= h.Count << countShift
+
+ rawPacket[1] = uint8(h.Type)
+
+ binary.BigEndian.PutUint16(rawPacket[2:], h.Length)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the Header from binary
+func (h *Header) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) < headerLength {
+ return errPacketTooShort
+ }
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V=2|P| RC | PT | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ version := rawPacket[0] >> versionShift & versionMask
+ if version != rtpVersion {
+ return errBadVersion
+ }
+
+ h.Padding = (rawPacket[0] >> paddingShift & paddingMask) > 0
+ h.Count = rawPacket[0] >> countShift & countMask
+
+ h.Type = PacketType(rawPacket[1])
+
+ h.Length = binary.BigEndian.Uint16(rawPacket[2:])
+
+ return nil
+}
diff --git a/vendor/github.com/pion/rtcp/packet.go b/vendor/github.com/pion/rtcp/packet.go
new file mode 100644
index 0000000..de08003
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/packet.go
@@ -0,0 +1,115 @@
+package rtcp
+
+// Packet represents an RTCP packet, a protocol used for out-of-band statistics and control information for an RTP session
+type Packet interface {
+ // DestinationSSRC returns an array of SSRC values that this packet refers to.
+ DestinationSSRC() []uint32
+
+ Marshal() ([]byte, error)
+ Unmarshal(rawPacket []byte) error
+}
+
+// Unmarshal takes an entire udp datagram (which may consist of multiple RTCP packets) and
+// returns the unmarshaled packets it contains.
+//
+// If this is a reduced-size RTCP packet a feedback packet (Goodbye, SliceLossIndication, etc)
+// will be returned. Otherwise, the underlying type of the returned packet will be
+// CompoundPacket.
+func Unmarshal(rawData []byte) ([]Packet, error) {
+ var packets []Packet
+ for len(rawData) != 0 {
+ p, processed, err := unmarshal(rawData)
+ if err != nil {
+ return nil, err
+ }
+
+ packets = append(packets, p)
+ rawData = rawData[processed:]
+ }
+
+ switch len(packets) {
+ // Empty packet
+ case 0:
+ return nil, errInvalidHeader
+ // Multiple Packets
+ default:
+ return packets, nil
+ }
+}
+
+// Marshal takes an array of Packets and serializes them to a single buffer
+func Marshal(packets []Packet) ([]byte, error) {
+ out := make([]byte, 0)
+ for _, p := range packets {
+ data, err := p.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ out = append(out, data...)
+ }
+ return out, nil
+}
+
+// unmarshal is a factory which pulls the first RTCP packet from a bytestream,
+// and returns it's parsed representation, and the amount of data that was processed.
+func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) {
+ var h Header
+
+ err = h.Unmarshal(rawData)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ bytesprocessed = int(h.Length+1) * 4
+ if bytesprocessed > len(rawData) {
+ return nil, 0, errPacketTooShort
+ }
+ inPacket := rawData[:bytesprocessed]
+
+ switch h.Type {
+ case TypeSenderReport:
+ packet = new(SenderReport)
+
+ case TypeReceiverReport:
+ packet = new(ReceiverReport)
+
+ case TypeSourceDescription:
+ packet = new(SourceDescription)
+
+ case TypeGoodbye:
+ packet = new(Goodbye)
+
+ case TypeTransportSpecificFeedback:
+ switch h.Count {
+ case FormatTLN:
+ packet = new(TransportLayerNack)
+ case FormatRRR:
+ packet = new(RapidResynchronizationRequest)
+ case FormatTCC:
+ packet = new(TransportLayerCC)
+ default:
+ packet = new(RawPacket)
+ }
+
+ case TypePayloadSpecificFeedback:
+ switch h.Count {
+ case FormatPLI:
+ packet = new(PictureLossIndication)
+ case FormatSLI:
+ packet = new(SliceLossIndication)
+ case FormatREMB:
+ packet = new(ReceiverEstimatedMaximumBitrate)
+ case FormatFIR:
+ packet = new(FullIntraRequest)
+ default:
+ packet = new(RawPacket)
+ }
+
+ default:
+ packet = new(RawPacket)
+ }
+
+ err = packet.Unmarshal(inPacket)
+
+ return packet, bytesprocessed, err
+}
diff --git a/vendor/github.com/pion/rtcp/picture_loss_indication.go b/vendor/github.com/pion/rtcp/picture_loss_indication.go
new file mode 100644
index 0000000..7216ecd
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/picture_loss_indication.go
@@ -0,0 +1,91 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// The PictureLossIndication packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures
+type PictureLossIndication struct {
+ // SSRC of sender
+ SenderSSRC uint32
+
+ // SSRC where the loss was experienced
+ MediaSSRC uint32
+}
+
+var _ Packet = (*PictureLossIndication)(nil) // assert is a Packet
+
+const (
+ pliLength = 2
+)
+
+// Marshal encodes the PictureLossIndication in binary
+func (p PictureLossIndication) Marshal() ([]byte, error) {
+ /*
+ * PLI does not require parameters. Therefore, the length field MUST be
+ * 2, and there MUST NOT be any Feedback Control Information.
+ *
+ * The semantics of this FB message is independent of the payload type.
+ */
+ rawPacket := make([]byte, p.len())
+ packetBody := rawPacket[headerLength:]
+
+ binary.BigEndian.PutUint32(packetBody, p.SenderSSRC)
+ binary.BigEndian.PutUint32(packetBody[4:], p.MediaSSRC)
+
+ h := Header{
+ Count: FormatPLI,
+ Type: TypePayloadSpecificFeedback,
+ Length: pliLength,
+ }
+ hData, err := h.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(rawPacket, hData)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the PictureLossIndication from binary
+func (p *PictureLossIndication) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) < (headerLength + (ssrcLength * 2)) {
+ return errPacketTooShort
+ }
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if h.Type != TypePayloadSpecificFeedback || h.Count != FormatPLI {
+ return errWrongType
+ }
+
+ p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
+ p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
+ return nil
+}
+
+// Header returns the Header associated with this packet.
+func (p *PictureLossIndication) Header() Header {
+ return Header{
+ Count: FormatPLI,
+ Type: TypePayloadSpecificFeedback,
+ Length: pliLength,
+ }
+}
+
+func (p *PictureLossIndication) len() int {
+ return headerLength + ssrcLength*2
+}
+
+func (p *PictureLossIndication) String() string {
+ return fmt.Sprintf("PictureLossIndication %x %x", p.SenderSSRC, p.MediaSSRC)
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (p *PictureLossIndication) DestinationSSRC() []uint32 {
+ return []uint32{p.MediaSSRC}
+}
diff --git a/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go b/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
new file mode 100644
index 0000000..5d27055
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
@@ -0,0 +1,88 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// The RapidResynchronizationRequest packet informs the encoder about the loss of an undefined amount of coded video data belonging to one or more pictures
+type RapidResynchronizationRequest struct {
+ // SSRC of sender
+ SenderSSRC uint32
+
+ // SSRC of the media source
+ MediaSSRC uint32
+}
+
+var _ Packet = (*RapidResynchronizationRequest)(nil) // assert is a Packet
+
+const (
+ rrrLength = 2
+ rrrHeaderLength = ssrcLength * 2
+ rrrMediaOffset = 4
+)
+
+// Marshal encodes the RapidResynchronizationRequest in binary
+func (p RapidResynchronizationRequest) Marshal() ([]byte, error) {
+ /*
+ * RRR does not require parameters. Therefore, the length field MUST be
+ * 2, and there MUST NOT be any Feedback Control Information.
+ *
+ * The semantics of this FB message is independent of the payload type.
+ */
+ rawPacket := make([]byte, p.len())
+ packetBody := rawPacket[headerLength:]
+
+ binary.BigEndian.PutUint32(packetBody, p.SenderSSRC)
+ binary.BigEndian.PutUint32(packetBody[rrrMediaOffset:], p.MediaSSRC)
+
+ hData, err := p.Header().Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(rawPacket, hData)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the RapidResynchronizationRequest from binary
+func (p *RapidResynchronizationRequest) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) < (headerLength + (ssrcLength * 2)) {
+ return errPacketTooShort
+ }
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if h.Type != TypeTransportSpecificFeedback || h.Count != FormatRRR {
+ return errWrongType
+ }
+
+ p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
+ p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
+ return nil
+}
+
+func (p *RapidResynchronizationRequest) len() int {
+ return headerLength + rrrHeaderLength
+}
+
+// Header returns the Header associated with this packet.
+func (p *RapidResynchronizationRequest) Header() Header {
+ return Header{
+ Count: FormatRRR,
+ Type: TypeTransportSpecificFeedback,
+ Length: rrrLength,
+ }
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (p *RapidResynchronizationRequest) DestinationSSRC() []uint32 {
+ return []uint32{p.MediaSSRC}
+}
+
+func (p *RapidResynchronizationRequest) String() string {
+ return fmt.Sprintf("RapidResynchronizationRequest %x %x", p.SenderSSRC, p.MediaSSRC)
+}
diff --git a/vendor/github.com/pion/rtcp/raw_packet.go b/vendor/github.com/pion/rtcp/raw_packet.go
new file mode 100644
index 0000000..3cb6eaf
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/raw_packet.go
@@ -0,0 +1,44 @@
+package rtcp
+
+import "fmt"
+
+// RawPacket represents an unparsed RTCP packet. It's returned by Unmarshal when
+// a packet with an unknown type is encountered.
+type RawPacket []byte
+
+var _ Packet = (*RawPacket)(nil) // assert is a Packet
+
+// Marshal encodes the packet in binary.
+func (r RawPacket) Marshal() ([]byte, error) {
+ return r, nil
+}
+
+// Unmarshal decodes the packet from binary.
+func (r *RawPacket) Unmarshal(b []byte) error {
+ if len(b) < (headerLength) {
+ return errPacketTooShort
+ }
+ *r = b
+
+ var h Header
+ return h.Unmarshal(b)
+}
+
+// Header returns the Header associated with this packet.
+func (r RawPacket) Header() Header {
+ var h Header
+ if err := h.Unmarshal(r); err != nil {
+ return Header{}
+ }
+ return h
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (r *RawPacket) DestinationSSRC() []uint32 {
+ return []uint32{}
+}
+
+func (r RawPacket) String() string {
+ out := fmt.Sprintf("RawPacket: %v", ([]byte)(r))
+ return out
+}
diff --git a/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go b/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
new file mode 100644
index 0000000..d37f49e
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
@@ -0,0 +1,284 @@
+package rtcp
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "math/bits"
+)
+
+// ReceiverEstimatedMaximumBitrate contains the receiver's estimated maximum bitrate.
+// see: https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03
+type ReceiverEstimatedMaximumBitrate struct {
+ // SSRC of sender
+ SenderSSRC uint32
+
+ // Estimated maximum bitrate
+ Bitrate uint64
+
+ // SSRC entries which this packet applies to
+ SSRCs []uint32
+}
+
+var _ Packet = (*ReceiverEstimatedMaximumBitrate)(nil) // assert is a Packet
+
+// Marshal serializes the packet and returns a byte slice.
+func (p ReceiverEstimatedMaximumBitrate) Marshal() (buf []byte, err error) {
+ // Allocate a buffer of the exact output size.
+ buf = make([]byte, p.MarshalSize())
+
+ // Write to our buffer.
+ n, err := p.MarshalTo(buf)
+ if err != nil {
+ return nil, err
+ }
+
+ // This will always be true but just to be safe.
+ if n != len(buf) {
+ return nil, errWrongMarshalSize
+ }
+
+ return buf, nil
+}
+
+// MarshalSize returns the size of the packet when marshaled.
+// This can be used in conjunction with `MarshalTo` to avoid allocations.
+func (p ReceiverEstimatedMaximumBitrate) MarshalSize() (n int) {
+ return 20 + 4*len(p.SSRCs)
+}
+
+// MarshalTo serializes the packet to the given byte slice.
+func (p ReceiverEstimatedMaximumBitrate) MarshalTo(buf []byte) (n int, err error) {
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |V=2|P| FMT=15 | PT=206 | length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC of packet sender |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC of media source |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Unique identifier 'R' 'E' 'M' 'B' |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Num SSRC | BR Exp | BR Mantissa |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC feedback |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ */
+
+ size := p.MarshalSize()
+ if len(buf) < size {
+ return 0, errPacketTooShort
+ }
+
+ buf[0] = 143 // v=2, p=0, fmt=15
+ buf[1] = 206
+
+ // Length of this packet in 32-bit words minus one.
+ length := uint16((p.MarshalSize() / 4) - 1)
+ binary.BigEndian.PutUint16(buf[2:4], length)
+
+ binary.BigEndian.PutUint32(buf[4:8], p.SenderSSRC)
+ binary.BigEndian.PutUint32(buf[8:12], 0) // always zero
+
+ // ALL HAIL REMB
+ buf[12] = 'R'
+ buf[13] = 'E'
+ buf[14] = 'M'
+ buf[15] = 'B'
+
+ // Write the length of the ssrcs to follow at the end
+ buf[16] = byte(len(p.SSRCs))
+
+ // We can only encode 18 bits of information in the mantissa.
+ // The exponent lets us shift to the left up to 64 places (6-bits).
+ // We actually need a uint82 to encode the largest possible number,
+ // but uint64 should be good enough for 2.3 exabytes per second.
+
+ // So we need to truncate the bitrate and use the exponent for the shift.
+ // bitrate = mantissa * (1 << exp)
+
+ // Calculate the total shift based on the leading number of zeroes.
+ // This will be negative if there is no shift required.
+ shift := uint(64 - bits.LeadingZeros64(p.Bitrate))
+
+ var mantissa uint
+ var exp uint
+
+ if shift <= 18 {
+ // Fit everything in the mantissa because we can.
+ mantissa = uint(p.Bitrate)
+ exp = 0
+ } else {
+ // We can only use 18 bits of precision, so truncate.
+ mantissa = uint(p.Bitrate >> (shift - 18))
+ exp = shift - 18
+ }
+
+ // We can't quite use the binary package because
+ // a) it's a uint24 and b) the exponent is only 6-bits
+ // Just trust me; this is big-endian encoding.
+ buf[17] = byte((exp << 2) | (mantissa >> 16))
+ buf[18] = byte(mantissa >> 8)
+ buf[19] = byte(mantissa)
+
+ // Write the SSRCs at the very end.
+ n = 20
+ for _, ssrc := range p.SSRCs {
+ binary.BigEndian.PutUint32(buf[n:n+4], ssrc)
+ n += 4
+ }
+
+ return n, nil
+}
+
+// Unmarshal reads a REMB packet from the given byte slice.
+func (p *ReceiverEstimatedMaximumBitrate) Unmarshal(buf []byte) (err error) {
+ /*
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |V=2|P| FMT=15 | PT=206 | length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC of packet sender |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC of media source |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Unique identifier 'R' 'E' 'M' 'B' |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Num SSRC | BR Exp | BR Mantissa |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC feedback |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | ... |
+ */
+
+ // 20 bytes is the size of the packet with no SSRCs
+ if len(buf) < 20 {
+ return errPacketTooShort
+ }
+
+ // version must be 2
+ version := buf[0] >> 6
+ if version != 2 {
+ return fmt.Errorf("%w expected(2) actual(%d)", errBadVersion, version)
+ }
+
+ // padding must be unset
+ padding := (buf[0] >> 5) & 1
+ if padding != 0 {
+ return fmt.Errorf("%w expected(0) actual(%d)", errWrongPadding, padding)
+ }
+
+ // fmt must be 15
+ fmtVal := buf[0] & 31
+ if fmtVal != 15 {
+ return fmt.Errorf("%w expected(15) actual(%d)", errWrongFeedbackType, fmtVal)
+ }
+
+ // Must be payload specific feedback
+ if buf[1] != 206 {
+ return fmt.Errorf("%w expected(206) actual(%d)", errWrongPayloadType, buf[1])
+ }
+
+ // length is the number of 32-bit words, minus 1
+ length := binary.BigEndian.Uint16(buf[2:4])
+ size := int((length + 1) * 4)
+
+ // There's not way this could be legit
+ if size < 20 {
+ return errHeaderTooSmall
+ }
+
+ // Make sure the buffer is large enough.
+ if len(buf) < size {
+ return errPacketTooShort
+ }
+
+ // The sender SSRC is 32-bits
+ p.SenderSSRC = binary.BigEndian.Uint32(buf[4:8])
+
+ // The destination SSRC must be 0
+ media := binary.BigEndian.Uint32(buf[8:12])
+ if media != 0 {
+ return errSSRCMustBeZero
+ }
+
+ // REMB rules all around me
+ if !bytes.Equal(buf[12:16], []byte{'R', 'E', 'M', 'B'}) {
+ return errMissingREMBidentifier
+ }
+
+ // The next byte is the number of SSRC entries at the end.
+ num := int(buf[16])
+
+ // Now we know the expected size, make sure they match.
+ if size != 20+4*num {
+ return errSSRCNumAndLengthMismatch
+ }
+
+ // Get the 6-bit exponent value.
+ exp := buf[17] >> 2
+
+ // The remaining 2-bits plus the next 16-bits are the mantissa.
+ mantissa := uint64(buf[17]&3)<<16 | uint64(buf[18])<<8 | uint64(buf[19])
+
+ // bitrate = mantissa * 2^exp
+
+ if exp > 46 {
+ // NOTE: We intentionally truncate values so they fit in a uint64.
+ // Otherwise we would need a uint82.
+ // This is 2.3 exabytes per second, which should be good enough.
+ p.Bitrate = ^uint64(0)
+ } else {
+ p.Bitrate = mantissa << exp
+ }
+
+ // Clear any existing SSRCs
+ p.SSRCs = nil
+
+ // Loop over and parse the SSRC entires at the end.
+ // We already verified that size == num * 4
+ for n := 20; n < size; n += 4 {
+ ssrc := binary.BigEndian.Uint32(buf[n : n+4])
+ p.SSRCs = append(p.SSRCs, ssrc)
+ }
+
+ return nil
+}
+
+// Header returns the Header associated with this packet.
+func (p *ReceiverEstimatedMaximumBitrate) Header() Header {
+ return Header{
+ Count: FormatREMB,
+ Type: TypePayloadSpecificFeedback,
+ Length: uint16((p.MarshalSize() / 4) - 1),
+ }
+}
+
+// String prints the REMB packet in a human-readable format.
+func (p *ReceiverEstimatedMaximumBitrate) String() string {
+ // Keep a table of powers to units for fast conversion.
+ bitUnits := []string{"b", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb"}
+
+ // Do some unit conversions because b/s is far too difficult to read.
+ bitrate := float64(p.Bitrate)
+ powers := 0
+
+ // Keep dividing the bitrate until it's under 1000
+ for bitrate >= 1000.0 && powers < len(bitUnits) {
+ bitrate /= 1000.0
+ powers++
+ }
+
+ unit := bitUnits[powers]
+
+ return fmt.Sprintf("ReceiverEstimatedMaximumBitrate %x %.2f %s/s", p.SenderSSRC, bitrate, unit)
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (p *ReceiverEstimatedMaximumBitrate) DestinationSSRC() []uint32 {
+ return p.SSRCs
+}
diff --git a/vendor/github.com/pion/rtcp/receiver_report.go b/vendor/github.com/pion/rtcp/receiver_report.go
new file mode 100644
index 0000000..cf28d39
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/receiver_report.go
@@ -0,0 +1,193 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// A ReceiverReport (RR) packet provides reception quality feedback for an RTP stream
+type ReceiverReport struct {
+ // The synchronization source identifier for the originator of this RR packet.
+ SSRC uint32
+ // Zero or more reception report blocks depending on the number of other
+ // sources heard by this sender since the last report. Each reception report
+ // block conveys statistics on the reception of RTP packets from a
+ // single synchronization source.
+ Reports []ReceptionReport
+ // Extension contains additional, payload-specific information that needs to
+ // be reported regularly about the receiver.
+ ProfileExtensions []byte
+}
+
+var _ Packet = (*ReceiverReport)(nil) // assert is a Packet
+
+const (
+ ssrcLength = 4
+ rrSSRCOffset = headerLength
+ rrReportOffset = rrSSRCOffset + ssrcLength
+)
+
+// Marshal encodes the ReceiverReport in binary
+func (r ReceiverReport) Marshal() ([]byte, error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * header |V=2|P| RC | PT=RR=201 | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SSRC of packet sender |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_1 (SSRC of first source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 | fraction lost | cumulative number of packets lost |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | extended highest sequence number received |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | interarrival jitter |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | last SR (LSR) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | delay since last SR (DLSR) |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_2 (SSRC of second source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 : ... :
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | profile-specific extensions |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ rawPacket := make([]byte, r.len())
+ packetBody := rawPacket[headerLength:]
+
+ binary.BigEndian.PutUint32(packetBody, r.SSRC)
+
+ for i, rp := range r.Reports {
+ data, err := rp.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ offset := ssrcLength + receptionReportLength*i
+ copy(packetBody[offset:], data)
+ }
+
+ if len(r.Reports) > countMax {
+ return nil, errTooManyReports
+ }
+
+ pe := make([]byte, len(r.ProfileExtensions))
+ copy(pe, r.ProfileExtensions)
+
+ // if the length of the profile extensions isn't devisible
+ // by 4, we need to pad the end.
+ for (len(pe) & 0x3) != 0 {
+ pe = append(pe, 0)
+ }
+
+ rawPacket = append(rawPacket, pe...)
+
+ hData, err := r.Header().Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(rawPacket, hData)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the ReceiverReport from binary
+func (r *ReceiverReport) Unmarshal(rawPacket []byte) error {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * header |V=2|P| RC | PT=RR=201 | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SSRC of packet sender |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_1 (SSRC of first source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 | fraction lost | cumulative number of packets lost |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | extended highest sequence number received |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | interarrival jitter |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | last SR (LSR) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | delay since last SR (DLSR) |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_2 (SSRC of second source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 : ... :
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | profile-specific extensions |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ if len(rawPacket) < (headerLength + ssrcLength) {
+ return errPacketTooShort
+ }
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if h.Type != TypeReceiverReport {
+ return errWrongType
+ }
+
+ r.SSRC = binary.BigEndian.Uint32(rawPacket[rrSSRCOffset:])
+
+ for i := rrReportOffset; i < len(rawPacket) && len(r.Reports) < int(h.Count); i += receptionReportLength {
+ var rr ReceptionReport
+ if err := rr.Unmarshal(rawPacket[i:]); err != nil {
+ return err
+ }
+ r.Reports = append(r.Reports, rr)
+ }
+ r.ProfileExtensions = rawPacket[rrReportOffset+(len(r.Reports)*receptionReportLength):]
+
+ if uint8(len(r.Reports)) != h.Count {
+ return errInvalidHeader
+ }
+
+ return nil
+}
+
+func (r *ReceiverReport) len() int {
+ repsLength := 0
+ for _, rep := range r.Reports {
+ repsLength += rep.len()
+ }
+ return headerLength + ssrcLength + repsLength
+}
+
+// Header returns the Header associated with this packet.
+func (r *ReceiverReport) Header() Header {
+ return Header{
+ Count: uint8(len(r.Reports)),
+ Type: TypeReceiverReport,
+ Length: uint16((r.len()/4)-1) + uint16(getPadding(len(r.ProfileExtensions))),
+ }
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (r *ReceiverReport) DestinationSSRC() []uint32 {
+ out := make([]uint32, len(r.Reports))
+ for i, v := range r.Reports {
+ out[i] = v.SSRC
+ }
+ return out
+}
+
+func (r ReceiverReport) String() string {
+ out := fmt.Sprintf("ReceiverReport from %x\n", r.SSRC)
+ out += "\tSSRC \tLost\tLastSequence\n"
+ for _, i := range r.Reports {
+ out += fmt.Sprintf("\t%x\t%d/%d\t%d\n", i.SSRC, i.FractionLost, i.TotalLost, i.LastSequenceNumber)
+ }
+ out += fmt.Sprintf("\tProfile Extension Data: %v\n", r.ProfileExtensions)
+ return out
+}
diff --git a/vendor/github.com/pion/rtcp/reception_report.go b/vendor/github.com/pion/rtcp/reception_report.go
new file mode 100644
index 0000000..5bff8f2
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/reception_report.go
@@ -0,0 +1,130 @@
+package rtcp
+
+import "encoding/binary"
+
+// A ReceptionReport block conveys statistics on the reception of RTP packets
+// from a single synchronization source.
+type ReceptionReport struct {
+ // The SSRC identifier of the source to which the information in this
+ // reception report block pertains.
+ SSRC uint32
+ // The fraction of RTP data packets from source SSRC lost since the
+ // previous SR or RR packet was sent, expressed as a fixed point
+ // number with the binary point at the left edge of the field.
+ FractionLost uint8
+ // The total number of RTP data packets from source SSRC that have
+ // been lost since the beginning of reception.
+ TotalLost uint32
+ // The low 16 bits contain the highest sequence number received in an
+ // RTP data packet from source SSRC, and the most significant 16
+ // bits extend that sequence number with the corresponding count of
+ // sequence number cycles.
+ LastSequenceNumber uint32
+ // An estimate of the statistical variance of the RTP data packet
+ // interarrival time, measured in timestamp units and expressed as an
+ // unsigned integer.
+ Jitter uint32
+ // The middle 32 bits out of 64 in the NTP timestamp received as part of
+ // the most recent RTCP sender report (SR) packet from source SSRC. If no
+ // SR has been received yet, the field is set to zero.
+ LastSenderReport uint32
+ // The delay, expressed in units of 1/65536 seconds, between receiving the
+ // last SR packet from source SSRC and sending this reception report block.
+ // If no SR packet has been received yet from SSRC, the field is set to zero.
+ Delay uint32
+}
+
+const (
+ receptionReportLength = 24
+ fractionLostOffset = 4
+ totalLostOffset = 5
+ lastSeqOffset = 8
+ jitterOffset = 12
+ lastSROffset = 16
+ delayOffset = 20
+)
+
+// Marshal encodes the ReceptionReport in binary
+func (r ReceptionReport) Marshal() ([]byte, error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | SSRC |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | fraction lost | cumulative number of packets lost |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | extended highest sequence number received |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | interarrival jitter |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | last SR (LSR) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | delay since last SR (DLSR) |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ */
+
+ rawPacket := make([]byte, receptionReportLength)
+
+ binary.BigEndian.PutUint32(rawPacket, r.SSRC)
+
+ rawPacket[fractionLostOffset] = r.FractionLost
+
+ // pack TotalLost into 24 bits
+ if r.TotalLost >= (1 << 25) {
+ return nil, errInvalidTotalLost
+ }
+ tlBytes := rawPacket[totalLostOffset:]
+ tlBytes[0] = byte(r.TotalLost >> 16)
+ tlBytes[1] = byte(r.TotalLost >> 8)
+ tlBytes[2] = byte(r.TotalLost)
+
+ binary.BigEndian.PutUint32(rawPacket[lastSeqOffset:], r.LastSequenceNumber)
+ binary.BigEndian.PutUint32(rawPacket[jitterOffset:], r.Jitter)
+ binary.BigEndian.PutUint32(rawPacket[lastSROffset:], r.LastSenderReport)
+ binary.BigEndian.PutUint32(rawPacket[delayOffset:], r.Delay)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the ReceptionReport from binary
+func (r *ReceptionReport) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) < receptionReportLength {
+ return errPacketTooShort
+ }
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | SSRC |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | fraction lost | cumulative number of packets lost |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | extended highest sequence number received |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | interarrival jitter |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | last SR (LSR) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | delay since last SR (DLSR) |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ */
+
+ r.SSRC = binary.BigEndian.Uint32(rawPacket)
+ r.FractionLost = rawPacket[fractionLostOffset]
+
+ tlBytes := rawPacket[totalLostOffset:]
+ r.TotalLost = uint32(tlBytes[2]) | uint32(tlBytes[1])<<8 | uint32(tlBytes[0])<<16
+
+ r.LastSequenceNumber = binary.BigEndian.Uint32(rawPacket[lastSeqOffset:])
+ r.Jitter = binary.BigEndian.Uint32(rawPacket[jitterOffset:])
+ r.LastSenderReport = binary.BigEndian.Uint32(rawPacket[lastSROffset:])
+ r.Delay = binary.BigEndian.Uint32(rawPacket[delayOffset:])
+
+ return nil
+}
+
+func (r *ReceptionReport) len() int {
+ return receptionReportLength
+}
diff --git a/vendor/github.com/pion/rtcp/renovate.json b/vendor/github.com/pion/rtcp/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/rtcp/sender_report.go b/vendor/github.com/pion/rtcp/sender_report.go
new file mode 100644
index 0000000..1b16380
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/sender_report.go
@@ -0,0 +1,260 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// A SenderReport (SR) packet provides reception quality feedback for an RTP stream
+type SenderReport struct {
+ // The synchronization source identifier for the originator of this SR packet.
+ SSRC uint32
+ // The wallclock time when this report was sent so that it may be used in
+ // combination with timestamps returned in reception reports from other
+ // receivers to measure round-trip propagation to those receivers.
+ NTPTime uint64
+ // Corresponds to the same time as the NTP timestamp (above), but in
+ // the same units and with the same random offset as the RTP
+ // timestamps in data packets. This correspondence may be used for
+ // intra- and inter-media synchronization for sources whose NTP
+ // timestamps are synchronized, and may be used by media-independent
+ // receivers to estimate the nominal RTP clock frequency.
+ RTPTime uint32
+ // The total number of RTP data packets transmitted by the sender
+ // since starting transmission up until the time this SR packet was
+ // generated.
+ PacketCount uint32
+ // The total number of payload octets (i.e., not including header or
+ // padding) transmitted in RTP data packets by the sender since
+ // starting transmission up until the time this SR packet was
+ // generated.
+ OctetCount uint32
+ // Zero or more reception report blocks depending on the number of other
+ // sources heard by this sender since the last report. Each reception report
+ // block conveys statistics on the reception of RTP packets from a
+ // single synchronization source.
+ Reports []ReceptionReport
+ // ProfileExtensions contains additional, payload-specific information that needs to
+ // be reported regularly about the sender.
+ ProfileExtensions []byte
+}
+
+var _ Packet = (*SenderReport)(nil) // assert is a Packet
+
+const (
+ srHeaderLength = 24
+ srSSRCOffset = 0
+ srNTPOffset = srSSRCOffset + ssrcLength
+ ntpTimeLength = 8
+ srRTPOffset = srNTPOffset + ntpTimeLength
+ rtpTimeLength = 4
+ srPacketCountOffset = srRTPOffset + rtpTimeLength
+ srPacketCountLength = 4
+ srOctetCountOffset = srPacketCountOffset + srPacketCountLength
+ srOctetCountLength = 4
+ srReportOffset = srOctetCountOffset + srOctetCountLength
+)
+
+// Marshal encodes the SenderReport in binary
+func (r SenderReport) Marshal() ([]byte, error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * header |V=2|P| RC | PT=SR=200 | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SSRC of sender |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * sender | NTP timestamp, most significant word |
+ * info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NTP timestamp, least significant word |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RTP timestamp |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | sender's packet count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | sender's octet count |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_1 (SSRC of first source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 | fraction lost | cumulative number of packets lost |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | extended highest sequence number received |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | interarrival jitter |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | last SR (LSR) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | delay since last SR (DLSR) |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_2 (SSRC of second source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 : ... :
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | profile-specific extensions |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ rawPacket := make([]byte, r.len())
+ packetBody := rawPacket[headerLength:]
+
+ binary.BigEndian.PutUint32(packetBody[srSSRCOffset:], r.SSRC)
+ binary.BigEndian.PutUint64(packetBody[srNTPOffset:], r.NTPTime)
+ binary.BigEndian.PutUint32(packetBody[srRTPOffset:], r.RTPTime)
+ binary.BigEndian.PutUint32(packetBody[srPacketCountOffset:], r.PacketCount)
+ binary.BigEndian.PutUint32(packetBody[srOctetCountOffset:], r.OctetCount)
+
+ offset := srHeaderLength
+ for _, rp := range r.Reports {
+ data, err := rp.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(packetBody[offset:], data)
+ offset += receptionReportLength
+ }
+
+ if len(r.Reports) > countMax {
+ return nil, errTooManyReports
+ }
+
+ copy(packetBody[offset:], r.ProfileExtensions)
+
+ hData, err := r.Header().Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(rawPacket, hData)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the SenderReport from binary
+func (r *SenderReport) Unmarshal(rawPacket []byte) error {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * header |V=2|P| RC | PT=SR=200 | length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SSRC of sender |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * sender | NTP timestamp, most significant word |
+ * info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | NTP timestamp, least significant word |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RTP timestamp |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | sender's packet count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | sender's octet count |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_1 (SSRC of first source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 | fraction lost | cumulative number of packets lost |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | extended highest sequence number received |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | interarrival jitter |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | last SR (LSR) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | delay since last SR (DLSR) |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * report | SSRC_2 (SSRC of second source) |
+ * block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 : ... :
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | profile-specific extensions |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ if len(rawPacket) < (headerLength + srHeaderLength) {
+ return errPacketTooShort
+ }
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if h.Type != TypeSenderReport {
+ return errWrongType
+ }
+
+ packetBody := rawPacket[headerLength:]
+
+ r.SSRC = binary.BigEndian.Uint32(packetBody[srSSRCOffset:])
+ r.NTPTime = binary.BigEndian.Uint64(packetBody[srNTPOffset:])
+ r.RTPTime = binary.BigEndian.Uint32(packetBody[srRTPOffset:])
+ r.PacketCount = binary.BigEndian.Uint32(packetBody[srPacketCountOffset:])
+ r.OctetCount = binary.BigEndian.Uint32(packetBody[srOctetCountOffset:])
+
+ offset := srReportOffset
+ for i := 0; i < int(h.Count); i++ {
+ rrEnd := offset + receptionReportLength
+ if rrEnd > len(packetBody) {
+ return errPacketTooShort
+ }
+ rrBody := packetBody[offset : offset+receptionReportLength]
+ offset = rrEnd
+
+ var rr ReceptionReport
+ if err := rr.Unmarshal(rrBody); err != nil {
+ return err
+ }
+ r.Reports = append(r.Reports, rr)
+ }
+
+ if offset < len(packetBody) {
+ r.ProfileExtensions = packetBody[offset:]
+ }
+
+ if uint8(len(r.Reports)) != h.Count {
+ return errInvalidHeader
+ }
+
+ return nil
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (r *SenderReport) DestinationSSRC() []uint32 {
+ out := make([]uint32, len(r.Reports)+1)
+ for i, v := range r.Reports {
+ out[i] = v.SSRC
+ }
+ out[len(r.Reports)] = r.SSRC
+ return out
+}
+
+func (r *SenderReport) len() int {
+ repsLength := 0
+ for _, rep := range r.Reports {
+ repsLength += rep.len()
+ }
+ return headerLength + srHeaderLength + repsLength + len(r.ProfileExtensions)
+}
+
+// Header returns the Header associated with this packet.
+func (r *SenderReport) Header() Header {
+ return Header{
+ Count: uint8(len(r.Reports)),
+ Type: TypeSenderReport,
+ Length: uint16((r.len() / 4) - 1),
+ }
+}
+
+func (r SenderReport) String() string {
+ out := fmt.Sprintf("SenderReport from %x\n", r.SSRC)
+ out += fmt.Sprintf("\tNTPTime:\t%d\n", r.NTPTime)
+ out += fmt.Sprintf("\tRTPTIme:\t%d\n", r.RTPTime)
+ out += fmt.Sprintf("\tPacketCount:\t%d\n", r.PacketCount)
+ out += fmt.Sprintf("\tOctetCount:\t%d\n", r.OctetCount)
+
+ out += "\tSSRC \tLost\tLastSequence\n"
+ for _, i := range r.Reports {
+ out += fmt.Sprintf("\t%x\t%d/%d\t%d\n", i.SSRC, i.FractionLost, i.TotalLost, i.LastSequenceNumber)
+ }
+ out += fmt.Sprintf("\tProfile Extension Data: %v\n", r.ProfileExtensions)
+ return out
+}
diff --git a/vendor/github.com/pion/rtcp/slice_loss_indication.go b/vendor/github.com/pion/rtcp/slice_loss_indication.go
new file mode 100644
index 0000000..7689309
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/slice_loss_indication.go
@@ -0,0 +1,115 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+)
+
+// SLIEntry represents a single entry to the SLI packet's
+// list of lost slices.
+type SLIEntry struct {
+ // ID of first lost slice
+ First uint16
+
+ // Number of lost slices
+ Number uint16
+
+ // ID of related picture
+ Picture uint8
+}
+
+// The SliceLossIndication packet informs the encoder about the loss of a picture slice
+type SliceLossIndication struct {
+ // SSRC of sender
+ SenderSSRC uint32
+
+ // SSRC of the media source
+ MediaSSRC uint32
+
+ SLI []SLIEntry
+}
+
+var _ Packet = (*SliceLossIndication)(nil) // assert is a Packet
+
+const (
+ sliLength = 2
+ sliOffset = 8
+)
+
+// Marshal encodes the SliceLossIndication in binary
+func (p SliceLossIndication) Marshal() ([]byte, error) {
+ if len(p.SLI)+sliLength > math.MaxUint8 {
+ return nil, errTooManyReports
+ }
+
+ rawPacket := make([]byte, sliOffset+(len(p.SLI)*4))
+ binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC)
+ binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC)
+ for i, s := range p.SLI {
+ sli := ((uint32(s.First) & 0x1FFF) << 19) |
+ ((uint32(s.Number) & 0x1FFF) << 6) |
+ (uint32(s.Picture) & 0x3F)
+ binary.BigEndian.PutUint32(rawPacket[sliOffset+(4*i):], sli)
+ }
+ hData, err := p.Header().Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(hData, rawPacket...), nil
+}
+
+// Unmarshal decodes the SliceLossIndication from binary
+func (p *SliceLossIndication) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) < (headerLength + ssrcLength) {
+ return errPacketTooShort
+ }
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if len(rawPacket) < (headerLength + int(4*h.Length)) {
+ return errPacketTooShort
+ }
+
+ if h.Type != TypeTransportSpecificFeedback || h.Count != FormatSLI {
+ return errWrongType
+ }
+
+ p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
+ p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
+ for i := headerLength + sliOffset; i < (headerLength + int(h.Length*4)); i += 4 {
+ sli := binary.BigEndian.Uint32(rawPacket[i:])
+ p.SLI = append(p.SLI, SLIEntry{
+ First: uint16((sli >> 19) & 0x1FFF),
+ Number: uint16((sli >> 6) & 0x1FFF),
+ Picture: uint8(sli & 0x3F),
+ })
+ }
+ return nil
+}
+
+func (p *SliceLossIndication) len() int {
+ return headerLength + sliOffset + (len(p.SLI) * 4)
+}
+
+// Header returns the Header associated with this packet.
+func (p *SliceLossIndication) Header() Header {
+ return Header{
+ Count: FormatSLI,
+ Type: TypeTransportSpecificFeedback,
+ Length: uint16((p.len() / 4) - 1),
+ }
+}
+
+func (p *SliceLossIndication) String() string {
+ return fmt.Sprintf("SliceLossIndication %x %x %+v", p.SenderSSRC, p.MediaSSRC, p.SLI)
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (p *SliceLossIndication) DestinationSSRC() []uint32 {
+ return []uint32{p.MediaSSRC}
+}
diff --git a/vendor/github.com/pion/rtcp/source_description.go b/vendor/github.com/pion/rtcp/source_description.go
new file mode 100644
index 0000000..4306b66
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/source_description.go
@@ -0,0 +1,352 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// SDESType is the item type used in the RTCP SDES control packet.
+type SDESType uint8
+
+// RTP SDES item types registered with IANA. See: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml#rtp-parameters-5
+const (
+ SDESEnd SDESType = iota // end of SDES list RFC 3550, 6.5
+ SDESCNAME // canonical name RFC 3550, 6.5.1
+ SDESName // user name RFC 3550, 6.5.2
+ SDESEmail // user's electronic mail address RFC 3550, 6.5.3
+ SDESPhone // user's phone number RFC 3550, 6.5.4
+ SDESLocation // geographic user location RFC 3550, 6.5.5
+ SDESTool // name of application or tool RFC 3550, 6.5.6
+ SDESNote // notice about the source RFC 3550, 6.5.7
+ SDESPrivate // private extensions RFC 3550, 6.5.8 (not implemented)
+)
+
+func (s SDESType) String() string {
+ switch s {
+ case SDESEnd:
+ return "END"
+ case SDESCNAME:
+ return "CNAME"
+ case SDESName:
+ return "NAME"
+ case SDESEmail:
+ return "EMAIL"
+ case SDESPhone:
+ return "PHONE"
+ case SDESLocation:
+ return "LOC"
+ case SDESTool:
+ return "TOOL"
+ case SDESNote:
+ return "NOTE"
+ case SDESPrivate:
+ return "PRIV"
+ default:
+ return string(s)
+ }
+}
+
+const (
+ sdesSourceLen = 4
+ sdesTypeLen = 1
+ sdesTypeOffset = 0
+ sdesOctetCountLen = 1
+ sdesOctetCountOffset = 1
+ sdesMaxOctetCount = (1 << 8) - 1
+ sdesTextOffset = 2
+)
+
+// A SourceDescription (SDES) packet describes the sources in an RTP stream.
+type SourceDescription struct {
+ Chunks []SourceDescriptionChunk
+}
+
+var _ Packet = (*SourceDescription)(nil) // assert is a Packet
+
+// Marshal encodes the SourceDescription in binary
+func (s SourceDescription) Marshal() ([]byte, error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * header |V=2|P| SC | PT=SDES=202 | length |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * chunk | SSRC/CSRC_1 |
+ * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SDES items |
+ * | ... |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * chunk | SSRC/CSRC_2 |
+ * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SDES items |
+ * | ... |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ */
+
+ rawPacket := make([]byte, s.len())
+ packetBody := rawPacket[headerLength:]
+
+ chunkOffset := 0
+ for _, c := range s.Chunks {
+ data, err := c.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(packetBody[chunkOffset:], data)
+ chunkOffset += len(data)
+ }
+
+ if len(s.Chunks) > countMax {
+ return nil, errTooManyChunks
+ }
+
+ hData, err := s.Header().Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(rawPacket, hData)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the SourceDescription from binary
+func (s *SourceDescription) Unmarshal(rawPacket []byte) error {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * header |V=2|P| SC | PT=SDES=202 | length |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * chunk | SSRC/CSRC_1 |
+ * 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SDES items |
+ * | ... |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * chunk | SSRC/CSRC_2 |
+ * 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SDES items |
+ * | ... |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ */
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if h.Type != TypeSourceDescription {
+ return errWrongType
+ }
+
+ for i := headerLength; i < len(rawPacket); {
+ var chunk SourceDescriptionChunk
+ if err := chunk.Unmarshal(rawPacket[i:]); err != nil {
+ return err
+ }
+ s.Chunks = append(s.Chunks, chunk)
+
+ i += chunk.len()
+ }
+
+ if len(s.Chunks) != int(h.Count) {
+ return errInvalidHeader
+ }
+
+ return nil
+}
+
+func (s *SourceDescription) len() int {
+ chunksLength := 0
+ for _, c := range s.Chunks {
+ chunksLength += c.len()
+ }
+ return headerLength + chunksLength
+}
+
+// Header returns the Header associated with this packet.
+func (s *SourceDescription) Header() Header {
+ return Header{
+ Count: uint8(len(s.Chunks)),
+ Type: TypeSourceDescription,
+ Length: uint16((s.len() / 4) - 1),
+ }
+}
+
+// A SourceDescriptionChunk contains items describing a single RTP source
+type SourceDescriptionChunk struct {
+ // The source (ssrc) or contributing source (csrc) identifier this packet describes
+ Source uint32
+ Items []SourceDescriptionItem
+}
+
+// Marshal encodes the SourceDescriptionChunk in binary
+func (s SourceDescriptionChunk) Marshal() ([]byte, error) {
+ /*
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | SSRC/CSRC_1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SDES items |
+ * | ... |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ */
+
+ rawPacket := make([]byte, sdesSourceLen)
+ binary.BigEndian.PutUint32(rawPacket, s.Source)
+
+ for _, it := range s.Items {
+ data, err := it.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ rawPacket = append(rawPacket, data...)
+ }
+
+ // The list of items in each chunk MUST be terminated by one or more null octets
+ rawPacket = append(rawPacket, uint8(SDESEnd))
+
+ // additional null octets MUST be included if needed to pad until the next 32-bit boundary
+ rawPacket = append(rawPacket, make([]byte, getPadding(len(rawPacket)))...)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the SourceDescriptionChunk from binary
+func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error {
+ /*
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | SSRC/CSRC_1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | SDES items |
+ * | ... |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ */
+
+ if len(rawPacket) < (sdesSourceLen + sdesTypeLen) {
+ return errPacketTooShort
+ }
+
+ s.Source = binary.BigEndian.Uint32(rawPacket)
+
+ for i := 4; i < len(rawPacket); {
+ if pktType := SDESType(rawPacket[i]); pktType == SDESEnd {
+ return nil
+ }
+
+ var it SourceDescriptionItem
+ if err := it.Unmarshal(rawPacket[i:]); err != nil {
+ return err
+ }
+ s.Items = append(s.Items, it)
+ i += it.len()
+ }
+
+ return errPacketTooShort
+}
+
+func (s SourceDescriptionChunk) len() int {
+ len := sdesSourceLen
+ for _, it := range s.Items {
+ len += it.len()
+ }
+ len += sdesTypeLen // for terminating null octet
+
+ // align to 32-bit boundary
+ len += getPadding(len)
+
+ return len
+}
+
+// A SourceDescriptionItem is a part of a SourceDescription that describes a stream.
+type SourceDescriptionItem struct {
+ // The type identifier for this item. eg, SDESCNAME for canonical name description.
+ //
+ // Type zero or SDESEnd is interpreted as the end of an item list and cannot be used.
+ Type SDESType
+ // Text is a unicode text blob associated with the item. Its meaning varies based on the item's Type.
+ Text string
+}
+
+func (s SourceDescriptionItem) len() int {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | CNAME=1 | length | user and domain name ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ return sdesTypeLen + sdesOctetCountLen + len([]byte(s.Text))
+}
+
+// Marshal encodes the SourceDescriptionItem in binary
+func (s SourceDescriptionItem) Marshal() ([]byte, error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | CNAME=1 | length | user and domain name ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ if s.Type == SDESEnd {
+ return nil, errSDESMissingType
+ }
+
+ rawPacket := make([]byte, sdesTypeLen+sdesOctetCountLen)
+
+ rawPacket[sdesTypeOffset] = uint8(s.Type)
+
+ txtBytes := []byte(s.Text)
+ octetCount := len(txtBytes)
+ if octetCount > sdesMaxOctetCount {
+ return nil, errSDESTextTooLong
+ }
+ rawPacket[sdesOctetCountOffset] = uint8(octetCount)
+
+ rawPacket = append(rawPacket, txtBytes...)
+
+ return rawPacket, nil
+}
+
+// Unmarshal decodes the SourceDescriptionItem from binary
+func (s *SourceDescriptionItem) Unmarshal(rawPacket []byte) error {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | CNAME=1 | length | user and domain name ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ if len(rawPacket) < (sdesTypeLen + sdesOctetCountLen) {
+ return errPacketTooShort
+ }
+
+ s.Type = SDESType(rawPacket[sdesTypeOffset])
+
+ octetCount := int(rawPacket[sdesOctetCountOffset])
+ if sdesTextOffset+octetCount > len(rawPacket) {
+ return errPacketTooShort
+ }
+
+ txtBytes := rawPacket[sdesTextOffset : sdesTextOffset+octetCount]
+ s.Text = string(txtBytes)
+
+ return nil
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (s *SourceDescription) DestinationSSRC() []uint32 {
+ out := make([]uint32, len(s.Chunks))
+ for i, v := range s.Chunks {
+ out[i] = v.Source
+ }
+ return out
+}
+
+func (s *SourceDescription) String() string {
+ out := "Source Description:\n"
+ for _, c := range s.Chunks {
+ out += fmt.Sprintf("\t%x: %s\n", c.Source, c.Items)
+ }
+ return out
+}
diff --git a/vendor/github.com/pion/rtcp/transport_layer_cc.go b/vendor/github.com/pion/rtcp/transport_layer_cc.go
new file mode 100644
index 0000000..9606581
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/transport_layer_cc.go
@@ -0,0 +1,560 @@
+package rtcp
+
+// Author: adwpc
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math"
+)
+
+// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |V=2|P| FMT=15 | PT=205 | length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of media source |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | base sequence number | packet status count |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | reference time | fb pkt. count |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | packet chunk | packet chunk |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// . .
+// . .
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | packet chunk | recv delta | recv delta |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// . .
+// . .
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | recv delta | recv delta | zero padding |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+// for packet status chunk
+const (
+ // type of packet status chunk
+ TypeTCCRunLengthChunk = 0
+ TypeTCCStatusVectorChunk = 1
+
+ // len of packet status chunk
+ packetStatusChunkLength = 2
+)
+
+// type of packet status symbol and recv delta
+const (
+ // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.1
+ TypeTCCPacketNotReceived = uint16(iota)
+ TypeTCCPacketReceivedSmallDelta
+ TypeTCCPacketReceivedLargeDelta
+ // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-7
+ // see Example 2: "packet received, w/o recv delta"
+ TypeTCCPacketReceivedWithoutDelta
+)
+
+// for status vector chunk
+const (
+ // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.4
+ TypeTCCSymbolSizeOneBit = 0
+ TypeTCCSymbolSizeTwoBit = 1
+
+ // Notice: RFC is wrong: "packet received" (0) and "packet not received" (1)
+ // if S == TypeTCCSymbolSizeOneBit, symbol list will be: TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta
+ // if S == TypeTCCSymbolSizeTwoBit, symbol list will be same as above:
+)
+
+func numOfBitsOfSymbolSize() map[uint16]uint16 {
+ return map[uint16]uint16{
+ TypeTCCSymbolSizeOneBit: 1,
+ TypeTCCSymbolSizeTwoBit: 2,
+ }
+}
+
+var _ Packet = (*TransportLayerCC)(nil) // assert is a Packet
+
+var (
+ errPacketStatusChunkLength = errors.New("packet status chunk must be 2 bytes")
+ errDeltaExceedLimit = errors.New("delta exceed limit")
+)
+
+// PacketStatusChunk has two kinds:
+// RunLengthChunk and StatusVectorChunk
+type PacketStatusChunk interface {
+ Marshal() ([]byte, error)
+ Unmarshal(rawPacket []byte) error
+}
+
+// RunLengthChunk T=TypeTCCRunLengthChunk
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |T| S | Run Length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type RunLengthChunk struct {
+ PacketStatusChunk
+
+ // T = TypeTCCRunLengthChunk
+ Type uint16
+
+ // S: type of packet status
+ // kind: TypeTCCPacketNotReceived or...
+ PacketStatusSymbol uint16
+
+ // RunLength: count of S
+ RunLength uint16
+}
+
+// Marshal ..
+func (r RunLengthChunk) Marshal() ([]byte, error) {
+ chunk := make([]byte, 2)
+
+ // append 1 bit '0'
+ dst, err := setNBitsOfUint16(0, 1, 0, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ // append 2 bit PacketStatusSymbol
+ dst, err = setNBitsOfUint16(dst, 2, 1, r.PacketStatusSymbol)
+ if err != nil {
+ return nil, err
+ }
+
+ // append 13 bit RunLength
+ dst, err = setNBitsOfUint16(dst, 13, 3, r.RunLength)
+ if err != nil {
+ return nil, err
+ }
+
+ binary.BigEndian.PutUint16(chunk, dst)
+ return chunk, nil
+}
+
+// Unmarshal ..
+func (r *RunLengthChunk) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) != packetStatusChunkLength {
+ return errPacketStatusChunkLength
+ }
+
+ // record type
+ r.Type = TypeTCCRunLengthChunk
+
+ // get PacketStatusSymbol
+ // r.PacketStatusSymbol = uint16(rawPacket[0] >> 5 & 0x03)
+ r.PacketStatusSymbol = getNBitsFromByte(rawPacket[0], 1, 2)
+
+ // get RunLength
+ // r.RunLength = uint16(rawPacket[0]&0x1F)*256 + uint16(rawPacket[1])
+ r.RunLength = getNBitsFromByte(rawPacket[0], 3, 5)<<8 + uint16(rawPacket[1])
+ return nil
+}
+
+// StatusVectorChunk T=typeStatusVecotrChunk
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |T|S| symbol list |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type StatusVectorChunk struct {
+ PacketStatusChunk
+ // T = TypeTCCRunLengthChunk
+ Type uint16
+
+ // TypeTCCSymbolSizeOneBit or TypeTCCSymbolSizeTwoBit
+ SymbolSize uint16
+
+ // when SymbolSize = TypeTCCSymbolSizeOneBit, SymbolList is 14*1bit:
+ // TypeTCCSymbolListPacketReceived or TypeTCCSymbolListPacketNotReceived
+ // when SymbolSize = TypeTCCSymbolSizeTwoBit, SymbolList is 7*2bit:
+ // TypeTCCPacketNotReceived TypeTCCPacketReceivedSmallDelta TypeTCCPacketReceivedLargeDelta or typePacketReserved
+ SymbolList []uint16
+}
+
+// Marshal ..
+func (r StatusVectorChunk) Marshal() ([]byte, error) {
+ chunk := make([]byte, 2)
+
+ // set first bit '1'
+ dst, err := setNBitsOfUint16(0, 1, 0, 1)
+ if err != nil {
+ return nil, err
+ }
+
+ // set second bit SymbolSize
+ dst, err = setNBitsOfUint16(dst, 1, 1, r.SymbolSize)
+ if err != nil {
+ return nil, err
+ }
+
+ numOfBits := numOfBitsOfSymbolSize()[r.SymbolSize]
+ // append 14 bit SymbolList
+ for i, s := range r.SymbolList {
+ index := numOfBits*uint16(i) + 2
+ dst, err = setNBitsOfUint16(dst, numOfBits, index, s)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ binary.BigEndian.PutUint16(chunk, dst)
+ // set SymbolList(bit8-15)
+ // chunk[1] = uint8(r.SymbolList) & 0x0f
+ return chunk, nil
+}
+
+// Unmarshal ..
+func (r *StatusVectorChunk) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) != packetStatusChunkLength {
+ return errPacketStatusChunkLength
+ }
+
+ r.Type = TypeTCCStatusVectorChunk
+ r.SymbolSize = getNBitsFromByte(rawPacket[0], 1, 1)
+
+ if r.SymbolSize == TypeTCCSymbolSizeOneBit {
+ for i := uint16(0); i < 6; i++ {
+ r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[0], 2+i, 1))
+ }
+ for i := uint16(0); i < 8; i++ {
+ r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[1], i, 1))
+ }
+ return nil
+ }
+ if r.SymbolSize == TypeTCCSymbolSizeTwoBit {
+ for i := uint16(0); i < 3; i++ {
+ r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[0], 2+i*2, 2))
+ }
+ for i := uint16(0); i < 4; i++ {
+ r.SymbolList = append(r.SymbolList, getNBitsFromByte(rawPacket[1], i*2, 2))
+ }
+ return nil
+ }
+
+ r.SymbolSize = getNBitsFromByte(rawPacket[0], 2, 6)<<8 + uint16(rawPacket[1])
+ return nil
+}
+
+const (
+ // TypeTCCDeltaScaleFactor https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5
+ TypeTCCDeltaScaleFactor = 250
+)
+
+// RecvDelta are represented as multiples of 250us
+// small delta is 1 byte: [0,63.75]ms = [0, 63750]us = [0, 255]*250us
+// big delta is 2 bytes: [-8192.0, 8191.75]ms = [-8192000, 8191750]us = [-32768, 32767]*250us
+// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-3.1.5
+type RecvDelta struct {
+ Type uint16
+ // us
+ Delta int64
+}
+
+// Marshal ..
+func (r RecvDelta) Marshal() ([]byte, error) {
+ delta := r.Delta / TypeTCCDeltaScaleFactor
+
+ // small delta
+ if r.Type == TypeTCCPacketReceivedSmallDelta && delta >= 0 && delta <= math.MaxUint8 {
+ deltaChunk := make([]byte, 1)
+ deltaChunk[0] = byte(delta)
+ return deltaChunk, nil
+ }
+
+ // big delta
+ if r.Type == TypeTCCPacketReceivedLargeDelta && delta >= math.MinInt16 && delta <= math.MaxInt16 {
+ deltaChunk := make([]byte, 2)
+ binary.BigEndian.PutUint16(deltaChunk, uint16(delta))
+ return deltaChunk, nil
+ }
+
+ // overflow
+ return nil, errDeltaExceedLimit
+}
+
+// Unmarshal ..
+func (r *RecvDelta) Unmarshal(rawPacket []byte) error {
+ chunkLen := len(rawPacket)
+
+ // must be 1 or 2 bytes
+ if chunkLen != 1 && chunkLen != 2 {
+ return errDeltaExceedLimit
+ }
+
+ if chunkLen == 1 {
+ r.Type = TypeTCCPacketReceivedSmallDelta
+ r.Delta = TypeTCCDeltaScaleFactor * int64(rawPacket[0])
+ return nil
+ }
+
+ r.Type = TypeTCCPacketReceivedLargeDelta
+ r.Delta = TypeTCCDeltaScaleFactor * int64(int16(binary.BigEndian.Uint16(rawPacket)))
+ return nil
+}
+
+const (
+ // the offset after header
+ baseSequenceNumberOffset = 8
+ packetStatusCountOffset = 10
+ referenceTimeOffset = 12
+ fbPktCountOffset = 15
+ packetChunkOffset = 16
+)
+
+// TransportLayerCC for sender-BWE
+// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5
+type TransportLayerCC struct {
+ // header
+ Header Header
+
+ // SSRC of sender
+ SenderSSRC uint32
+
+ // SSRC of the media source
+ MediaSSRC uint32
+
+ // Transport wide sequence of rtp extension
+ BaseSequenceNumber uint16
+
+ // PacketStatusCount
+ PacketStatusCount uint16
+
+ // ReferenceTime
+ ReferenceTime uint32
+
+ // FbPktCount
+ FbPktCount uint8
+
+ // PacketChunks
+ PacketChunks []PacketStatusChunk
+
+ // RecvDeltas
+ RecvDeltas []*RecvDelta
+}
+
+// Header returns the Header associated with this packet.
+// func (t *TransportLayerCC) Header() Header {
+// return t.Header
+// return Header{
+// Padding: true,
+// Count: FormatTCC,
+// Type: TypeTCCTransportSpecificFeedback,
+// // https://tools.ietf.org/html/rfc4585#page-33
+// Length: uint16((t.len() / 4) - 1),
+// }
+// }
+
+func (t *TransportLayerCC) packetLen() uint16 {
+ n := uint16(headerLength + packetChunkOffset + len(t.PacketChunks)*2)
+ for _, d := range t.RecvDeltas {
+ delta := d.Delta / TypeTCCDeltaScaleFactor
+
+ // small delta
+ if delta >= 0 && delta <= math.MaxUint8 {
+ n++
+ } else {
+ n += 2
+ }
+ }
+ return n
+}
+
+// Len return total bytes with padding
+func (t *TransportLayerCC) Len() uint16 {
+ n := t.packetLen()
+ // has padding
+ if n%4 != 0 {
+ n = (n/4 + 1) * 4
+ }
+
+ return n
+}
+
+func (t TransportLayerCC) String() string {
+ out := fmt.Sprintf("TransportLayerCC:\n\tHeader %v\n", t.Header)
+ out += fmt.Sprintf("TransportLayerCC:\n\tSender Ssrc %d\n", t.SenderSSRC)
+ out += fmt.Sprintf("\tMedia Ssrc %d\n", t.MediaSSRC)
+ out += fmt.Sprintf("\tBase Sequence Number %d\n", t.BaseSequenceNumber)
+ out += fmt.Sprintf("\tStatus Count %d\n", t.PacketStatusCount)
+ out += fmt.Sprintf("\tReference Time %d\n", t.ReferenceTime)
+ out += fmt.Sprintf("\tFeedback Packet Count %d\n", t.FbPktCount)
+ out += "\tPacketChunks "
+ for _, chunk := range t.PacketChunks {
+ out += fmt.Sprintf("%+v ", chunk)
+ }
+ out += "\n\tRecvDeltas "
+ for _, delta := range t.RecvDeltas {
+ out += fmt.Sprintf("%+v ", delta)
+ }
+ out += "\n"
+ return out
+}
+
+// Marshal encodes the TransportLayerCC in binary
+func (t TransportLayerCC) Marshal() ([]byte, error) {
+ header, err := t.Header.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ payload := make([]byte, t.Len()-headerLength)
+ binary.BigEndian.PutUint32(payload, t.SenderSSRC)
+ binary.BigEndian.PutUint32(payload[4:], t.MediaSSRC)
+ binary.BigEndian.PutUint16(payload[baseSequenceNumberOffset:], t.BaseSequenceNumber)
+ binary.BigEndian.PutUint16(payload[packetStatusCountOffset:], t.PacketStatusCount)
+ ReferenceTimeAndFbPktCount := appendNBitsToUint32(0, 24, t.ReferenceTime)
+ ReferenceTimeAndFbPktCount = appendNBitsToUint32(ReferenceTimeAndFbPktCount, 8, uint32(t.FbPktCount))
+ binary.BigEndian.PutUint32(payload[referenceTimeOffset:], ReferenceTimeAndFbPktCount)
+
+ for i, chunk := range t.PacketChunks {
+ b, err := chunk.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ copy(payload[packetChunkOffset+i*2:], b)
+ }
+
+ recvDeltaOffset := packetChunkOffset + len(t.PacketChunks)*2
+ var i int
+ for _, delta := range t.RecvDeltas {
+ b, err := delta.Marshal()
+ if err == nil {
+ copy(payload[recvDeltaOffset+i:], b)
+ i++
+ if delta.Type == TypeTCCPacketReceivedLargeDelta {
+ i++
+ }
+ }
+ }
+
+ if t.Header.Padding {
+ payload[len(payload)-1] = uint8(t.Len() - t.packetLen())
+ }
+
+ return append(header, payload...), nil
+}
+
+// Unmarshal ..
+func (t *TransportLayerCC) Unmarshal(rawPacket []byte) error { //nolint:gocognit
+ if len(rawPacket) < (headerLength + ssrcLength) {
+ return errPacketTooShort
+ }
+
+ if err := t.Header.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ // https://tools.ietf.org/html/rfc4585#page-33
+ // header's length + payload's length
+ totalLength := 4 * (t.Header.Length + 1)
+
+ if totalLength <= headerLength+packetChunkOffset {
+ return errPacketTooShort
+ }
+
+ if len(rawPacket) < int(totalLength) {
+ return errPacketTooShort
+ }
+
+ if t.Header.Type != TypeTransportSpecificFeedback || t.Header.Count != FormatTCC {
+ return errWrongType
+ }
+
+ t.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
+ t.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
+ t.BaseSequenceNumber = binary.BigEndian.Uint16(rawPacket[headerLength+baseSequenceNumberOffset:])
+ t.PacketStatusCount = binary.BigEndian.Uint16(rawPacket[headerLength+packetStatusCountOffset:])
+ t.ReferenceTime = get24BitsFromBytes(rawPacket[headerLength+referenceTimeOffset : headerLength+referenceTimeOffset+3])
+ t.FbPktCount = rawPacket[headerLength+fbPktCountOffset]
+
+ packetStatusPos := uint16(headerLength + packetChunkOffset)
+ var processedPacketNum uint16
+ for processedPacketNum < t.PacketStatusCount {
+ if packetStatusPos+packetStatusChunkLength >= totalLength {
+ return errPacketTooShort
+ }
+ typ := getNBitsFromByte(rawPacket[packetStatusPos : packetStatusPos+1][0], 0, 1)
+ var iPacketStatus PacketStatusChunk
+ switch typ {
+ case TypeTCCRunLengthChunk:
+ packetStatus := &RunLengthChunk{Type: typ}
+ iPacketStatus = packetStatus
+ err := packetStatus.Unmarshal(rawPacket[packetStatusPos : packetStatusPos+2])
+ if err != nil {
+ return err
+ }
+
+ packetNumberToProcess := min(t.PacketStatusCount-processedPacketNum, packetStatus.RunLength)
+ if packetStatus.PacketStatusSymbol == TypeTCCPacketReceivedSmallDelta ||
+ packetStatus.PacketStatusSymbol == TypeTCCPacketReceivedLargeDelta {
+ for j := uint16(0); j < packetNumberToProcess; j++ {
+ t.RecvDeltas = append(t.RecvDeltas, &RecvDelta{Type: packetStatus.PacketStatusSymbol})
+ }
+ }
+ processedPacketNum += packetNumberToProcess
+ case TypeTCCStatusVectorChunk:
+ packetStatus := &StatusVectorChunk{Type: typ}
+ iPacketStatus = packetStatus
+ err := packetStatus.Unmarshal(rawPacket[packetStatusPos : packetStatusPos+2])
+ if err != nil {
+ return err
+ }
+ if packetStatus.SymbolSize == TypeTCCSymbolSizeOneBit {
+ for j := 0; j < len(packetStatus.SymbolList); j++ {
+ if packetStatus.SymbolList[j] == TypeTCCPacketReceivedSmallDelta {
+ t.RecvDeltas = append(t.RecvDeltas, &RecvDelta{Type: TypeTCCPacketReceivedSmallDelta})
+ }
+ }
+ }
+ if packetStatus.SymbolSize == TypeTCCSymbolSizeTwoBit {
+ for j := 0; j < len(packetStatus.SymbolList); j++ {
+ if packetStatus.SymbolList[j] == TypeTCCPacketReceivedSmallDelta || packetStatus.SymbolList[j] == TypeTCCPacketReceivedLargeDelta {
+ t.RecvDeltas = append(t.RecvDeltas, &RecvDelta{Type: packetStatus.SymbolList[j]})
+ }
+ }
+ }
+ processedPacketNum += uint16(len(packetStatus.SymbolList))
+ }
+ packetStatusPos += packetStatusChunkLength
+ t.PacketChunks = append(t.PacketChunks, iPacketStatus)
+ }
+
+ recvDeltasPos := packetStatusPos
+ for _, delta := range t.RecvDeltas {
+ if recvDeltasPos >= totalLength {
+ return errPacketTooShort
+ }
+ if delta.Type == TypeTCCPacketReceivedSmallDelta {
+ err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+1])
+ if err != nil {
+ return err
+ }
+ recvDeltasPos++
+ }
+ if delta.Type == TypeTCCPacketReceivedLargeDelta {
+ err := delta.Unmarshal(rawPacket[recvDeltasPos : recvDeltasPos+2])
+ if err != nil {
+ return err
+ }
+ recvDeltasPos += 2
+ }
+ }
+
+ return nil
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (t TransportLayerCC) DestinationSSRC() []uint32 {
+ return []uint32{t.MediaSSRC}
+}
+
+func min(x, y uint16) uint16 {
+ if x < y {
+ return x
+ }
+ return y
+}
diff --git a/vendor/github.com/pion/rtcp/transport_layer_nack.go b/vendor/github.com/pion/rtcp/transport_layer_nack.go
new file mode 100644
index 0000000..f7ab803
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/transport_layer_nack.go
@@ -0,0 +1,174 @@
+package rtcp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+)
+
+// PacketBitmap shouldn't be used like a normal integral,
+// so it's type is masked here. Access it with PacketList().
+type PacketBitmap uint16
+
+// NackPair is a wire-representation of a collection of
+// Lost RTP packets
+type NackPair struct {
+ // ID of lost packets
+ PacketID uint16
+
+ // Bitmask of following lost packets
+ LostPackets PacketBitmap
+}
+
+// The TransportLayerNack packet informs the encoder about the loss of a transport packet
+// IETF RFC 4585, Section 6.2.1
+// https://tools.ietf.org/html/rfc4585#section-6.2.1
+type TransportLayerNack struct {
+ // SSRC of sender
+ SenderSSRC uint32
+
+ // SSRC of the media source
+ MediaSSRC uint32
+
+ Nacks []NackPair
+}
+
+var _ Packet = (*TransportLayerNack)(nil) // assert is a Packet
+
+// NackPairsFromSequenceNumbers generates a slice of NackPair from a list of SequenceNumbers
+// This handles generating the proper values for PacketID/LostPackets
+func NackPairsFromSequenceNumbers(sequenceNumbers []uint16) (pairs []NackPair) {
+ if len(sequenceNumbers) == 0 {
+ return []NackPair{}
+ }
+
+ nackPair := &NackPair{PacketID: sequenceNumbers[0]}
+ for i := 1; i < len(sequenceNumbers); i++ {
+ m := sequenceNumbers[i]
+
+ if m-nackPair.PacketID > 16 {
+ pairs = append(pairs, *nackPair)
+ nackPair = &NackPair{PacketID: m}
+ continue
+ }
+
+ nackPair.LostPackets |= 1 << (m - nackPair.PacketID - 1)
+ }
+ pairs = append(pairs, *nackPair)
+ return
+}
+
+// Range calls f sequentially for each sequence number covered by n.
+// If f returns false, Range stops the iteration.
+func (n *NackPair) Range(f func(seqno uint16) bool) {
+ more := f(n.PacketID)
+ if !more {
+ return
+ }
+
+ b := n.LostPackets
+ for i := uint16(0); b != 0; i++ {
+ if (b & (1 << i)) != 0 {
+ b &^= (1 << i)
+ more = f(n.PacketID + i + 1)
+ if !more {
+ return
+ }
+ }
+ }
+}
+
+// PacketList returns a list of Nack'd packets that's referenced by a NackPair
+func (n *NackPair) PacketList() []uint16 {
+ out := make([]uint16, 0, 17)
+ n.Range(func(seqno uint16) bool {
+ out = append(out, seqno)
+ return true
+ })
+ return out
+}
+
+const (
+ tlnLength = 2
+ nackOffset = 8
+)
+
+// Marshal encodes the TransportLayerNack in binary
+func (p TransportLayerNack) Marshal() ([]byte, error) {
+ if len(p.Nacks)+tlnLength > math.MaxUint8 {
+ return nil, errTooManyReports
+ }
+
+ rawPacket := make([]byte, nackOffset+(len(p.Nacks)*4))
+ binary.BigEndian.PutUint32(rawPacket, p.SenderSSRC)
+ binary.BigEndian.PutUint32(rawPacket[4:], p.MediaSSRC)
+ for i := 0; i < len(p.Nacks); i++ {
+ binary.BigEndian.PutUint16(rawPacket[nackOffset+(4*i):], p.Nacks[i].PacketID)
+ binary.BigEndian.PutUint16(rawPacket[nackOffset+(4*i)+2:], uint16(p.Nacks[i].LostPackets))
+ }
+ h := p.Header()
+ hData, err := h.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(hData, rawPacket...), nil
+}
+
+// Unmarshal decodes the TransportLayerNack from binary
+func (p *TransportLayerNack) Unmarshal(rawPacket []byte) error {
+ if len(rawPacket) < (headerLength + ssrcLength) {
+ return errPacketTooShort
+ }
+
+ var h Header
+ if err := h.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ if len(rawPacket) < (headerLength + int(4*h.Length)) {
+ return errPacketTooShort
+ }
+
+ if h.Type != TypeTransportSpecificFeedback || h.Count != FormatTLN {
+ return errWrongType
+ }
+
+ p.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:])
+ p.MediaSSRC = binary.BigEndian.Uint32(rawPacket[headerLength+ssrcLength:])
+ for i := headerLength + nackOffset; i < (headerLength + int(h.Length*4)); i += 4 {
+ p.Nacks = append(p.Nacks, NackPair{
+ binary.BigEndian.Uint16(rawPacket[i:]),
+ PacketBitmap(binary.BigEndian.Uint16(rawPacket[i+2:])),
+ })
+ }
+ return nil
+}
+
+func (p *TransportLayerNack) len() int {
+ return headerLength + nackOffset + (len(p.Nacks) * 4)
+}
+
+// Header returns the Header associated with this packet.
+func (p *TransportLayerNack) Header() Header {
+ return Header{
+ Count: FormatTLN,
+ Type: TypeTransportSpecificFeedback,
+ Length: uint16((p.len() / 4) - 1),
+ }
+}
+
+func (p TransportLayerNack) String() string {
+ out := fmt.Sprintf("TransportLayerNack from %x\n", p.SenderSSRC)
+ out += fmt.Sprintf("\tMedia Ssrc %x\n", p.MediaSSRC)
+ out += "\tID\tLostPackets\n"
+ for _, i := range p.Nacks {
+ out += fmt.Sprintf("\t%d\t%b\n", i.PacketID, i.LostPackets)
+ }
+ return out
+}
+
+// DestinationSSRC returns an array of SSRC values that this packet refers to.
+func (p *TransportLayerNack) DestinationSSRC() []uint32 {
+ return []uint32{p.MediaSSRC}
+}
diff --git a/vendor/github.com/pion/rtcp/util.go b/vendor/github.com/pion/rtcp/util.go
new file mode 100644
index 0000000..5702d35
--- /dev/null
+++ b/vendor/github.com/pion/rtcp/util.go
@@ -0,0 +1,38 @@
+package rtcp
+
+// getPadding Returns the padding required to make the length a multiple of 4
+func getPadding(len int) int {
+ if len%4 == 0 {
+ return 0
+ }
+ return 4 - (len % 4)
+}
+
+// setNBitsOfUint16 will truncate the value to size, left-shift to startIndex position and set
+func setNBitsOfUint16(src, size, startIndex, val uint16) (uint16, error) {
+ if startIndex+size > 16 {
+ return 0, errInvalidSizeOrStartIndex
+ }
+
+ // truncate val to size bits
+ val &= (1 << size) - 1
+
+ return src | (val << (16 - size - startIndex)), nil
+}
+
+// appendBit32 will left-shift and append n bits of val
+func appendNBitsToUint32(src, n, val uint32) uint32 {
+ return (src << n) | (val & (0xFFFFFFFF >> (32 - n)))
+}
+
+// getNBit get n bits from 1 byte, begin with a position
+func getNBitsFromByte(b byte, begin, n uint16) uint16 {
+ endShift := 8 - (begin + n)
+ mask := (0xFF >> begin) & uint8(0xFF<<endShift)
+ return uint16(b&mask) >> endShift
+}
+
+// get24BitFromBytes get 24bits from `[3]byte` slice
+func get24BitsFromBytes(b []byte) uint32 {
+ return uint32(b[0])<<16 + uint32(b[1])<<8 + uint32(b[2])
+}
diff --git a/vendor/github.com/pion/rtp/.gitignore b/vendor/github.com/pion/rtp/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/rtp/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/rtp/.golangci.yml b/vendor/github.com/pion/rtp/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/rtp/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/rtp/LICENSE b/vendor/github.com/pion/rtp/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/rtp/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/rtp/README.md b/vendor/github.com/pion/rtp/README.md
new file mode 100644
index 0000000..b17709e
--- /dev/null
+++ b/vendor/github.com/pion/rtp/README.md
@@ -0,0 +1,53 @@
+<h1 align="center">
+ <br>
+ Pion RTP
+ <br>
+</h1>
+<h4 align="center">A Go implementation of RTP</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-rtp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion RTP"></a>
+ <a href="https://sourcegraph.com/github.com/pion/rtp?badge"><img src="https://sourcegraph.com/github.com/pion/rtp/-/badge.svg" alt="Sourcegraph Widget"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/rtp"><img src="https://travis-ci.org/pion/rtp.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/rtp"><img src="https://godoc.org/github.com/pion/rtp?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/rtp"><img src="https://codecov.io/gh/pion/rtp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/rtp"><img src="https://goreportcard.com/badge/github.com/pion/rtp" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
+* [Michael MacDonald](https://github.com/mjmac)
+* [Luke Curley](https://github.com/kixelated) *Performance*
+* [Antoine Baché](https://github.com/Antonito) *Fixed crashes*
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Raphael Derosso Pereira](https://github.com/raphaelpereira)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [adwpc](https://github.com/adwpc) *add transport-cc extension*
+* [Bao Nguyen](https://github.com/sysbot) *add VP9 noop, bug fixes.
+* [Tarrence van As](https://github.com/tarrencev) *add audio level extension*
+* [Simone Gotti](https://github.com/sgotti)
+* [Guilherme Souza](https://github.com/gqgs)
+* [Rob Lofthouse](https://github.com/roblofthouse)
+* [Kazuyuki Honda](https://github.com/hakobera)
+* [Haiyang Wang](https://github.com/ocean2811)
+* [lxb](https://github.com/lxb531)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/rtp/abssendtimeextension.go b/vendor/github.com/pion/rtp/abssendtimeextension.go
new file mode 100644
index 0000000..fc9731d
--- /dev/null
+++ b/vendor/github.com/pion/rtp/abssendtimeextension.go
@@ -0,0 +1,78 @@
+package rtp
+
+import (
+ "time"
+)
+
+const (
+ absSendTimeExtensionSize = 3
+)
+
+// AbsSendTimeExtension is a extension payload format in
+// http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
+type AbsSendTimeExtension struct {
+ Timestamp uint64
+}
+
+// Marshal serializes the members to buffer.
+func (t *AbsSendTimeExtension) Marshal() ([]byte, error) {
+ return []byte{
+ byte(t.Timestamp & 0xFF0000 >> 16),
+ byte(t.Timestamp & 0xFF00 >> 8),
+ byte(t.Timestamp & 0xFF),
+ }, nil
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the members.
+func (t *AbsSendTimeExtension) Unmarshal(rawData []byte) error {
+ if len(rawData) < absSendTimeExtensionSize {
+ return errTooSmall
+ }
+ t.Timestamp = uint64(rawData[0])<<16 | uint64(rawData[1])<<8 | uint64(rawData[2])
+ return nil
+}
+
+// Estimate absolute send time according to the receive time.
+// Note that if the transmission delay is larger than 64 seconds, estimated time will be wrong.
+func (t *AbsSendTimeExtension) Estimate(receive time.Time) time.Time {
+ receiveNTP := toNtpTime(receive)
+ ntp := receiveNTP&0xFFFFFFC000000000 | (t.Timestamp&0xFFFFFF)<<14
+ if receiveNTP < ntp {
+ // Receive time must be always later than send time
+ ntp -= 0x1000000 << 14
+ }
+
+ return toTime(ntp)
+}
+
+// NewAbsSendTimeExtension makes new AbsSendTimeExtension from time.Time.
+func NewAbsSendTimeExtension(sendTime time.Time) *AbsSendTimeExtension {
+ return &AbsSendTimeExtension{
+ Timestamp: toNtpTime(sendTime) >> 14,
+ }
+}
+
+func toNtpTime(t time.Time) uint64 {
+ var s uint64
+ var f uint64
+ u := uint64(t.UnixNano())
+ s = u / 1e9
+ s += 0x83AA7E80 // offset in seconds between unix epoch and ntp epoch
+ f = u % 1e9
+ f <<= 32
+ f /= 1e9
+ s <<= 32
+
+ return s | f
+}
+
+func toTime(t uint64) time.Time {
+ s := t >> 32
+ f := t & 0xFFFFFFFF
+ f *= 1e9
+ f >>= 32
+ s -= 0x83AA7E80
+ u := s*1e9 + f
+
+ return time.Unix(0, int64(u))
+}
diff --git a/vendor/github.com/pion/rtp/audiolevelextension.go b/vendor/github.com/pion/rtp/audiolevelextension.go
new file mode 100644
index 0000000..f8701e1
--- /dev/null
+++ b/vendor/github.com/pion/rtp/audiolevelextension.go
@@ -0,0 +1,60 @@
+package rtp
+
+import (
+ "errors"
+)
+
+const (
+ // audioLevelExtensionSize One byte header size
+ audioLevelExtensionSize = 1
+)
+
+var errAudioLevelOverflow = errors.New("audio level overflow")
+
+// AudioLevelExtension is a extension payload format described in
+// https://tools.ietf.org/html/rfc6464
+//
+// Implementation based on:
+// https://chromium.googlesource.com/external/webrtc/+/e2a017725570ead5946a4ca8235af27470ca0df9/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc#49
+//
+// One byte format:
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID | len=0 |V| level |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// Two byte format:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID | len=1 |V| level | 0 (pad) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type AudioLevelExtension struct {
+ Level uint8
+ Voice bool
+}
+
+// Marshal serializes the members to buffer
+func (a *AudioLevelExtension) Marshal() ([]byte, error) {
+ if a.Level > 127 {
+ return nil, errAudioLevelOverflow
+ }
+ voice := uint8(0x00)
+ if a.Voice {
+ voice = 0x80
+ }
+ buf := make([]byte, audioLevelExtensionSize)
+ buf[0] = voice | a.Level
+ return buf, nil
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the members
+func (a *AudioLevelExtension) Unmarshal(rawData []byte) error {
+ if len(rawData) < audioLevelExtensionSize {
+ return errTooSmall
+ }
+ a.Level = rawData[0] & 0x7F
+ a.Voice = rawData[0]&0x80 != 0
+ return nil
+}
diff --git a/vendor/github.com/pion/rtp/codecov.yml b/vendor/github.com/pion/rtp/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/rtp/codecs/codecs.go b/vendor/github.com/pion/rtp/codecs/codecs.go
new file mode 100644
index 0000000..0e07897
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/codecs.go
@@ -0,0 +1,2 @@
+// Package codecs implements codec specific RTP payloader/depayloaders
+package codecs
diff --git a/vendor/github.com/pion/rtp/codecs/common.go b/vendor/github.com/pion/rtp/codecs/common.go
new file mode 100644
index 0000000..39336d2
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/common.go
@@ -0,0 +1,8 @@
+package codecs
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/vendor/github.com/pion/rtp/codecs/error.go b/vendor/github.com/pion/rtp/codecs/error.go
new file mode 100644
index 0000000..38ee907
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/error.go
@@ -0,0 +1,11 @@
+package codecs
+
+import "errors"
+
+var (
+ errShortPacket = errors.New("packet is not large enough")
+ errNilPacket = errors.New("invalid nil packet")
+ errTooManyPDiff = errors.New("too many PDiff")
+ errTooManySpatialLayers = errors.New("too many spatial layers")
+ errUnhandledNALUType = errors.New("NALU Type is unhandled")
+)
diff --git a/vendor/github.com/pion/rtp/codecs/g711_packet.go b/vendor/github.com/pion/rtp/codecs/g711_packet.go
new file mode 100644
index 0000000..a74876f
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/g711_packet.go
@@ -0,0 +1,22 @@
+package codecs
+
+// G711Payloader payloads G711 packets
+type G711Payloader struct{}
+
+// Payload fragments an G711 packet across one or more byte arrays
+func (p *G711Payloader) Payload(mtu int, payload []byte) [][]byte {
+ var out [][]byte
+ if payload == nil || mtu <= 0 {
+ return out
+ }
+
+ for len(payload) > mtu {
+ o := make([]byte, mtu)
+ copy(o, payload[:mtu])
+ payload = payload[mtu:]
+ out = append(out, o)
+ }
+ o := make([]byte, len(payload))
+ copy(o, payload)
+ return append(out, o)
+}
diff --git a/vendor/github.com/pion/rtp/codecs/g722_packet.go b/vendor/github.com/pion/rtp/codecs/g722_packet.go
new file mode 100644
index 0000000..70c9883
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/g722_packet.go
@@ -0,0 +1,22 @@
+package codecs
+
+// G722Payloader payloads G722 packets
+type G722Payloader struct{}
+
+// Payload fragments an G722 packet across one or more byte arrays
+func (p *G722Payloader) Payload(mtu int, payload []byte) [][]byte {
+ var out [][]byte
+ if payload == nil || mtu <= 0 {
+ return out
+ }
+
+ for len(payload) > mtu {
+ o := make([]byte, mtu)
+ copy(o, payload[:mtu])
+ payload = payload[mtu:]
+ out = append(out, o)
+ }
+ o := make([]byte, len(payload))
+ copy(o, payload)
+ return append(out, o)
+}
diff --git a/vendor/github.com/pion/rtp/codecs/h264_packet.go b/vendor/github.com/pion/rtp/codecs/h264_packet.go
new file mode 100644
index 0000000..3ee5926
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/h264_packet.go
@@ -0,0 +1,205 @@
+package codecs
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+// H264Payloader payloads H264 packets
+type H264Payloader struct{}
+
+const (
+ stapaNALUType = 24
+ fuaNALUType = 28
+
+ fuaHeaderSize = 2
+ stapaHeaderSize = 1
+ stapaNALULengthSize = 2
+
+ naluTypeBitmask = 0x1F
+ naluRefIdcBitmask = 0x60
+ fuaStartBitmask = 0x80
+)
+
+func annexbNALUStartCode() []byte { return []byte{0x00, 0x00, 0x00, 0x01} }
+
+func emitNalus(nals []byte, emit func([]byte)) {
+ nextInd := func(nalu []byte, start int) (indStart int, indLen int) {
+ zeroCount := 0
+
+ for i, b := range nalu[start:] {
+ if b == 0 {
+ zeroCount++
+ continue
+ } else if b == 1 {
+ if zeroCount >= 2 {
+ return start + i - zeroCount, zeroCount + 1
+ }
+ }
+ zeroCount = 0
+ }
+ return -1, -1
+ }
+
+ nextIndStart, nextIndLen := nextInd(nals, 0)
+ if nextIndStart == -1 {
+ emit(nals)
+ } else {
+ for nextIndStart != -1 {
+ prevStart := nextIndStart + nextIndLen
+ nextIndStart, nextIndLen = nextInd(nals, prevStart)
+ if nextIndStart != -1 {
+ emit(nals[prevStart:nextIndStart])
+ } else {
+ // Emit until end of stream, no end indicator found
+ emit(nals[prevStart:])
+ }
+ }
+ }
+}
+
+// Payload fragments a H264 packet across one or more byte arrays
+func (p *H264Payloader) Payload(mtu int, payload []byte) [][]byte {
+ var payloads [][]byte
+ if len(payload) == 0 {
+ return payloads
+ }
+
+ emitNalus(payload, func(nalu []byte) {
+ if len(nalu) == 0 {
+ return
+ }
+
+ naluType := nalu[0] & naluTypeBitmask
+ naluRefIdc := nalu[0] & naluRefIdcBitmask
+
+ if naluType == 9 || naluType == 12 {
+ return
+ }
+
+ // Single NALU
+ if len(nalu) <= mtu {
+ out := make([]byte, len(nalu))
+ copy(out, nalu)
+ payloads = append(payloads, out)
+ return
+ }
+
+ // FU-A
+ maxFragmentSize := mtu - fuaHeaderSize
+
+ // The FU payload consists of fragments of the payload of the fragmented
+ // NAL unit so that if the fragmentation unit payloads of consecutive
+ // FUs are sequentially concatenated, the payload of the fragmented NAL
+ // unit can be reconstructed. The NAL unit type octet of the fragmented
+ // NAL unit is not included as such in the fragmentation unit payload,
+ // but rather the information of the NAL unit type octet of the
+ // fragmented NAL unit is conveyed in the F and NRI fields of the FU
+ // indicator octet of the fragmentation unit and in the type field of
+ // the FU header. An FU payload MAY have any number of octets and MAY
+ // be empty.
+
+ naluData := nalu
+ // According to the RFC, the first octet is skipped due to redundant information
+ naluDataIndex := 1
+ naluDataLength := len(nalu) - naluDataIndex
+ naluDataRemaining := naluDataLength
+
+ if min(maxFragmentSize, naluDataRemaining) <= 0 {
+ return
+ }
+
+ for naluDataRemaining > 0 {
+ currentFragmentSize := min(maxFragmentSize, naluDataRemaining)
+ out := make([]byte, fuaHeaderSize+currentFragmentSize)
+
+ // +---------------+
+ // |0|1|2|3|4|5|6|7|
+ // +-+-+-+-+-+-+-+-+
+ // |F|NRI| Type |
+ // +---------------+
+ out[0] = fuaNALUType
+ out[0] |= naluRefIdc
+
+ // +---------------+
+ // |0|1|2|3|4|5|6|7|
+ // +-+-+-+-+-+-+-+-+
+ // |S|E|R| Type |
+ // +---------------+
+
+ out[1] = naluType
+ if naluDataRemaining == naluDataLength {
+ // Set start bit
+ out[1] |= 1 << 7
+ } else if naluDataRemaining-currentFragmentSize == 0 {
+ // Set end bit
+ out[1] |= 1 << 6
+ }
+
+ copy(out[fuaHeaderSize:], naluData[naluDataIndex:naluDataIndex+currentFragmentSize])
+ payloads = append(payloads, out)
+
+ naluDataRemaining -= currentFragmentSize
+ naluDataIndex += currentFragmentSize
+ }
+ })
+
+ return payloads
+}
+
+// H264Packet represents the H264 header that is stored in the payload of an RTP Packet
+type H264Packet struct {
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the H264Packet this method is called upon
+func (p *H264Packet) Unmarshal(payload []byte) ([]byte, error) {
+ if payload == nil {
+ return nil, errNilPacket
+ } else if len(payload) <= 2 {
+ return nil, fmt.Errorf("%w: %d <= 2", errShortPacket, len(payload))
+ }
+
+ // NALU Types
+ // https://tools.ietf.org/html/rfc6184#section-5.4
+ naluType := payload[0] & naluTypeBitmask
+ switch {
+ case naluType > 0 && naluType < 24:
+ return append(annexbNALUStartCode(), payload...), nil
+
+ case naluType == stapaNALUType:
+ currOffset := int(stapaHeaderSize)
+ result := []byte{}
+ for currOffset < len(payload) {
+ naluSize := int(binary.BigEndian.Uint16(payload[currOffset:]))
+ currOffset += stapaNALULengthSize
+
+ if len(payload) < currOffset+naluSize {
+ return nil, fmt.Errorf("%w STAP-A declared size(%d) is larger than buffer(%d)", errShortPacket, naluSize, len(payload)-currOffset)
+ }
+
+ result = append(result, annexbNALUStartCode()...)
+ result = append(result, payload[currOffset:currOffset+naluSize]...)
+ currOffset += naluSize
+ }
+ return result, nil
+
+ case naluType == fuaNALUType:
+ if len(payload) < fuaHeaderSize {
+ return nil, errShortPacket
+ }
+
+ if payload[1]&fuaStartBitmask != 0 {
+ naluRefIdc := payload[0] & naluRefIdcBitmask
+ fragmentedNaluType := payload[1] & naluTypeBitmask
+
+ // Take a copy of payload since we are mutating it.
+ payloadCopy := append([]byte{}, payload...)
+ payloadCopy[fuaHeaderSize-1] = naluRefIdc | fragmentedNaluType
+ return append(annexbNALUStartCode(), payloadCopy[fuaHeaderSize-1:]...), nil
+ }
+
+ return payload[fuaHeaderSize:], nil
+ }
+
+ return nil, fmt.Errorf("%w: %d", errUnhandledNALUType, naluType)
+}
diff --git a/vendor/github.com/pion/rtp/codecs/opus_packet.go b/vendor/github.com/pion/rtp/codecs/opus_packet.go
new file mode 100644
index 0000000..504741f
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/opus_packet.go
@@ -0,0 +1,44 @@
+package codecs
+
+// OpusPayloader payloads Opus packets
+type OpusPayloader struct{}
+
+// Payload fragments an Opus packet across one or more byte arrays
+func (p *OpusPayloader) Payload(mtu int, payload []byte) [][]byte {
+ if payload == nil {
+ return [][]byte{}
+ }
+
+ out := make([]byte, len(payload))
+ copy(out, payload)
+ return [][]byte{out}
+}
+
+// OpusPacket represents the Opus header that is stored in the payload of an RTP Packet
+type OpusPacket struct {
+ Payload []byte
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the OpusPacket this method is called upon
+func (p *OpusPacket) Unmarshal(packet []byte) ([]byte, error) {
+ if packet == nil {
+ return nil, errNilPacket
+ } else if len(packet) == 0 {
+ return nil, errShortPacket
+ }
+
+ p.Payload = packet
+ return packet, nil
+}
+
+// OpusPartitionHeadChecker checks Opus partition head
+type OpusPartitionHeadChecker struct{}
+
+// IsPartitionHead checks whether if this is a head of the Opus partition
+func (*OpusPartitionHeadChecker) IsPartitionHead(packet []byte) bool {
+ p := &OpusPacket{}
+ if _, err := p.Unmarshal(packet); err != nil {
+ return false
+ }
+ return true
+}
diff --git a/vendor/github.com/pion/rtp/codecs/vp8_packet.go b/vendor/github.com/pion/rtp/codecs/vp8_packet.go
new file mode 100644
index 0000000..7ade7da
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/vp8_packet.go
@@ -0,0 +1,143 @@
+package codecs
+
+// VP8Payloader payloads VP8 packets
+type VP8Payloader struct{}
+
+const (
+ vp8HeaderSize = 1
+)
+
+// Payload fragments a VP8 packet across one or more byte arrays
+func (p *VP8Payloader) Payload(mtu int, payload []byte) [][]byte {
+ /*
+ * https://tools.ietf.org/html/rfc7741#section-4.2
+ *
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |X|R|N|S|R| PID | (REQUIRED)
+ * +-+-+-+-+-+-+-+-+
+ * X: |I|L|T|K| RSV | (OPTIONAL)
+ * +-+-+-+-+-+-+-+-+
+ * I: |M| PictureID | (OPTIONAL)
+ * +-+-+-+-+-+-+-+-+
+ * L: | TL0PICIDX | (OPTIONAL)
+ * +-+-+-+-+-+-+-+-+
+ * T/K: |TID|Y| KEYIDX | (OPTIONAL)
+ * +-+-+-+-+-+-+-+-+
+ * S: Start of VP8 partition. SHOULD be set to 1 when the first payload
+ * octet of the RTP packet is the beginning of a new VP8 partition,
+ * and MUST NOT be 1 otherwise. The S bit MUST be set to 1 for the
+ * first packet of each encoded frame.
+ */
+
+ maxFragmentSize := mtu - vp8HeaderSize
+
+ payloadData := payload
+ payloadDataRemaining := len(payload)
+
+ payloadDataIndex := 0
+ var payloads [][]byte
+
+ // Make sure the fragment/payload size is correct
+ if min(maxFragmentSize, payloadDataRemaining) <= 0 {
+ return payloads
+ }
+ for payloadDataRemaining > 0 {
+ currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
+ out := make([]byte, vp8HeaderSize+currentFragmentSize)
+ if payloadDataRemaining == len(payload) {
+ out[0] = 0x10
+ }
+
+ copy(out[vp8HeaderSize:], payloadData[payloadDataIndex:payloadDataIndex+currentFragmentSize])
+ payloads = append(payloads, out)
+
+ payloadDataRemaining -= currentFragmentSize
+ payloadDataIndex += currentFragmentSize
+ }
+
+ return payloads
+}
+
+// VP8Packet represents the VP8 header that is stored in the payload of an RTP Packet
+type VP8Packet struct {
+ // Required Header
+ X uint8 /* extended controlbits present */
+ N uint8 /* (non-reference frame) when set to 1 this frame can be discarded */
+ S uint8 /* start of VP8 partition */
+ PID uint8 /* partition index */
+
+ // Optional Header
+ I uint8 /* 1 if PictureID is present */
+ L uint8 /* 1 if TL0PICIDX is present */
+ T uint8 /* 1 if TID is present */
+ K uint8 /* 1 if KEYIDX is present */
+ PictureID uint16 /* 8 or 16 bits, picture ID */
+ TL0PICIDX uint8 /* 8 bits temporal level zero index */
+
+ Payload []byte
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the VP8Packet this method is called upon
+func (p *VP8Packet) Unmarshal(payload []byte) ([]byte, error) {
+ if payload == nil {
+ return nil, errNilPacket
+ }
+
+ payloadLen := len(payload)
+
+ if payloadLen < 4 {
+ return nil, errShortPacket
+ }
+
+ payloadIndex := 0
+
+ p.X = (payload[payloadIndex] & 0x80) >> 7
+ p.N = (payload[payloadIndex] & 0x20) >> 5
+ p.S = (payload[payloadIndex] & 0x10) >> 4
+ p.PID = payload[payloadIndex] & 0x07
+
+ payloadIndex++
+
+ if p.X == 1 {
+ p.I = (payload[payloadIndex] & 0x80) >> 7
+ p.L = (payload[payloadIndex] & 0x40) >> 6
+ p.T = (payload[payloadIndex] & 0x20) >> 5
+ p.K = (payload[payloadIndex] & 0x10) >> 4
+ payloadIndex++
+ }
+
+ if p.I == 1 { // PID present?
+ if payload[payloadIndex]&0x80 > 0 { // M == 1, PID is 16bit
+ payloadIndex += 2
+ } else {
+ payloadIndex++
+ }
+ }
+
+ if p.L == 1 {
+ payloadIndex++
+ }
+
+ if p.T == 1 || p.K == 1 {
+ payloadIndex++
+ }
+
+ if payloadIndex >= payloadLen {
+ return nil, errShortPacket
+ }
+ p.Payload = payload[payloadIndex:]
+ return p.Payload, nil
+}
+
+// VP8PartitionHeadChecker checks VP8 partition head
+type VP8PartitionHeadChecker struct{}
+
+// IsPartitionHead checks whether if this is a head of the VP8 partition
+func (*VP8PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
+ p := &VP8Packet{}
+ if _, err := p.Unmarshal(packet); err != nil {
+ return false
+ }
+ return p.S == 1
+}
diff --git a/vendor/github.com/pion/rtp/codecs/vp9_packet.go b/vendor/github.com/pion/rtp/codecs/vp9_packet.go
new file mode 100644
index 0000000..5cb619b
--- /dev/null
+++ b/vendor/github.com/pion/rtp/codecs/vp9_packet.go
@@ -0,0 +1,385 @@
+package codecs
+
+import (
+ "github.com/pion/randutil"
+)
+
+// Use global random generator to properly seed by crypto grade random.
+var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
+
+// VP9Payloader payloads VP9 packets
+type VP9Payloader struct {
+ pictureID uint16
+ initialized bool
+
+ // InitialPictureIDFn is a function that returns random initial picture ID.
+ InitialPictureIDFn func() uint16
+}
+
+const (
+ vp9HeaderSize = 3 // Flexible mode 15 bit picture ID
+ maxSpatialLayers = 5
+ maxVP9RefPics = 3
+)
+
+// Payload fragments an VP9 packet across one or more byte arrays
+func (p *VP9Payloader) Payload(mtu int, payload []byte) [][]byte {
+ /*
+ * https://www.ietf.org/id/draft-ietf-payload-vp9-10.txt
+ *
+ * Flexible mode (F=1)
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |I|P|L|F|B|E|V|-| (REQUIRED)
+ * +-+-+-+-+-+-+-+-+
+ * I: |M| PICTURE ID | (REQUIRED)
+ * +-+-+-+-+-+-+-+-+
+ * M: | EXTENDED PID | (RECOMMENDED)
+ * +-+-+-+-+-+-+-+-+
+ * L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED)
+ * +-+-+-+-+-+-+-+-+ -\
+ * P,F: | P_DIFF |N| (CONDITIONALLY REQUIRED) - up to 3 times
+ * +-+-+-+-+-+-+-+-+ -/
+ * V: | SS |
+ * | .. |
+ * +-+-+-+-+-+-+-+-+
+ *
+ * Non-flexible mode (F=0)
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |I|P|L|F|B|E|V|-| (REQUIRED)
+ * +-+-+-+-+-+-+-+-+
+ * I: |M| PICTURE ID | (RECOMMENDED)
+ * +-+-+-+-+-+-+-+-+
+ * M: | EXTENDED PID | (RECOMMENDED)
+ * +-+-+-+-+-+-+-+-+
+ * L: | TID |U| SID |D| (CONDITIONALLY RECOMMENDED)
+ * +-+-+-+-+-+-+-+-+
+ * | TL0PICIDX | (CONDITIONALLY REQUIRED)
+ * +-+-+-+-+-+-+-+-+
+ * V: | SS |
+ * | .. |
+ * +-+-+-+-+-+-+-+-+
+ */
+
+ if !p.initialized {
+ if p.InitialPictureIDFn == nil {
+ p.InitialPictureIDFn = func() uint16 {
+ return uint16(globalMathRandomGenerator.Intn(0x7FFF))
+ }
+ }
+ p.pictureID = p.InitialPictureIDFn() & 0x7FFF
+ p.initialized = true
+ }
+ if payload == nil {
+ return [][]byte{}
+ }
+
+ maxFragmentSize := mtu - vp9HeaderSize
+ payloadDataRemaining := len(payload)
+ payloadDataIndex := 0
+
+ if min(maxFragmentSize, payloadDataRemaining) <= 0 {
+ return [][]byte{}
+ }
+
+ var payloads [][]byte
+ for payloadDataRemaining > 0 {
+ currentFragmentSize := min(maxFragmentSize, payloadDataRemaining)
+ out := make([]byte, vp9HeaderSize+currentFragmentSize)
+
+ out[0] = 0x90 // F=1 I=1
+ if payloadDataIndex == 0 {
+ out[0] |= 0x08 // B=1
+ }
+ if payloadDataRemaining == currentFragmentSize {
+ out[0] |= 0x04 // E=1
+ }
+ out[1] = byte(p.pictureID>>8) | 0x80
+ out[2] = byte(p.pictureID)
+ copy(out[vp9HeaderSize:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize])
+ payloads = append(payloads, out)
+
+ payloadDataRemaining -= currentFragmentSize
+ payloadDataIndex += currentFragmentSize
+ }
+ p.pictureID++
+ if p.pictureID >= 0x8000 {
+ p.pictureID = 0
+ }
+
+ return payloads
+}
+
+// VP9Packet represents the VP9 header that is stored in the payload of an RTP Packet
+type VP9Packet struct {
+ // Required header
+ I bool // PictureID is present
+ P bool // Inter-picture predicted frame
+ L bool // Layer indices is present
+ F bool // Flexible mode
+ B bool // Start of a frame
+ E bool // End of a frame
+ V bool // Scalability structure (SS) data present
+
+ // Recommended headers
+ PictureID uint16 // 7 or 16 bits, picture ID
+
+ // Conditionally recommended headers
+ TID uint8 // Temporal layer ID
+ U bool // Switching up point
+ SID uint8 // Spatial layer ID
+ D bool // Inter-layer dependency used
+
+ // Conditionally required headers
+ PDiff []uint8 // Reference index (F=1)
+ TL0PICIDX uint8 // Temporal layer zero index (F=0)
+
+ // Scalability structure headers
+ NS uint8 // N_S + 1 indicates the number of spatial layers present in the VP9 stream
+ Y bool // Each spatial layer's frame resolution present
+ G bool // PG description present flag.
+ NG uint8 // N_G indicates the number of pictures in a Picture Group (PG)
+ Width []uint16
+ Height []uint16
+ PGTID []uint8 // Temporal layer ID of pictures in a Picture Group
+ PGU []bool // Switching up point of pictures in a Picture Group
+ PGPDiff [][]uint8 // Reference indecies of pictures in a Picture Group
+
+ Payload []byte
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the VP9Packet this method is called upon
+func (p *VP9Packet) Unmarshal(packet []byte) ([]byte, error) {
+ if packet == nil {
+ return nil, errNilPacket
+ }
+ if len(packet) < 1 {
+ return nil, errShortPacket
+ }
+
+ p.I = packet[0]&0x80 != 0
+ p.P = packet[0]&0x40 != 0
+ p.L = packet[0]&0x20 != 0
+ p.F = packet[0]&0x10 != 0
+ p.B = packet[0]&0x08 != 0
+ p.E = packet[0]&0x04 != 0
+ p.V = packet[0]&0x02 != 0
+
+ pos := 1
+ var err error
+
+ if p.I {
+ pos, err = p.parsePictureID(packet, pos)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if p.L {
+ pos, err = p.parseLayerInfo(packet, pos)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if p.F && p.P {
+ pos, err = p.parseRefIndices(packet, pos)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if p.V {
+ pos, err = p.parseSSData(packet, pos)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ p.Payload = packet[pos:]
+ return p.Payload, nil
+}
+
+// Picture ID:
+//
+// +-+-+-+-+-+-+-+-+
+// I: |M| PICTURE ID | M:0 => picture id is 7 bits.
+// +-+-+-+-+-+-+-+-+ M:1 => picture id is 15 bits.
+// M: | EXTENDED PID |
+// +-+-+-+-+-+-+-+-+
+//
+func (p *VP9Packet) parsePictureID(packet []byte, pos int) (int, error) {
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+
+ p.PictureID = uint16(packet[pos] & 0x7F)
+ if packet[pos]&0x80 != 0 {
+ pos++
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+ p.PictureID = p.PictureID<<8 | uint16(packet[pos])
+ }
+ pos++
+ return pos, nil
+}
+
+func (p *VP9Packet) parseLayerInfo(packet []byte, pos int) (int, error) {
+ pos, err := p.parseLayerInfoCommon(packet, pos)
+ if err != nil {
+ return pos, err
+ }
+
+ if p.F {
+ return pos, nil
+ }
+
+ return p.parseLayerInfoNonFlexibleMode(packet, pos)
+}
+
+// Layer indices (flexible mode):
+//
+// +-+-+-+-+-+-+-+-+
+// L: | T |U| S |D|
+// +-+-+-+-+-+-+-+-+
+//
+func (p *VP9Packet) parseLayerInfoCommon(packet []byte, pos int) (int, error) {
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+
+ p.TID = packet[pos] >> 5
+ p.U = packet[pos]&0x10 != 0
+ p.SID = (packet[pos] >> 1) & 0x7
+ p.D = packet[pos]&0x01 != 0
+
+ if p.SID >= maxSpatialLayers {
+ return pos, errTooManySpatialLayers
+ }
+
+ pos++
+ return pos, nil
+}
+
+// Layer indices (non-flexible mode):
+//
+// +-+-+-+-+-+-+-+-+
+// L: | T |U| S |D|
+// +-+-+-+-+-+-+-+-+
+// | TL0PICIDX |
+// +-+-+-+-+-+-+-+-+
+//
+func (p *VP9Packet) parseLayerInfoNonFlexibleMode(packet []byte, pos int) (int, error) {
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+
+ p.TL0PICIDX = packet[pos]
+ pos++
+ return pos, nil
+}
+
+// Reference indices:
+//
+// +-+-+-+-+-+-+-+-+ P=1,F=1: At least one reference index
+// P,F: | P_DIFF |N| up to 3 times has to be specified.
+// +-+-+-+-+-+-+-+-+ N=1: An additional P_DIFF follows
+// current P_DIFF.
+//
+func (p *VP9Packet) parseRefIndices(packet []byte, pos int) (int, error) {
+ for {
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+ p.PDiff = append(p.PDiff, packet[pos]>>1)
+ if packet[pos]&0x01 == 0 {
+ break
+ }
+ if len(p.PDiff) >= maxVP9RefPics {
+ return pos, errTooManyPDiff
+ }
+ pos++
+ }
+ pos++
+
+ return pos, nil
+}
+
+// Scalability structure (SS):
+//
+// +-+-+-+-+-+-+-+-+
+// V: | N_S |Y|G|-|-|-|
+// +-+-+-+-+-+-+-+-+ -|
+// Y: | WIDTH | (OPTIONAL) .
+// + + .
+// | | (OPTIONAL) .
+// +-+-+-+-+-+-+-+-+ . N_S + 1 times
+// | HEIGHT | (OPTIONAL) .
+// + + .
+// | | (OPTIONAL) .
+// +-+-+-+-+-+-+-+-+ -|
+// G: | N_G | (OPTIONAL)
+// +-+-+-+-+-+-+-+-+ -|
+// N_G: | T |U| R |-|-| (OPTIONAL) .
+// +-+-+-+-+-+-+-+-+ -| . N_G times
+// | P_DIFF | (OPTIONAL) . R times .
+// +-+-+-+-+-+-+-+-+ -| -|
+//
+func (p *VP9Packet) parseSSData(packet []byte, pos int) (int, error) {
+ if len(packet) <= pos {
+ return pos, errShortPacket
+ }
+
+ p.NS = packet[pos] >> 5
+ p.Y = packet[pos]&0x10 != 0
+ p.G = (packet[pos]>>1)&0x7 != 0
+ pos++
+
+ NS := p.NS + 1
+ p.NG = 0
+
+ if p.Y {
+ p.Width = make([]uint16, NS)
+ p.Height = make([]uint16, NS)
+ for i := 0; i < int(NS); i++ {
+ p.Width[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
+ pos += 2
+ p.Height[i] = uint16(packet[pos])<<8 | uint16(packet[pos+1])
+ pos += 2
+ }
+ }
+
+ if p.G {
+ p.NG = packet[pos]
+ pos++
+ }
+
+ for i := 0; i < int(p.NG); i++ {
+ p.PGTID = append(p.PGTID, packet[pos]>>5)
+ p.PGU = append(p.PGU, packet[pos]&0x10 != 0)
+ R := (packet[pos] >> 2) & 0x3
+ pos++
+
+ p.PGPDiff = append(p.PGPDiff, []uint8{})
+ for j := 0; j < int(R); j++ {
+ p.PGPDiff[i] = append(p.PGPDiff[i], packet[pos])
+ pos++
+ }
+ }
+
+ return pos, nil
+}
+
+// VP9PartitionHeadChecker checks VP9 partition head
+type VP9PartitionHeadChecker struct{}
+
+// IsPartitionHead checks whether if this is a head of the VP9 partition
+func (*VP9PartitionHeadChecker) IsPartitionHead(packet []byte) bool {
+ p := &VP9Packet{}
+ if _, err := p.Unmarshal(packet); err != nil {
+ return false
+ }
+ return p.B
+}
diff --git a/vendor/github.com/pion/rtp/depacketizer.go b/vendor/github.com/pion/rtp/depacketizer.go
new file mode 100644
index 0000000..b8c09da
--- /dev/null
+++ b/vendor/github.com/pion/rtp/depacketizer.go
@@ -0,0 +1,6 @@
+package rtp
+
+// Depacketizer depacketizes a RTP payload, removing any RTP specific data from the payload
+type Depacketizer interface {
+ Unmarshal(packet []byte) ([]byte, error)
+}
diff --git a/vendor/github.com/pion/rtp/error.go b/vendor/github.com/pion/rtp/error.go
new file mode 100644
index 0000000..5458c6f
--- /dev/null
+++ b/vendor/github.com/pion/rtp/error.go
@@ -0,0 +1,21 @@
+package rtp
+
+import (
+ "errors"
+)
+
+var (
+ errHeaderSizeInsufficient = errors.New("RTP header size insufficient")
+ errHeaderSizeInsufficientForExtension = errors.New("RTP header size insufficient for extension")
+ errTooSmall = errors.New("buffer too small")
+ errHeaderExtensionsNotEnabled = errors.New("h.Extension not enabled")
+ errHeaderExtensionNotFound = errors.New("extension not found")
+
+ errRFC8285OneByteHeaderIDRange = errors.New("header extension id must be between 1 and 14 for RFC 5285 one byte extensions")
+ errRFC8285OneByteHeaderSize = errors.New("header extension payload must be 16bytes or less for RFC 5285 one byte extensions")
+
+ errRFC8285TwoByteHeaderIDRange = errors.New("header extension id must be between 1 and 255 for RFC 5285 two byte extensions")
+ errRFC8285TwoByteHeaderSize = errors.New("header extension payload must be 255bytes or less for RFC 5285 two byte extensions")
+
+ errRFC3550HeaderIDRange = errors.New("header extension id must be 0 for non-RFC 5285 extensions")
+)
diff --git a/vendor/github.com/pion/rtp/go.mod b/vendor/github.com/pion/rtp/go.mod
new file mode 100644
index 0000000..412ae63
--- /dev/null
+++ b/vendor/github.com/pion/rtp/go.mod
@@ -0,0 +1,5 @@
+module github.com/pion/rtp
+
+go 1.13
+
+require github.com/pion/randutil v0.1.0
diff --git a/vendor/github.com/pion/rtp/go.sum b/vendor/github.com/pion/rtp/go.sum
new file mode 100644
index 0000000..401b903
--- /dev/null
+++ b/vendor/github.com/pion/rtp/go.sum
@@ -0,0 +1,2 @@
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
diff --git a/vendor/github.com/pion/rtp/packet.go b/vendor/github.com/pion/rtp/packet.go
new file mode 100644
index 0000000..b237b0a
--- /dev/null
+++ b/vendor/github.com/pion/rtp/packet.go
@@ -0,0 +1,490 @@
+package rtp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+// Extension RTP Header extension
+type Extension struct {
+ id uint8
+ payload []byte
+}
+
+// Header represents an RTP packet header
+// NOTE: PayloadOffset is populated by Marshal/Unmarshal and should not be modified
+type Header struct {
+ Version uint8
+ Padding bool
+ Extension bool
+ Marker bool
+ PayloadOffset int
+ PayloadType uint8
+ SequenceNumber uint16
+ Timestamp uint32
+ SSRC uint32
+ CSRC []uint32
+ ExtensionProfile uint16
+ Extensions []Extension
+}
+
+// Packet represents an RTP Packet
+// NOTE: Raw is populated by Marshal/Unmarshal and should not be modified
+type Packet struct {
+ Header
+ Raw []byte
+ Payload []byte
+}
+
+const (
+ headerLength = 4
+ versionShift = 6
+ versionMask = 0x3
+ paddingShift = 5
+ paddingMask = 0x1
+ extensionShift = 4
+ extensionMask = 0x1
+ extensionProfileOneByte = 0xBEDE
+ extensionProfileTwoByte = 0x1000
+ extensionIDReserved = 0xF
+ ccMask = 0xF
+ markerShift = 7
+ markerMask = 0x1
+ ptMask = 0x7F
+ seqNumOffset = 2
+ seqNumLength = 2
+ timestampOffset = 4
+ timestampLength = 4
+ ssrcOffset = 8
+ ssrcLength = 4
+ csrcOffset = 12
+ csrcLength = 4
+)
+
+// String helps with debugging by printing packet information in a readable way
+func (p Packet) String() string {
+ out := "RTP PACKET:\n"
+
+ out += fmt.Sprintf("\tVersion: %v\n", p.Version)
+ out += fmt.Sprintf("\tMarker: %v\n", p.Marker)
+ out += fmt.Sprintf("\tPayload Type: %d\n", p.PayloadType)
+ out += fmt.Sprintf("\tSequence Number: %d\n", p.SequenceNumber)
+ out += fmt.Sprintf("\tTimestamp: %d\n", p.Timestamp)
+ out += fmt.Sprintf("\tSSRC: %d (%x)\n", p.SSRC, p.SSRC)
+ out += fmt.Sprintf("\tPayload Length: %d\n", len(p.Payload))
+
+ return out
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the Header this method is called upon
+func (h *Header) Unmarshal(rawPacket []byte) error { //nolint:gocognit
+ if len(rawPacket) < headerLength {
+ return fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficient, len(rawPacket), headerLength)
+ }
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V=2|P|X| CC |M| PT | sequence number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | timestamp |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | synchronization source (SSRC) identifier |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | contributing source (CSRC) identifiers |
+ * | .... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ h.Version = rawPacket[0] >> versionShift & versionMask
+ h.Padding = (rawPacket[0] >> paddingShift & paddingMask) > 0
+ h.Extension = (rawPacket[0] >> extensionShift & extensionMask) > 0
+ nCSRC := int(rawPacket[0] & ccMask)
+ if cap(h.CSRC) < nCSRC || h.CSRC == nil {
+ h.CSRC = make([]uint32, nCSRC)
+ } else {
+ h.CSRC = h.CSRC[:nCSRC]
+ }
+
+ currOffset := csrcOffset + (nCSRC * csrcLength)
+ if len(rawPacket) < currOffset {
+ return fmt.Errorf("size %d < %d: %w", len(rawPacket), currOffset, errHeaderSizeInsufficient)
+ }
+
+ h.Marker = (rawPacket[1] >> markerShift & markerMask) > 0
+ h.PayloadType = rawPacket[1] & ptMask
+
+ h.SequenceNumber = binary.BigEndian.Uint16(rawPacket[seqNumOffset : seqNumOffset+seqNumLength])
+ h.Timestamp = binary.BigEndian.Uint32(rawPacket[timestampOffset : timestampOffset+timestampLength])
+ h.SSRC = binary.BigEndian.Uint32(rawPacket[ssrcOffset : ssrcOffset+ssrcLength])
+
+ for i := range h.CSRC {
+ offset := csrcOffset + (i * csrcLength)
+ h.CSRC[i] = binary.BigEndian.Uint32(rawPacket[offset:])
+ }
+
+ if h.Extensions != nil {
+ h.Extensions = h.Extensions[:0]
+ }
+
+ if h.Extension {
+ if expected := currOffset + 4; len(rawPacket) < expected {
+ return fmt.Errorf("size %d < %d: %w",
+ len(rawPacket), expected,
+ errHeaderSizeInsufficientForExtension,
+ )
+ }
+
+ h.ExtensionProfile = binary.BigEndian.Uint16(rawPacket[currOffset:])
+ currOffset += 2
+ extensionLength := int(binary.BigEndian.Uint16(rawPacket[currOffset:])) * 4
+ currOffset += 2
+
+ if expected := currOffset + extensionLength; len(rawPacket) < expected {
+ return fmt.Errorf("size %d < %d: %w",
+ len(rawPacket), expected,
+ errHeaderSizeInsufficientForExtension,
+ )
+ }
+
+ switch h.ExtensionProfile {
+ // RFC 8285 RTP One Byte Header Extension
+ case extensionProfileOneByte:
+ end := currOffset + extensionLength
+ for currOffset < end {
+ if rawPacket[currOffset] == 0x00 { // padding
+ currOffset++
+ continue
+ }
+
+ extid := rawPacket[currOffset] >> 4
+ len := int(rawPacket[currOffset]&^0xF0 + 1)
+ currOffset++
+
+ if extid == extensionIDReserved {
+ break
+ }
+
+ extension := Extension{id: extid, payload: rawPacket[currOffset : currOffset+len]}
+ h.Extensions = append(h.Extensions, extension)
+ currOffset += len
+ }
+
+ // RFC 8285 RTP Two Byte Header Extension
+ case extensionProfileTwoByte:
+ end := currOffset + extensionLength
+ for currOffset < end {
+ if rawPacket[currOffset] == 0x00 { // padding
+ currOffset++
+ continue
+ }
+
+ extid := rawPacket[currOffset]
+ currOffset++
+
+ len := int(rawPacket[currOffset])
+ currOffset++
+
+ extension := Extension{id: extid, payload: rawPacket[currOffset : currOffset+len]}
+ h.Extensions = append(h.Extensions, extension)
+ currOffset += len
+ }
+
+ default: // RFC3550 Extension
+ if len(rawPacket) < currOffset+extensionLength {
+ return fmt.Errorf("%w: %d < %d", errHeaderSizeInsufficientForExtension, len(rawPacket), currOffset+extensionLength)
+ }
+
+ extension := Extension{id: 0, payload: rawPacket[currOffset : currOffset+extensionLength]}
+ h.Extensions = append(h.Extensions, extension)
+ currOffset += len(h.Extensions[0].payload)
+ }
+ }
+
+ h.PayloadOffset = currOffset
+
+ return nil
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the Packet this method is called upon
+func (p *Packet) Unmarshal(rawPacket []byte) error {
+ if err := p.Header.Unmarshal(rawPacket); err != nil {
+ return err
+ }
+
+ p.Payload = rawPacket[p.PayloadOffset:]
+ p.Raw = rawPacket
+ return nil
+}
+
+// Marshal serializes the header into bytes.
+func (h *Header) Marshal() (buf []byte, err error) {
+ buf = make([]byte, h.MarshalSize())
+
+ n, err := h.MarshalTo(buf)
+ if err != nil {
+ return nil, err
+ }
+
+ return buf[:n], nil
+}
+
+// MarshalTo serializes the header and writes to the buffer.
+func (h *Header) MarshalTo(buf []byte) (n int, err error) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |V=2|P|X| CC |M| PT | sequence number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | timestamp |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | synchronization source (SSRC) identifier |
+ * +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+ * | contributing source (CSRC) identifiers |
+ * | .... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ size := h.MarshalSize()
+ if size > len(buf) {
+ return 0, io.ErrShortBuffer
+ }
+
+ // The first byte contains the version, padding bit, extension bit, and csrc size
+ buf[0] = (h.Version << versionShift) | uint8(len(h.CSRC))
+ if h.Padding {
+ buf[0] |= 1 << paddingShift
+ }
+
+ if h.Extension {
+ buf[0] |= 1 << extensionShift
+ }
+
+ // The second byte contains the marker bit and payload type.
+ buf[1] = h.PayloadType
+ if h.Marker {
+ buf[1] |= 1 << markerShift
+ }
+
+ binary.BigEndian.PutUint16(buf[2:4], h.SequenceNumber)
+ binary.BigEndian.PutUint32(buf[4:8], h.Timestamp)
+ binary.BigEndian.PutUint32(buf[8:12], h.SSRC)
+
+ n = 12
+ for _, csrc := range h.CSRC {
+ binary.BigEndian.PutUint32(buf[n:n+4], csrc)
+ n += 4
+ }
+
+ if h.Extension {
+ extHeaderPos := n
+ binary.BigEndian.PutUint16(buf[n+0:n+2], h.ExtensionProfile)
+ n += 4
+ startExtensionsPos := n
+
+ switch h.ExtensionProfile {
+ // RFC 8285 RTP One Byte Header Extension
+ case extensionProfileOneByte:
+ for _, extension := range h.Extensions {
+ buf[n] = extension.id<<4 | (uint8(len(extension.payload)) - 1)
+ n++
+ n += copy(buf[n:], extension.payload)
+ }
+ // RFC 8285 RTP Two Byte Header Extension
+ case extensionProfileTwoByte:
+ for _, extension := range h.Extensions {
+ buf[n] = extension.id
+ n++
+ buf[n] = uint8(len(extension.payload))
+ n++
+ n += copy(buf[n:], extension.payload)
+ }
+ default: // RFC3550 Extension
+ extlen := len(h.Extensions[0].payload)
+ if extlen%4 != 0 {
+ // the payload must be in 32-bit words.
+ return 0, io.ErrShortBuffer
+ }
+ n += copy(buf[n:], h.Extensions[0].payload)
+ }
+
+ // calculate extensions size and round to 4 bytes boundaries
+ extSize := n - startExtensionsPos
+ roundedExtSize := ((extSize + 3) / 4) * 4
+
+ binary.BigEndian.PutUint16(buf[extHeaderPos+2:extHeaderPos+4], uint16(roundedExtSize/4))
+
+ // add padding to reach 4 bytes boundaries
+ for i := 0; i < roundedExtSize-extSize; i++ {
+ buf[n] = 0
+ n++
+ }
+ }
+
+ h.PayloadOffset = n
+
+ return n, nil
+}
+
+// MarshalSize returns the size of the header once marshaled.
+func (h *Header) MarshalSize() int {
+ // NOTE: Be careful to match the MarshalTo() method.
+ size := 12 + (len(h.CSRC) * csrcLength)
+
+ if h.Extension {
+ extSize := 4
+
+ switch h.ExtensionProfile {
+ // RFC 8285 RTP One Byte Header Extension
+ case extensionProfileOneByte:
+ for _, extension := range h.Extensions {
+ extSize += 1 + len(extension.payload)
+ }
+ // RFC 8285 RTP Two Byte Header Extension
+ case extensionProfileTwoByte:
+ for _, extension := range h.Extensions {
+ extSize += 2 + len(extension.payload)
+ }
+ default:
+ extSize += len(h.Extensions[0].payload)
+ }
+
+ // extensions size must have 4 bytes boundaries
+ size += ((extSize + 3) / 4) * 4
+ }
+
+ return size
+}
+
+// SetExtension sets an RTP header extension
+func (h *Header) SetExtension(id uint8, payload []byte) error { //nolint:gocognit
+ if h.Extension {
+ switch h.ExtensionProfile {
+ // RFC 8285 RTP One Byte Header Extension
+ case extensionProfileOneByte:
+ if id < 1 || id > 14 {
+ return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderIDRange, id)
+ }
+ if len(payload) > 16 {
+ return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderSize, len(payload))
+ }
+ // RFC 8285 RTP Two Byte Header Extension
+ case extensionProfileTwoByte:
+ if id < 1 || id > 255 {
+ return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id)
+ }
+ if len(payload) > 255 {
+ return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderSize, len(payload))
+ }
+ default: // RFC3550 Extension
+ if id != 0 {
+ return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id)
+ }
+ }
+
+ // Update existing if it exists else add new extension
+ for i, extension := range h.Extensions {
+ if extension.id == id {
+ h.Extensions[i].payload = payload
+ return nil
+ }
+ }
+ h.Extensions = append(h.Extensions, Extension{id: id, payload: payload})
+ return nil
+ }
+
+ // No existing header extensions
+ h.Extension = true
+
+ switch len := len(payload); {
+ case len <= 16:
+ h.ExtensionProfile = extensionProfileOneByte
+ case len > 16 && len < 256:
+ h.ExtensionProfile = extensionProfileTwoByte
+ }
+
+ h.Extensions = append(h.Extensions, Extension{id: id, payload: payload})
+ return nil
+}
+
+// GetExtensionIDs returns an extension id array
+func (h *Header) GetExtensionIDs() []uint8 {
+ if !h.Extension {
+ return nil
+ }
+
+ if len(h.Extensions) == 0 {
+ return nil
+ }
+
+ ids := make([]uint8, 0, len(h.Extensions))
+ for _, extension := range h.Extensions {
+ ids = append(ids, extension.id)
+ }
+ return ids
+}
+
+// GetExtension returns an RTP header extension
+func (h *Header) GetExtension(id uint8) []byte {
+ if !h.Extension {
+ return nil
+ }
+ for _, extension := range h.Extensions {
+ if extension.id == id {
+ return extension.payload
+ }
+ }
+ return nil
+}
+
+// DelExtension Removes an RTP Header extension
+func (h *Header) DelExtension(id uint8) error {
+ if !h.Extension {
+ return errHeaderExtensionsNotEnabled
+ }
+ for i, extension := range h.Extensions {
+ if extension.id == id {
+ h.Extensions = append(h.Extensions[:i], h.Extensions[i+1:]...)
+ return nil
+ }
+ }
+ return errHeaderExtensionNotFound
+}
+
+// Marshal serializes the packet into bytes.
+func (p *Packet) Marshal() (buf []byte, err error) {
+ buf = make([]byte, p.MarshalSize())
+
+ n, err := p.MarshalTo(buf)
+ if err != nil {
+ return nil, err
+ }
+
+ return buf[:n], nil
+}
+
+// MarshalTo serializes the packet and writes to the buffer.
+func (p *Packet) MarshalTo(buf []byte) (n int, err error) {
+ n, err = p.Header.MarshalTo(buf)
+ if err != nil {
+ return 0, err
+ }
+
+ // Make sure the buffer is large enough to hold the packet.
+ if n+len(p.Payload) > len(buf) {
+ return 0, io.ErrShortBuffer
+ }
+
+ m := copy(buf[n:], p.Payload)
+ p.Raw = buf[:n+m]
+
+ return n + m, nil
+}
+
+// MarshalSize returns the size of the packet once marshaled.
+func (p *Packet) MarshalSize() int {
+ return p.Header.MarshalSize() + len(p.Payload)
+}
diff --git a/vendor/github.com/pion/rtp/packetizer.go b/vendor/github.com/pion/rtp/packetizer.go
new file mode 100644
index 0000000..5a8482f
--- /dev/null
+++ b/vendor/github.com/pion/rtp/packetizer.go
@@ -0,0 +1,91 @@
+package rtp
+
+import (
+ "time"
+)
+
+// Payloader payloads a byte array for use as rtp.Packet payloads
+type Payloader interface {
+ Payload(mtu int, payload []byte) [][]byte
+}
+
+// Packetizer packetizes a payload
+type Packetizer interface {
+ Packetize(payload []byte, samples uint32) []*Packet
+ EnableAbsSendTime(value int)
+}
+
+type packetizer struct {
+ MTU int
+ PayloadType uint8
+ SSRC uint32
+ Payloader Payloader
+ Sequencer Sequencer
+ Timestamp uint32
+ ClockRate uint32
+ extensionNumbers struct { // put extension numbers in here. If they're 0, the extension is disabled (0 is not a legal extension number)
+ AbsSendTime int // http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
+ }
+ timegen func() time.Time
+}
+
+// NewPacketizer returns a new instance of a Packetizer for a specific payloader
+func NewPacketizer(mtu int, pt uint8, ssrc uint32, payloader Payloader, sequencer Sequencer, clockRate uint32) Packetizer {
+ return &packetizer{
+ MTU: mtu,
+ PayloadType: pt,
+ SSRC: ssrc,
+ Payloader: payloader,
+ Sequencer: sequencer,
+ Timestamp: globalMathRandomGenerator.Uint32(),
+ ClockRate: clockRate,
+ timegen: time.Now,
+ }
+}
+
+func (p *packetizer) EnableAbsSendTime(value int) {
+ p.extensionNumbers.AbsSendTime = value
+}
+
+// Packetize packetizes the payload of an RTP packet and returns one or more RTP packets
+func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
+ // Guard against an empty payload
+ if len(payload) == 0 {
+ return nil
+ }
+
+ payloads := p.Payloader.Payload(p.MTU-12, payload)
+ packets := make([]*Packet, len(payloads))
+
+ for i, pp := range payloads {
+ packets[i] = &Packet{
+ Header: Header{
+ Version: 2,
+ Padding: false,
+ Extension: false,
+ Marker: i == len(payloads)-1,
+ PayloadType: p.PayloadType,
+ SequenceNumber: p.Sequencer.NextSequenceNumber(),
+ Timestamp: p.Timestamp, // Figure out how to do timestamps
+ SSRC: p.SSRC,
+ },
+ Payload: pp,
+ }
+ }
+ p.Timestamp += samples
+
+ if len(packets) != 0 && p.extensionNumbers.AbsSendTime != 0 {
+ sendTime := NewAbsSendTimeExtension(p.timegen())
+ // apply http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
+ b, err := sendTime.Marshal()
+ if err != nil {
+ return nil // never happens
+ }
+ err = packets[len(packets)-1].SetExtension(uint8(p.extensionNumbers.AbsSendTime), b)
+ if err != nil {
+ return nil // never happens
+ }
+ }
+
+ return packets
+}
diff --git a/vendor/github.com/pion/rtp/partitionheadchecker.go b/vendor/github.com/pion/rtp/partitionheadchecker.go
new file mode 100644
index 0000000..6ec2a76
--- /dev/null
+++ b/vendor/github.com/pion/rtp/partitionheadchecker.go
@@ -0,0 +1,6 @@
+package rtp
+
+// PartitionHeadChecker is the interface that checks whether the packet is keyframe or not
+type PartitionHeadChecker interface {
+ IsPartitionHead([]byte) bool
+}
diff --git a/vendor/github.com/pion/rtp/rand.go b/vendor/github.com/pion/rtp/rand.go
new file mode 100644
index 0000000..ee85523
--- /dev/null
+++ b/vendor/github.com/pion/rtp/rand.go
@@ -0,0 +1,8 @@
+package rtp
+
+import (
+ "github.com/pion/randutil"
+)
+
+// Use global random generator to properly seed by crypto grade random.
+var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
diff --git a/vendor/github.com/pion/rtp/renovate.json b/vendor/github.com/pion/rtp/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/rtp/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/rtp/rtp.go b/vendor/github.com/pion/rtp/rtp.go
new file mode 100644
index 0000000..b66b2e4
--- /dev/null
+++ b/vendor/github.com/pion/rtp/rtp.go
@@ -0,0 +1,2 @@
+// Package rtp provides RTP packetizer and depacketizer
+package rtp
diff --git a/vendor/github.com/pion/rtp/sequencer.go b/vendor/github.com/pion/rtp/sequencer.go
new file mode 100644
index 0000000..2b4a507
--- /dev/null
+++ b/vendor/github.com/pion/rtp/sequencer.go
@@ -0,0 +1,57 @@
+package rtp
+
+import (
+ "math"
+ "sync"
+)
+
+// Sequencer generates sequential sequence numbers for building RTP packets
+type Sequencer interface {
+ NextSequenceNumber() uint16
+ RollOverCount() uint64
+}
+
+// NewRandomSequencer returns a new sequencer starting from a random sequence
+// number
+func NewRandomSequencer() Sequencer {
+ return &sequencer{
+ sequenceNumber: uint16(globalMathRandomGenerator.Intn(math.MaxUint16)),
+ }
+}
+
+// NewFixedSequencer returns a new sequencer starting from a specific
+// sequence number
+func NewFixedSequencer(s uint16) Sequencer {
+ return &sequencer{
+ sequenceNumber: s - 1, // -1 because the first sequence number prepends 1
+ }
+}
+
+type sequencer struct {
+ sequenceNumber uint16
+ rollOverCount uint64
+ mutex sync.Mutex
+}
+
+// NextSequenceNumber increment and returns a new sequence number for
+// building RTP packets
+func (s *sequencer) NextSequenceNumber() uint16 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ s.sequenceNumber++
+ if s.sequenceNumber == 0 {
+ s.rollOverCount++
+ }
+
+ return s.sequenceNumber
+}
+
+// RollOverCount returns the amount of times the 16bit sequence number
+// has wrapped
+func (s *sequencer) RollOverCount() uint64 {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+
+ return s.rollOverCount
+}
diff --git a/vendor/github.com/pion/rtp/transportccextension.go b/vendor/github.com/pion/rtp/transportccextension.go
new file mode 100644
index 0000000..f9ffe4e
--- /dev/null
+++ b/vendor/github.com/pion/rtp/transportccextension.go
@@ -0,0 +1,39 @@
+package rtp
+
+import (
+ "encoding/binary"
+)
+
+const (
+ // transport-wide sequence
+ transportCCExtensionSize = 2
+)
+
+// TransportCCExtension is a extension payload format in
+// https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | 0xBE | 0xDE | length=1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | ID | L=1 |transport-wide sequence number | zero padding |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type TransportCCExtension struct {
+ TransportSequence uint16
+}
+
+// Marshal serializes the members to buffer
+func (t *TransportCCExtension) Marshal() ([]byte, error) {
+ buf := make([]byte, transportCCExtensionSize)
+ binary.BigEndian.PutUint16(buf[0:2], t.TransportSequence)
+ return buf, nil
+}
+
+// Unmarshal parses the passed byte slice and stores the result in the members
+func (t *TransportCCExtension) Unmarshal(rawData []byte) error {
+ if len(rawData) < transportCCExtensionSize {
+ return errTooSmall
+ }
+ t.TransportSequence = binary.BigEndian.Uint16(rawData[0:2])
+ return nil
+}
diff --git a/vendor/github.com/pion/sctp/.gitignore b/vendor/github.com/pion/sctp/.gitignore
new file mode 100644
index 0000000..d39fb86
--- /dev/null
+++ b/vendor/github.com/pion/sctp/.gitignore
@@ -0,0 +1 @@
+*.sw[poe]
diff --git a/vendor/github.com/pion/sctp/.golangci.yml b/vendor/github.com/pion/sctp/.golangci.yml
new file mode 100644
index 0000000..4213697
--- /dev/null
+++ b/vendor/github.com/pion/sctp/.golangci.yml
@@ -0,0 +1,82 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/sctp/DESIGN.md b/vendor/github.com/pion/sctp/DESIGN.md
new file mode 100644
index 0000000..02ac161
--- /dev/null
+++ b/vendor/github.com/pion/sctp/DESIGN.md
@@ -0,0 +1,20 @@
+<h1 align="center">
+ Design
+</h1>
+
+### Portable
+Pion SCTP is written in Go and extremely portable. Anywhere Golang runs, Pion SCTP should work as well! Instead of dealing with complicated
+cross-compiling of multiple libraries, you now can run anywhere with one `go build`
+
+### Simple API
+The API is based on an io.ReadWriteCloser.
+
+### Readable
+If code comes from an RFC we try to make sure everything is commented with a link to the spec.
+This makes learning and debugging easier, this library was written to also serve as a guide for others.
+
+### Tested
+Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on.
+
+### Shared libraries
+Every pion product is built using shared libraries, allowing others to review and reuse our libraries.
diff --git a/vendor/github.com/pion/sctp/LICENSE b/vendor/github.com/pion/sctp/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/sctp/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/sctp/README.md b/vendor/github.com/pion/sctp/README.md
new file mode 100644
index 0000000..18b5693
--- /dev/null
+++ b/vendor/github.com/pion/sctp/README.md
@@ -0,0 +1,54 @@
+<h1 align="center">
+ <br>
+ Pion SCTP
+ <br>
+</h1>
+<h4 align="center">A Go implementation of SCTP</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-sctp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion SCTP"></a>
+ <!--<a href="https://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/badge.svg" alt="Sourcegraph Widget"></a>-->
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/sctp"><img src="https://travis-ci.org/pion/sctp.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/sctp"><img src="https://godoc.org/github.com/pion/sctp?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/sctp"><img src="https://codecov.io/gh/pion/sctp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/sctp"><img src="https://goreportcard.com/badge/github.com/pion/sctp" alt="Go Report Card"></a>
+ <!--<a href="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>-->
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *Public API, Initialization*
+* [Konstantin Itskov](https://github.com/trivigy) - *Fix documentation*
+* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter*
+* [Ronan J](https://github.com/ronanj) - *Fix PPID*
+* [Michael MacDonald](https://github.com/mjmac) - *Fix races*
+* [Yutaka Takeda](https://github.com/enobufs) - *PR-SCTP, Retransmissions, Congestion Control*
+* [Antoine Baché](https://github.com/Antonito) - *SCTP Profiling*
+* [Cecylia Bocovich](https://github.com/cohosh) - *Fix SCTP reads*
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Lukas Herman](https://github.com/lherman-cs)
+* [Luke Curley](https://github.com/kixelated) - *Performance*
+* [Aaron France](https://github.com/AeroNotix)
+* [ZHENK](https://github.com/scorpionknifes)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/sctp/ack_timer.go b/vendor/github.com/pion/sctp/ack_timer.go
new file mode 100644
index 0000000..ba23d54
--- /dev/null
+++ b/vendor/github.com/pion/sctp/ack_timer.go
@@ -0,0 +1,105 @@
+package sctp
+
+import (
+ "sync"
+ "time"
+)
+
+const (
+ ackInterval time.Duration = 200 * time.Millisecond
+)
+
+// ackTimerObserver is the inteface to an ack timer observer.
+type ackTimerObserver interface {
+ onAckTimeout()
+}
+
+// ackTimer provides the retnransmission timer conforms with RFC 4960 Sec 6.3.1
+type ackTimer struct {
+ observer ackTimerObserver
+ interval time.Duration
+ stopFunc stopAckTimerLoop
+ closed bool
+ mutex sync.RWMutex
+}
+
+type stopAckTimerLoop func()
+
+// newAckTimer creates a new acknowledgement timer used to enable delayed ack.
+func newAckTimer(observer ackTimerObserver) *ackTimer {
+ return &ackTimer{
+ observer: observer,
+ interval: ackInterval,
+ }
+}
+
+// start starts the timer.
+func (t *ackTimer) start() bool {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ // this timer is already closed
+ if t.closed {
+ return false
+ }
+
+ // this is a noop if the timer is already running
+ if t.stopFunc != nil {
+ return false
+ }
+
+ cancelCh := make(chan struct{})
+
+ go func() {
+ timer := time.NewTimer(t.interval)
+
+ select {
+ case <-timer.C:
+ t.stop()
+ t.observer.onAckTimeout()
+ case <-cancelCh:
+ timer.Stop()
+ }
+ }()
+
+ t.stopFunc = func() {
+ close(cancelCh)
+ }
+
+ return true
+}
+
+// stops the timer. this is similar to stop() but subsequent start() call
+// will fail (the timer is no longer usable)
+func (t *ackTimer) stop() {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ if t.stopFunc != nil {
+ t.stopFunc()
+ t.stopFunc = nil
+ }
+}
+
+// closes the timer. this is similar to stop() but subsequent start() call
+// will fail (the timer is no longer usable)
+func (t *ackTimer) close() {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ if t.stopFunc != nil {
+ t.stopFunc()
+ t.stopFunc = nil
+ }
+
+ t.closed = true
+}
+
+// isRunning tests if the timer is running.
+// Debug purpose only
+func (t *ackTimer) isRunning() bool {
+ t.mutex.RLock()
+ defer t.mutex.RUnlock()
+
+ return (t.stopFunc != nil)
+}
diff --git a/vendor/github.com/pion/sctp/association.go b/vendor/github.com/pion/sctp/association.go
new file mode 100644
index 0000000..1393cb8
--- /dev/null
+++ b/vendor/github.com/pion/sctp/association.go
@@ -0,0 +1,2241 @@
+package sctp
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "math"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/randutil"
+ "github.com/pkg/errors"
+)
+
+// Use global random generator to properly seed by crypto grade random.
+var (
+ globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
+ errChunk = errors.New("Abort chunk, with following errors")
+)
+
+const (
+ receiveMTU uint32 = 8192 // MTU for inbound packet (from DTLS)
+ initialMTU uint32 = 1228 // initial MTU for outgoing packets (to DTLS)
+ initialRecvBufSize uint32 = 1024 * 1024
+ commonHeaderSize uint32 = 12
+ dataChunkHeaderSize uint32 = 16
+ defaultMaxMessageSize uint32 = 65536
+)
+
+// association state enums
+const (
+ closed uint32 = iota
+ cookieWait
+ cookieEchoed
+ established
+ shutdownAckSent
+ shutdownPending
+ shutdownReceived
+ shutdownSent
+)
+
+// retransmission timer IDs
+const (
+ timerT1Init int = iota
+ timerT1Cookie
+ timerT3RTX
+ timerReconfig
+)
+
+// ack mode (for testing)
+const (
+ ackModeNormal int = iota
+ ackModeNoDelay
+ ackModeAlwaysDelay
+)
+
+// ack transmission state
+const (
+ ackStateIdle int = iota // ack timer is off
+ ackStateImmediate // ack timer is on (ack is being delayed)
+ ackStateDelay // will send ack immediately
+)
+
+// other constants
+const (
+ acceptChSize = 16
+)
+
+func getAssociationStateString(a uint32) string {
+ switch a {
+ case closed:
+ return "Closed"
+ case cookieWait:
+ return "CookieWait"
+ case cookieEchoed:
+ return "CookieEchoed"
+ case established:
+ return "Established"
+ case shutdownPending:
+ return "ShutdownPending"
+ case shutdownSent:
+ return "ShutdownSent"
+ case shutdownReceived:
+ return "ShutdownReceived"
+ case shutdownAckSent:
+ return "ShutdownAckSent"
+ default:
+ return fmt.Sprintf("Invalid association state %d", a)
+ }
+}
+
+// Association represents an SCTP association
+// 13.2. Parameters Necessary per Association (i.e., the TCB)
+// Peer : Tag value to be sent in every packet and is received
+// Verification: in the INIT or INIT ACK chunk.
+// Tag :
+//
+// My : Tag expected in every inbound packet and sent in the
+// Verification: INIT or INIT ACK chunk.
+//
+// Tag :
+// State : A state variable indicating what state the association
+// : is in, i.e., COOKIE-WAIT, COOKIE-ECHOED, ESTABLISHED,
+// : SHUTDOWN-PENDING, SHUTDOWN-SENT, SHUTDOWN-RECEIVED,
+// : SHUTDOWN-ACK-SENT.
+//
+// Note: No "CLOSED" state is illustrated since if a
+// association is "CLOSED" its TCB SHOULD be removed.
+type Association struct {
+ bytesReceived uint64
+ bytesSent uint64
+
+ lock sync.RWMutex
+
+ netConn net.Conn
+
+ peerVerificationTag uint32
+ myVerificationTag uint32
+ state uint32
+ myNextTSN uint32 // nextTSN
+ peerLastTSN uint32 // lastRcvdTSN
+ minTSN2MeasureRTT uint32 // for RTT measurement
+ willSendForwardTSN bool
+ willRetransmitFast bool
+ willRetransmitReconfig bool
+
+ // Reconfig
+ myNextRSN uint32
+ reconfigs map[uint32]*chunkReconfig
+ reconfigRequests map[uint32]*paramOutgoingResetRequest
+
+ // Non-RFC internal data
+ sourcePort uint16
+ destinationPort uint16
+ myMaxNumInboundStreams uint16
+ myMaxNumOutboundStreams uint16
+ myCookie *paramStateCookie
+ payloadQueue *payloadQueue
+ inflightQueue *payloadQueue
+ pendingQueue *pendingQueue
+ controlQueue *controlQueue
+ mtu uint32
+ maxPayloadSize uint32 // max DATA chunk payload size
+ cumulativeTSNAckPoint uint32
+ advancedPeerTSNAckPoint uint32
+ useForwardTSN bool
+
+ // Congestion control parameters
+ maxReceiveBufferSize uint32
+ maxMessageSize uint32
+ cwnd uint32 // my congestion window size
+ rwnd uint32 // calculated peer's receiver windows size
+ ssthresh uint32 // slow start threshold
+ partialBytesAcked uint32
+ inFastRecovery bool
+ fastRecoverExitPoint uint32
+
+ // RTX & Ack timer
+ rtoMgr *rtoManager
+ t1Init *rtxTimer
+ t1Cookie *rtxTimer
+ t3RTX *rtxTimer
+ tReconfig *rtxTimer
+ ackTimer *ackTimer
+
+ // Chunks stored for retransmission
+ storedInit *chunkInit
+ storedCookieEcho *chunkCookieEcho
+
+ streams map[uint16]*Stream
+ acceptCh chan *Stream
+ readLoopCloseCh chan struct{}
+ awakeWriteLoopCh chan struct{}
+ closeWriteLoopCh chan struct{}
+ handshakeCompletedCh chan error
+
+ closeWriteLoopOnce sync.Once
+
+ // local error
+ silentError error
+
+ ackState int
+ ackMode int // for testing
+
+ // stats
+ stats *associationStats
+
+ // per inbound packet context
+ delayedAckTriggered bool
+ immediateAckTriggered bool
+
+ name string
+ log logging.LeveledLogger
+}
+
+// Config collects the arguments to createAssociation construction into
+// a single structure
+type Config struct {
+ NetConn net.Conn
+ MaxReceiveBufferSize uint32
+ MaxMessageSize uint32
+ LoggerFactory logging.LoggerFactory
+}
+
+// Server accepts a SCTP stream over a conn
+func Server(config Config) (*Association, error) {
+ a := createAssociation(config)
+ a.init(false)
+
+ select {
+ case err := <-a.handshakeCompletedCh:
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+ case <-a.readLoopCloseCh:
+ return nil, errors.Errorf("association closed before connecting")
+ }
+}
+
+// Client opens a SCTP stream over a conn
+func Client(config Config) (*Association, error) {
+ a := createAssociation(config)
+ a.init(true)
+
+ select {
+ case err := <-a.handshakeCompletedCh:
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+ case <-a.readLoopCloseCh:
+ return nil, errors.Errorf("association closed before connecting")
+ }
+}
+
+func createAssociation(config Config) *Association {
+ var maxReceiveBufferSize uint32
+ if config.MaxReceiveBufferSize == 0 {
+ maxReceiveBufferSize = initialRecvBufSize
+ } else {
+ maxReceiveBufferSize = config.MaxReceiveBufferSize
+ }
+
+ var maxMessageSize uint32
+ if config.MaxMessageSize == 0 {
+ maxMessageSize = defaultMaxMessageSize
+ } else {
+ maxMessageSize = config.MaxMessageSize
+ }
+
+ tsn := globalMathRandomGenerator.Uint32()
+ a := &Association{
+ netConn: config.NetConn,
+ maxReceiveBufferSize: maxReceiveBufferSize,
+ maxMessageSize: maxMessageSize,
+ myMaxNumOutboundStreams: math.MaxUint16,
+ myMaxNumInboundStreams: math.MaxUint16,
+ payloadQueue: newPayloadQueue(),
+ inflightQueue: newPayloadQueue(),
+ pendingQueue: newPendingQueue(),
+ controlQueue: newControlQueue(),
+ mtu: initialMTU,
+ maxPayloadSize: initialMTU - (commonHeaderSize + dataChunkHeaderSize),
+ myVerificationTag: globalMathRandomGenerator.Uint32(),
+ myNextTSN: tsn,
+ myNextRSN: tsn,
+ minTSN2MeasureRTT: tsn,
+ state: closed,
+ rtoMgr: newRTOManager(),
+ streams: map[uint16]*Stream{},
+ reconfigs: map[uint32]*chunkReconfig{},
+ reconfigRequests: map[uint32]*paramOutgoingResetRequest{},
+ acceptCh: make(chan *Stream, acceptChSize),
+ readLoopCloseCh: make(chan struct{}),
+ awakeWriteLoopCh: make(chan struct{}, 1),
+ closeWriteLoopCh: make(chan struct{}),
+ handshakeCompletedCh: make(chan error),
+ cumulativeTSNAckPoint: tsn - 1,
+ advancedPeerTSNAckPoint: tsn - 1,
+ silentError: errors.Errorf("silently discard"),
+ stats: &associationStats{},
+ log: config.LoggerFactory.NewLogger("sctp"),
+ }
+
+ a.name = fmt.Sprintf("%p", a)
+
+ // RFC 4690 Sec 7.2.1
+ // o The initial cwnd before DATA transmission or after a sufficiently
+ // long idle period MUST be set to min(4*MTU, max (2*MTU, 4380
+ // bytes)).
+ a.cwnd = min32(4*a.mtu, max32(2*a.mtu, 4380))
+ a.log.Tracef("[%s] updated cwnd=%d ssthresh=%d inflight=%d (INI)",
+ a.name, a.cwnd, a.ssthresh, a.inflightQueue.getNumBytes())
+
+ a.t1Init = newRTXTimer(timerT1Init, a, maxInitRetrans)
+ a.t1Cookie = newRTXTimer(timerT1Cookie, a, maxInitRetrans)
+ a.t3RTX = newRTXTimer(timerT3RTX, a, noMaxRetrans) // retransmit forever
+ a.tReconfig = newRTXTimer(timerReconfig, a, noMaxRetrans) // retransmit forever
+ a.ackTimer = newAckTimer(a)
+
+ return a
+}
+
+func (a *Association) init(isClient bool) {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ go a.readLoop()
+ go a.writeLoop()
+
+ if isClient {
+ a.setState(cookieWait)
+ init := &chunkInit{}
+ init.initialTSN = a.myNextTSN
+ init.numOutboundStreams = a.myMaxNumOutboundStreams
+ init.numInboundStreams = a.myMaxNumInboundStreams
+ init.initiateTag = a.myVerificationTag
+ init.advertisedReceiverWindowCredit = a.maxReceiveBufferSize
+ setSupportedExtensions(&init.chunkInitCommon)
+ a.storedInit = init
+
+ err := a.sendInit()
+ if err != nil {
+ a.log.Errorf("[%s] failed to send init: %s", a.name, err.Error())
+ }
+
+ a.t1Init.start(a.rtoMgr.getRTO())
+ }
+}
+
+// caller must hold a.lock
+func (a *Association) sendInit() error {
+ a.log.Debugf("[%s] sending INIT", a.name)
+ if a.storedInit == nil {
+ return errors.Errorf("the init not stored to send")
+ }
+
+ outbound := &packet{}
+ outbound.verificationTag = a.peerVerificationTag
+ a.sourcePort = 5000 // Spec??
+ a.destinationPort = 5000 // Spec??
+ outbound.sourcePort = a.sourcePort
+ outbound.destinationPort = a.destinationPort
+
+ outbound.chunks = []chunk{a.storedInit}
+
+ a.controlQueue.push(outbound)
+ a.awakeWriteLoop()
+
+ return nil
+}
+
+// caller must hold a.lock
+func (a *Association) sendCookieEcho() error {
+ if a.storedCookieEcho == nil {
+ return errors.Errorf("cookieEcho not stored to send")
+ }
+
+ a.log.Debugf("[%s] sending COOKIE-ECHO", a.name)
+
+ outbound := &packet{}
+ outbound.verificationTag = a.peerVerificationTag
+ outbound.sourcePort = a.sourcePort
+ outbound.destinationPort = a.destinationPort
+ outbound.chunks = []chunk{a.storedCookieEcho}
+
+ a.controlQueue.push(outbound)
+ a.awakeWriteLoop()
+
+ return nil
+}
+
+// Close ends the SCTP Association and cleans up any state
+func (a *Association) Close() error {
+ a.log.Debugf("[%s] closing association..", a.name)
+
+ a.setState(closed)
+
+ err := a.netConn.Close()
+
+ a.closeAllTimers()
+
+ // awake writeLoop to exit
+ a.closeWriteLoopOnce.Do(func() { close(a.closeWriteLoopCh) })
+
+ // Wait for readLoop to end
+ <-a.readLoopCloseCh
+
+ a.log.Debugf("[%s] association closed", a.name)
+ a.log.Debugf("[%s] stats nDATAs (in) : %d", a.name, a.stats.getNumDATAs())
+ a.log.Debugf("[%s] stats nSACKs (in) : %d", a.name, a.stats.getNumSACKs())
+ a.log.Debugf("[%s] stats nT3Timeouts : %d", a.name, a.stats.getNumT3Timeouts())
+ a.log.Debugf("[%s] stats nAckTimeouts: %d", a.name, a.stats.getNumAckTimeouts())
+ a.log.Debugf("[%s] stats nFastRetrans: %d", a.name, a.stats.getNumFastRetrans())
+ return err
+}
+
+func (a *Association) closeAllTimers() {
+ // Close all retransmission & ack timers
+ a.t1Init.close()
+ a.t1Cookie.close()
+ a.t3RTX.close()
+ a.tReconfig.close()
+ a.ackTimer.close()
+}
+
+func (a *Association) readLoop() {
+ var closeErr error
+ defer func() {
+ // also stop writeLoop, otherwise writeLoop can be leaked
+ // if connection is lost when there is no writing packet.
+ a.closeWriteLoopOnce.Do(func() { close(a.closeWriteLoopCh) })
+
+ a.lock.Lock()
+ for _, s := range a.streams {
+ a.unregisterStream(s, closeErr)
+ }
+ a.lock.Unlock()
+ close(a.acceptCh)
+ close(a.readLoopCloseCh)
+ }()
+
+ a.log.Debugf("[%s] readLoop entered", a.name)
+ buffer := make([]byte, receiveMTU)
+
+ for {
+ n, err := a.netConn.Read(buffer)
+ if err != nil {
+ closeErr = err
+ break
+ }
+ // Make a buffer sized to what we read, then copy the data we
+ // read from the underlying transport. We do this because the
+ // user data is passed to the reassembly queue without
+ // copying.
+ inbound := make([]byte, n)
+ copy(inbound, buffer[:n])
+ atomic.AddUint64(&a.bytesReceived, uint64(n))
+ if err = a.handleInbound(inbound); err != nil {
+ closeErr = err
+ break
+ }
+ }
+
+ a.log.Debugf("[%s] readLoop exited %s", a.name, closeErr)
+}
+
+func (a *Association) writeLoop() {
+ a.log.Debugf("[%s] writeLoop entered", a.name)
+
+loop:
+ for {
+ rawPackets := a.gatherOutbound()
+
+ for _, raw := range rawPackets {
+ _, err := a.netConn.Write(raw)
+ if err != nil {
+ if err != io.EOF {
+ a.log.Warnf("[%s] failed to write packets on netConn: %v", a.name, err)
+ }
+ a.log.Debugf("[%s] writeLoop ended", a.name)
+ break loop
+ }
+ atomic.AddUint64(&a.bytesSent, uint64(len(raw)))
+ }
+
+ select {
+ case <-a.awakeWriteLoopCh:
+ case <-a.closeWriteLoopCh:
+ break loop
+ }
+ }
+
+ a.setState(closed)
+ a.closeAllTimers()
+
+ a.log.Debugf("[%s] writeLoop exited", a.name)
+}
+
+func (a *Association) awakeWriteLoop() {
+ select {
+ case a.awakeWriteLoopCh <- struct{}{}:
+ default:
+ }
+}
+
+// unregisterStream un-registers a stream from the association
+// The caller should hold the association write lock.
+func (a *Association) unregisterStream(s *Stream, err error) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ delete(a.streams, s.streamIdentifier)
+ s.readErr = err
+ s.readNotifier.Broadcast()
+}
+
+// handleInbound parses incoming raw packets
+func (a *Association) handleInbound(raw []byte) error {
+ p := &packet{}
+ if err := p.unmarshal(raw); err != nil {
+ a.log.Warnf("[%s] unable to parse SCTP packet %s", a.name, err)
+ return nil
+ }
+
+ if err := checkPacket(p); err != nil {
+ a.log.Warnf("[%s] failed validating packet %s", a.name, err)
+ return nil
+ }
+
+ a.handleChunkStart()
+
+ for _, c := range p.chunks {
+ if err := a.handleChunk(p, c); err != nil {
+ return err
+ }
+ }
+
+ a.handleChunkEnd()
+
+ return nil
+}
+
+// The caller should hold the lock
+func (a *Association) gatherOutboundDataAndReconfigPackets(rawPackets [][]byte) [][]byte {
+ for _, p := range a.getDataPacketsToRetransmit() {
+ raw, err := p.marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a DATA packet to be retransmitted", a.name)
+ continue
+ }
+ rawPackets = append(rawPackets, raw)
+ }
+
+ // Pop unsent data chunks from the pending queue to send as much as
+ // cwnd and rwnd allow.
+ chunks, sisToReset := a.popPendingDataChunksToSend()
+ if len(chunks) > 0 {
+ // Start timer. (noop if already started)
+ a.log.Tracef("[%s] T3-rtx timer start (pt1)", a.name)
+ a.t3RTX.start(a.rtoMgr.getRTO())
+ for _, p := range a.bundleDataChunksIntoPackets(chunks) {
+ raw, err := p.marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a DATA packet", a.name)
+ continue
+ }
+ rawPackets = append(rawPackets, raw)
+ }
+ }
+
+ if len(sisToReset) > 0 || a.willRetransmitReconfig {
+ if a.willRetransmitReconfig {
+ a.willRetransmitReconfig = false
+ a.log.Debugf("[%s] retransmit %d RECONFIG chunk(s)", a.name, len(a.reconfigs))
+ for _, c := range a.reconfigs {
+ p := a.createPacket([]chunk{c})
+ raw, err := p.marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a RECONFIG packet to be retransmitted", a.name)
+ } else {
+ rawPackets = append(rawPackets, raw)
+ }
+ }
+ }
+
+ if len(sisToReset) > 0 {
+ rsn := a.generateNextRSN()
+ tsn := a.myNextTSN - 1
+ c := &chunkReconfig{
+ paramA: &paramOutgoingResetRequest{
+ reconfigRequestSequenceNumber: rsn,
+ senderLastTSN: tsn,
+ streamIdentifiers: sisToReset,
+ },
+ }
+ a.reconfigs[rsn] = c // store in the map for retransmission
+ a.log.Debugf("[%s] sending RECONFIG: rsn=%d tsn=%d streams=%v",
+ a.name, rsn, a.myNextTSN-1, sisToReset)
+ p := a.createPacket([]chunk{c})
+ raw, err := p.marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a RECONFIG packet to be transmitted", a.name)
+ } else {
+ rawPackets = append(rawPackets, raw)
+ }
+ }
+
+ if len(a.reconfigs) > 0 {
+ a.tReconfig.start(a.rtoMgr.getRTO())
+ }
+ }
+
+ return rawPackets
+}
+
+// The caller should hold the lock
+func (a *Association) gatherOutboundFrastRetransmissionPackets(rawPackets [][]byte) [][]byte {
+ if a.willRetransmitFast {
+ a.willRetransmitFast = false
+
+ toFastRetrans := []chunk{}
+ fastRetransSize := commonHeaderSize
+
+ for i := 0; ; i++ {
+ c, ok := a.inflightQueue.get(a.cumulativeTSNAckPoint + uint32(i) + 1)
+ if !ok {
+ break // end of pending data
+ }
+
+ if c.acked || c.abandoned() {
+ continue
+ }
+
+ if c.nSent > 1 || c.missIndicator < 3 {
+ continue
+ }
+
+ // RFC 4960 Sec 7.2.4 Fast Retransmit on Gap Reports
+ // 3) Determine how many of the earliest (i.e., lowest TSN) DATA chunks
+ // marked for retransmission will fit into a single packet, subject
+ // to constraint of the path MTU of the destination transport
+ // address to which the packet is being sent. Call this value K.
+ // Retransmit those K DATA chunks in a single packet. When a Fast
+ // Retransmit is being performed, the sender SHOULD ignore the value
+ // of cwnd and SHOULD NOT delay retransmission for this single
+ // packet.
+
+ dataChunkSize := dataChunkHeaderSize + uint32(len(c.userData))
+ if a.mtu < fastRetransSize+dataChunkSize {
+ break
+ }
+
+ fastRetransSize += dataChunkSize
+ a.stats.incFastRetrans()
+ c.nSent++
+ a.checkPartialReliabilityStatus(c)
+ toFastRetrans = append(toFastRetrans, c)
+ a.log.Tracef("[%s] fast-retransmit: tsn=%d sent=%d htna=%d",
+ a.name, c.tsn, c.nSent, a.fastRecoverExitPoint)
+ }
+
+ if len(toFastRetrans) > 0 {
+ raw, err := a.createPacket(toFastRetrans).marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a DATA packet to be fast-retransmitted", a.name)
+ } else {
+ rawPackets = append(rawPackets, raw)
+ }
+ }
+ }
+
+ return rawPackets
+}
+
+// The caller should hold the lock
+func (a *Association) gatherOutboundSackPackets(rawPackets [][]byte) [][]byte {
+ if a.ackState == ackStateImmediate {
+ a.ackState = ackStateIdle
+ sack := a.createSelectiveAckChunk()
+ a.log.Debugf("[%s] sending SACK: %s", a.name, sack.String())
+ raw, err := a.createPacket([]chunk{sack}).marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a SACK packet", a.name)
+ } else {
+ rawPackets = append(rawPackets, raw)
+ }
+ }
+
+ return rawPackets
+}
+
+// The caller should hold the lock
+func (a *Association) gatherOutboundForwardTSNPackets(rawPackets [][]byte) [][]byte {
+ if a.willSendForwardTSN {
+ a.willSendForwardTSN = false
+ if sna32GT(a.advancedPeerTSNAckPoint, a.cumulativeTSNAckPoint) {
+ fwdtsn := a.createForwardTSN()
+ raw, err := a.createPacket([]chunk{fwdtsn}).marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a Forward TSN packet", a.name)
+ } else {
+ rawPackets = append(rawPackets, raw)
+ }
+ }
+ }
+
+ return rawPackets
+}
+
+// gatherOutbound gathers outgoing packets
+func (a *Association) gatherOutbound() [][]byte {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ rawPackets := [][]byte{}
+
+ if a.controlQueue.size() > 0 {
+ for _, p := range a.controlQueue.popAll() {
+ raw, err := p.marshal()
+ if err != nil {
+ a.log.Warnf("[%s] failed to serialize a control packet", a.name)
+ continue
+ }
+ rawPackets = append(rawPackets, raw)
+ }
+ }
+
+ state := a.getState()
+
+ if state == established {
+ rawPackets = a.gatherOutboundDataAndReconfigPackets(rawPackets)
+ rawPackets = a.gatherOutboundFrastRetransmissionPackets(rawPackets)
+ rawPackets = a.gatherOutboundSackPackets(rawPackets)
+ rawPackets = a.gatherOutboundForwardTSNPackets(rawPackets)
+ }
+
+ return rawPackets
+}
+
+func checkPacket(p *packet) error {
+ // All packets must adhere to these rules
+
+ // This is the SCTP sender's port number. It can be used by the
+ // receiver in combination with the source IP address, the SCTP
+ // destination port, and possibly the destination IP address to
+ // identify the association to which this packet belongs. The port
+ // number 0 MUST NOT be used.
+ if p.sourcePort == 0 {
+ return errors.Errorf("sctp packet must not have a source port of 0")
+ }
+
+ // This is the SCTP port number to which this packet is destined.
+ // The receiving host will use this port number to de-multiplex the
+ // SCTP packet to the correct receiving endpoint/application. The
+ // port number 0 MUST NOT be used.
+ if p.destinationPort == 0 {
+ return errors.Errorf("sctp packet must not have a destination port of 0")
+ }
+
+ // Check values on the packet that are specific to a particular chunk type
+ for _, c := range p.chunks {
+ switch c.(type) { // nolint:gocritic
+ case *chunkInit:
+ // An INIT or INIT ACK chunk MUST NOT be bundled with any other chunk.
+ // They MUST be the only chunks present in the SCTP packets that carry
+ // them.
+ if len(p.chunks) != 1 {
+ return errors.Errorf("init chunk must not be bundled with any other chunk")
+ }
+
+ // A packet containing an INIT chunk MUST have a zero Verification
+ // Tag.
+ if p.verificationTag != 0 {
+ return errors.Errorf("init chunk expects a verification tag of 0 on the packet when out-of-the-blue")
+ }
+ }
+ }
+
+ return nil
+}
+
+func min16(a, b uint16) uint16 {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func max32(a, b uint32) uint32 {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func min32(a, b uint32) uint32 {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+// setState atomically sets the state of the Association.
+// The caller should hold the lock.
+func (a *Association) setState(newState uint32) {
+ oldState := atomic.SwapUint32(&a.state, newState)
+ if newState != oldState {
+ a.log.Debugf("[%s] state change: '%s' => '%s'",
+ a.name,
+ getAssociationStateString(oldState),
+ getAssociationStateString(newState))
+ }
+}
+
+// getState atomically returns the state of the Association.
+func (a *Association) getState() uint32 {
+ return atomic.LoadUint32(&a.state)
+}
+
+// BytesSent returns the number of bytes sent
+func (a *Association) BytesSent() uint64 {
+ return atomic.LoadUint64(&a.bytesSent)
+}
+
+// BytesReceived returns the number of bytes received
+func (a *Association) BytesReceived() uint64 {
+ return atomic.LoadUint64(&a.bytesReceived)
+}
+
+func setSupportedExtensions(init *chunkInitCommon) {
+ // nolint:godox
+ // TODO RFC5061 https://tools.ietf.org/html/rfc6525#section-5.2
+ // An implementation supporting this (Supported Extensions Parameter)
+ // extension MUST list the ASCONF, the ASCONF-ACK, and the AUTH chunks
+ // in its INIT and INIT-ACK parameters.
+ init.params = append(init.params, &paramSupportedExtensions{
+ ChunkTypes: []chunkType{ctReconfig, ctForwardTSN},
+ })
+}
+
+// The caller should hold the lock.
+func (a *Association) handleInit(p *packet, i *chunkInit) ([]*packet, error) {
+ state := a.getState()
+ a.log.Debugf("[%s] chunkInit received in state '%s'", a.name, getAssociationStateString(state))
+
+ // https://tools.ietf.org/html/rfc4960#section-5.2.1
+ // Upon receipt of an INIT in the COOKIE-WAIT state, an endpoint MUST
+ // respond with an INIT ACK using the same parameters it sent in its
+ // original INIT chunk (including its Initiate Tag, unchanged). When
+ // responding, the endpoint MUST send the INIT ACK back to the same
+ // address that the original INIT (sent by this endpoint) was sent.
+
+ if state != closed && state != cookieWait && state != cookieEchoed {
+ // 5.2.2. Unexpected INIT in States Other than CLOSED, COOKIE-ECHOED,
+ // COOKIE-WAIT, and SHUTDOWN-ACK-SENT
+ return nil, errors.Errorf("todo: handle Init when in state %s", getAssociationStateString(state))
+ }
+
+ // Should we be setting any of these permanently until we've ACKed further?
+ a.myMaxNumInboundStreams = min16(i.numInboundStreams, a.myMaxNumInboundStreams)
+ a.myMaxNumOutboundStreams = min16(i.numOutboundStreams, a.myMaxNumOutboundStreams)
+ a.peerVerificationTag = i.initiateTag
+ a.sourcePort = p.destinationPort
+ a.destinationPort = p.sourcePort
+
+ // 13.2 This is the last TSN received in sequence. This value
+ // is set initially by taking the peer's initial TSN,
+ // received in the INIT or INIT ACK chunk, and
+ // subtracting one from it.
+ a.peerLastTSN = i.initialTSN - 1
+
+ for _, param := range i.params {
+ switch v := param.(type) { // nolint:gocritic
+ case *paramSupportedExtensions:
+ for _, t := range v.ChunkTypes {
+ if t == ctForwardTSN {
+ a.log.Debugf("[%s] use ForwardTSN (on init)\n", a.name)
+ a.useForwardTSN = true
+ }
+ }
+ }
+ }
+ if !a.useForwardTSN {
+ a.log.Warnf("[%s] not using ForwardTSN (on init)\n", a.name)
+ }
+
+ outbound := &packet{}
+ outbound.verificationTag = a.peerVerificationTag
+ outbound.sourcePort = a.sourcePort
+ outbound.destinationPort = a.destinationPort
+
+ initAck := &chunkInitAck{}
+
+ initAck.initialTSN = a.myNextTSN
+ initAck.numOutboundStreams = a.myMaxNumOutboundStreams
+ initAck.numInboundStreams = a.myMaxNumInboundStreams
+ initAck.initiateTag = a.myVerificationTag
+ initAck.advertisedReceiverWindowCredit = a.maxReceiveBufferSize
+
+ if a.myCookie == nil {
+ var err error
+ if a.myCookie, err = newRandomStateCookie(); err != nil {
+ return nil, err
+ }
+ }
+
+ initAck.params = []param{a.myCookie}
+
+ setSupportedExtensions(&initAck.chunkInitCommon)
+
+ outbound.chunks = []chunk{initAck}
+
+ return pack(outbound), nil
+}
+
+// The caller should hold the lock.
+func (a *Association) handleInitAck(p *packet, i *chunkInitAck) error {
+ state := a.getState()
+ a.log.Debugf("[%s] chunkInitAck received in state '%s'", a.name, getAssociationStateString(state))
+ if state != cookieWait {
+ // RFC 4960
+ // 5.2.3. Unexpected INIT ACK
+ // If an INIT ACK is received by an endpoint in any state other than the
+ // COOKIE-WAIT state, the endpoint should discard the INIT ACK chunk.
+ // An unexpected INIT ACK usually indicates the processing of an old or
+ // duplicated INIT chunk.
+ return nil
+ }
+
+ a.myMaxNumInboundStreams = min16(i.numInboundStreams, a.myMaxNumInboundStreams)
+ a.myMaxNumOutboundStreams = min16(i.numOutboundStreams, a.myMaxNumOutboundStreams)
+ a.peerVerificationTag = i.initiateTag
+ a.peerLastTSN = i.initialTSN - 1
+ if a.sourcePort != p.destinationPort ||
+ a.destinationPort != p.sourcePort {
+ a.log.Warnf("[%s] handleInitAck: port mismatch", a.name)
+ return nil
+ }
+
+ a.rwnd = i.advertisedReceiverWindowCredit
+ a.log.Debugf("[%s] initial rwnd=%d", a.name, a.rwnd)
+
+ // RFC 4690 Sec 7.2.1
+ // o The initial value of ssthresh MAY be arbitrarily high (for
+ // example, implementations MAY use the size of the receiver
+ // advertised window).
+ a.ssthresh = a.rwnd
+ a.log.Tracef("[%s] updated cwnd=%d ssthresh=%d inflight=%d (INI)",
+ a.name, a.cwnd, a.ssthresh, a.inflightQueue.getNumBytes())
+
+ a.t1Init.stop()
+ a.storedInit = nil
+
+ var cookieParam *paramStateCookie
+ for _, param := range i.params {
+ switch v := param.(type) {
+ case *paramStateCookie:
+ cookieParam = v
+ case *paramSupportedExtensions:
+ for _, t := range v.ChunkTypes {
+ if t == ctForwardTSN {
+ a.log.Debugf("[%s] use ForwardTSN (on initAck)\n", a.name)
+ a.useForwardTSN = true
+ }
+ }
+ }
+ }
+ if !a.useForwardTSN {
+ a.log.Warnf("[%s] not using ForwardTSN (on initAck)\n", a.name)
+ }
+ if cookieParam == nil {
+ return errors.Errorf("no cookie in InitAck")
+ }
+
+ a.storedCookieEcho = &chunkCookieEcho{}
+ a.storedCookieEcho.cookie = cookieParam.cookie
+
+ err := a.sendCookieEcho()
+ if err != nil {
+ a.log.Errorf("[%s] failed to send init: %s", a.name, err.Error())
+ }
+
+ a.t1Cookie.start(a.rtoMgr.getRTO())
+ a.setState(cookieEchoed)
+ return nil
+}
+
+// The caller should hold the lock.
+func (a *Association) handleHeartbeat(c *chunkHeartbeat) []*packet {
+ a.log.Tracef("[%s] chunkHeartbeat", a.name)
+ hbi, ok := c.params[0].(*paramHeartbeatInfo)
+ if !ok {
+ a.log.Warnf("[%s] failed to handle Heartbeat, no ParamHeartbeatInfo", a.name)
+ }
+
+ return pack(&packet{
+ verificationTag: a.peerVerificationTag,
+ sourcePort: a.sourcePort,
+ destinationPort: a.destinationPort,
+ chunks: []chunk{&chunkHeartbeatAck{
+ params: []param{
+ &paramHeartbeatInfo{
+ heartbeatInformation: hbi.heartbeatInformation,
+ },
+ },
+ }},
+ })
+}
+
+// The caller should hold the lock.
+func (a *Association) handleCookieEcho(c *chunkCookieEcho) []*packet {
+ state := a.getState()
+ a.log.Debugf("[%s] COOKIE-ECHO received in state '%s'", a.name, getAssociationStateString(state))
+ switch state {
+ default:
+ return nil
+ case established:
+ if !bytes.Equal(a.myCookie.cookie, c.cookie) {
+ return nil
+ }
+ case closed, cookieWait, cookieEchoed:
+ if !bytes.Equal(a.myCookie.cookie, c.cookie) {
+ return nil
+ }
+
+ a.t1Init.stop()
+ a.storedInit = nil
+
+ a.t1Cookie.stop()
+ a.storedCookieEcho = nil
+
+ a.setState(established)
+ a.handshakeCompletedCh <- nil
+ }
+
+ p := &packet{
+ verificationTag: a.peerVerificationTag,
+ sourcePort: a.sourcePort,
+ destinationPort: a.destinationPort,
+ chunks: []chunk{&chunkCookieAck{}},
+ }
+ return pack(p)
+}
+
+// The caller should hold the lock.
+func (a *Association) handleCookieAck() {
+ state := a.getState()
+ a.log.Debugf("[%s] COOKIE-ACK received in state '%s'", a.name, getAssociationStateString(state))
+ if state != cookieEchoed {
+ // RFC 4960
+ // 5.2.5. Handle Duplicate COOKIE-ACK.
+ // At any state other than COOKIE-ECHOED, an endpoint should silently
+ // discard a received COOKIE ACK chunk.
+ return
+ }
+
+ a.t1Cookie.stop()
+ a.storedCookieEcho = nil
+
+ a.setState(established)
+ a.handshakeCompletedCh <- nil
+}
+
+// The caller should hold the lock.
+func (a *Association) handleData(d *chunkPayloadData) []*packet {
+ a.log.Tracef("[%s] DATA: tsn=%d immediateSack=%v len=%d",
+ a.name, d.tsn, d.immediateSack, len(d.userData))
+ a.stats.incDATAs()
+
+ canPush := a.payloadQueue.canPush(d, a.peerLastTSN)
+ if canPush {
+ s := a.getOrCreateStream(d.streamIdentifier)
+ if s == nil {
+ // silentely discard the data. (sender will retry on T3-rtx timeout)
+ // see pion/sctp#30
+ a.log.Debugf("discard %d", d.streamSequenceNumber)
+ return nil
+ }
+
+ if a.getMyReceiverWindowCredit() > 0 {
+ // Pass the new chunk to stream level as soon as it arrives
+ a.payloadQueue.push(d, a.peerLastTSN)
+ s.handleData(d)
+ } else {
+ // Receive buffer is full
+ lastTSN, ok := a.payloadQueue.getLastTSNReceived()
+ if ok && sna32LT(d.tsn, lastTSN) {
+ a.log.Debugf("[%s] receive buffer full, but accepted as this is a missing chunk with tsn=%d ssn=%d", a.name, d.tsn, d.streamSequenceNumber)
+ a.payloadQueue.push(d, a.peerLastTSN)
+ s.handleData(d)
+ } else {
+ a.log.Debugf("[%s] receive buffer full. dropping DATA with tsn=%d ssn=%d", a.name, d.tsn, d.streamSequenceNumber)
+ }
+ }
+ }
+
+ return a.handlePeerLastTSNAndAcknowledgement(d.immediateSack)
+}
+
+// A common routine for handleData and handleForwardTSN routines
+// The caller should hold the lock.
+func (a *Association) handlePeerLastTSNAndAcknowledgement(sackImmediately bool) []*packet {
+ var reply []*packet
+
+ // Try to advance peerLastTSN
+
+ // From RFC 3758 Sec 3.6:
+ // .. and then MUST further advance its cumulative TSN point locally
+ // if possible
+ // Meaning, if peerLastTSN+1 points to a chunk that is received,
+ // advance peerLastTSN until peerLastTSN+1 points to unreceived chunk.
+ for {
+ if _, popOk := a.payloadQueue.pop(a.peerLastTSN + 1); !popOk {
+ break
+ }
+ a.peerLastTSN++
+
+ for _, rstReq := range a.reconfigRequests {
+ resp := a.resetStreamsIfAny(rstReq)
+ if resp != nil {
+ a.log.Debugf("[%s] RESET RESPONSE: %+v", a.name, resp)
+ reply = append(reply, resp)
+ }
+ }
+ }
+
+ hasPacketLoss := (a.payloadQueue.size() > 0)
+ if hasPacketLoss {
+ a.log.Tracef("[%s] packetloss: %s", a.name, a.payloadQueue.getGapAckBlocksString(a.peerLastTSN))
+ }
+
+ if (a.ackState != ackStateImmediate && !sackImmediately && !hasPacketLoss && a.ackMode == ackModeNormal) || a.ackMode == ackModeAlwaysDelay {
+ if a.ackState == ackStateIdle {
+ a.delayedAckTriggered = true
+ } else {
+ a.immediateAckTriggered = true
+ }
+ } else {
+ a.immediateAckTriggered = true
+ }
+
+ return reply
+}
+
+// The caller should hold the lock.
+func (a *Association) getMyReceiverWindowCredit() uint32 {
+ var bytesQueued uint32
+ for _, s := range a.streams {
+ bytesQueued += uint32(s.getNumBytesInReassemblyQueue())
+ }
+
+ if bytesQueued >= a.maxReceiveBufferSize {
+ return 0
+ }
+ return a.maxReceiveBufferSize - bytesQueued
+}
+
+// OpenStream opens a stream
+func (a *Association) OpenStream(streamIdentifier uint16, defaultPayloadType PayloadProtocolIdentifier) (*Stream, error) {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ if _, ok := a.streams[streamIdentifier]; ok {
+ return nil, errors.Errorf("there already exists a stream with identifier %d", streamIdentifier)
+ }
+
+ s := a.createStream(streamIdentifier, false)
+ s.setDefaultPayloadType(defaultPayloadType)
+
+ return s, nil
+}
+
+// AcceptStream accepts a stream
+func (a *Association) AcceptStream() (*Stream, error) {
+ s, ok := <-a.acceptCh
+ if !ok {
+ return nil, io.EOF // no more incoming streams
+ }
+ return s, nil
+}
+
+// createStream creates a stream. The caller should hold the lock and check no stream exists for this id.
+func (a *Association) createStream(streamIdentifier uint16, accept bool) *Stream {
+ s := &Stream{
+ association: a,
+ streamIdentifier: streamIdentifier,
+ reassemblyQueue: newReassemblyQueue(streamIdentifier),
+ log: a.log,
+ name: fmt.Sprintf("%d:%s", streamIdentifier, a.name),
+ }
+
+ s.readNotifier = sync.NewCond(&s.lock)
+
+ if accept {
+ select {
+ case a.acceptCh <- s:
+ a.streams[streamIdentifier] = s
+ a.log.Debugf("[%s] accepted a new stream (streamIdentifier: %d)",
+ a.name, streamIdentifier)
+ default:
+ a.log.Debugf("[%s] dropped a new stream (acceptCh size: %d)",
+ a.name, len(a.acceptCh))
+ return nil
+ }
+ } else {
+ a.streams[streamIdentifier] = s
+ }
+
+ return s
+}
+
+// getOrCreateStream gets or creates a stream. The caller should hold the lock.
+func (a *Association) getOrCreateStream(streamIdentifier uint16) *Stream {
+ if s, ok := a.streams[streamIdentifier]; ok {
+ return s
+ }
+
+ return a.createStream(streamIdentifier, true)
+}
+
+// The caller should hold the lock.
+func (a *Association) processSelectiveAck(d *chunkSelectiveAck) (map[uint16]int, uint32, error) { // nolint:gocognit
+ bytesAckedPerStream := map[uint16]int{}
+
+ // New ack point, so pop all ACKed packets from inflightQueue
+ // We add 1 because the "currentAckPoint" has already been popped from the inflight queue
+ // For the first SACK we take care of this by setting the ackpoint to cumAck - 1
+ for i := a.cumulativeTSNAckPoint + 1; sna32LTE(i, d.cumulativeTSNAck); i++ {
+ c, ok := a.inflightQueue.pop(i)
+ if !ok {
+ return nil, 0, errors.Errorf("tsn %v unable to be popped from inflight queue", i)
+ }
+
+ if !c.acked {
+ // RFC 4096 sec 6.3.2. Retransmission Timer Rules
+ // R3) Whenever a SACK is received that acknowledges the DATA chunk
+ // with the earliest outstanding TSN for that address, restart the
+ // T3-rtx timer for that address with its current RTO (if there is
+ // still outstanding data on that address).
+ if i == a.cumulativeTSNAckPoint+1 {
+ // T3 timer needs to be reset. Stop it for now.
+ a.t3RTX.stop()
+ }
+
+ nBytesAcked := len(c.userData)
+
+ // Sum the number of bytes acknowledged per stream
+ if amount, ok := bytesAckedPerStream[c.streamIdentifier]; ok {
+ bytesAckedPerStream[c.streamIdentifier] = amount + nBytesAcked
+ } else {
+ bytesAckedPerStream[c.streamIdentifier] = nBytesAcked
+ }
+
+ // RFC 4960 sec 6.3.1. RTO Calculation
+ // C4) When data is in flight and when allowed by rule C5 below, a new
+ // RTT measurement MUST be made each round trip. Furthermore, new
+ // RTT measurements SHOULD be made no more than once per round trip
+ // for a given destination transport address.
+ // C5) Karn's algorithm: RTT measurements MUST NOT be made using
+ // packets that were retransmitted (and thus for which it is
+ // ambiguous whether the reply was for the first instance of the
+ // chunk or for a later instance)
+ if c.nSent == 1 && sna32GTE(c.tsn, a.minTSN2MeasureRTT) {
+ a.minTSN2MeasureRTT = a.myNextTSN
+ rtt := time.Since(c.since).Seconds() * 1000.0
+ srtt := a.rtoMgr.setNewRTT(rtt)
+ a.log.Tracef("[%s] SACK: measured-rtt=%f srtt=%f new-rto=%f",
+ a.name, rtt, srtt, a.rtoMgr.getRTO())
+ }
+ }
+
+ if a.inFastRecovery && c.tsn == a.fastRecoverExitPoint {
+ a.log.Debugf("[%s] exit fast-recovery", a.name)
+ a.inFastRecovery = false
+ }
+ }
+
+ htna := d.cumulativeTSNAck
+
+ // Mark selectively acknowledged chunks as "acked"
+ for _, g := range d.gapAckBlocks {
+ for i := g.start; i <= g.end; i++ {
+ tsn := d.cumulativeTSNAck + uint32(i)
+ c, ok := a.inflightQueue.get(tsn)
+ if !ok {
+ return nil, 0, errors.Errorf("requested non-existent TSN %v", tsn)
+ }
+
+ if !c.acked {
+ nBytesAcked := a.inflightQueue.markAsAcked(tsn)
+
+ // Sum the number of bytes acknowledged per stream
+ if amount, ok := bytesAckedPerStream[c.streamIdentifier]; ok {
+ bytesAckedPerStream[c.streamIdentifier] = amount + nBytesAcked
+ } else {
+ bytesAckedPerStream[c.streamIdentifier] = nBytesAcked
+ }
+
+ a.log.Tracef("[%s] tsn=%d has been sacked", a.name, c.tsn)
+
+ if c.nSent == 1 {
+ a.minTSN2MeasureRTT = a.myNextTSN
+ rtt := time.Since(c.since).Seconds() * 1000.0
+ srtt := a.rtoMgr.setNewRTT(rtt)
+ a.log.Tracef("[%s] SACK: measured-rtt=%f srtt=%f new-rto=%f",
+ a.name, rtt, srtt, a.rtoMgr.getRTO())
+ }
+
+ if sna32LT(htna, tsn) {
+ htna = tsn
+ }
+ }
+ }
+ }
+
+ return bytesAckedPerStream, htna, nil
+}
+
+// The caller should hold the lock.
+func (a *Association) onCumulativeTSNAckPointAdvanced(totalBytesAcked int) {
+ // RFC 4096, sec 6.3.2. Retransmission Timer Rules
+ // R2) Whenever all outstanding data sent to an address have been
+ // acknowledged, turn off the T3-rtx timer of that address.
+ if a.inflightQueue.size() == 0 {
+ a.log.Tracef("[%s] SACK: no more packet in-flight (pending=%d)", a.name, a.pendingQueue.size())
+ a.t3RTX.stop()
+ } else {
+ a.log.Tracef("[%s] T3-rtx timer start (pt2)", a.name)
+ a.t3RTX.start(a.rtoMgr.getRTO())
+ }
+
+ // Update congestion control parameters
+ if a.cwnd <= a.ssthresh {
+ // RFC 4096, sec 7.2.1. Slow-Start
+ // o When cwnd is less than or equal to ssthresh, an SCTP endpoint MUST
+ // use the slow-start algorithm to increase cwnd only if the current
+ // congestion window is being fully utilized, an incoming SACK
+ // advances the Cumulative TSN Ack Point, and the data sender is not
+ // in Fast Recovery. Only when these three conditions are met can
+ // the cwnd be increased; otherwise, the cwnd MUST not be increased.
+ // If these conditions are met, then cwnd MUST be increased by, at
+ // most, the lesser of 1) the total size of the previously
+ // outstanding DATA chunk(s) acknowledged, and 2) the destination's
+ // path MTU.
+ if !a.inFastRecovery &&
+ a.pendingQueue.size() > 0 {
+ a.cwnd += min32(uint32(totalBytesAcked), a.cwnd) // TCP way
+ // a.cwnd += min32(uint32(totalBytesAcked), a.mtu) // SCTP way (slow)
+ a.log.Tracef("[%s] updated cwnd=%d ssthresh=%d acked=%d (SS)",
+ a.name, a.cwnd, a.ssthresh, totalBytesAcked)
+ } else {
+ a.log.Tracef("[%s] cwnd did not grow: cwnd=%d ssthresh=%d acked=%d FR=%v pending=%d",
+ a.name, a.cwnd, a.ssthresh, totalBytesAcked, a.inFastRecovery, a.pendingQueue.size())
+ }
+ } else {
+ // RFC 4096, sec 7.2.2. Congestion Avoidance
+ // o Whenever cwnd is greater than ssthresh, upon each SACK arrival
+ // that advances the Cumulative TSN Ack Point, increase
+ // partial_bytes_acked by the total number of bytes of all new chunks
+ // acknowledged in that SACK including chunks acknowledged by the new
+ // Cumulative TSN Ack and by Gap Ack Blocks.
+ a.partialBytesAcked += uint32(totalBytesAcked)
+
+ // o When partial_bytes_acked is equal to or greater than cwnd and
+ // before the arrival of the SACK the sender had cwnd or more bytes
+ // of data outstanding (i.e., before arrival of the SACK, flight size
+ // was greater than or equal to cwnd), increase cwnd by MTU, and
+ // reset partial_bytes_acked to (partial_bytes_acked - cwnd).
+ if a.partialBytesAcked >= a.cwnd && a.pendingQueue.size() > 0 {
+ a.partialBytesAcked -= a.cwnd
+ a.cwnd += a.mtu
+ a.log.Tracef("[%s] updated cwnd=%d ssthresh=%d acked=%d (CA)",
+ a.name, a.cwnd, a.ssthresh, totalBytesAcked)
+ }
+ }
+}
+
+// The caller should hold the lock.
+func (a *Association) processFastRetransmission(cumTSNAckPoint, htna uint32, cumTSNAckPointAdvanced bool) error {
+ // HTNA algorithm - RFC 4960 Sec 7.2.4
+ // Increment missIndicator of each chunks that the SACK reported missing
+ // when either of the following is met:
+ // a) Not in fast-recovery
+ // miss indications are incremented only for missing TSNs prior to the
+ // highest TSN newly acknowledged in the SACK.
+ // b) In fast-recovery AND the Cumulative TSN Ack Point advanced
+ // the miss indications are incremented for all TSNs reported missing
+ // in the SACK.
+ if !a.inFastRecovery || (a.inFastRecovery && cumTSNAckPointAdvanced) {
+ var maxTSN uint32
+ if !a.inFastRecovery {
+ // a) increment only for missing TSNs prior to the HTNA
+ maxTSN = htna
+ } else {
+ // b) increment for all TSNs reported missing
+ maxTSN = cumTSNAckPoint + uint32(a.inflightQueue.size()) + 1
+ }
+
+ for tsn := cumTSNAckPoint + 1; sna32LT(tsn, maxTSN); tsn++ {
+ c, ok := a.inflightQueue.get(tsn)
+ if !ok {
+ return errors.Errorf("requested non-existent TSN %v", tsn)
+ }
+ if !c.acked && !c.abandoned() && c.missIndicator < 3 {
+ c.missIndicator++
+ if c.missIndicator == 3 {
+ if !a.inFastRecovery {
+ // 2) If not in Fast Recovery, adjust the ssthresh and cwnd of the
+ // destination address(es) to which the missing DATA chunks were
+ // last sent, according to the formula described in Section 7.2.3.
+ a.inFastRecovery = true
+ a.fastRecoverExitPoint = htna
+ a.ssthresh = max32(a.cwnd/2, 4*a.mtu)
+ a.cwnd = a.ssthresh
+ a.partialBytesAcked = 0
+ a.willRetransmitFast = true
+
+ a.log.Tracef("[%s] updated cwnd=%d ssthresh=%d inflight=%d (FR)",
+ a.name, a.cwnd, a.ssthresh, a.inflightQueue.getNumBytes())
+ }
+ }
+ }
+ }
+ }
+
+ if a.inFastRecovery && cumTSNAckPointAdvanced {
+ a.willRetransmitFast = true
+ }
+
+ return nil
+}
+
+// The caller should hold the lock.
+func (a *Association) handleSack(d *chunkSelectiveAck) error {
+ a.log.Tracef("[%s] SACK: cumTSN=%d a_rwnd=%d", a.name, d.cumulativeTSNAck, d.advertisedReceiverWindowCredit)
+ state := a.getState()
+ if state != established {
+ return nil
+ }
+
+ a.stats.incSACKs()
+
+ if sna32GT(a.cumulativeTSNAckPoint, d.cumulativeTSNAck) {
+ // RFC 4960 sec 6.2.1. Processing a Received SACK
+ // D)
+ // i) If Cumulative TSN Ack is less than the Cumulative TSN Ack
+ // Point, then drop the SACK. Since Cumulative TSN Ack is
+ // monotonically increasing, a SACK whose Cumulative TSN Ack is
+ // less than the Cumulative TSN Ack Point indicates an out-of-
+ // order SACK.
+
+ a.log.Debugf("[%s] SACK Cumulative ACK %v is older than ACK point %v",
+ a.name,
+ d.cumulativeTSNAck,
+ a.cumulativeTSNAckPoint)
+
+ return nil
+ }
+
+ // Process selective ack
+ bytesAckedPerStream, htna, err := a.processSelectiveAck(d)
+ if err != nil {
+ return err
+ }
+
+ var totalBytesAcked int
+ for _, nBytesAcked := range bytesAckedPerStream {
+ totalBytesAcked += nBytesAcked
+ }
+
+ cumTSNAckPointAdvanced := false
+ if sna32LT(a.cumulativeTSNAckPoint, d.cumulativeTSNAck) {
+ a.log.Tracef("[%s] SACK: cumTSN advanced: %d -> %d",
+ a.name,
+ a.cumulativeTSNAckPoint,
+ d.cumulativeTSNAck)
+
+ a.cumulativeTSNAckPoint = d.cumulativeTSNAck
+ cumTSNAckPointAdvanced = true
+ a.onCumulativeTSNAckPointAdvanced(totalBytesAcked)
+ }
+
+ for si, nBytesAcked := range bytesAckedPerStream {
+ if s, ok := a.streams[si]; ok {
+ a.lock.Unlock()
+ s.onBufferReleased(nBytesAcked)
+ a.lock.Lock()
+ }
+ }
+
+ // New rwnd value
+ // RFC 4960 sec 6.2.1. Processing a Received SACK
+ // D)
+ // ii) Set rwnd equal to the newly received a_rwnd minus the number
+ // of bytes still outstanding after processing the Cumulative
+ // TSN Ack and the Gap Ack Blocks.
+
+ // bytes acked were already subtracted by markAsAcked() method
+ bytesOutstanding := uint32(a.inflightQueue.getNumBytes())
+ if bytesOutstanding >= d.advertisedReceiverWindowCredit {
+ a.rwnd = 0
+ } else {
+ a.rwnd = d.advertisedReceiverWindowCredit - bytesOutstanding
+ }
+
+ err = a.processFastRetransmission(d.cumulativeTSNAck, htna, cumTSNAckPointAdvanced)
+ if err != nil {
+ return err
+ }
+
+ if a.useForwardTSN {
+ // RFC 3758 Sec 3.5 C1
+ if sna32LT(a.advancedPeerTSNAckPoint, a.cumulativeTSNAckPoint) {
+ a.advancedPeerTSNAckPoint = a.cumulativeTSNAckPoint
+ }
+
+ // RFC 3758 Sec 3.5 C2
+ for i := a.advancedPeerTSNAckPoint + 1; ; i++ {
+ c, ok := a.inflightQueue.get(i)
+ if !ok {
+ break
+ }
+ if !c.abandoned() {
+ break
+ }
+ a.advancedPeerTSNAckPoint = i
+ }
+
+ // RFC 3758 Sec 3.5 C3
+ if sna32GT(a.advancedPeerTSNAckPoint, a.cumulativeTSNAckPoint) {
+ a.willSendForwardTSN = true
+ }
+ a.awakeWriteLoop()
+ }
+
+ if a.inflightQueue.size() > 0 {
+ // Start timer. (noop if already started)
+ a.log.Tracef("[%s] T3-rtx timer start (pt3)", a.name)
+ a.t3RTX.start(a.rtoMgr.getRTO())
+ }
+
+ if cumTSNAckPointAdvanced {
+ a.awakeWriteLoop()
+ }
+
+ return nil
+}
+
+// createForwardTSN generates ForwardTSN chunk.
+// This method will be be called if useForwardTSN is set to false.
+// The caller should hold the lock.
+func (a *Association) createForwardTSN() *chunkForwardTSN {
+ // RFC 3758 Sec 3.5 C4
+ streamMap := map[uint16]uint16{} // to report only once per SI
+ for i := a.cumulativeTSNAckPoint + 1; sna32LTE(i, a.advancedPeerTSNAckPoint); i++ {
+ c, ok := a.inflightQueue.get(i)
+ if !ok {
+ break
+ }
+
+ ssn, ok := streamMap[c.streamIdentifier]
+ if !ok {
+ streamMap[c.streamIdentifier] = c.streamSequenceNumber
+ } else if sna16LT(ssn, c.streamSequenceNumber) {
+ // to report only once with greatest SSN
+ streamMap[c.streamIdentifier] = c.streamSequenceNumber
+ }
+ }
+
+ fwdtsn := &chunkForwardTSN{
+ newCumulativeTSN: a.advancedPeerTSNAckPoint,
+ streams: []chunkForwardTSNStream{},
+ }
+
+ var streamStr string
+ for si, ssn := range streamMap {
+ streamStr += fmt.Sprintf("(si=%d ssn=%d)", si, ssn)
+ fwdtsn.streams = append(fwdtsn.streams, chunkForwardTSNStream{
+ identifier: si,
+ sequence: ssn,
+ })
+ }
+ a.log.Tracef("[%s] building fwdtsn: newCumulativeTSN=%d cumTSN=%d - %s", a.name, fwdtsn.newCumulativeTSN, a.cumulativeTSNAckPoint, streamStr)
+
+ return fwdtsn
+}
+
+// createPacket wraps chunks in a packet.
+// The caller should hold the read lock.
+func (a *Association) createPacket(cs []chunk) *packet {
+ return &packet{
+ verificationTag: a.peerVerificationTag,
+ sourcePort: a.sourcePort,
+ destinationPort: a.destinationPort,
+ chunks: cs,
+ }
+}
+
+// The caller should hold the lock.
+func (a *Association) handleReconfig(c *chunkReconfig) ([]*packet, error) {
+ a.log.Tracef("[%s] handleReconfig", a.name)
+
+ pp := make([]*packet, 0)
+
+ p, err := a.handleReconfigParam(c.paramA)
+ if err != nil {
+ return nil, err
+ }
+ if p != nil {
+ pp = append(pp, p)
+ }
+
+ if c.paramB != nil {
+ p, err = a.handleReconfigParam(c.paramB)
+ if err != nil {
+ return nil, err
+ }
+ if p != nil {
+ pp = append(pp, p)
+ }
+ }
+ return pp, nil
+}
+
+// The caller should hold the lock.
+func (a *Association) handleForwardTSN(c *chunkForwardTSN) []*packet {
+ a.log.Tracef("[%s] FwdTSN: %s", a.name, c.String())
+
+ if !a.useForwardTSN {
+ a.log.Warn("[%s] received FwdTSN but not enabled")
+ // Return an error chunk
+ cerr := &chunkError{
+ errorCauses: []errorCause{&errorCauseUnrecognizedChunkType{}},
+ }
+ outbound := &packet{}
+ outbound.verificationTag = a.peerVerificationTag
+ outbound.sourcePort = a.sourcePort
+ outbound.destinationPort = a.destinationPort
+ outbound.chunks = []chunk{cerr}
+ return []*packet{outbound}
+ }
+
+ // From RFC 3758 Sec 3.6:
+ // Note, if the "New Cumulative TSN" value carried in the arrived
+ // FORWARD TSN chunk is found to be behind or at the current cumulative
+ // TSN point, the data receiver MUST treat this FORWARD TSN as out-of-
+ // date and MUST NOT update its Cumulative TSN. The receiver SHOULD
+ // send a SACK to its peer (the sender of the FORWARD TSN) since such a
+ // duplicate may indicate the previous SACK was lost in the network.
+
+ a.log.Tracef("[%s] should send ack? newCumTSN=%d peerLastTSN=%d\n",
+ a.name, c.newCumulativeTSN, a.peerLastTSN)
+ if sna32LTE(c.newCumulativeTSN, a.peerLastTSN) {
+ a.log.Tracef("[%s] sending ack on Forward TSN", a.name)
+ a.ackState = ackStateImmediate
+ a.ackTimer.stop()
+ a.awakeWriteLoop()
+ return nil
+ }
+
+ // From RFC 3758 Sec 3.6:
+ // the receiver MUST perform the same TSN handling, including duplicate
+ // detection, gap detection, SACK generation, cumulative TSN
+ // advancement, etc. as defined in RFC 2960 [2]---with the following
+ // exceptions and additions.
+
+ // When a FORWARD TSN chunk arrives, the data receiver MUST first update
+ // its cumulative TSN point to the value carried in the FORWARD TSN
+ // chunk,
+
+ // Advance peerLastTSN
+ for sna32LT(a.peerLastTSN, c.newCumulativeTSN) {
+ a.payloadQueue.pop(a.peerLastTSN + 1) // may not exist
+ a.peerLastTSN++
+ }
+
+ // Report new peerLastTSN value and abandoned largest SSN value to
+ // corresponding streams so that the abandoned chunks can be removed
+ // from the reassemblyQueue.
+ for _, forwarded := range c.streams {
+ if s, ok := a.streams[forwarded.identifier]; ok {
+ s.handleForwardTSNForOrdered(forwarded.sequence)
+ }
+ }
+
+ // TSN may be forewared for unordered chunks. ForwardTSN chunk does not
+ // report which stream identifier it skipped for unordered chunks.
+ // Therefore, we need to broadcast this event to all existing streams for
+ // unordered chunks.
+ // See https://github.com/pion/sctp/issues/106
+ for _, s := range a.streams {
+ s.handleForwardTSNForUnordered(c.newCumulativeTSN)
+ }
+
+ return a.handlePeerLastTSNAndAcknowledgement(false)
+}
+
+func (a *Association) sendResetRequest(streamIdentifier uint16) error {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ state := a.getState()
+ if state != established {
+ return errors.Errorf("sending reset packet in non-established state: state=%s",
+ getAssociationStateString(state))
+ }
+
+ // Create DATA chunk which only contains valid stream identifier with
+ // nil userData and use it as a EOS from the stream.
+ c := &chunkPayloadData{
+ streamIdentifier: streamIdentifier,
+ beginningFragment: true,
+ endingFragment: true,
+ userData: nil,
+ }
+
+ a.pendingQueue.push(c)
+ a.awakeWriteLoop()
+ return nil
+}
+
+// The caller should hold the lock.
+func (a *Association) handleReconfigParam(raw param) (*packet, error) {
+ switch p := raw.(type) {
+ case *paramOutgoingResetRequest:
+ a.reconfigRequests[p.reconfigRequestSequenceNumber] = p
+ resp := a.resetStreamsIfAny(p)
+ if resp != nil {
+ return resp, nil
+ }
+ return nil, nil
+
+ case *paramReconfigResponse:
+ delete(a.reconfigs, p.reconfigResponseSequenceNumber)
+ if len(a.reconfigs) == 0 {
+ a.tReconfig.stop()
+ }
+ return nil, nil
+ default:
+ return nil, errors.Errorf("unexpected parameter type %T", p)
+ }
+}
+
+// The caller should hold the lock.
+func (a *Association) resetStreamsIfAny(p *paramOutgoingResetRequest) *packet {
+ result := reconfigResultSuccessPerformed
+ if sna32LTE(p.senderLastTSN, a.peerLastTSN) {
+ a.log.Debugf("[%s] resetStream(): senderLastTSN=%d <= peerLastTSN=%d",
+ a.name, p.senderLastTSN, a.peerLastTSN)
+ for _, id := range p.streamIdentifiers {
+ s, ok := a.streams[id]
+ if !ok {
+ continue
+ }
+ a.unregisterStream(s, io.EOF)
+ }
+ delete(a.reconfigRequests, p.reconfigRequestSequenceNumber)
+ } else {
+ a.log.Debugf("[%s] resetStream(): senderLastTSN=%d > peerLastTSN=%d",
+ a.name, p.senderLastTSN, a.peerLastTSN)
+ result = reconfigResultInProgress
+ }
+
+ return a.createPacket([]chunk{&chunkReconfig{
+ paramA: &paramReconfigResponse{
+ reconfigResponseSequenceNumber: p.reconfigRequestSequenceNumber,
+ result: result,
+ },
+ }})
+}
+
+// Move the chunk peeked with a.pendingQueue.peek() to the inflightQueue.
+// The caller should hold the lock.
+func (a *Association) movePendingDataChunkToInflightQueue(c *chunkPayloadData) {
+ if err := a.pendingQueue.pop(c); err != nil {
+ a.log.Errorf("[%s] failed to pop from pending queue: %s", a.name, err.Error())
+ }
+
+ // Mark all fragements are in-flight now
+ if c.endingFragment {
+ c.setAllInflight()
+ }
+
+ // Assign TSN
+ c.tsn = a.generateNextTSN()
+
+ c.since = time.Now() // use to calculate RTT and also for maxPacketLifeTime
+ c.nSent = 1 // being sent for the first time
+
+ a.checkPartialReliabilityStatus(c)
+
+ a.log.Tracef("[%s] sending ppi=%d tsn=%d ssn=%d sent=%d len=%d (%v,%v)",
+ a.name, c.payloadType, c.tsn, c.streamSequenceNumber, c.nSent, len(c.userData), c.beginningFragment, c.endingFragment)
+
+ // Push it into the inflightQueue
+ a.inflightQueue.pushNoCheck(c)
+}
+
+// popPendingDataChunksToSend pops chunks from the pending queues as many as
+// the cwnd and rwnd allows to send.
+// The caller should hold the lock.
+func (a *Association) popPendingDataChunksToSend() ([]*chunkPayloadData, []uint16) {
+ chunks := []*chunkPayloadData{}
+ var sisToReset []uint16 // stream identifieres to reset
+
+ if a.pendingQueue.size() > 0 {
+ // RFC 4960 sec 6.1. Transmission of DATA Chunks
+ // A) At any given time, the data sender MUST NOT transmit new data to
+ // any destination transport address if its peer's rwnd indicates
+ // that the peer has no buffer space (i.e., rwnd is 0; see Section
+ // 6.2.1). However, regardless of the value of rwnd (including if it
+ // is 0), the data sender can always have one DATA chunk in flight to
+ // the receiver if allowed by cwnd (see rule B, below).
+
+ for {
+ c := a.pendingQueue.peek()
+ if c == nil {
+ break // no more pending data
+ }
+
+ dataLen := uint32(len(c.userData))
+ if dataLen == 0 {
+ sisToReset = append(sisToReset, c.streamIdentifier)
+ err := a.pendingQueue.pop(c)
+ if err != nil {
+ a.log.Errorf("failed to pop from pending queue: %s", err.Error())
+ }
+ continue
+ }
+
+ if uint32(a.inflightQueue.getNumBytes())+dataLen > a.cwnd {
+ break // would exceeds cwnd
+ }
+
+ if dataLen > a.rwnd {
+ break // no more rwnd
+ }
+
+ a.rwnd -= dataLen
+
+ a.movePendingDataChunkToInflightQueue(c)
+ chunks = append(chunks, c)
+ }
+
+ // the data sender can always have one DATA chunk in flight to the receiver
+ if len(chunks) == 0 && a.inflightQueue.size() == 0 {
+ // Send zero window probe
+ c := a.pendingQueue.peek()
+ if c != nil {
+ a.movePendingDataChunkToInflightQueue(c)
+ chunks = append(chunks, c)
+ }
+ }
+ }
+
+ return chunks, sisToReset
+}
+
+// bundleDataChunksIntoPackets packs DATA chunks into packets. It tries to bundle
+// DATA chunks into a packet so long as the resulting packet size does not exceed
+// the path MTU.
+// The caller should hold the lock.
+func (a *Association) bundleDataChunksIntoPackets(chunks []*chunkPayloadData) []*packet {
+ packets := []*packet{}
+ chunksToSend := []chunk{}
+ bytesInPacket := int(commonHeaderSize)
+
+ for _, c := range chunks {
+ // RFC 4960 sec 6.1. Transmission of DATA Chunks
+ // Multiple DATA chunks committed for transmission MAY be bundled in a
+ // single packet. Furthermore, DATA chunks being retransmitted MAY be
+ // bundled with new DATA chunks, as long as the resulting packet size
+ // does not exceed the path MTU.
+ if bytesInPacket+len(c.userData) > int(a.mtu) {
+ packets = append(packets, a.createPacket(chunksToSend))
+ chunksToSend = []chunk{}
+ bytesInPacket = int(commonHeaderSize)
+ }
+
+ chunksToSend = append(chunksToSend, c)
+ bytesInPacket += int(dataChunkHeaderSize) + len(c.userData)
+ }
+
+ if len(chunksToSend) > 0 {
+ packets = append(packets, a.createPacket(chunksToSend))
+ }
+
+ return packets
+}
+
+// sendPayloadData sends the data chunks.
+func (a *Association) sendPayloadData(chunks []*chunkPayloadData) error {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ state := a.getState()
+ if state != established {
+ return errors.Errorf("sending payload data in non-established state: state=%s",
+ getAssociationStateString(state))
+ }
+
+ // Push the chunks into the pending queue first.
+ for _, c := range chunks {
+ a.pendingQueue.push(c)
+ }
+
+ a.awakeWriteLoop()
+ return nil
+}
+
+// The caller should hold the lock.
+func (a *Association) checkPartialReliabilityStatus(c *chunkPayloadData) {
+ if !a.useForwardTSN {
+ return
+ }
+
+ // draft-ietf-rtcweb-data-protocol-09.txt section 6
+ // 6. Procedures
+ // All Data Channel Establishment Protocol messages MUST be sent using
+ // ordered delivery and reliable transmission.
+ //
+ if c.payloadType == PayloadTypeWebRTCDCEP {
+ return
+ }
+
+ // PR-SCTP
+ if s, ok := a.streams[c.streamIdentifier]; ok {
+ s.lock.RLock()
+ if s.reliabilityType == ReliabilityTypeRexmit {
+ if c.nSent >= s.reliabilityValue {
+ c.setAbandoned(true)
+ a.log.Tracef("[%s] marked as abandoned: tsn=%d ppi=%d (remix: %d)", a.name, c.tsn, c.payloadType, c.nSent)
+ }
+ } else if s.reliabilityType == ReliabilityTypeTimed {
+ elapsed := int64(time.Since(c.since).Seconds() * 1000)
+ if elapsed >= int64(s.reliabilityValue) {
+ c.setAbandoned(true)
+ a.log.Tracef("[%s] marked as abandoned: tsn=%d ppi=%d (timed: %d)", a.name, c.tsn, c.payloadType, elapsed)
+ }
+ }
+ s.lock.RUnlock()
+ } else {
+ a.log.Errorf("[%s] stream %d not found)", a.name, c.streamIdentifier)
+ }
+}
+
+// getDataPacketsToRetransmit is called when T3-rtx is timed out and retransmit outstanding data chunks
+// that are not acked or abandoned yet.
+// The caller should hold the lock.
+func (a *Association) getDataPacketsToRetransmit() []*packet {
+ awnd := min32(a.cwnd, a.rwnd)
+ chunks := []*chunkPayloadData{}
+ var bytesToSend int
+ var done bool
+
+ for i := 0; !done; i++ {
+ c, ok := a.inflightQueue.get(a.cumulativeTSNAckPoint + uint32(i) + 1)
+ if !ok {
+ break // end of pending data
+ }
+
+ if !c.retransmit {
+ continue
+ }
+
+ if i == 0 && int(a.rwnd) < len(c.userData) {
+ // Send it as a zero window probe
+ done = true
+ } else if bytesToSend+len(c.userData) > int(awnd) {
+ break
+ }
+
+ // reset the retransmit flag not to retransmit again before the next
+ // t3-rtx timer fires
+ c.retransmit = false
+ bytesToSend += len(c.userData)
+
+ c.nSent++
+
+ a.checkPartialReliabilityStatus(c)
+
+ a.log.Tracef("[%s] retransmitting tsn=%d ssn=%d sent=%d", a.name, c.tsn, c.streamSequenceNumber, c.nSent)
+
+ chunks = append(chunks, c)
+ }
+
+ return a.bundleDataChunksIntoPackets(chunks)
+}
+
+// generateNextTSN returns the myNextTSN and increases it. The caller should hold the lock.
+// The caller should hold the lock.
+func (a *Association) generateNextTSN() uint32 {
+ tsn := a.myNextTSN
+ a.myNextTSN++
+ return tsn
+}
+
+// generateNextRSN returns the myNextRSN and increases it. The caller should hold the lock.
+// The caller should hold the lock.
+func (a *Association) generateNextRSN() uint32 {
+ rsn := a.myNextRSN
+ a.myNextRSN++
+ return rsn
+}
+
+func (a *Association) createSelectiveAckChunk() *chunkSelectiveAck {
+ sack := &chunkSelectiveAck{}
+ sack.cumulativeTSNAck = a.peerLastTSN
+ sack.advertisedReceiverWindowCredit = a.getMyReceiverWindowCredit()
+ sack.duplicateTSN = a.payloadQueue.popDuplicates()
+ sack.gapAckBlocks = a.payloadQueue.getGapAckBlocks(a.peerLastTSN)
+ return sack
+}
+
+func pack(p *packet) []*packet {
+ return []*packet{p}
+}
+
+func (a *Association) handleChunkStart() {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ a.delayedAckTriggered = false
+ a.immediateAckTriggered = false
+}
+
+func (a *Association) handleChunkEnd() {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ if a.immediateAckTriggered {
+ // Send SACK now!
+ a.ackState = ackStateImmediate
+ a.ackTimer.stop()
+ a.awakeWriteLoop()
+ } else if a.delayedAckTriggered {
+ // Will send delayed ack in the next ack timeout
+ a.ackState = ackStateDelay
+ a.ackTimer.start()
+ }
+}
+
+func (a *Association) handleChunk(p *packet, c chunk) error {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ var packets []*packet
+ var err error
+
+ if _, err = c.check(); err != nil {
+ a.log.Errorf("[ %s ] failed validating chunk: %s ", a.name, err)
+ return nil
+ }
+
+ switch c := c.(type) {
+ case *chunkInit:
+ packets, err = a.handleInit(p, c)
+
+ case *chunkInitAck:
+ err = a.handleInitAck(p, c)
+
+ case *chunkAbort:
+ var errStr string
+ for _, e := range c.errorCauses {
+ errStr += fmt.Sprintf("(%s)", e)
+ }
+ return fmt.Errorf("[%s] %w: %s", a.name, errChunk, errStr)
+
+ case *chunkError:
+ var errStr string
+ for _, e := range c.errorCauses {
+ errStr += fmt.Sprintf("(%s)", e)
+ }
+ a.log.Debugf("[%s] Error chunk, with following errors: %s", a.name, errStr)
+
+ case *chunkHeartbeat:
+ packets = a.handleHeartbeat(c)
+
+ case *chunkCookieEcho:
+ packets = a.handleCookieEcho(c)
+
+ case *chunkCookieAck:
+ a.handleCookieAck()
+
+ case *chunkPayloadData:
+ packets = a.handleData(c)
+
+ case *chunkSelectiveAck:
+ err = a.handleSack(c)
+
+ case *chunkReconfig:
+ packets, err = a.handleReconfig(c)
+
+ case *chunkForwardTSN:
+ packets = a.handleForwardTSN(c)
+
+ default:
+ err = errors.Errorf("unhandled chunk type")
+ }
+
+ // Log and return, the only condition that is fatal is a ABORT chunk
+ if err != nil {
+ a.log.Errorf("Failed to handle chunk: %v", err)
+ return nil
+ }
+
+ if len(packets) > 0 {
+ a.controlQueue.pushAll(packets)
+ a.awakeWriteLoop()
+ }
+
+ return nil
+}
+
+func (a *Association) onRetransmissionTimeout(id int, nRtos uint) {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ if id == timerT1Init {
+ err := a.sendInit()
+ if err != nil {
+ a.log.Debugf("[%s] failed to retransmit init (nRtos=%d): %v", a.name, nRtos, err)
+ }
+ return
+ }
+
+ if id == timerT1Cookie {
+ err := a.sendCookieEcho()
+ if err != nil {
+ a.log.Debugf("[%s] failed to retransmit cookie-echo (nRtos=%d): %v", a.name, nRtos, err)
+ }
+ return
+ }
+
+ if id == timerT3RTX {
+ a.stats.incT3Timeouts()
+
+ // RFC 4960 sec 6.3.3
+ // E1) For the destination address for which the timer expires, adjust
+ // its ssthresh with rules defined in Section 7.2.3 and set the
+ // cwnd <- MTU.
+ // RFC 4960 sec 7.2.3
+ // When the T3-rtx timer expires on an address, SCTP should perform slow
+ // start by:
+ // ssthresh = max(cwnd/2, 4*MTU)
+ // cwnd = 1*MTU
+
+ a.ssthresh = max32(a.cwnd/2, 4*a.mtu)
+ a.cwnd = a.mtu
+ a.log.Tracef("[%s] updated cwnd=%d ssthresh=%d inflight=%d (RTO)",
+ a.name, a.cwnd, a.ssthresh, a.inflightQueue.getNumBytes())
+
+ // RFC 3758 sec 3.5
+ // A5) Any time the T3-rtx timer expires, on any destination, the sender
+ // SHOULD try to advance the "Advanced.Peer.Ack.Point" by following
+ // the procedures outlined in C2 - C5.
+ if a.useForwardTSN {
+ // RFC 3758 Sec 3.5 C2
+ for i := a.advancedPeerTSNAckPoint + 1; ; i++ {
+ c, ok := a.inflightQueue.get(i)
+ if !ok {
+ break
+ }
+ if !c.abandoned() {
+ break
+ }
+ a.advancedPeerTSNAckPoint = i
+ }
+
+ // RFC 3758 Sec 3.5 C3
+ if sna32GT(a.advancedPeerTSNAckPoint, a.cumulativeTSNAckPoint) {
+ a.willSendForwardTSN = true
+ }
+ }
+
+ a.log.Debugf("[%s] T3-rtx timed out: nRtos=%d cwnd=%d ssthresh=%d", a.name, nRtos, a.cwnd, a.ssthresh)
+
+ /*
+ a.log.Debugf(" - advancedPeerTSNAckPoint=%d", a.advancedPeerTSNAckPoint)
+ a.log.Debugf(" - cumulativeTSNAckPoint=%d", a.cumulativeTSNAckPoint)
+ a.inflightQueue.updateSortedKeys()
+ for i, tsn := range a.inflightQueue.sorted {
+ if c, ok := a.inflightQueue.get(tsn); ok {
+ a.log.Debugf(" - [%d] tsn=%d acked=%v abandoned=%v (%v,%v) len=%d",
+ i, c.tsn, c.acked, c.abandoned(), c.beginningFragment, c.endingFragment, len(c.userData))
+ }
+ }
+ */
+
+ a.inflightQueue.markAllToRetrasmit()
+ a.awakeWriteLoop()
+
+ return
+ }
+
+ if id == timerReconfig {
+ a.willRetransmitReconfig = true
+ a.awakeWriteLoop()
+ }
+}
+
+func (a *Association) onRetransmissionFailure(id int) {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ if id == timerT1Init {
+ a.log.Errorf("[%s] retransmission failure: T1-init", a.name)
+ a.handshakeCompletedCh <- errors.Errorf("handshake failed (INIT ACK)")
+ return
+ }
+
+ if id == timerT1Cookie {
+ a.log.Errorf("[%s] retransmission failure: T1-cookie", a.name)
+ a.handshakeCompletedCh <- errors.Errorf("handshake failed (COOKIE ECHO)")
+ return
+ }
+
+ if id == timerT3RTX {
+ // T3-rtx timer will not fail by design
+ // Justifications:
+ // * ICE would fail if the connectivity is lost
+ // * WebRTC spec is not clear how this incident should be reported to ULP
+ a.log.Errorf("[%s] retransmission failure: T3-rtx (DATA)", a.name)
+ return
+ }
+}
+
+func (a *Association) onAckTimeout() {
+ a.lock.Lock()
+ defer a.lock.Unlock()
+
+ a.log.Tracef("[%s] ack timed out (ackState: %d)", a.name, a.ackState)
+ a.stats.incAckTimeouts()
+
+ a.ackState = ackStateImmediate
+ a.awakeWriteLoop()
+}
+
+// bufferedAmount returns total amount (in bytes) of currently buffered user data.
+// This is used only by testing.
+func (a *Association) bufferedAmount() int {
+ a.lock.RLock()
+ defer a.lock.RUnlock()
+
+ return a.pendingQueue.getNumBytes() + a.inflightQueue.getNumBytes()
+}
+
+// MaxMessageSize returns the maximum message size you can send.
+func (a *Association) MaxMessageSize() uint32 {
+ return atomic.LoadUint32(&a.maxMessageSize)
+}
+
+// SetMaxMessageSize sets the maximum message size you can send.
+func (a *Association) SetMaxMessageSize(maxMsgSize uint32) {
+ atomic.StoreUint32(&a.maxMessageSize, maxMsgSize)
+}
diff --git a/vendor/github.com/pion/sctp/association_stats.go b/vendor/github.com/pion/sctp/association_stats.go
new file mode 100644
index 0000000..4ccb7be
--- /dev/null
+++ b/vendor/github.com/pion/sctp/association_stats.go
@@ -0,0 +1,61 @@
+package sctp
+
+import (
+ "sync/atomic"
+)
+
+type associationStats struct {
+ nDATAs uint64
+ nSACKs uint64
+ nT3Timeouts uint64
+ nAckTimeouts uint64
+ nFastRetrans uint64
+}
+
+func (s *associationStats) incDATAs() {
+ atomic.AddUint64(&s.nDATAs, 1)
+}
+
+func (s *associationStats) getNumDATAs() uint64 {
+ return atomic.LoadUint64(&s.nDATAs)
+}
+
+func (s *associationStats) incSACKs() {
+ atomic.AddUint64(&s.nSACKs, 1)
+}
+
+func (s *associationStats) getNumSACKs() uint64 {
+ return atomic.LoadUint64(&s.nSACKs)
+}
+
+func (s *associationStats) incT3Timeouts() {
+ atomic.AddUint64(&s.nT3Timeouts, 1)
+}
+
+func (s *associationStats) getNumT3Timeouts() uint64 {
+ return atomic.LoadUint64(&s.nT3Timeouts)
+}
+
+func (s *associationStats) incAckTimeouts() {
+ atomic.AddUint64(&s.nAckTimeouts, 1)
+}
+
+func (s *associationStats) getNumAckTimeouts() uint64 {
+ return atomic.LoadUint64(&s.nAckTimeouts)
+}
+
+func (s *associationStats) incFastRetrans() {
+ atomic.AddUint64(&s.nFastRetrans, 1)
+}
+
+func (s *associationStats) getNumFastRetrans() uint64 {
+ return atomic.LoadUint64(&s.nFastRetrans)
+}
+
+func (s *associationStats) reset() {
+ atomic.StoreUint64(&s.nDATAs, 0)
+ atomic.StoreUint64(&s.nSACKs, 0)
+ atomic.StoreUint64(&s.nT3Timeouts, 0)
+ atomic.StoreUint64(&s.nAckTimeouts, 0)
+ atomic.StoreUint64(&s.nFastRetrans, 0)
+}
diff --git a/vendor/github.com/pion/sctp/chunk.go b/vendor/github.com/pion/sctp/chunk.go
new file mode 100644
index 0000000..ec47da1
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk.go
@@ -0,0 +1,9 @@
+package sctp
+
+type chunk interface {
+ unmarshal(raw []byte) error
+ marshal() ([]byte, error)
+ check() (bool, error)
+
+ valueLength() int
+}
diff --git a/vendor/github.com/pion/sctp/chunk_abort.go b/vendor/github.com/pion/sctp/chunk_abort.go
new file mode 100644
index 0000000..9fd4692
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_abort.go
@@ -0,0 +1,88 @@
+package sctp // nolint:dupl
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+/*
+Abort represents an SCTP Chunk of type ABORT
+
+The ABORT chunk is sent to the peer of an association to close the
+association. The ABORT chunk may contain Cause Parameters to inform
+the receiver about the reason of the abort. DATA chunks MUST NOT be
+bundled with ABORT. Control chunks (except for INIT, INIT ACK, and
+SHUTDOWN COMPLETE) MAY be bundled with an ABORT, but they MUST be
+placed before the ABORT in the SCTP packet or they will be ignored by
+the receiver.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 6 |Reserved |T| Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| zero or more Error Causes |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+type chunkAbort struct {
+ chunkHeader
+ errorCauses []errorCause
+}
+
+func (a *chunkAbort) unmarshal(raw []byte) error {
+ if err := a.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if a.typ != ctAbort {
+ return errors.Errorf("ChunkType is not of type ABORT, actually is %s", a.typ.String())
+ }
+
+ offset := chunkHeaderSize
+ for {
+ if len(raw)-offset < 4 {
+ break
+ }
+
+ e, err := buildErrorCause(raw[offset:])
+ if err != nil {
+ return errors.Wrap(err, "Failed build Abort Chunk")
+ }
+
+ offset += int(e.length())
+ a.errorCauses = append(a.errorCauses, e)
+ }
+ return nil
+}
+
+func (a *chunkAbort) marshal() ([]byte, error) {
+ a.chunkHeader.typ = ctAbort
+ a.flags = 0x00
+ a.raw = []byte{}
+ for _, ec := range a.errorCauses {
+ raw, err := ec.marshal()
+ if err != nil {
+ return nil, err
+ }
+ a.raw = append(a.raw, raw...)
+ }
+ return a.chunkHeader.marshal()
+}
+
+func (a *chunkAbort) check() (abort bool, err error) {
+ return false, nil
+}
+
+// String makes chunkAbort printable
+func (a *chunkAbort) String() string {
+ res := a.chunkHeader.String()
+
+ for _, cause := range a.errorCauses {
+ res += fmt.Sprintf("\n - %s", cause)
+ }
+
+ return res
+}
diff --git a/vendor/github.com/pion/sctp/chunk_cookie_ack.go b/vendor/github.com/pion/sctp/chunk_cookie_ack.go
new file mode 100644
index 0000000..83b6f71
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_cookie_ack.go
@@ -0,0 +1,44 @@
+package sctp
+
+import (
+ "github.com/pkg/errors"
+)
+
+/*
+chunkCookieAck represents an SCTP Chunk of type chunkCookieAck
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 11 |Chunk Flags | Length = 4 |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+type chunkCookieAck struct {
+ chunkHeader
+}
+
+func (c *chunkCookieAck) unmarshal(raw []byte) error {
+ if err := c.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if c.typ != ctCookieAck {
+ return errors.Errorf("ChunkType is not of type COOKIEACK, actually is %s", c.typ.String())
+ }
+
+ return nil
+}
+
+func (c *chunkCookieAck) marshal() ([]byte, error) {
+ c.chunkHeader.typ = ctCookieAck
+ return c.chunkHeader.marshal()
+}
+
+func (c *chunkCookieAck) check() (abort bool, err error) {
+ return false, nil
+}
+
+// String makes chunkCookieAck printable
+func (c *chunkCookieAck) String() string {
+ return c.chunkHeader.String()
+}
diff --git a/vendor/github.com/pion/sctp/chunk_cookie_echo.go b/vendor/github.com/pion/sctp/chunk_cookie_echo.go
new file mode 100644
index 0000000..3f0ed36
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_cookie_echo.go
@@ -0,0 +1,46 @@
+package sctp
+
+import (
+ "github.com/pkg/errors"
+)
+
+/*
+CookieEcho represents an SCTP Chunk of type CookieEcho
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 10 |Chunk Flags | Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Cookie |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+*/
+type chunkCookieEcho struct {
+ chunkHeader
+ cookie []byte
+}
+
+func (c *chunkCookieEcho) unmarshal(raw []byte) error {
+ if err := c.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if c.typ != ctCookieEcho {
+ return errors.Errorf("ChunkType is not of type COOKIEECHO, actually is %s", c.typ.String())
+ }
+ c.cookie = c.raw
+
+ return nil
+}
+
+func (c *chunkCookieEcho) marshal() ([]byte, error) {
+ c.chunkHeader.typ = ctCookieEcho
+ c.chunkHeader.raw = c.cookie
+ return c.chunkHeader.marshal()
+}
+
+func (c *chunkCookieEcho) check() (abort bool, err error) {
+ return false, nil
+}
diff --git a/vendor/github.com/pion/sctp/chunk_error.go b/vendor/github.com/pion/sctp/chunk_error.go
new file mode 100644
index 0000000..eb29324
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_error.go
@@ -0,0 +1,95 @@
+package sctp // nolint:dupl
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+/*
+ Operation Error (ERROR) (9)
+
+ An endpoint sends this chunk to its peer endpoint to notify it of
+ certain error conditions. It contains one or more error causes. An
+ Operation Error is not considered fatal in and of itself, but may be
+ used with an ERROR chunk to report a fatal condition. It has the
+ following parameters:
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type = 9 | Chunk Flags | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ \ \
+ / one or more Error Causes /
+ \ \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Chunk Flags: 8 bits
+
+ Set to 0 on transmit and ignored on receipt.
+
+ Length: 16 bits (unsigned integer)
+
+ Set to the size of the chunk in bytes, including the chunk header
+ and all the Error Cause fields present.
+*/
+type chunkError struct {
+ chunkHeader
+ errorCauses []errorCause
+}
+
+func (a *chunkError) unmarshal(raw []byte) error {
+ if err := a.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if a.typ != ctError {
+ return errors.Errorf("ChunkType is not of type ctError, actually is %s", a.typ.String())
+ }
+
+ offset := chunkHeaderSize
+ for {
+ if len(raw)-offset < 4 {
+ break
+ }
+
+ e, err := buildErrorCause(raw[offset:])
+ if err != nil {
+ return errors.Wrap(err, "Failed build Error Chunk")
+ }
+
+ offset += int(e.length())
+ a.errorCauses = append(a.errorCauses, e)
+ }
+ return nil
+}
+
+func (a *chunkError) marshal() ([]byte, error) {
+ a.chunkHeader.typ = ctError
+ a.flags = 0x00
+ a.raw = []byte{}
+ for _, ec := range a.errorCauses {
+ raw, err := ec.marshal()
+ if err != nil {
+ return nil, err
+ }
+ a.raw = append(a.raw, raw...)
+ }
+ return a.chunkHeader.marshal()
+}
+
+func (a *chunkError) check() (abort bool, err error) {
+ return false, nil
+}
+
+// String makes chunkError printable
+func (a *chunkError) String() string {
+ res := a.chunkHeader.String()
+
+ for _, cause := range a.errorCauses {
+ res += fmt.Sprintf("\n - %s", cause)
+ }
+
+ return res
+}
diff --git a/vendor/github.com/pion/sctp/chunk_forward_tsn.go b/vendor/github.com/pion/sctp/chunk_forward_tsn.go
new file mode 100644
index 0000000..f6ff357
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_forward_tsn.go
@@ -0,0 +1,145 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+// This chunk shall be used by the data sender to inform the data
+// receiver to adjust its cumulative received TSN point forward because
+// some missing TSNs are associated with data chunks that SHOULD NOT be
+// transmitted or retransmitted by the sender.
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Type = 192 | Flags = 0x00 | Length = Variable |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | New Cumulative TSN |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Stream-1 | Stream Sequence-1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// \ /
+// / \
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Stream-N | Stream Sequence-N |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+type chunkForwardTSN struct {
+ chunkHeader
+
+ // This indicates the new cumulative TSN to the data receiver. Upon
+ // the reception of this value, the data receiver MUST consider
+ // any missing TSNs earlier than or equal to this value as received,
+ // and stop reporting them as gaps in any subsequent SACKs.
+ newCumulativeTSN uint32
+
+ streams []chunkForwardTSNStream
+}
+
+const (
+ newCumulativeTSNLength = 4
+ forwardTSNStreamLength = 4
+)
+
+var errMarshalStreamFailed = errors.New("failed to marshal stream")
+
+func (c *chunkForwardTSN) unmarshal(raw []byte) error {
+ if err := c.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if len(c.raw) < newCumulativeTSNLength {
+ return errors.New("chunk to short")
+ }
+
+ c.newCumulativeTSN = binary.BigEndian.Uint32(c.raw[0:])
+
+ offset := newCumulativeTSNLength
+ remaining := len(c.raw) - offset
+ for remaining > 0 {
+ s := chunkForwardTSNStream{}
+
+ if err := s.unmarshal(c.raw[offset:]); err != nil {
+ return fmt.Errorf("failed to unmarshal stream: %w", err)
+ }
+
+ c.streams = append(c.streams, s)
+
+ offset += s.length()
+ remaining -= s.length()
+ }
+
+ return nil
+}
+
+func (c *chunkForwardTSN) marshal() ([]byte, error) {
+ out := make([]byte, newCumulativeTSNLength)
+ binary.BigEndian.PutUint32(out[0:], c.newCumulativeTSN)
+
+ for _, s := range c.streams {
+ b, err := s.marshal()
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errMarshalStreamFailed, err)
+ }
+ out = append(out, b...)
+ }
+
+ c.typ = ctForwardTSN
+ c.raw = out
+ return c.chunkHeader.marshal()
+}
+
+func (c *chunkForwardTSN) check() (abort bool, err error) {
+ return true, nil
+}
+
+// String makes chunkForwardTSN printable
+func (c *chunkForwardTSN) String() string {
+ res := fmt.Sprintf("New Cumulative TSN: %d\n", c.newCumulativeTSN)
+ for _, s := range c.streams {
+ res += fmt.Sprintf(" - si=%d, ssn=%d\n", s.identifier, s.sequence)
+ }
+ return res
+}
+
+type chunkForwardTSNStream struct {
+ // This field holds a stream number that was skipped by this
+ // FWD-TSN.
+ identifier uint16
+
+ // This field holds the sequence number associated with the stream
+ // that was skipped. The stream sequence field holds the largest
+ // stream sequence number in this stream being skipped. The receiver
+ // of the FWD-TSN's can use the Stream-N and Stream Sequence-N fields
+ // to enable delivery of any stranded TSN's that remain on the stream
+ // re-ordering queues. This field MUST NOT report TSN's corresponding
+ // to DATA chunks that are marked as unordered. For ordered DATA
+ // chunks this field MUST be filled in.
+ sequence uint16
+}
+
+func (s *chunkForwardTSNStream) length() int {
+ return forwardTSNStreamLength
+}
+
+func (s *chunkForwardTSNStream) unmarshal(raw []byte) error {
+ if len(raw) < forwardTSNStreamLength {
+ return errors.New("stream to short")
+ }
+ s.identifier = binary.BigEndian.Uint16(raw[0:])
+ s.sequence = binary.BigEndian.Uint16(raw[2:])
+
+ return nil
+}
+
+func (s *chunkForwardTSNStream) marshal() ([]byte, error) { // nolint:unparam
+ out := make([]byte, forwardTSNStreamLength)
+
+ binary.BigEndian.PutUint16(out[0:], s.identifier)
+ binary.BigEndian.PutUint16(out[2:], s.sequence)
+
+ return out, nil
+}
diff --git a/vendor/github.com/pion/sctp/chunk_heartbeat.go b/vendor/github.com/pion/sctp/chunk_heartbeat.go
new file mode 100644
index 0000000..f2026a4
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_heartbeat.go
@@ -0,0 +1,75 @@
+package sctp
+
+import (
+ "github.com/pkg/errors"
+)
+
+/*
+chunkHeartbeat represents an SCTP Chunk of type HEARTBEAT
+
+An endpoint should send this chunk to its peer endpoint to probe the
+reachability of a particular destination transport address defined in
+the present association.
+
+The parameter field contains the Heartbeat Information, which is a
+variable-length opaque data structure understood only by the sender.
+
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 4 | Chunk Flags | Heartbeat Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Heartbeat Information TLV (Variable-Length) |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Defined as a variable-length parameter using the format described
+in Section 3.2.1, i.e.:
+
+Variable Parameters Status Type Value
+-------------------------------------------------------------
+heartbeat Info Mandatory 1
+
+*/
+type chunkHeartbeat struct {
+ chunkHeader
+ params []param
+}
+
+func (h *chunkHeartbeat) unmarshal(raw []byte) error {
+ if err := h.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ } else if h.typ != ctHeartbeat {
+ return errors.Errorf("ChunkType is not of type HEARTBEAT, actually is %s", h.typ.String())
+ }
+
+ if len(raw) <= chunkHeaderSize {
+ return errors.Errorf("Heartbeat is not long enough to contain Heartbeat Info %d", len(raw))
+ }
+
+ pType, err := parseParamType(raw[chunkHeaderSize:])
+ if err != nil {
+ return errors.Wrap(err, "failed to parse param type")
+ }
+ if pType != heartbeatInfo {
+ return errors.Errorf("Heartbeat should only have HEARTBEAT param, instead have %s", pType.String())
+ }
+
+ p, err := buildParam(pType, raw[chunkHeaderSize:])
+ if err != nil {
+ return errors.Wrap(err, "Failed unmarshalling param in Heartbeat Chunk")
+ }
+ h.params = append(h.params, p)
+
+ return nil
+}
+
+func (h *chunkHeartbeat) Marshal() ([]byte, error) {
+ return nil, errors.Errorf("Unimplemented")
+}
+
+func (h *chunkHeartbeat) check() (abort bool, err error) {
+ return false, nil
+}
diff --git a/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go b/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go
new file mode 100644
index 0000000..ff84090
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go
@@ -0,0 +1,86 @@
+package sctp
+
+import (
+ "github.com/pkg/errors"
+)
+
+/*
+chunkHeartbeatAck represents an SCTP Chunk of type HEARTBEAT ACK
+
+An endpoint should send this chunk to its peer endpoint as a response
+to a HEARTBEAT chunk (see Section 8.3). A HEARTBEAT ACK is always
+sent to the source IP address of the IP datagram containing the
+HEARTBEAT chunk to which this ack is responding.
+
+The parameter field contains a variable-length opaque data structure.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 5 | Chunk Flags | Heartbeat Ack Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Heartbeat Information TLV (Variable-Length) |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Defined as a variable-length parameter using the format described
+in Section 3.2.1, i.e.:
+
+Variable Parameters Status Type Value
+-------------------------------------------------------------
+Heartbeat Info Mandatory 1
+
+*/
+type chunkHeartbeatAck struct {
+ chunkHeader
+ params []param
+}
+
+func (h *chunkHeartbeatAck) unmarshal(raw []byte) error {
+ return errors.Errorf("Unimplemented")
+}
+
+func (h *chunkHeartbeatAck) marshal() ([]byte, error) {
+ if len(h.params) != 1 {
+ return nil, errors.Errorf("Heartbeat Ack must have one param")
+ }
+
+ switch h.params[0].(type) {
+ case *paramHeartbeatInfo:
+ // ParamHeartbeatInfo is valid
+ default:
+ return nil, errors.Errorf("Heartbeat Ack must have one param, and it should be a HeartbeatInfo")
+ }
+
+ out := make([]byte, 0)
+ for idx, p := range h.params {
+ pp, err := p.marshal()
+ if err != nil {
+ return nil, errors.Wrap(err, "Unable to marshal parameter for Heartbeat Ack")
+ }
+
+ out = append(out, pp...)
+
+ // Chunks (including Type, Length, and Value fields) are padded out
+ // by the sender with all zero bytes to be a multiple of 4 bytes
+ // long. This padding MUST NOT be more than 3 bytes in total. The
+ // Chunk Length value does not include terminating padding of the
+ // chunk. *However, it does include padding of any variable-length
+ // parameter except the last parameter in the chunk.* The receiver
+ // MUST ignore the padding.
+ if idx != len(h.params)-1 {
+ out = padByte(out, getPadding(len(pp)))
+ }
+ }
+
+ h.chunkHeader.typ = ctHeartbeatAck
+ h.chunkHeader.raw = out
+
+ return h.chunkHeader.marshal()
+}
+
+func (h *chunkHeartbeatAck) check() (abort bool, err error) {
+ return false, nil
+}
diff --git a/vendor/github.com/pion/sctp/chunk_init.go b/vendor/github.com/pion/sctp/chunk_init.go
new file mode 100644
index 0000000..65b9b1a
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_init.go
@@ -0,0 +1,123 @@
+package sctp // nolint:dupl
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+/*
+Init represents an SCTP Chunk of type INIT
+
+See chunkInitCommon for the fixed headers
+
+Variable Parameters Status Type Value
+-------------------------------------------------------------
+IPv4 IP (Note 1) Optional 5
+IPv6 IP (Note 1) Optional 6
+Cookie Preservative Optional 9
+Reserved for ECN Capable (Note 2) Optional 32768 (0x8000)
+Host Name IP (Note 3) Optional 11
+Supported IP Types (Note 4) Optional 12
+*/
+type chunkInit struct {
+ chunkHeader
+ chunkInitCommon
+}
+
+func (i *chunkInit) unmarshal(raw []byte) error {
+ if err := i.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if i.typ != ctInit {
+ return errors.Errorf("ChunkType is not of type INIT, actually is %s", i.typ.String())
+ } else if len(i.raw) < initChunkMinLength {
+ return errors.Errorf("Chunk Value isn't long enough for mandatory parameters exp: %d actual: %d", initChunkMinLength, len(i.raw))
+ }
+
+ // The Chunk Flags field in INIT is reserved, and all bits in it should
+ // be set to 0 by the sender and ignored by the receiver. The sequence
+ // of parameters within an INIT can be processed in any order.
+ if i.flags != 0 {
+ return errors.New("ChunkType of type INIT flags must be all 0")
+ }
+
+ if err := i.chunkInitCommon.unmarshal(i.raw); err != nil {
+ return errors.Wrap(err, "Failed to unmarshal INIT body")
+ }
+
+ return nil
+}
+
+func (i *chunkInit) marshal() ([]byte, error) {
+ initShared, err := i.chunkInitCommon.marshal()
+ if err != nil {
+ return nil, errors.Wrap(err, "Failed marshaling INIT common data")
+ }
+
+ i.chunkHeader.typ = ctInit
+ i.chunkHeader.raw = initShared
+ return i.chunkHeader.marshal()
+}
+
+func (i *chunkInit) check() (abort bool, err error) {
+ // The receiver of the INIT (the responding end) records the value of
+ // the Initiate Tag parameter. This value MUST be placed into the
+ // Verification Tag field of every SCTP packet that the receiver of
+ // the INIT transmits within this association.
+ //
+ // The Initiate Tag is allowed to have any value except 0. See
+ // Section 5.3.1 for more on the selection of the tag value.
+ //
+ // If the value of the Initiate Tag in a received INIT chunk is found
+ // to be 0, the receiver MUST treat it as an error and close the
+ // association by transmitting an ABORT.
+ if i.initiateTag == 0 {
+ abort = true
+ return abort, errors.New("ChunkType of type INIT InitiateTag must not be 0")
+ }
+
+ // Defines the maximum number of streams the sender of this INIT
+ // chunk allows the peer end to create in this association. The
+ // value 0 MUST NOT be used.
+ //
+ // Note: There is no negotiation of the actual number of streams but
+ // instead the two endpoints will use the min(requested, offered).
+ // See Section 5.1.1 for details.
+ //
+ // Note: A receiver of an INIT with the MIS value of 0 SHOULD abort
+ // the association.
+ if i.numInboundStreams == 0 {
+ abort = true
+ return abort, errors.New("INIT inbound stream request must be > 0")
+ }
+
+ // Defines the number of outbound streams the sender of this INIT
+ // chunk wishes to create in this association. The value of 0 MUST
+ // NOT be used.
+ //
+ // Note: A receiver of an INIT with the OS value set to 0 SHOULD
+ // abort the association.
+
+ if i.numOutboundStreams == 0 {
+ abort = true
+ return abort, errors.New("INIT outbound stream request must be > 0")
+ }
+
+ // An SCTP receiver MUST be able to receive a minimum of 1500 bytes in
+ // one SCTP packet. This means that an SCTP endpoint MUST NOT indicate
+ // less than 1500 bytes in its initial a_rwnd sent in the INIT or INIT
+ // ACK.
+ if i.advertisedReceiverWindowCredit < 1500 {
+ abort = true
+ return abort, errors.New("INIT Advertised Receiver Window Credit (a_rwnd) must be >= 1500")
+ }
+
+ return false, nil
+}
+
+// String makes chunkInit printable
+func (i *chunkInit) String() string {
+ return fmt.Sprintf("%s\n%s", i.chunkHeader, i.chunkInitCommon)
+}
diff --git a/vendor/github.com/pion/sctp/chunk_init_ack.go b/vendor/github.com/pion/sctp/chunk_init_ack.go
new file mode 100644
index 0000000..551bcea
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_init_ack.go
@@ -0,0 +1,126 @@
+package sctp // nolint:dupl
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+/*
+chunkInitAck represents an SCTP Chunk of type INIT ACK
+
+See chunkInitCommon for the fixed headers
+
+Variable Parameters Status Type Value
+-------------------------------------------------------------
+State Cookie Mandatory 7
+IPv4 IP (Note 1) Optional 5
+IPv6 IP (Note 1) Optional 6
+Unrecognized Parameter Optional 8
+Reserved for ECN Capable (Note 2) Optional 32768 (0x8000)
+Host Name IP (Note 3) Optional 11<Paste>
+
+*/
+type chunkInitAck struct {
+ chunkHeader
+ chunkInitCommon
+}
+
+func (i *chunkInitAck) unmarshal(raw []byte) error {
+ if err := i.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if i.typ != ctInitAck {
+ return errors.Errorf("ChunkType is not of type INIT ACK, actually is %s", i.typ.String())
+ } else if len(i.raw) < initChunkMinLength {
+ return errors.Errorf("Chunk Value isn't long enough for mandatory parameters exp: %d actual: %d", initChunkMinLength, len(i.raw))
+ }
+
+ // The Chunk Flags field in INIT is reserved, and all bits in it should
+ // be set to 0 by the sender and ignored by the receiver. The sequence
+ // of parameters within an INIT can be processed in any order.
+ if i.flags != 0 {
+ return errors.New("ChunkType of type INIT ACK flags must be all 0")
+ }
+
+ if err := i.chunkInitCommon.unmarshal(i.raw); err != nil {
+ return errors.Wrap(err, "Failed to unmarshal INIT body")
+ }
+
+ return nil
+}
+
+func (i *chunkInitAck) marshal() ([]byte, error) {
+ initShared, err := i.chunkInitCommon.marshal()
+ if err != nil {
+ return nil, errors.Wrap(err, "Failed marshaling INIT common data")
+ }
+
+ i.chunkHeader.typ = ctInitAck
+ i.chunkHeader.raw = initShared
+ return i.chunkHeader.marshal()
+}
+
+func (i *chunkInitAck) check() (abort bool, err error) {
+ // The receiver of the INIT ACK records the value of the Initiate Tag
+ // parameter. This value MUST be placed into the Verification Tag
+ // field of every SCTP packet that the INIT ACK receiver transmits
+ // within this association.
+ //
+ // The Initiate Tag MUST NOT take the value 0. See Section 5.3.1 for
+ // more on the selection of the Initiate Tag value.
+ //
+ // If the value of the Initiate Tag in a received INIT ACK chunk is
+ // found to be 0, the receiver MUST destroy the association
+ // discarding its TCB. The receiver MAY send an ABORT for debugging
+ // purpose.
+ if i.initiateTag == 0 {
+ abort = true
+ return abort, errors.New("ChunkType of type INIT ACK InitiateTag must not be 0")
+ }
+
+ // Defines the maximum number of streams the sender of this INIT ACK
+ // chunk allows the peer end to create in this association. The
+ // value 0 MUST NOT be used.
+ //
+ // Note: There is no negotiation of the actual number of streams but
+ // instead the two endpoints will use the min(requested, offered).
+ // See Section 5.1.1 for details.
+ //
+ // Note: A receiver of an INIT ACK with the MIS value set to 0 SHOULD
+ // destroy the association discarding its TCB.
+ if i.numInboundStreams == 0 {
+ abort = true
+ return abort, errors.New("INIT ACK inbound stream request must be > 0")
+ }
+
+ // Defines the number of outbound streams the sender of this INIT ACK
+ // chunk wishes to create in this association. The value of 0 MUST
+ // NOT be used, and the value MUST NOT be greater than the MIS value
+ // sent in the INIT chunk.
+ //
+ // Note: A receiver of an INIT ACK with the OS value set to 0 SHOULD
+ // destroy the association discarding its TCB.
+
+ if i.numOutboundStreams == 0 {
+ abort = true
+ return abort, errors.New("INIT ACK outbound stream request must be > 0")
+ }
+
+ // An SCTP receiver MUST be able to receive a minimum of 1500 bytes in
+ // one SCTP packet. This means that an SCTP endpoint MUST NOT indicate
+ // less than 1500 bytes in its initial a_rwnd sent in the INIT or INIT
+ // ACK.
+ if i.advertisedReceiverWindowCredit < 1500 {
+ abort = true
+ return abort, errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500")
+ }
+
+ return false, nil
+}
+
+// String makes chunkInitAck printable
+func (i *chunkInitAck) String() string {
+ return fmt.Sprintf("%s\n%s", i.chunkHeader, i.chunkInitCommon)
+}
diff --git a/vendor/github.com/pion/sctp/chunk_init_common.go b/vendor/github.com/pion/sctp/chunk_init_common.go
new file mode 100644
index 0000000..f64be78
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_init_common.go
@@ -0,0 +1,155 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+/*
+chunkInitCommon represents an SCTP Chunk body of type INIT and INIT ACK
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 1 | Chunk Flags | Chunk Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Initiate Tag |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Advertised Receiver Window Credit (a_rwnd) |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Number of Outbound Streams | Number of Inbound Streams |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Initial TSN |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Optional/Variable-Length Parameters |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+The INIT chunk contains the following parameters. Unless otherwise
+noted, each parameter MUST only be included once in the INIT chunk.
+
+Fixed Parameters Status
+----------------------------------------------
+Initiate Tag Mandatory
+Advertised Receiver Window Credit Mandatory
+Number of Outbound Streams Mandatory
+Number of Inbound Streams Mandatory
+Initial TSN Mandatory
+*/
+
+type chunkInitCommon struct {
+ initiateTag uint32
+ advertisedReceiverWindowCredit uint32
+ numOutboundStreams uint16
+ numInboundStreams uint16
+ initialTSN uint32
+ params []param
+}
+
+const (
+ initChunkMinLength = 16
+ initOptionalVarHeaderLength = 4
+)
+
+func (i *chunkInitCommon) unmarshal(raw []byte) error {
+ i.initiateTag = binary.BigEndian.Uint32(raw[0:])
+ i.advertisedReceiverWindowCredit = binary.BigEndian.Uint32(raw[4:])
+ i.numOutboundStreams = binary.BigEndian.Uint16(raw[8:])
+ i.numInboundStreams = binary.BigEndian.Uint16(raw[10:])
+ i.initialTSN = binary.BigEndian.Uint32(raw[12:])
+
+ // https://tools.ietf.org/html/rfc4960#section-3.2.1
+ //
+ // Chunk values of SCTP control chunks consist of a chunk-type-specific
+ // header of required fields, followed by zero or more parameters. The
+ // optional and variable-length parameters contained in a chunk are
+ // defined in a Type-Length-Value format as shown below.
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Parameter Type | Parameter Length |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // | Parameter Value |
+ // | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ offset := initChunkMinLength
+ remaining := len(raw) - offset
+ for remaining > 0 {
+ if remaining > initOptionalVarHeaderLength {
+ pType, err := parseParamType(raw[offset:])
+ if err != nil {
+ return errors.Wrap(err, "failed to parse param type")
+ }
+ p, err := buildParam(pType, raw[offset:])
+ if err != nil {
+ return errors.Wrap(err, "Failed unmarshalling param in Init Chunk")
+ }
+ i.params = append(i.params, p)
+ padding := getPadding(p.length())
+ offset += p.length() + padding
+ remaining -= p.length() + padding
+ } else {
+ break
+ }
+ }
+
+ return nil
+}
+
+func (i *chunkInitCommon) marshal() ([]byte, error) {
+ out := make([]byte, initChunkMinLength)
+ binary.BigEndian.PutUint32(out[0:], i.initiateTag)
+ binary.BigEndian.PutUint32(out[4:], i.advertisedReceiverWindowCredit)
+ binary.BigEndian.PutUint16(out[8:], i.numOutboundStreams)
+ binary.BigEndian.PutUint16(out[10:], i.numInboundStreams)
+ binary.BigEndian.PutUint32(out[12:], i.initialTSN)
+ for idx, p := range i.params {
+ pp, err := p.marshal()
+ if err != nil {
+ return nil, errors.Wrap(err, "Unable to marshal parameter for INIT/INITACK")
+ }
+
+ out = append(out, pp...)
+
+ // Chunks (including Type, Length, and Value fields) are padded out
+ // by the sender with all zero bytes to be a multiple of 4 bytes
+ // long. This padding MUST NOT be more than 3 bytes in total. The
+ // Chunk Length value does not include terminating padding of the
+ // chunk. *However, it does include padding of any variable-length
+ // parameter except the last parameter in the chunk.* The receiver
+ // MUST ignore the padding.
+ if idx != len(i.params)-1 {
+ out = padByte(out, getPadding(len(pp)))
+ }
+ }
+
+ return out, nil
+}
+
+// String makes chunkInitCommon printable
+func (i chunkInitCommon) String() string {
+ format := `initiateTag: %d
+ advertisedReceiverWindowCredit: %d
+ numOutboundStreams: %d
+ numInboundStreams: %d
+ initialTSN: %d`
+
+ res := fmt.Sprintf(format,
+ i.initiateTag,
+ i.advertisedReceiverWindowCredit,
+ i.numOutboundStreams,
+ i.numInboundStreams,
+ i.initialTSN,
+ )
+
+ for i, param := range i.params {
+ res += fmt.Sprintf("Param %d:\n %s", i, param)
+ }
+ return res
+}
diff --git a/vendor/github.com/pion/sctp/chunk_payload_data.go b/vendor/github.com/pion/sctp/chunk_payload_data.go
new file mode 100644
index 0000000..3e5e346
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_payload_data.go
@@ -0,0 +1,195 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "time"
+)
+
+/*
+chunkPayloadData represents an SCTP Chunk of type DATA
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 0 | Reserved|U|B|E| Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| TSN |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Stream Identifier S | Stream Sequence Number n |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Payload Protocol Identifier |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| User Data (seq n of Stream S) |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+An unfragmented user message shall have both the B and E bits set to
+'1'. Setting both B and E bits to '0' indicates a middle fragment of
+a multi-fragment user message, as summarized in the following table:
+ B E Description
+============================================================
+| 1 0 | First piece of a fragmented user message |
++----------------------------------------------------------+
+| 0 0 | Middle piece of a fragmented user message |
++----------------------------------------------------------+
+| 0 1 | Last piece of a fragmented user message |
++----------------------------------------------------------+
+| 1 1 | Unfragmented message |
+============================================================
+| Table 1: Fragment Description Flags |
+============================================================
+*/
+type chunkPayloadData struct {
+ chunkHeader
+
+ unordered bool
+ beginningFragment bool
+ endingFragment bool
+ immediateSack bool
+
+ tsn uint32
+ streamIdentifier uint16
+ streamSequenceNumber uint16
+ payloadType PayloadProtocolIdentifier
+ userData []byte
+
+ // Whether this data chunk was acknowledged (received by peer)
+ acked bool
+ missIndicator uint32
+
+ // Partial-reliability parameters used only by sender
+ since time.Time
+ nSent uint32 // number of transmission made for this chunk
+ _abandoned bool
+ _allInflight bool // valid only with the first fragment
+
+ // Retransmission flag set when T1-RTX timeout occurred and this
+ // chunk is still in the inflight queue
+ retransmit bool
+
+ head *chunkPayloadData // link to the head of the fragment
+}
+
+const (
+ payloadDataEndingFragmentBitmask = 1
+ payloadDataBeginingFragmentBitmask = 2
+ payloadDataUnorderedBitmask = 4
+ payloadDataImmediateSACK = 8
+
+ payloadDataHeaderSize = 12
+)
+
+// PayloadProtocolIdentifier is an enum for DataChannel payload types
+type PayloadProtocolIdentifier uint32
+
+// PayloadProtocolIdentifier enums
+// https://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml#sctp-parameters-25
+const (
+ PayloadTypeWebRTCDCEP PayloadProtocolIdentifier = 50
+ PayloadTypeWebRTCString PayloadProtocolIdentifier = 51
+ PayloadTypeWebRTCBinary PayloadProtocolIdentifier = 53
+ PayloadTypeWebRTCStringEmpty PayloadProtocolIdentifier = 56
+ PayloadTypeWebRTCBinaryEmpty PayloadProtocolIdentifier = 57
+)
+
+func (p PayloadProtocolIdentifier) String() string {
+ switch p {
+ case PayloadTypeWebRTCDCEP:
+ return "WebRTC DCEP"
+ case PayloadTypeWebRTCString:
+ return "WebRTC String"
+ case PayloadTypeWebRTCBinary:
+ return "WebRTC Binary"
+ case PayloadTypeWebRTCStringEmpty:
+ return "WebRTC String (Empty)"
+ case PayloadTypeWebRTCBinaryEmpty:
+ return "WebRTC Binary (Empty)"
+ default:
+ return fmt.Sprintf("Unknown Payload Protocol Identifier: %d", p)
+ }
+}
+
+func (p *chunkPayloadData) unmarshal(raw []byte) error {
+ if err := p.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ p.immediateSack = p.flags&payloadDataImmediateSACK != 0
+ p.unordered = p.flags&payloadDataUnorderedBitmask != 0
+ p.beginningFragment = p.flags&payloadDataBeginingFragmentBitmask != 0
+ p.endingFragment = p.flags&payloadDataEndingFragmentBitmask != 0
+
+ p.tsn = binary.BigEndian.Uint32(p.raw[0:])
+ p.streamIdentifier = binary.BigEndian.Uint16(p.raw[4:])
+ p.streamSequenceNumber = binary.BigEndian.Uint16(p.raw[6:])
+ p.payloadType = PayloadProtocolIdentifier(binary.BigEndian.Uint32(p.raw[8:]))
+ p.userData = p.raw[payloadDataHeaderSize:]
+
+ return nil
+}
+
+func (p *chunkPayloadData) marshal() ([]byte, error) {
+ payRaw := make([]byte, payloadDataHeaderSize+len(p.userData))
+
+ binary.BigEndian.PutUint32(payRaw[0:], p.tsn)
+ binary.BigEndian.PutUint16(payRaw[4:], p.streamIdentifier)
+ binary.BigEndian.PutUint16(payRaw[6:], p.streamSequenceNumber)
+ binary.BigEndian.PutUint32(payRaw[8:], uint32(p.payloadType))
+ copy(payRaw[payloadDataHeaderSize:], p.userData)
+
+ flags := uint8(0)
+ if p.endingFragment {
+ flags = 1
+ }
+ if p.beginningFragment {
+ flags |= 1 << 1
+ }
+ if p.unordered {
+ flags |= 1 << 2
+ }
+ if p.immediateSack {
+ flags |= 1 << 3
+ }
+
+ p.chunkHeader.flags = flags
+ p.chunkHeader.typ = ctPayloadData
+ p.chunkHeader.raw = payRaw
+ return p.chunkHeader.marshal()
+}
+
+func (p *chunkPayloadData) check() (abort bool, err error) {
+ return false, nil
+}
+
+// String makes chunkPayloadData printable
+func (p *chunkPayloadData) String() string {
+ return fmt.Sprintf("%s\n%d", p.chunkHeader, p.tsn)
+}
+
+func (p *chunkPayloadData) abandoned() bool {
+ if p.head != nil {
+ return p.head._abandoned && p.head._allInflight
+ }
+ return p._abandoned && p._allInflight
+}
+
+func (p *chunkPayloadData) setAbandoned(abandoned bool) {
+ if p.head != nil {
+ p.head._abandoned = abandoned
+ return
+ }
+ p._abandoned = abandoned
+}
+
+func (p *chunkPayloadData) setAllInflight() {
+ if p.endingFragment {
+ if p.head != nil {
+ p.head._allInflight = true
+ } else {
+ p._allInflight = true
+ }
+ }
+}
diff --git a/vendor/github.com/pion/sctp/chunk_reconfig.go b/vendor/github.com/pion/sctp/chunk_reconfig.go
new file mode 100644
index 0000000..a53ad5e
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_reconfig.go
@@ -0,0 +1,99 @@
+package sctp
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+// https://tools.ietf.org/html/rfc6525#section-3.1
+// chunkReconfig represents an SCTP Chunk used to reconfigure streams.
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Type = 130 | Chunk Flags | Chunk Length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// \ \
+// / Re-configuration Parameter /
+// \ \
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// \ \
+// / Re-configuration Parameter (optional) /
+// \ \
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+type chunkReconfig struct {
+ chunkHeader
+ paramA param
+ paramB param
+}
+
+func (c *chunkReconfig) unmarshal(raw []byte) error {
+ if err := c.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+ pType, err := parseParamType(c.raw)
+ if err != nil {
+ return errors.Wrap(err, "failed to parse param type")
+ }
+ a, err := buildParam(pType, c.raw)
+ if err != nil {
+ return err
+ }
+ c.paramA = a
+
+ padding := getPadding(a.length())
+ offset := a.length() + padding
+ if len(c.raw) > offset {
+ pType, err := parseParamType(c.raw[offset:])
+ if err != nil {
+ return errors.Wrap(err, "failed to parse param type")
+ }
+ b, err := buildParam(pType, c.raw[offset:])
+ if err != nil {
+ return err
+ }
+ c.paramB = b
+ }
+
+ return nil
+}
+
+func (c *chunkReconfig) marshal() ([]byte, error) {
+ out, err := c.paramA.marshal()
+ if err != nil {
+ return nil, errors.Wrap(err, "Unable to marshal parameter A for reconfig")
+ }
+ if c.paramB != nil {
+ // Pad param A
+ out = padByte(out, getPadding(len(out)))
+
+ outB, err := c.paramB.marshal()
+ if err != nil {
+ return nil, errors.Wrap(err, "Unable to marshal parameter B for reconfig")
+ }
+
+ out = append(out, outB...)
+ }
+
+ c.typ = ctReconfig
+ c.raw = out
+ return c.chunkHeader.marshal()
+}
+
+func (c *chunkReconfig) check() (abort bool, err error) {
+ // nolint:godox
+ // TODO: check allowed combinations:
+ // https://tools.ietf.org/html/rfc6525#section-3.1
+ return true, nil
+}
+
+// String makes chunkReconfig printable
+func (c *chunkReconfig) String() string {
+ res := fmt.Sprintf("Param A:\n %s", c.paramA)
+ if c.paramB != nil {
+ res += fmt.Sprintf("Param B:\n %s", c.paramB)
+ }
+ return res
+}
diff --git a/vendor/github.com/pion/sctp/chunk_selective_ack.go b/vendor/github.com/pion/sctp/chunk_selective_ack.go
new file mode 100644
index 0000000..920d562
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunk_selective_ack.go
@@ -0,0 +1,142 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+/*
+chunkSelectiveAck represents an SCTP Chunk of type SACK
+
+This chunk is sent to the peer endpoint to acknowledge received DATA
+chunks and to inform the peer endpoint of gaps in the received
+subsequences of DATA chunks as represented by their TSNs.
+0 1 2 3
+0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Type = 3 |Chunk Flags | Chunk Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Cumulative TSN Ack |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Advertised Receiver Window Credit (a_rwnd) |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Number of Gap Ack Blocks = N | Number of Duplicate TSNs = X |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Gap Ack Block #1 Start | Gap Ack Block #1 End |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+/ /
+\ ... \
+/ /
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Gap Ack Block #N Start | Gap Ack Block #N End |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Duplicate TSN 1 |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+/ /
+\ ... \
+/ /
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Duplicate TSN X |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+type gapAckBlock struct {
+ start uint16
+ end uint16
+}
+
+// String makes gapAckBlock printable
+func (g gapAckBlock) String() string {
+ return fmt.Sprintf("%d - %d", g.start, g.end)
+}
+
+type chunkSelectiveAck struct {
+ chunkHeader
+ cumulativeTSNAck uint32
+ advertisedReceiverWindowCredit uint32
+ gapAckBlocks []gapAckBlock
+ duplicateTSN []uint32
+}
+
+const (
+ selectiveAckHeaderSize = 12
+)
+
+func (s *chunkSelectiveAck) unmarshal(raw []byte) error {
+ if err := s.chunkHeader.unmarshal(raw); err != nil {
+ return err
+ }
+
+ if s.typ != ctSack {
+ return errors.Errorf("ChunkType is not of type SACK, actually is %s", s.typ.String())
+ }
+
+ if len(s.raw) < selectiveAckHeaderSize {
+ return errors.Errorf("SACK Chunk size is not large enough to contain header (%v remaining, needs %v bytes)",
+ len(s.raw), selectiveAckHeaderSize)
+ }
+
+ s.cumulativeTSNAck = binary.BigEndian.Uint32(s.raw[0:])
+ s.advertisedReceiverWindowCredit = binary.BigEndian.Uint32(s.raw[4:])
+ s.gapAckBlocks = make([]gapAckBlock, binary.BigEndian.Uint16(s.raw[8:]))
+ s.duplicateTSN = make([]uint32, binary.BigEndian.Uint16(s.raw[10:]))
+
+ if len(s.raw) != selectiveAckHeaderSize+(4*len(s.gapAckBlocks)+(4*len(s.duplicateTSN))) {
+ return errors.Errorf("SACK Chunk size does not match predicted amount from header values")
+ }
+
+ offset := selectiveAckHeaderSize
+ for i := range s.gapAckBlocks {
+ s.gapAckBlocks[i].start = binary.BigEndian.Uint16(s.raw[offset:])
+ s.gapAckBlocks[i].end = binary.BigEndian.Uint16(s.raw[offset+2:])
+ offset += 4
+ }
+ for i := range s.duplicateTSN {
+ s.duplicateTSN[i] = binary.BigEndian.Uint32(s.raw[offset:])
+ offset += 4
+ }
+
+ return nil
+}
+
+func (s *chunkSelectiveAck) marshal() ([]byte, error) {
+ sackRaw := make([]byte, selectiveAckHeaderSize+(4*len(s.gapAckBlocks)+(4*len(s.duplicateTSN))))
+ binary.BigEndian.PutUint32(sackRaw[0:], s.cumulativeTSNAck)
+ binary.BigEndian.PutUint32(sackRaw[4:], s.advertisedReceiverWindowCredit)
+ binary.BigEndian.PutUint16(sackRaw[8:], uint16(len(s.gapAckBlocks)))
+ binary.BigEndian.PutUint16(sackRaw[10:], uint16(len(s.duplicateTSN)))
+ offset := selectiveAckHeaderSize
+ for _, g := range s.gapAckBlocks {
+ binary.BigEndian.PutUint16(sackRaw[offset:], g.start)
+ binary.BigEndian.PutUint16(sackRaw[offset+2:], g.end)
+ offset += 4
+ }
+ for _, t := range s.duplicateTSN {
+ binary.BigEndian.PutUint32(sackRaw[offset:], t)
+ offset += 4
+ }
+
+ s.chunkHeader.typ = ctSack
+ s.chunkHeader.raw = sackRaw
+ return s.chunkHeader.marshal()
+}
+
+func (s *chunkSelectiveAck) check() (abort bool, err error) {
+ return false, nil
+}
+
+// String makes chunkSelectiveAck printable
+func (s *chunkSelectiveAck) String() string {
+ res := fmt.Sprintf("SACK cumTsnAck=%d arwnd=%d dupTsn=%d",
+ s.cumulativeTSNAck,
+ s.advertisedReceiverWindowCredit,
+ s.duplicateTSN)
+
+ for _, gap := range s.gapAckBlocks {
+ res = fmt.Sprintf("%s\n gap ack: %s", res, gap)
+ }
+
+ return res
+}
diff --git a/vendor/github.com/pion/sctp/chunkheader.go b/vendor/github.com/pion/sctp/chunkheader.go
new file mode 100644
index 0000000..a4a78db
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunkheader.go
@@ -0,0 +1,90 @@
+package sctp
+
+import (
+ "encoding/binary"
+
+ "github.com/pkg/errors"
+)
+
+/*
+chunkHeader represents a SCTP Chunk header, defined in https://tools.ietf.org/html/rfc4960#section-3.2
+The figure below illustrates the field format for the chunks to be
+transmitted in the SCTP packet. Each chunk is formatted with a Chunk
+Type field, a chunk-specific Flag field, a Chunk Length field, and a
+Value field.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Chunk Type | Chunk Flags | Chunk Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Chunk Value |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+type chunkHeader struct {
+ typ chunkType
+ flags byte
+ raw []byte
+}
+
+const (
+ chunkHeaderSize = 4
+)
+
+func (c *chunkHeader) unmarshal(raw []byte) error {
+ if len(raw) < chunkHeaderSize {
+ return errors.Errorf("raw only %d bytes, %d is the minimum length for a SCTP chunk", len(raw), chunkHeaderSize)
+ }
+
+ c.typ = chunkType(raw[0])
+ c.flags = raw[1]
+ length := binary.BigEndian.Uint16(raw[2:])
+
+ // Length includes Chunk header
+ valueLength := int(length - chunkHeaderSize)
+ lengthAfterValue := len(raw) - (chunkHeaderSize + valueLength)
+
+ if lengthAfterValue < 0 {
+ return errors.Errorf("Not enough data left in SCTP packet to satisfy requested length remain %d req %d ", valueLength, len(raw)-chunkHeaderSize)
+ } else if lengthAfterValue < 4 {
+ // https://tools.ietf.org/html/rfc4960#section-3.2
+ // The Chunk Length field does not count any chunk padding.
+ // Chunks (including Type, Length, and Value fields) are padded out
+ // by the sender with all zero bytes to be a multiple of 4 bytes
+ // long. This padding MUST NOT be more than 3 bytes in total. The
+ // Chunk Length value does not include terminating padding of the
+ // chunk. However, it does include padding of any variable-length
+ // parameter except the last parameter in the chunk. The receiver
+ // MUST ignore the padding.
+ for i := lengthAfterValue; i > 0; i-- {
+ paddingOffset := chunkHeaderSize + valueLength + (i - 1)
+ if raw[paddingOffset] != 0 {
+ return errors.Errorf("Chunk padding is non-zero at offset %d ", paddingOffset)
+ }
+ }
+ }
+
+ c.raw = raw[chunkHeaderSize : chunkHeaderSize+valueLength]
+ return nil
+}
+
+func (c *chunkHeader) marshal() ([]byte, error) {
+ raw := make([]byte, 4+len(c.raw))
+
+ raw[0] = uint8(c.typ)
+ raw[1] = c.flags
+ binary.BigEndian.PutUint16(raw[2:], uint16(len(c.raw)+chunkHeaderSize))
+ copy(raw[4:], c.raw)
+ return raw, nil
+}
+
+func (c *chunkHeader) valueLength() int {
+ return len(c.raw)
+}
+
+// String makes chunkHeader printable
+func (c chunkHeader) String() string {
+ return c.typ.String()
+}
diff --git a/vendor/github.com/pion/sctp/chunktype.go b/vendor/github.com/pion/sctp/chunktype.go
new file mode 100644
index 0000000..65b57a4
--- /dev/null
+++ b/vendor/github.com/pion/sctp/chunktype.go
@@ -0,0 +1,67 @@
+package sctp
+
+import "fmt"
+
+// chunkType is an enum for SCTP Chunk Type field
+// This field identifies the type of information contained in the
+// Chunk Value field.
+type chunkType uint8
+
+// List of known chunkType enums
+const (
+ ctPayloadData chunkType = 0
+ ctInit chunkType = 1
+ ctInitAck chunkType = 2
+ ctSack chunkType = 3
+ ctHeartbeat chunkType = 4
+ ctHeartbeatAck chunkType = 5
+ ctAbort chunkType = 6
+ ctShutdown chunkType = 7
+ ctShutdownAck chunkType = 8
+ ctError chunkType = 9
+ ctCookieEcho chunkType = 10
+ ctCookieAck chunkType = 11
+ ctCWR chunkType = 13
+ ctShutdownComplete chunkType = 14
+ ctReconfig chunkType = 130
+ ctForwardTSN chunkType = 192
+)
+
+func (c chunkType) String() string {
+ switch c {
+ case ctPayloadData:
+ return "DATA"
+ case ctInit:
+ return "INIT"
+ case ctInitAck:
+ return "INIT-ACK"
+ case ctSack:
+ return "SACK"
+ case ctHeartbeat:
+ return "HEARTBEAT"
+ case ctHeartbeatAck:
+ return "HEARTBEAT-ACK"
+ case ctAbort:
+ return "ABORT"
+ case ctShutdown:
+ return "SHUTDOWN"
+ case ctShutdownAck:
+ return "SHUTDOWN-ACK"
+ case ctError:
+ return "ERROR"
+ case ctCookieEcho:
+ return "COOKIE-ECHO"
+ case ctCookieAck:
+ return "COOKIE-ACK"
+ case ctCWR:
+ return "ECNE" // Explicit Congestion Notification Echo
+ case ctShutdownComplete:
+ return "SHUTDOWN-COMPLETE"
+ case ctReconfig:
+ return "RECONFIG" // Re-configuration
+ case ctForwardTSN:
+ return "FORWARD-TSN"
+ default:
+ return fmt.Sprintf("Unknown ChunkType: %d", c)
+ }
+}
diff --git a/vendor/github.com/pion/sctp/codecov.yml b/vendor/github.com/pion/sctp/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/sctp/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/sctp/control_queue.go b/vendor/github.com/pion/sctp/control_queue.go
new file mode 100644
index 0000000..7e13469
--- /dev/null
+++ b/vendor/github.com/pion/sctp/control_queue.go
@@ -0,0 +1,29 @@
+package sctp
+
+// control queue
+
+type controlQueue struct {
+ queue []*packet
+}
+
+func newControlQueue() *controlQueue {
+ return &controlQueue{queue: []*packet{}}
+}
+
+func (q *controlQueue) push(c *packet) {
+ q.queue = append(q.queue, c)
+}
+
+func (q *controlQueue) pushAll(packets []*packet) {
+ q.queue = append(q.queue, packets...)
+}
+
+func (q *controlQueue) popAll() []*packet {
+ packets := q.queue
+ q.queue = []*packet{}
+ return packets
+}
+
+func (q *controlQueue) size() int {
+ return len(q.queue)
+}
diff --git a/vendor/github.com/pion/sctp/error_cause.go b/vendor/github.com/pion/sctp/error_cause.go
new file mode 100644
index 0000000..e94cc5c
--- /dev/null
+++ b/vendor/github.com/pion/sctp/error_cause.go
@@ -0,0 +1,91 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+// errorCauseCode is a cause code that appears in either a ERROR or ABORT chunk
+type errorCauseCode uint16
+
+type errorCause interface {
+ unmarshal([]byte) error
+ marshal() ([]byte, error)
+ length() uint16
+ String() string
+
+ errorCauseCode() errorCauseCode
+}
+
+// buildErrorCause delegates the building of a error cause from raw bytes to the correct structure
+func buildErrorCause(raw []byte) (errorCause, error) {
+ var e errorCause
+
+ c := errorCauseCode(binary.BigEndian.Uint16(raw[0:]))
+ switch c {
+ case invalidMandatoryParameter:
+ e = &errorCauseInvalidMandatoryParameter{}
+ case unrecognizedChunkType:
+ e = &errorCauseUnrecognizedChunkType{}
+ case protocolViolation:
+ e = &errorCauseProtocolViolation{}
+ default:
+ return nil, errors.Errorf("BuildErrorCause does not handle %s", c.String())
+ }
+
+ if err := e.unmarshal(raw); err != nil {
+ return nil, err
+ }
+ return e, nil
+}
+
+const (
+ invalidStreamIdentifier errorCauseCode = 1
+ missingMandatoryParameter errorCauseCode = 2
+ staleCookieError errorCauseCode = 3
+ outOfResource errorCauseCode = 4
+ unresolvableAddress errorCauseCode = 5
+ unrecognizedChunkType errorCauseCode = 6
+ invalidMandatoryParameter errorCauseCode = 7
+ unrecognizedParameters errorCauseCode = 8
+ noUserData errorCauseCode = 9
+ cookieReceivedWhileShuttingDown errorCauseCode = 10
+ restartOfAnAssociationWithNewAddresses errorCauseCode = 11
+ userInitiatedAbort errorCauseCode = 12
+ protocolViolation errorCauseCode = 13
+)
+
+func (e errorCauseCode) String() string {
+ switch e {
+ case invalidStreamIdentifier:
+ return "Invalid Stream Identifier"
+ case missingMandatoryParameter:
+ return "Missing Mandatory Parameter"
+ case staleCookieError:
+ return "Stale Cookie Error"
+ case outOfResource:
+ return "Out Of Resource"
+ case unresolvableAddress:
+ return "Unresolvable IP"
+ case unrecognizedChunkType:
+ return "Unrecognized Chunk Type"
+ case invalidMandatoryParameter:
+ return "Invalid Mandatory Parameter"
+ case unrecognizedParameters:
+ return "Unrecognized Parameters"
+ case noUserData:
+ return "No User Data"
+ case cookieReceivedWhileShuttingDown:
+ return "Cookie Received While Shutting Down"
+ case restartOfAnAssociationWithNewAddresses:
+ return "Restart Of An Association With New Addresses"
+ case userInitiatedAbort:
+ return "User Initiated Abort"
+ case protocolViolation:
+ return "Protocol Violation"
+ default:
+ return fmt.Sprintf("Unknown CauseCode: %d", e)
+ }
+}
diff --git a/vendor/github.com/pion/sctp/error_cause_header.go b/vendor/github.com/pion/sctp/error_cause_header.go
new file mode 100644
index 0000000..1ad6e1d
--- /dev/null
+++ b/vendor/github.com/pion/sctp/error_cause_header.go
@@ -0,0 +1,47 @@
+package sctp
+
+import (
+ "encoding/binary"
+)
+
+// errorCauseHeader represents the shared header that is shared by all error causes
+type errorCauseHeader struct {
+ code errorCauseCode
+ len uint16
+ raw []byte
+}
+
+const (
+ errorCauseHeaderLength = 4
+)
+
+func (e *errorCauseHeader) marshal() ([]byte, error) {
+ e.len = uint16(len(e.raw)) + uint16(errorCauseHeaderLength)
+ raw := make([]byte, e.len)
+ binary.BigEndian.PutUint16(raw[0:], uint16(e.code))
+ binary.BigEndian.PutUint16(raw[2:], e.len)
+ copy(raw[errorCauseHeaderLength:], e.raw)
+
+ return raw, nil
+}
+
+func (e *errorCauseHeader) unmarshal(raw []byte) error {
+ e.code = errorCauseCode(binary.BigEndian.Uint16(raw[0:]))
+ e.len = binary.BigEndian.Uint16(raw[2:])
+ valueLength := e.len - errorCauseHeaderLength
+ e.raw = raw[errorCauseHeaderLength : errorCauseHeaderLength+valueLength]
+ return nil
+}
+
+func (e *errorCauseHeader) length() uint16 {
+ return e.len
+}
+
+func (e *errorCauseHeader) errorCauseCode() errorCauseCode {
+ return e.code
+}
+
+// String makes errorCauseHeader printable
+func (e errorCauseHeader) String() string {
+ return e.code.String()
+}
diff --git a/vendor/github.com/pion/sctp/error_cause_invalid_mandatory_parameter.go b/vendor/github.com/pion/sctp/error_cause_invalid_mandatory_parameter.go
new file mode 100644
index 0000000..3da8b47
--- /dev/null
+++ b/vendor/github.com/pion/sctp/error_cause_invalid_mandatory_parameter.go
@@ -0,0 +1,19 @@
+package sctp
+
+// errorCauseInvalidMandatoryParameter represents an SCTP error cause
+type errorCauseInvalidMandatoryParameter struct {
+ errorCauseHeader
+}
+
+func (e *errorCauseInvalidMandatoryParameter) marshal() ([]byte, error) {
+ return e.errorCauseHeader.marshal()
+}
+
+func (e *errorCauseInvalidMandatoryParameter) unmarshal(raw []byte) error {
+ return e.errorCauseHeader.unmarshal(raw)
+}
+
+// String makes errorCauseInvalidMandatoryParameter printable
+func (e *errorCauseInvalidMandatoryParameter) String() string {
+ return e.errorCauseHeader.String()
+}
diff --git a/vendor/github.com/pion/sctp/error_cause_protocol_violation.go b/vendor/github.com/pion/sctp/error_cause_protocol_violation.go
new file mode 100644
index 0000000..8b457f4
--- /dev/null
+++ b/vendor/github.com/pion/sctp/error_cause_protocol_violation.go
@@ -0,0 +1,50 @@
+package sctp
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+/*
+ This error cause MAY be included in ABORT chunks that are sent
+ because an SCTP endpoint detects a protocol violation of the peer
+ that is not covered by the error causes described in Section 3.3.10.1
+ to Section 3.3.10.12. An implementation MAY provide additional
+ information specifying what kind of protocol violation has been
+ detected.
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Cause Code=13 | Cause Length=Variable |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ / Additional Information /
+ \ \
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+type errorCauseProtocolViolation struct {
+ errorCauseHeader
+ additionalInformation []byte
+}
+
+func (e *errorCauseProtocolViolation) marshal() ([]byte, error) {
+ e.raw = e.additionalInformation
+ return e.errorCauseHeader.marshal()
+}
+
+func (e *errorCauseProtocolViolation) unmarshal(raw []byte) error {
+ err := e.errorCauseHeader.unmarshal(raw)
+ if err != nil {
+ return errors.Wrap(err, "Unable to unmarshal Protocol Violation error")
+ }
+
+ e.additionalInformation = e.raw
+
+ return nil
+}
+
+// String makes errorCauseProtocolViolation printable
+func (e *errorCauseProtocolViolation) String() string {
+ return fmt.Sprintf("%s: %s", e.errorCauseHeader, e.additionalInformation)
+}
diff --git a/vendor/github.com/pion/sctp/error_cause_unrecognized_chunk_type.go b/vendor/github.com/pion/sctp/error_cause_unrecognized_chunk_type.go
new file mode 100644
index 0000000..fee9a36
--- /dev/null
+++ b/vendor/github.com/pion/sctp/error_cause_unrecognized_chunk_type.go
@@ -0,0 +1,28 @@
+package sctp
+
+// errorCauseUnrecognizedChunkType represents an SCTP error cause
+type errorCauseUnrecognizedChunkType struct {
+ errorCauseHeader
+ unrecognizedChunk []byte
+}
+
+func (e *errorCauseUnrecognizedChunkType) marshal() ([]byte, error) {
+ e.code = unrecognizedChunkType
+ e.errorCauseHeader.raw = e.unrecognizedChunk
+ return e.errorCauseHeader.marshal()
+}
+
+func (e *errorCauseUnrecognizedChunkType) unmarshal(raw []byte) error {
+ err := e.errorCauseHeader.unmarshal(raw)
+ if err != nil {
+ return err
+ }
+
+ e.unrecognizedChunk = e.errorCauseHeader.raw
+ return nil
+}
+
+// String makes errorCauseUnrecognizedChunkType printable
+func (e *errorCauseUnrecognizedChunkType) String() string {
+ return e.errorCauseHeader.String()
+}
diff --git a/vendor/github.com/pion/sctp/go.mod b/vendor/github.com/pion/sctp/go.mod
new file mode 100644
index 0000000..022e2fc
--- /dev/null
+++ b/vendor/github.com/pion/sctp/go.mod
@@ -0,0 +1,14 @@
+module github.com/pion/sctp
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/kr/pretty v0.1.0 // indirect
+ github.com/pion/logging v0.2.2
+ github.com/pion/randutil v0.1.0
+ github.com/pion/transport v0.10.1
+ github.com/pkg/errors v0.9.1
+ github.com/stretchr/testify v1.6.1
+ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
+)
+
+go 1.13
diff --git a/vendor/github.com/pion/sctp/go.sum b/vendor/github.com/pion/sctp/go.sum
new file mode 100644
index 0000000..3744262
--- /dev/null
+++ b/vendor/github.com/pion/sctp/go.sum
@@ -0,0 +1,36 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM=
+github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/sctp/packet.go b/vendor/github.com/pion/sctp/packet.go
new file mode 100644
index 0000000..dcc650b
--- /dev/null
+++ b/vendor/github.com/pion/sctp/packet.go
@@ -0,0 +1,178 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "hash/crc32"
+
+ "github.com/pkg/errors"
+)
+
+// Create the crc32 table we'll use for the checksum
+var castagnoliTable = crc32.MakeTable(crc32.Castagnoli) // nolint:gochecknoglobals
+
+// Allocate and zero this data once.
+// We need to use it for the checksum and don't want to allocate/clear each time.
+var fourZeroes [4]byte // nolint:gochecknoglobals
+
+/*
+Packet represents an SCTP packet, defined in https://tools.ietf.org/html/rfc4960#section-3
+An SCTP packet is composed of a common header and chunks. A chunk
+contains either control information or user data.
+
+
+ SCTP Packet Format
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Common Header |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Chunk #1 |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| ... |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Chunk #n |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+ SCTP Common Header Format
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Source Value Number | Destination Value Number |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Verification Tag |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Checksum |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+*/
+type packet struct {
+ sourcePort uint16
+ destinationPort uint16
+ verificationTag uint32
+ chunks []chunk
+}
+
+const (
+ packetHeaderSize = 12
+)
+
+func (p *packet) unmarshal(raw []byte) error {
+ if len(raw) < packetHeaderSize {
+ return errors.Errorf("raw only %d bytes, %d is the minimum length for a SCTP packet", len(raw), packetHeaderSize)
+ }
+
+ p.sourcePort = binary.BigEndian.Uint16(raw[0:])
+ p.destinationPort = binary.BigEndian.Uint16(raw[2:])
+ p.verificationTag = binary.BigEndian.Uint32(raw[4:])
+
+ offset := packetHeaderSize
+ for {
+ // Exact match, no more chunks
+ if offset == len(raw) {
+ break
+ } else if offset+chunkHeaderSize > len(raw) {
+ return errors.Errorf("Unable to parse SCTP chunk, not enough data for complete header: offset %d remaining %d", offset, len(raw))
+ }
+
+ var c chunk
+ switch chunkType(raw[offset]) {
+ case ctInit:
+ c = &chunkInit{}
+ case ctInitAck:
+ c = &chunkInitAck{}
+ case ctAbort:
+ c = &chunkAbort{}
+ case ctCookieEcho:
+ c = &chunkCookieEcho{}
+ case ctCookieAck:
+ c = &chunkCookieAck{}
+ case ctHeartbeat:
+ c = &chunkHeartbeat{}
+ case ctPayloadData:
+ c = &chunkPayloadData{}
+ case ctSack:
+ c = &chunkSelectiveAck{}
+ case ctReconfig:
+ c = &chunkReconfig{}
+ case ctForwardTSN:
+ c = &chunkForwardTSN{}
+ case ctError:
+ c = &chunkError{}
+ default:
+ return errors.Errorf("Failed to unmarshal, contains unknown chunk type %s", chunkType(raw[offset]).String())
+ }
+
+ if err := c.unmarshal(raw[offset:]); err != nil {
+ return err
+ }
+
+ p.chunks = append(p.chunks, c)
+ chunkValuePadding := getPadding(c.valueLength())
+ offset += chunkHeaderSize + c.valueLength() + chunkValuePadding
+ }
+ theirChecksum := binary.LittleEndian.Uint32(raw[8:])
+ ourChecksum := generatePacketChecksum(raw)
+ if theirChecksum != ourChecksum {
+ return errors.Errorf("Checksum mismatch theirs: %d ours: %d", theirChecksum, ourChecksum)
+ }
+ return nil
+}
+
+func (p *packet) marshal() ([]byte, error) {
+ raw := make([]byte, packetHeaderSize)
+
+ // Populate static headers
+ // 8-12 is Checksum which will be populated when packet is complete
+ binary.BigEndian.PutUint16(raw[0:], p.sourcePort)
+ binary.BigEndian.PutUint16(raw[2:], p.destinationPort)
+ binary.BigEndian.PutUint32(raw[4:], p.verificationTag)
+
+ // Populate chunks
+ for _, c := range p.chunks {
+ chunkRaw, err := c.marshal()
+ if err != nil {
+ return nil, err
+ }
+ raw = append(raw, chunkRaw...)
+
+ paddingNeeded := getPadding(len(raw))
+ if paddingNeeded != 0 {
+ raw = append(raw, make([]byte, paddingNeeded)...)
+ }
+ }
+
+ // Checksum is already in BigEndian
+ // Using LittleEndian.PutUint32 stops it from being flipped
+ binary.LittleEndian.PutUint32(raw[8:], generatePacketChecksum(raw))
+ return raw, nil
+}
+
+func generatePacketChecksum(raw []byte) (sum uint32) {
+ // Fastest way to do a crc32 without allocating.
+ sum = crc32.Update(sum, castagnoliTable, raw[0:8])
+ sum = crc32.Update(sum, castagnoliTable, fourZeroes[:])
+ sum = crc32.Update(sum, castagnoliTable, raw[12:])
+ return sum
+}
+
+// String makes packet printable
+func (p *packet) String() string {
+ format := `Packet:
+ sourcePort: %d
+ destinationPort: %d
+ verificationTag: %d
+ `
+ res := fmt.Sprintf(format,
+ p.sourcePort,
+ p.destinationPort,
+ p.verificationTag,
+ )
+ for i, chunk := range p.chunks {
+ res += fmt.Sprintf("Chunk %d:\n %s", i, chunk)
+ }
+ return res
+}
diff --git a/vendor/github.com/pion/sctp/param.go b/vendor/github.com/pion/sctp/param.go
new file mode 100644
index 0000000..08e46d1
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param.go
@@ -0,0 +1,35 @@
+package sctp
+
+import (
+ "github.com/pkg/errors"
+)
+
+type param interface {
+ marshal() ([]byte, error)
+ length() int
+}
+
+func buildParam(t paramType, rawParam []byte) (param, error) {
+ switch t {
+ case forwardTSNSupp:
+ return (&paramForwardTSNSupported{}).unmarshal(rawParam)
+ case supportedExt:
+ return (&paramSupportedExtensions{}).unmarshal(rawParam)
+ case random:
+ return (&paramRandom{}).unmarshal(rawParam)
+ case reqHMACAlgo:
+ return (&paramRequestedHMACAlgorithm{}).unmarshal(rawParam)
+ case chunkList:
+ return (&paramChunkList{}).unmarshal(rawParam)
+ case stateCookie:
+ return (&paramStateCookie{}).unmarshal(rawParam)
+ case heartbeatInfo:
+ return (&paramHeartbeatInfo{}).unmarshal(rawParam)
+ case outSSNResetReq:
+ return (&paramOutgoingResetRequest{}).unmarshal(rawParam)
+ case reconfigResp:
+ return (&paramReconfigResponse{}).unmarshal(rawParam)
+ default:
+ return nil, errors.Errorf("Unhandled ParamType %v", t)
+ }
+}
diff --git a/vendor/github.com/pion/sctp/param_chunk_list.go b/vendor/github.com/pion/sctp/param_chunk_list.go
new file mode 100644
index 0000000..4ea484c
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_chunk_list.go
@@ -0,0 +1,28 @@
+package sctp
+
+type paramChunkList struct {
+ paramHeader
+ chunkTypes []chunkType
+}
+
+func (c *paramChunkList) marshal() ([]byte, error) {
+ c.typ = chunkList
+ c.raw = make([]byte, len(c.chunkTypes))
+ for i, t := range c.chunkTypes {
+ c.raw[i] = byte(t)
+ }
+
+ return c.paramHeader.marshal()
+}
+
+func (c *paramChunkList) unmarshal(raw []byte) (param, error) {
+ err := c.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+ for _, t := range c.raw {
+ c.chunkTypes = append(c.chunkTypes, chunkType(t))
+ }
+
+ return c, nil
+}
diff --git a/vendor/github.com/pion/sctp/param_forward_tsn_supported.go b/vendor/github.com/pion/sctp/param_forward_tsn_supported.go
new file mode 100644
index 0000000..62de155
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_forward_tsn_supported.go
@@ -0,0 +1,28 @@
+package sctp
+
+// At the initialization of the association, the sender of the INIT or
+// INIT ACK chunk MAY include this OPTIONAL parameter to inform its peer
+// that it is able to support the Forward TSN chunk
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Parameter Type = 49152 | Parameter Length = 4 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+type paramForwardTSNSupported struct {
+ paramHeader
+}
+
+func (f *paramForwardTSNSupported) marshal() ([]byte, error) {
+ f.typ = forwardTSNSupp
+ f.raw = []byte{}
+ return f.paramHeader.marshal()
+}
+
+func (f *paramForwardTSNSupported) unmarshal(raw []byte) (param, error) {
+ err := f.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
diff --git a/vendor/github.com/pion/sctp/param_heartbeat_info.go b/vendor/github.com/pion/sctp/param_heartbeat_info.go
new file mode 100644
index 0000000..47f64eb
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_heartbeat_info.go
@@ -0,0 +1,21 @@
+package sctp
+
+type paramHeartbeatInfo struct {
+ paramHeader
+ heartbeatInformation []byte
+}
+
+func (h *paramHeartbeatInfo) marshal() ([]byte, error) {
+ h.typ = heartbeatInfo
+ h.raw = h.heartbeatInformation
+ return h.paramHeader.marshal()
+}
+
+func (h *paramHeartbeatInfo) unmarshal(raw []byte) (param, error) {
+ err := h.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+ h.heartbeatInformation = h.raw
+ return h, nil
+}
diff --git a/vendor/github.com/pion/sctp/param_outgoing_reset_request.go b/vendor/github.com/pion/sctp/param_outgoing_reset_request.go
new file mode 100644
index 0000000..ceae178
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_outgoing_reset_request.go
@@ -0,0 +1,88 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "errors"
+)
+
+const (
+ paramOutgoingResetRequestStreamIdentifiersOffset = 12
+)
+
+// This parameter is used by the sender to request the reset of some or
+// all outgoing streams.
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Parameter Type = 13 | Parameter Length = 16 + 2 * N |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Re-configuration Request Sequence Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Re-configuration Response Sequence Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Sender's Last Assigned TSN |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Stream Number 1 (optional) | Stream Number 2 (optional) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// / ...... /
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Stream Number N-1 (optional) | Stream Number N (optional) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+type paramOutgoingResetRequest struct {
+ paramHeader
+ // reconfigRequestSequenceNumber is used to identify the request. It is a monotonically
+ // increasing number that is initialized to the same value as the
+ // initial TSN. It is increased by 1 whenever sending a new Re-
+ // configuration Request Parameter.
+ reconfigRequestSequenceNumber uint32
+ // When this Outgoing SSN Reset Request Parameter is sent in response
+ // to an Incoming SSN Reset Request Parameter, this parameter is also
+ // an implicit response to the incoming request. This field then
+ // holds the Re-configuration Request Sequence Number of the incoming
+ // request. In other cases, it holds the next expected
+ // Re-configuration Request Sequence Number minus 1.
+ reconfigResponseSequenceNumber uint32
+ // This value holds the next TSN minus 1 -- in other words, the last
+ // TSN that this sender assigned.
+ senderLastTSN uint32
+ // This optional field, if included, is used to indicate specific
+ // streams that are to be reset. If no streams are listed, then all
+ // streams are to be reset.
+ streamIdentifiers []uint16
+}
+
+var errSSNResetRequestParamTooShort = errors.New("outgoing SSN reset request parameter too short")
+
+func (r *paramOutgoingResetRequest) marshal() ([]byte, error) {
+ r.typ = outSSNResetReq
+ r.raw = make([]byte, paramOutgoingResetRequestStreamIdentifiersOffset+2*len(r.streamIdentifiers))
+ binary.BigEndian.PutUint32(r.raw, r.reconfigRequestSequenceNumber)
+ binary.BigEndian.PutUint32(r.raw[4:], r.reconfigResponseSequenceNumber)
+ binary.BigEndian.PutUint32(r.raw[8:], r.senderLastTSN)
+ for i, sID := range r.streamIdentifiers {
+ binary.BigEndian.PutUint16(r.raw[paramOutgoingResetRequestStreamIdentifiersOffset+2*i:], sID)
+ }
+ return r.paramHeader.marshal()
+}
+
+func (r *paramOutgoingResetRequest) unmarshal(raw []byte) (param, error) {
+ err := r.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+ if len(r.raw) < paramOutgoingResetRequestStreamIdentifiersOffset {
+ return nil, errSSNResetRequestParamTooShort
+ }
+ r.reconfigRequestSequenceNumber = binary.BigEndian.Uint32(r.raw)
+ r.reconfigResponseSequenceNumber = binary.BigEndian.Uint32(r.raw[4:])
+ r.senderLastTSN = binary.BigEndian.Uint32(r.raw[8:])
+
+ lim := (len(r.raw) - paramOutgoingResetRequestStreamIdentifiersOffset) / 2
+ r.streamIdentifiers = make([]uint16, lim)
+ for i := 0; i < lim; i++ {
+ r.streamIdentifiers[i] = binary.BigEndian.Uint16(r.raw[paramOutgoingResetRequestStreamIdentifiersOffset+2*i:])
+ }
+
+ return r, nil
+}
diff --git a/vendor/github.com/pion/sctp/param_random.go b/vendor/github.com/pion/sctp/param_random.go
new file mode 100644
index 0000000..dc454b3
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_random.go
@@ -0,0 +1,21 @@
+package sctp
+
+type paramRandom struct {
+ paramHeader
+ randomData []byte
+}
+
+func (r *paramRandom) marshal() ([]byte, error) {
+ r.typ = random
+ r.raw = r.randomData
+ return r.paramHeader.marshal()
+}
+
+func (r *paramRandom) unmarshal(raw []byte) (param, error) {
+ err := r.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+ r.randomData = r.raw
+ return r, nil
+}
diff --git a/vendor/github.com/pion/sctp/param_reconfig_response.go b/vendor/github.com/pion/sctp/param_reconfig_response.go
new file mode 100644
index 0000000..d9eab55
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_reconfig_response.go
@@ -0,0 +1,92 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+)
+
+// This parameter is used by the receiver of a Re-configuration Request
+// Parameter to respond to the request.
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Parameter Type = 16 | Parameter Length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Re-configuration Response Sequence Number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Result |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Sender's Next TSN (optional) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Receiver's Next TSN (optional) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+type paramReconfigResponse struct {
+ paramHeader
+ // This value is copied from the request parameter and is used by the
+ // receiver of the Re-configuration Response Parameter to tie the
+ // response to the request.
+ reconfigResponseSequenceNumber uint32
+ // This value describes the result of the processing of the request.
+ result reconfigResult
+}
+
+type reconfigResult uint32
+
+const (
+ reconfigResultSuccessNOP reconfigResult = 0
+ reconfigResultSuccessPerformed reconfigResult = 1
+ reconfigResultDenied reconfigResult = 2
+ reconfigResultErrorWrongSSN reconfigResult = 3
+ reconfigResultErrorRequestAlreadyInProgress reconfigResult = 4
+ reconfigResultErrorBadSequenceNumber reconfigResult = 5
+ reconfigResultInProgress reconfigResult = 6
+)
+
+var errReconfigRespParamTooShort = errors.New("reconfig response parameter too short")
+
+func (t reconfigResult) String() string {
+ switch t {
+ case reconfigResultSuccessNOP:
+ return "0: Success - Nothing to do"
+ case reconfigResultSuccessPerformed:
+ return "1: Success - Performed"
+ case reconfigResultDenied:
+ return "2: Denied"
+ case reconfigResultErrorWrongSSN:
+ return "3: Error - Wrong SSN"
+ case reconfigResultErrorRequestAlreadyInProgress:
+ return "4: Error - Request already in progress"
+ case reconfigResultErrorBadSequenceNumber:
+ return "5: Error - Bad Sequence Number"
+ case reconfigResultInProgress:
+ return "6: In progress"
+ default:
+ return fmt.Sprintf("Unknown reconfigResult: %d", t)
+ }
+}
+
+func (r *paramReconfigResponse) marshal() ([]byte, error) {
+ r.typ = reconfigResp
+ r.raw = make([]byte, 8)
+ binary.BigEndian.PutUint32(r.raw, r.reconfigResponseSequenceNumber)
+ binary.BigEndian.PutUint32(r.raw[4:], uint32(r.result))
+
+ return r.paramHeader.marshal()
+}
+
+func (r *paramReconfigResponse) unmarshal(raw []byte) (param, error) {
+ err := r.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+ if len(r.raw) < 8 {
+ return nil, errReconfigRespParamTooShort
+ }
+ r.reconfigResponseSequenceNumber = binary.BigEndian.Uint32(r.raw)
+ r.result = reconfigResult(binary.BigEndian.Uint32(r.raw[4:]))
+
+ return r, nil
+}
diff --git a/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go b/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go
new file mode 100644
index 0000000..b520fe3
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go
@@ -0,0 +1,73 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+type hmacAlgorithm uint16
+
+const (
+ hmacResv1 hmacAlgorithm = 0
+ hmacSHA128 = 1
+ hmacResv2 hmacAlgorithm = 2
+ hmacSHA256 hmacAlgorithm = 3
+)
+
+func (c hmacAlgorithm) String() string {
+ switch c {
+ case hmacResv1:
+ return "HMAC Reserved (0x00)"
+ case hmacSHA128:
+ return "HMAC SHA-128"
+ case hmacResv2:
+ return "HMAC Reserved (0x02)"
+ case hmacSHA256:
+ return "HMAC SHA-256"
+ default:
+ return fmt.Sprintf("Unknown HMAC Algorithm type: %d", c)
+ }
+}
+
+type paramRequestedHMACAlgorithm struct {
+ paramHeader
+ availableAlgorithms []hmacAlgorithm
+}
+
+func (r *paramRequestedHMACAlgorithm) marshal() ([]byte, error) {
+ r.typ = reqHMACAlgo
+ r.raw = make([]byte, len(r.availableAlgorithms)*2)
+ i := 0
+ for _, a := range r.availableAlgorithms {
+ binary.BigEndian.PutUint16(r.raw[i:], uint16(a))
+ i += 2
+ }
+
+ return r.paramHeader.marshal()
+}
+
+func (r *paramRequestedHMACAlgorithm) unmarshal(raw []byte) (param, error) {
+ err := r.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+
+ i := 0
+ for i < len(r.raw) {
+ a := hmacAlgorithm(binary.BigEndian.Uint16(r.raw[i:]))
+ switch a {
+ case hmacSHA128:
+ fallthrough
+ case hmacSHA256:
+ r.availableAlgorithms = append(r.availableAlgorithms, a)
+ default:
+ return nil, errors.Errorf("Invalid algorithm type '%v'", a)
+ }
+
+ i += 2
+ }
+
+ return r, nil
+}
diff --git a/vendor/github.com/pion/sctp/param_state_cookie.go b/vendor/github.com/pion/sctp/param_state_cookie.go
new file mode 100644
index 0000000..9681267
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_state_cookie.go
@@ -0,0 +1,46 @@
+package sctp
+
+import (
+ "crypto/rand"
+ "fmt"
+)
+
+type paramStateCookie struct {
+ paramHeader
+ cookie []byte
+}
+
+func newRandomStateCookie() (*paramStateCookie, error) {
+ randCookie := make([]byte, 32)
+ _, err := rand.Read(randCookie)
+ // crypto/rand.Read returns n == len(b) if and only if err == nil.
+ if err != nil {
+ return nil, err
+ }
+
+ s := &paramStateCookie{
+ cookie: randCookie,
+ }
+
+ return s, nil
+}
+
+func (s *paramStateCookie) marshal() ([]byte, error) {
+ s.typ = stateCookie
+ s.raw = s.cookie
+ return s.paramHeader.marshal()
+}
+
+func (s *paramStateCookie) unmarshal(raw []byte) (param, error) {
+ err := s.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+ s.cookie = s.raw
+ return s, nil
+}
+
+// String makes paramStateCookie printable
+func (s *paramStateCookie) String() string {
+ return fmt.Sprintf("%s: %s", s.paramHeader, s.cookie)
+}
diff --git a/vendor/github.com/pion/sctp/param_supported_extensions.go b/vendor/github.com/pion/sctp/param_supported_extensions.go
new file mode 100644
index 0000000..2935524
--- /dev/null
+++ b/vendor/github.com/pion/sctp/param_supported_extensions.go
@@ -0,0 +1,29 @@
+package sctp
+
+type paramSupportedExtensions struct {
+ paramHeader
+ ChunkTypes []chunkType
+}
+
+func (s *paramSupportedExtensions) marshal() ([]byte, error) {
+ s.typ = supportedExt
+ s.raw = make([]byte, len(s.ChunkTypes))
+ for i, c := range s.ChunkTypes {
+ s.raw[i] = byte(c)
+ }
+
+ return s.paramHeader.marshal()
+}
+
+func (s *paramSupportedExtensions) unmarshal(raw []byte) (param, error) {
+ err := s.paramHeader.unmarshal(raw)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, t := range s.raw {
+ s.ChunkTypes = append(s.ChunkTypes, chunkType(t))
+ }
+
+ return s, nil
+}
diff --git a/vendor/github.com/pion/sctp/paramheader.go b/vendor/github.com/pion/sctp/paramheader.go
new file mode 100644
index 0000000..d88642b
--- /dev/null
+++ b/vendor/github.com/pion/sctp/paramheader.go
@@ -0,0 +1,63 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+type paramHeader struct {
+ typ paramType
+ len int
+ raw []byte
+}
+
+const (
+ paramHeaderLength = 4
+)
+
+func (p *paramHeader) marshal() ([]byte, error) {
+ paramLengthPlusHeader := paramHeaderLength + len(p.raw)
+
+ rawParam := make([]byte, paramLengthPlusHeader)
+ binary.BigEndian.PutUint16(rawParam[0:], uint16(p.typ))
+ binary.BigEndian.PutUint16(rawParam[2:], uint16(paramLengthPlusHeader))
+ copy(rawParam[paramHeaderLength:], p.raw)
+
+ return rawParam, nil
+}
+
+func (p *paramHeader) unmarshal(raw []byte) error {
+ if len(raw) < paramHeaderLength {
+ return errors.New("param header too short")
+ }
+
+ paramLengthPlusHeader := binary.BigEndian.Uint16(raw[2:])
+ if int(paramLengthPlusHeader) < paramHeaderLength {
+ return errors.Errorf("param self reported length (%d) smaller than header length (%d)", int(paramLengthPlusHeader), paramHeaderLength)
+ }
+ if len(raw) < int(paramLengthPlusHeader) {
+ return errors.Errorf("param length (%d) shorter than its self reported length (%d)", len(raw), int(paramLengthPlusHeader))
+ }
+
+ typ, err := parseParamType(raw[0:])
+ if err != nil {
+ return errors.Wrap(err, "failed to parse param type")
+ }
+ p.typ = typ
+ p.raw = raw[paramHeaderLength:paramLengthPlusHeader]
+ p.len = int(paramLengthPlusHeader)
+
+ return nil
+}
+
+func (p *paramHeader) length() int {
+ return p.len
+}
+
+// String makes paramHeader printable
+func (p paramHeader) String() string {
+ return fmt.Sprintf("%s (%d): %s", p.typ, p.len, hex.Dump(p.raw))
+}
diff --git a/vendor/github.com/pion/sctp/paramtype.go b/vendor/github.com/pion/sctp/paramtype.go
new file mode 100644
index 0000000..bb0ee82
--- /dev/null
+++ b/vendor/github.com/pion/sctp/paramtype.go
@@ -0,0 +1,106 @@
+package sctp
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pkg/errors"
+)
+
+// paramType represents a SCTP INIT/INITACK parameter
+type paramType uint16
+
+const (
+ heartbeatInfo paramType = 1 // Heartbeat Info [RFC4960]
+ ipV4Addr paramType = 5 // IPv4 IP [RFC4960]
+ ipV6Addr paramType = 6 // IPv6 IP [RFC4960]
+ stateCookie paramType = 7 // State Cookie [RFC4960]
+ unrecognizedParam paramType = 8 // Unrecognized Parameters [RFC4960]
+ cookiePreservative paramType = 9 // Cookie Preservative [RFC4960]
+ hostNameAddr paramType = 11 // Host Name IP [RFC4960]
+ supportedAddrTypes paramType = 12 // Supported IP Types [RFC4960]
+ outSSNResetReq paramType = 13 // Outgoing SSN Reset Request Parameter [RFC6525]
+ incSSNResetReq paramType = 14 // Incoming SSN Reset Request Parameter [RFC6525]
+ ssnTSNResetReq paramType = 15 // SSN/TSN Reset Request Parameter [RFC6525]
+ reconfigResp paramType = 16 // Re-configuration Response Parameter [RFC6525]
+ addOutStreamsReq paramType = 17 // Add Outgoing Streams Request Parameter [RFC6525]
+ addIncStreamsReq paramType = 18 // Add Incoming Streams Request Parameter [RFC6525]
+ random paramType = 32770 // Random (0x8002) [RFC4805]
+ chunkList paramType = 32771 // Chunk List (0x8003) [RFC4895]
+ reqHMACAlgo paramType = 32772 // Requested HMAC Algorithm Parameter (0x8004) [RFC4895]
+ padding paramType = 32773 // Padding (0x8005)
+ supportedExt paramType = 32776 // Supported Extensions (0x8008) [RFC5061]
+ forwardTSNSupp paramType = 49152 // Forward TSN supported (0xC000) [RFC3758]
+ addIPAddr paramType = 49153 // Add IP IP (0xC001) [RFC5061]
+ delIPAddr paramType = 49154 // Delete IP IP (0xC002) [RFC5061]
+ errClauseInd paramType = 49155 // Error Cause Indication (0xC003) [RFC5061]
+ setPriAddr paramType = 49156 // Set Primary IP (0xC004) [RFC5061]
+ successInd paramType = 49157 // Success Indication (0xC005) [RFC5061]
+ adaptLayerInd paramType = 49158 // Adaptation Layer Indication (0xC006) [RFC5061]
+)
+
+func parseParamType(raw []byte) (paramType, error) {
+ if len(raw) < 2 {
+ return paramType(0), errors.New("packet to short")
+ }
+ return paramType(binary.BigEndian.Uint16(raw)), nil
+}
+
+func (p paramType) String() string {
+ switch p {
+ case heartbeatInfo:
+ return "Heartbeat Info"
+ case ipV4Addr:
+ return "IPv4 IP"
+ case ipV6Addr:
+ return "IPv6 IP"
+ case stateCookie:
+ return "State Cookie"
+ case unrecognizedParam:
+ return "Unrecognized Parameters"
+ case cookiePreservative:
+ return "Cookie Preservative"
+ case hostNameAddr:
+ return "Host Name IP"
+ case supportedAddrTypes:
+ return "Supported IP Types"
+ case outSSNResetReq:
+ return "Outgoing SSN Reset Request Parameter"
+ case incSSNResetReq:
+ return "Incoming SSN Reset Request Parameter"
+ case ssnTSNResetReq:
+ return "SSN/TSN Reset Request Parameter"
+ case reconfigResp:
+ return "Re-configuration Response Parameter"
+ case addOutStreamsReq:
+ return "Add Outgoing Streams Request Parameter"
+ case addIncStreamsReq:
+ return "Add Incoming Streams Request Parameter"
+ case random:
+ return "Random"
+ case chunkList:
+ return "Chunk List"
+ case reqHMACAlgo:
+ return "Requested HMAC Algorithm Parameter"
+ case padding:
+ return "Padding"
+ case supportedExt:
+ return "Supported Extensions"
+ case forwardTSNSupp:
+ return "Forward TSN supported"
+ case addIPAddr:
+ return "Add IP IP"
+ case delIPAddr:
+ return "Delete IP IP"
+ case errClauseInd:
+ return "Error Cause Indication"
+ case setPriAddr:
+ return "Set Primary IP"
+ case successInd:
+ return "Success Indication"
+ case adaptLayerInd:
+ return "Adaptation Layer Indication"
+ default:
+ return fmt.Sprintf("Unknown ParamType: %d", p)
+ }
+}
diff --git a/vendor/github.com/pion/sctp/payload_queue.go b/vendor/github.com/pion/sctp/payload_queue.go
new file mode 100644
index 0000000..2d1a35a
--- /dev/null
+++ b/vendor/github.com/pion/sctp/payload_queue.go
@@ -0,0 +1,179 @@
+package sctp
+
+import (
+ "fmt"
+ "sort"
+)
+
+type payloadQueue struct {
+ chunkMap map[uint32]*chunkPayloadData
+ sorted []uint32
+ dupTSN []uint32
+ nBytes int
+}
+
+func newPayloadQueue() *payloadQueue {
+ return &payloadQueue{chunkMap: map[uint32]*chunkPayloadData{}}
+}
+
+func (q *payloadQueue) updateSortedKeys() {
+ if q.sorted != nil {
+ return
+ }
+
+ q.sorted = make([]uint32, len(q.chunkMap))
+ i := 0
+ for k := range q.chunkMap {
+ q.sorted[i] = k
+ i++
+ }
+
+ sort.Slice(q.sorted, func(i, j int) bool {
+ return sna32LT(q.sorted[i], q.sorted[j])
+ })
+}
+
+func (q *payloadQueue) canPush(p *chunkPayloadData, cumulativeTSN uint32) bool {
+ _, ok := q.chunkMap[p.tsn]
+ if ok || sna32LTE(p.tsn, cumulativeTSN) {
+ return false
+ }
+ return true
+}
+
+func (q *payloadQueue) pushNoCheck(p *chunkPayloadData) {
+ q.chunkMap[p.tsn] = p
+ q.nBytes += len(p.userData)
+ q.sorted = nil
+}
+
+// push pushes a payload data. If the payload data is already in our queue or
+// older than our cumulativeTSN marker, it will be recored as duplications,
+// which can later be retrieved using popDuplicates.
+func (q *payloadQueue) push(p *chunkPayloadData, cumulativeTSN uint32) bool {
+ _, ok := q.chunkMap[p.tsn]
+ if ok || sna32LTE(p.tsn, cumulativeTSN) {
+ // Found the packet, log in dups
+ q.dupTSN = append(q.dupTSN, p.tsn)
+ return false
+ }
+
+ q.chunkMap[p.tsn] = p
+ q.nBytes += len(p.userData)
+ q.sorted = nil
+ return true
+}
+
+// pop pops only if the oldest chunk's TSN matches the given TSN.
+func (q *payloadQueue) pop(tsn uint32) (*chunkPayloadData, bool) {
+ q.updateSortedKeys()
+
+ if len(q.chunkMap) > 0 && tsn == q.sorted[0] {
+ q.sorted = q.sorted[1:]
+ if c, ok := q.chunkMap[tsn]; ok {
+ delete(q.chunkMap, tsn)
+ q.nBytes -= len(c.userData)
+ return c, true
+ }
+ }
+
+ return nil, false
+}
+
+// get returns reference to chunkPayloadData with the given TSN value.
+func (q *payloadQueue) get(tsn uint32) (*chunkPayloadData, bool) {
+ c, ok := q.chunkMap[tsn]
+ return c, ok
+}
+
+// popDuplicates returns an array of TSN values that were found duplicate.
+func (q *payloadQueue) popDuplicates() []uint32 {
+ dups := q.dupTSN
+ q.dupTSN = []uint32{}
+ return dups
+}
+
+func (q *payloadQueue) getGapAckBlocks(cumulativeTSN uint32) (gapAckBlocks []gapAckBlock) {
+ var b gapAckBlock
+
+ if len(q.chunkMap) == 0 {
+ return []gapAckBlock{}
+ }
+
+ q.updateSortedKeys()
+
+ for i, tsn := range q.sorted {
+ if i == 0 {
+ b.start = uint16(tsn - cumulativeTSN)
+ b.end = b.start
+ continue
+ }
+ diff := uint16(tsn - cumulativeTSN)
+ if b.end+1 == diff {
+ b.end++
+ } else {
+ gapAckBlocks = append(gapAckBlocks, gapAckBlock{
+ start: b.start,
+ end: b.end,
+ })
+ b.start = diff
+ b.end = diff
+ }
+ }
+
+ gapAckBlocks = append(gapAckBlocks, gapAckBlock{
+ start: b.start,
+ end: b.end,
+ })
+
+ return gapAckBlocks
+}
+
+func (q *payloadQueue) getGapAckBlocksString(cumulativeTSN uint32) string {
+ gapAckBlocks := q.getGapAckBlocks(cumulativeTSN)
+ str := fmt.Sprintf("cumTSN=%d", cumulativeTSN)
+ for _, b := range gapAckBlocks {
+ str += fmt.Sprintf(",%d-%d", b.start, b.end)
+ }
+ return str
+}
+
+func (q *payloadQueue) markAsAcked(tsn uint32) int {
+ var nBytesAcked int
+ if c, ok := q.chunkMap[tsn]; ok {
+ c.acked = true
+ c.retransmit = false
+ nBytesAcked = len(c.userData)
+ q.nBytes -= nBytesAcked
+ c.userData = []byte{}
+ }
+
+ return nBytesAcked
+}
+
+func (q *payloadQueue) getLastTSNReceived() (uint32, bool) {
+ q.updateSortedKeys()
+
+ qlen := len(q.sorted)
+ if qlen == 0 {
+ return 0, false
+ }
+ return q.sorted[qlen-1], true
+}
+
+func (q *payloadQueue) markAllToRetrasmit() {
+ for _, c := range q.chunkMap {
+ if c.acked || c.abandoned() {
+ continue
+ }
+ c.retransmit = true
+ }
+}
+
+func (q *payloadQueue) getNumBytes() int {
+ return q.nBytes
+}
+
+func (q *payloadQueue) size() int {
+ return len(q.chunkMap)
+}
diff --git a/vendor/github.com/pion/sctp/pending_queue.go b/vendor/github.com/pion/sctp/pending_queue.go
new file mode 100644
index 0000000..5f204d3
--- /dev/null
+++ b/vendor/github.com/pion/sctp/pending_queue.go
@@ -0,0 +1,138 @@
+package sctp
+
+import (
+ "github.com/pkg/errors"
+)
+
+// pendingBaseQueue
+
+type pendingBaseQueue struct {
+ queue []*chunkPayloadData
+}
+
+func newPendingBaseQueue() *pendingBaseQueue {
+ return &pendingBaseQueue{queue: []*chunkPayloadData{}}
+}
+
+func (q *pendingBaseQueue) push(c *chunkPayloadData) {
+ q.queue = append(q.queue, c)
+}
+
+func (q *pendingBaseQueue) pop() *chunkPayloadData {
+ if len(q.queue) == 0 {
+ return nil
+ }
+ c := q.queue[0]
+ q.queue = q.queue[1:]
+ return c
+}
+
+func (q *pendingBaseQueue) get(i int) *chunkPayloadData {
+ if len(q.queue) == 0 || i < 0 || i >= len(q.queue) {
+ return nil
+ }
+ return q.queue[i]
+}
+
+func (q *pendingBaseQueue) size() int {
+ return len(q.queue)
+}
+
+// pendingQueue
+
+type pendingQueue struct {
+ unorderedQueue *pendingBaseQueue
+ orderedQueue *pendingBaseQueue
+ nBytes int
+ selected bool
+ unorderedIsSelected bool
+}
+
+var (
+ errUnexpectedChuckPoppedUnordered = errors.New("unexpected chunk popped (unordered)")
+ errUnexpectedChuckPoppedOrdered = errors.New("unexpected chunk popped (ordered)")
+ errUnexpectedQState = errors.New("unexpected q state (should've been selected)")
+)
+
+func newPendingQueue() *pendingQueue {
+ return &pendingQueue{
+ unorderedQueue: newPendingBaseQueue(),
+ orderedQueue: newPendingBaseQueue(),
+ }
+}
+
+func (q *pendingQueue) push(c *chunkPayloadData) {
+ if c.unordered {
+ q.unorderedQueue.push(c)
+ } else {
+ q.orderedQueue.push(c)
+ }
+ q.nBytes += len(c.userData)
+}
+
+func (q *pendingQueue) peek() *chunkPayloadData {
+ if q.selected {
+ if q.unorderedIsSelected {
+ return q.unorderedQueue.get(0)
+ }
+ return q.orderedQueue.get(0)
+ }
+
+ if c := q.unorderedQueue.get(0); c != nil {
+ return c
+ }
+ return q.orderedQueue.get(0)
+}
+
+func (q *pendingQueue) pop(c *chunkPayloadData) error {
+ if q.selected {
+ var popped *chunkPayloadData
+ if q.unorderedIsSelected {
+ popped = q.unorderedQueue.pop()
+ if popped != c {
+ return errUnexpectedChuckPoppedUnordered
+ }
+ } else {
+ popped = q.orderedQueue.pop()
+ if popped != c {
+ return errUnexpectedChuckPoppedOrdered
+ }
+ }
+ if popped.endingFragment {
+ q.selected = false
+ }
+ } else {
+ if !c.beginningFragment {
+ return errUnexpectedQState
+ }
+ if c.unordered {
+ popped := q.unorderedQueue.pop()
+ if popped != c {
+ return errUnexpectedChuckPoppedUnordered
+ }
+ if !popped.endingFragment {
+ q.selected = true
+ q.unorderedIsSelected = true
+ }
+ } else {
+ popped := q.orderedQueue.pop()
+ if popped != c {
+ return errUnexpectedChuckPoppedOrdered
+ }
+ if !popped.endingFragment {
+ q.selected = true
+ q.unorderedIsSelected = false
+ }
+ }
+ }
+ q.nBytes -= len(c.userData)
+ return nil
+}
+
+func (q *pendingQueue) getNumBytes() int {
+ return q.nBytes
+}
+
+func (q *pendingQueue) size() int {
+ return q.unorderedQueue.size() + q.orderedQueue.size()
+}
diff --git a/vendor/github.com/pion/sctp/reassembly_queue.go b/vendor/github.com/pion/sctp/reassembly_queue.go
new file mode 100644
index 0000000..f71d59c
--- /dev/null
+++ b/vendor/github.com/pion/sctp/reassembly_queue.go
@@ -0,0 +1,353 @@
+package sctp
+
+import (
+ "io"
+ "sort"
+ "sync/atomic"
+
+ "github.com/pkg/errors"
+)
+
+func sortChunksByTSN(a []*chunkPayloadData) {
+ sort.Slice(a, func(i, j int) bool {
+ return sna32LT(a[i].tsn, a[j].tsn)
+ })
+}
+
+func sortChunksBySSN(a []*chunkSet) {
+ sort.Slice(a, func(i, j int) bool {
+ return sna16LT(a[i].ssn, a[j].ssn)
+ })
+}
+
+// chunkSet is a set of chunks that share the same SSN
+type chunkSet struct {
+ ssn uint16 // used only with the ordered chunks
+ ppi PayloadProtocolIdentifier
+ chunks []*chunkPayloadData
+}
+
+func newChunkSet(ssn uint16, ppi PayloadProtocolIdentifier) *chunkSet {
+ return &chunkSet{
+ ssn: ssn,
+ ppi: ppi,
+ chunks: []*chunkPayloadData{},
+ }
+}
+
+func (set *chunkSet) push(chunk *chunkPayloadData) bool {
+ // check if dup
+ for _, c := range set.chunks {
+ if c.tsn == chunk.tsn {
+ return false
+ }
+ }
+
+ // append and sort
+ set.chunks = append(set.chunks, chunk)
+ sortChunksByTSN(set.chunks)
+
+ // Check if we now have a complete set
+ complete := set.isComplete()
+ return complete
+}
+
+func (set *chunkSet) isComplete() bool {
+ // Condition for complete set
+ // 0. Has at least one chunk.
+ // 1. Begins with beginningFragment set to true
+ // 2. Ends with endingFragment set to true
+ // 3. TSN monotinically increase by 1 from beginning to end
+
+ // 0.
+ nChunks := len(set.chunks)
+ if nChunks == 0 {
+ return false
+ }
+
+ // 1.
+ if !set.chunks[0].beginningFragment {
+ return false
+ }
+
+ // 2.
+ if !set.chunks[nChunks-1].endingFragment {
+ return false
+ }
+
+ // 3.
+ var lastTSN uint32
+ for i, c := range set.chunks {
+ if i > 0 {
+ // Fragments must have contiguous TSN
+ // From RFC 4960 Section 3.3.1:
+ // When a user message is fragmented into multiple chunks, the TSNs are
+ // used by the receiver to reassemble the message. This means that the
+ // TSNs for each fragment of a fragmented user message MUST be strictly
+ // sequential.
+ if c.tsn != lastTSN+1 {
+ // mid or end fragment is missing
+ return false
+ }
+ }
+
+ lastTSN = c.tsn
+ }
+
+ return true
+}
+
+type reassemblyQueue struct {
+ si uint16
+ nextSSN uint16 // expected SSN for next ordered chunk
+ ordered []*chunkSet
+ unordered []*chunkSet
+ unorderedChunks []*chunkPayloadData
+ nBytes uint64
+}
+
+var errTryAgain = errors.New("try again")
+
+func newReassemblyQueue(si uint16) *reassemblyQueue {
+ // From RFC 4960 Sec 6.5:
+ // The Stream Sequence Number in all the streams MUST start from 0 when
+ // the association is established. Also, when the Stream Sequence
+ // Number reaches the value 65535 the next Stream Sequence Number MUST
+ // be set to 0.
+ return &reassemblyQueue{
+ si: si,
+ nextSSN: 0, // From RFC 4960 Sec 6.5:
+ ordered: make([]*chunkSet, 0),
+ unordered: make([]*chunkSet, 0),
+ }
+}
+
+func (r *reassemblyQueue) push(chunk *chunkPayloadData) bool {
+ var cset *chunkSet
+
+ if chunk.streamIdentifier != r.si {
+ return false
+ }
+
+ if chunk.unordered {
+ // First, insert into unorderedChunks array
+ r.unorderedChunks = append(r.unorderedChunks, chunk)
+ atomic.AddUint64(&r.nBytes, uint64(len(chunk.userData)))
+ sortChunksByTSN(r.unorderedChunks)
+
+ // Scan unorderedChunks that are contiguous (in TSN)
+ cset = r.findCompleteUnorderedChunkSet()
+
+ // If found, append the complete set to the unordered array
+ if cset != nil {
+ r.unordered = append(r.unordered, cset)
+ return true
+ }
+
+ return false
+ }
+
+ // This is an ordered chunk
+
+ if sna16LT(chunk.streamSequenceNumber, r.nextSSN) {
+ return false
+ }
+
+ // Check if a chunkSet with the SSN already exists
+ for _, set := range r.ordered {
+ if set.ssn == chunk.streamSequenceNumber {
+ cset = set
+ break
+ }
+ }
+
+ // If not found, create a new chunkSet
+ if cset == nil {
+ cset = newChunkSet(chunk.streamSequenceNumber, chunk.payloadType)
+ r.ordered = append(r.ordered, cset)
+ if !chunk.unordered {
+ sortChunksBySSN(r.ordered)
+ }
+ }
+
+ atomic.AddUint64(&r.nBytes, uint64(len(chunk.userData)))
+
+ return cset.push(chunk)
+}
+
+func (r *reassemblyQueue) findCompleteUnorderedChunkSet() *chunkSet {
+ startIdx := -1
+ nChunks := 0
+ var lastTSN uint32
+ var found bool
+
+ for i, c := range r.unorderedChunks {
+ // seek beigining
+ if c.beginningFragment {
+ startIdx = i
+ nChunks = 1
+ lastTSN = c.tsn
+
+ if c.endingFragment {
+ found = true
+ break
+ }
+ continue
+ }
+
+ if startIdx < 0 {
+ continue
+ }
+
+ // Check if contiguous in TSN
+ if c.tsn != lastTSN+1 {
+ startIdx = -1
+ continue
+ }
+
+ lastTSN = c.tsn
+ nChunks++
+
+ if c.endingFragment {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ return nil
+ }
+
+ // Extract the range of chunks
+ var chunks []*chunkPayloadData
+ chunks = append(chunks, r.unorderedChunks[startIdx:startIdx+nChunks]...)
+
+ r.unorderedChunks = append(
+ r.unorderedChunks[:startIdx],
+ r.unorderedChunks[startIdx+nChunks:]...)
+
+ chunkSet := newChunkSet(0, chunks[0].payloadType)
+ chunkSet.chunks = chunks
+
+ return chunkSet
+}
+
+func (r *reassemblyQueue) isReadable() bool {
+ // Check unordered first
+ if len(r.unordered) > 0 {
+ // The chunk sets in r.unordered should all be complete.
+ return true
+ }
+
+ // Check ordered sets
+ if len(r.ordered) > 0 {
+ cset := r.ordered[0]
+ if cset.isComplete() {
+ if sna16LTE(cset.ssn, r.nextSSN) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (r *reassemblyQueue) read(buf []byte) (int, PayloadProtocolIdentifier, error) {
+ var cset *chunkSet
+ // Check unordered first
+ switch {
+ case len(r.unordered) > 0:
+ cset = r.unordered[0]
+ r.unordered = r.unordered[1:]
+ case len(r.ordered) > 0:
+ // Now, check ordered
+ cset = r.ordered[0]
+ if !cset.isComplete() {
+ return 0, 0, errTryAgain
+ }
+ if sna16GT(cset.ssn, r.nextSSN) {
+ return 0, 0, errTryAgain
+ }
+ r.ordered = r.ordered[1:]
+ if cset.ssn == r.nextSSN {
+ r.nextSSN++
+ }
+ default:
+ return 0, 0, errTryAgain
+ }
+
+ // Concat all fragments into the buffer
+ nWritten := 0
+ ppi := cset.ppi
+ var err error
+ for _, c := range cset.chunks {
+ toCopy := len(c.userData)
+ r.subtractNumBytes(toCopy)
+ if err == nil {
+ n := copy(buf[nWritten:], c.userData)
+ nWritten += n
+ if n < toCopy {
+ err = io.ErrShortBuffer
+ }
+ }
+ }
+
+ return nWritten, ppi, err
+}
+
+func (r *reassemblyQueue) forwardTSNForOrdered(lastSSN uint16) {
+ // Use lastSSN to locate a chunkSet then remove it if the set has
+ // not been complete
+ keep := []*chunkSet{}
+ for _, set := range r.ordered {
+ if sna16LTE(set.ssn, lastSSN) {
+ if !set.isComplete() {
+ // drop the set
+ for _, c := range set.chunks {
+ r.subtractNumBytes(len(c.userData))
+ }
+ continue
+ }
+ }
+ keep = append(keep, set)
+ }
+ r.ordered = keep
+
+ // Finally, forward nextSSN
+ if sna16LTE(r.nextSSN, lastSSN) {
+ r.nextSSN = lastSSN + 1
+ }
+}
+
+func (r *reassemblyQueue) forwardTSNForUnordered(newCumulativeTSN uint32) {
+ // Remove all fragments in the unordered sets that contains chunks
+ // equal to or older than `newCumulativeTSN`.
+ // We know all sets in the r.unordered are complete ones.
+ // Just remove chunks that are equal to or older than newCumulativeTSN
+ // from the unorderedChunks
+ lastIdx := -1
+ for i, c := range r.unorderedChunks {
+ if sna32GT(c.tsn, newCumulativeTSN) {
+ break
+ }
+ lastIdx = i
+ }
+ if lastIdx >= 0 {
+ for _, c := range r.unorderedChunks[0 : lastIdx+1] {
+ r.subtractNumBytes(len(c.userData))
+ }
+ r.unorderedChunks = r.unorderedChunks[lastIdx+1:]
+ }
+}
+
+func (r *reassemblyQueue) subtractNumBytes(nBytes int) {
+ cur := atomic.LoadUint64(&r.nBytes)
+ if int(cur) >= nBytes {
+ atomic.AddUint64(&r.nBytes, -uint64(nBytes))
+ } else {
+ atomic.StoreUint64(&r.nBytes, 0)
+ }
+}
+
+func (r *reassemblyQueue) getNumBytes() int {
+ return int(atomic.LoadUint64(&r.nBytes))
+}
diff --git a/vendor/github.com/pion/sctp/renovate.json b/vendor/github.com/pion/sctp/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/sctp/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/sctp/rtx_timer.go b/vendor/github.com/pion/sctp/rtx_timer.go
new file mode 100644
index 0000000..14bc39d
--- /dev/null
+++ b/vendor/github.com/pion/sctp/rtx_timer.go
@@ -0,0 +1,219 @@
+package sctp
+
+import (
+ "math"
+ "sync"
+ "time"
+)
+
+const (
+ rtoInitial float64 = 3.0 * 1000 // msec
+ rtoMin float64 = 1.0 * 1000 // msec
+ rtoMax float64 = 60.0 * 1000 // msec
+ rtoAlpha float64 = 0.125
+ rtoBeta float64 = 0.25
+ maxInitRetrans uint = 8
+ pathMaxRetrans uint = 5
+ noMaxRetrans uint = 0
+)
+
+// rtoManager manages Rtx timeout values.
+// This is an implementation of RFC 4960 sec 6.3.1.
+type rtoManager struct {
+ srtt float64
+ rttvar float64
+ rto float64
+ noUpdate bool
+ mutex sync.RWMutex
+}
+
+// newRTOManager creates a new rtoManager.
+func newRTOManager() *rtoManager {
+ return &rtoManager{
+ rto: rtoInitial,
+ }
+}
+
+// setNewRTT takes a newly measured RTT then adjust the RTO in msec.
+func (m *rtoManager) setNewRTT(rtt float64) float64 {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if m.noUpdate {
+ return m.srtt
+ }
+
+ if m.srtt == 0 {
+ // First measurement
+ m.srtt = rtt
+ m.rttvar = rtt / 2
+ } else {
+ // Subsequent rtt measurement
+ m.rttvar = (1-rtoBeta)*m.rttvar + rtoBeta*(math.Abs(m.srtt-rtt))
+ m.srtt = (1-rtoAlpha)*m.srtt + rtoAlpha*rtt
+ }
+ m.rto = math.Min(math.Max(m.srtt+4*m.rttvar, rtoMin), rtoMax)
+ return m.srtt
+}
+
+// getRTO simply returns the current RTO in msec.
+func (m *rtoManager) getRTO() float64 {
+ m.mutex.RLock()
+ defer m.mutex.RUnlock()
+
+ return m.rto
+}
+
+// reset resets the RTO variables to the initial values.
+func (m *rtoManager) reset() {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if m.noUpdate {
+ return
+ }
+
+ m.srtt = 0
+ m.rttvar = 0
+ m.rto = rtoInitial
+}
+
+// set RTO value for testing
+func (m *rtoManager) setRTO(rto float64, noUpdate bool) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ m.rto = rto
+ m.noUpdate = noUpdate
+}
+
+// rtxTimerObserver is the inteface to a timer observer.
+// NOTE: Observers MUST NOT call start() or stop() method on rtxTimer
+// from within these callbacks.
+type rtxTimerObserver interface {
+ onRetransmissionTimeout(timerID int, n uint)
+ onRetransmissionFailure(timerID int)
+}
+
+// rtxTimer provides the retnransmission timer conforms with RFC 4960 Sec 6.3.1
+type rtxTimer struct {
+ id int
+ observer rtxTimerObserver
+ maxRetrans uint
+ stopFunc stopTimerLoop
+ closed bool
+ mutex sync.RWMutex
+}
+
+type stopTimerLoop func()
+
+// newRTXTimer creates a new retransmission timer.
+// if maxRetrans is set to 0, it will keep retransmitting until stop() is called.
+// (it will never make onRetransmissionFailure() callback.
+func newRTXTimer(id int, observer rtxTimerObserver, maxRetrans uint) *rtxTimer {
+ return &rtxTimer{
+ id: id,
+ observer: observer,
+ maxRetrans: maxRetrans,
+ }
+}
+
+// start starts the timer.
+func (t *rtxTimer) start(rto float64) bool {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ // this timer is already closed
+ if t.closed {
+ return false
+ }
+
+ // this is a noop if the timer is always running
+ if t.stopFunc != nil {
+ return false
+ }
+
+ // Note: rto value is intentionally not capped by RTO.Min to allow
+ // fast timeout for the tests. Non-test code should pass in the
+ // rto generated by rtoManager getRTO() method which caps the
+ // value at RTO.Min or at RTO.Max.
+ var nRtos uint
+
+ cancelCh := make(chan struct{})
+
+ go func() {
+ canceling := false
+
+ for !canceling {
+ timeout := calculateNextTimeout(rto, nRtos)
+ timer := time.NewTimer(time.Duration(timeout) * time.Millisecond)
+
+ select {
+ case <-timer.C:
+ nRtos++
+ if t.maxRetrans == 0 || nRtos <= t.maxRetrans {
+ t.observer.onRetransmissionTimeout(t.id, nRtos)
+ } else {
+ t.stop()
+ t.observer.onRetransmissionFailure(t.id)
+ }
+ case <-cancelCh:
+ canceling = true
+ timer.Stop()
+ }
+ }
+ }()
+
+ t.stopFunc = func() {
+ close(cancelCh)
+ }
+
+ return true
+}
+
+// stop stops the timer.
+func (t *rtxTimer) stop() {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ if t.stopFunc != nil {
+ t.stopFunc()
+ t.stopFunc = nil
+ }
+}
+
+// closes the timer. this is similar to stop() but subsequent start() call
+// will fail (the timer is no longer usable)
+func (t *rtxTimer) close() {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ if t.stopFunc != nil {
+ t.stopFunc()
+ t.stopFunc = nil
+ }
+
+ t.closed = true
+}
+
+// isRunning tests if the timer is running.
+// Debug purpose only
+func (t *rtxTimer) isRunning() bool {
+ t.mutex.RLock()
+ defer t.mutex.RUnlock()
+
+ return (t.stopFunc != nil)
+}
+
+func calculateNextTimeout(rto float64, nRtos uint) float64 {
+ // RFC 4096 sec 6.3.3. Handle T3-rtx Expiration
+ // E2) For the destination address for which the timer expires, set RTO
+ // <- RTO * 2 ("back off the timer"). The maximum value discussed
+ // in rule C7 above (RTO.max) may be used to provide an upper bound
+ // to this doubling operation.
+ if nRtos < 31 {
+ m := 1 << nRtos
+ return math.Min(rto*float64(m), rtoMax)
+ }
+ return rtoMax
+}
diff --git a/vendor/github.com/pion/sctp/sctp.go b/vendor/github.com/pion/sctp/sctp.go
new file mode 100644
index 0000000..e601342
--- /dev/null
+++ b/vendor/github.com/pion/sctp/sctp.go
@@ -0,0 +1,2 @@
+// Package sctp implements the SCTP spec
+package sctp
diff --git a/vendor/github.com/pion/sctp/stream.go b/vendor/github.com/pion/sctp/stream.go
new file mode 100644
index 0000000..8a17b97
--- /dev/null
+++ b/vendor/github.com/pion/sctp/stream.go
@@ -0,0 +1,357 @@
+package sctp
+
+import (
+ "io"
+ "math"
+ "sync"
+
+ "github.com/pion/logging"
+ "github.com/pkg/errors"
+)
+
+const (
+ // ReliabilityTypeReliable is used for reliable transmission
+ ReliabilityTypeReliable byte = 0
+ // ReliabilityTypeRexmit is used for partial reliability by retransmission count
+ ReliabilityTypeRexmit byte = 1
+ // ReliabilityTypeTimed is used for partial reliability by retransmission duration
+ ReliabilityTypeTimed byte = 2
+)
+
+// Stream represents an SCTP stream
+type Stream struct {
+ association *Association
+ lock sync.RWMutex
+ streamIdentifier uint16
+ defaultPayloadType PayloadProtocolIdentifier
+ reassemblyQueue *reassemblyQueue
+ sequenceNumber uint16
+ readNotifier *sync.Cond
+ readErr error
+ writeErr error
+ unordered bool
+ reliabilityType byte
+ reliabilityValue uint32
+ bufferedAmount uint64
+ bufferedAmountLow uint64
+ onBufferedAmountLow func()
+ log logging.LeveledLogger
+ name string
+}
+
+// StreamIdentifier returns the Stream identifier associated to the stream.
+func (s *Stream) StreamIdentifier() uint16 {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ return s.streamIdentifier
+}
+
+// SetDefaultPayloadType sets the default payload type used by Write.
+func (s *Stream) SetDefaultPayloadType(defaultPayloadType PayloadProtocolIdentifier) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.setDefaultPayloadType(defaultPayloadType)
+}
+
+// setDefaultPayloadType sets the defaultPayloadType. The caller should hold the lock.
+func (s *Stream) setDefaultPayloadType(defaultPayloadType PayloadProtocolIdentifier) {
+ s.defaultPayloadType = defaultPayloadType
+}
+
+// SetReliabilityParams sets reliability parameters for this stream.
+func (s *Stream) SetReliabilityParams(unordered bool, relType byte, relVal uint32) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.setReliabilityParams(unordered, relType, relVal)
+}
+
+// setReliabilityParams sets reliability parameters for this stream.
+// The caller should hold the lock.
+func (s *Stream) setReliabilityParams(unordered bool, relType byte, relVal uint32) {
+ s.log.Debugf("[%s] reliability params: ordered=%v type=%d value=%d",
+ s.name, !unordered, relType, relVal)
+ s.unordered = unordered
+ s.reliabilityType = relType
+ s.reliabilityValue = relVal
+}
+
+// Read reads a packet of len(p) bytes, dropping the Payload Protocol Identifier.
+// Returns EOF when the stream is reset or an error if the stream is closed
+// otherwise.
+func (s *Stream) Read(p []byte) (int, error) {
+ n, _, err := s.ReadSCTP(p)
+ return n, err
+}
+
+// ReadSCTP reads a packet of len(p) bytes and returns the associated Payload
+// Protocol Identifier.
+// Returns EOF when the stream is reset or an error if the stream is closed
+// otherwise.
+func (s *Stream) ReadSCTP(p []byte) (int, PayloadProtocolIdentifier, error) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ for {
+ n, ppi, err := s.reassemblyQueue.read(p)
+ if err == nil {
+ return n, ppi, nil
+ } else if errors.Is(err, io.ErrShortBuffer) {
+ return 0, PayloadProtocolIdentifier(0), err
+ }
+
+ err = s.readErr
+ if err != nil {
+ return 0, PayloadProtocolIdentifier(0), err
+ }
+
+ s.readNotifier.Wait()
+ }
+}
+
+func (s *Stream) handleData(pd *chunkPayloadData) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ var readable bool
+ if s.reassemblyQueue.push(pd) {
+ readable = s.reassemblyQueue.isReadable()
+ s.log.Debugf("[%s] reassemblyQueue readable=%v", s.name, readable)
+ if readable {
+ s.log.Debugf("[%s] readNotifier.signal()", s.name)
+ s.readNotifier.Signal()
+ s.log.Debugf("[%s] readNotifier.signal() done", s.name)
+ }
+ }
+}
+
+func (s *Stream) handleForwardTSNForOrdered(ssn uint16) {
+ var readable bool
+
+ func() {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ if s.unordered {
+ return // unordered chunks are handled by handleForwardUnordered method
+ }
+
+ // Remove all chunks older than or equal to the new TSN from
+ // the reassemblyQueue.
+ s.reassemblyQueue.forwardTSNForOrdered(ssn)
+ readable = s.reassemblyQueue.isReadable()
+ }()
+
+ // Notify the reader asynchronously if there's a data chunk to read.
+ if readable {
+ s.readNotifier.Signal()
+ }
+}
+
+func (s *Stream) handleForwardTSNForUnordered(newCumulativeTSN uint32) {
+ var readable bool
+
+ func() {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ if !s.unordered {
+ return // ordered chunks are handled by handleForwardTSNOrdered method
+ }
+
+ // Remove all chunks older than or equal to the new TSN from
+ // the reassemblyQueue.
+ s.reassemblyQueue.forwardTSNForUnordered(newCumulativeTSN)
+ readable = s.reassemblyQueue.isReadable()
+ }()
+
+ // Notify the reader asynchronously if there's a data chunk to read.
+ if readable {
+ s.readNotifier.Signal()
+ }
+}
+
+// Write writes len(p) bytes from p with the default Payload Protocol Identifier
+func (s *Stream) Write(p []byte) (n int, err error) {
+ return s.WriteSCTP(p, s.defaultPayloadType)
+}
+
+// WriteSCTP writes len(p) bytes from p to the DTLS connection
+func (s *Stream) WriteSCTP(p []byte, ppi PayloadProtocolIdentifier) (n int, err error) {
+ maxMessageSize := s.association.MaxMessageSize()
+ if len(p) > int(maxMessageSize) {
+ return 0, errors.Errorf("Outbound packet larger than maximum message size %v", math.MaxUint16)
+ }
+
+ s.lock.RLock()
+ err = s.writeErr
+ s.lock.RUnlock()
+ if err != nil {
+ return 0, err
+ }
+
+ chunks := s.packetize(p, ppi)
+
+ return len(p), s.association.sendPayloadData(chunks)
+}
+
+func (s *Stream) packetize(raw []byte, ppi PayloadProtocolIdentifier) []*chunkPayloadData {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ i := uint32(0)
+ remaining := uint32(len(raw))
+
+ // From draft-ietf-rtcweb-data-protocol-09, section 6:
+ // All Data Channel Establishment Protocol messages MUST be sent using
+ // ordered delivery and reliable transmission.
+ unordered := ppi != PayloadTypeWebRTCDCEP && s.unordered
+
+ var chunks []*chunkPayloadData
+ var head *chunkPayloadData
+ for remaining != 0 {
+ fragmentSize := min32(s.association.maxPayloadSize, remaining)
+
+ // Copy the userdata since we'll have to store it until acked
+ // and the caller may re-use the buffer in the mean time
+ userData := make([]byte, fragmentSize)
+ copy(userData, raw[i:i+fragmentSize])
+
+ chunk := &chunkPayloadData{
+ streamIdentifier: s.streamIdentifier,
+ userData: userData,
+ unordered: unordered,
+ beginningFragment: i == 0,
+ endingFragment: remaining-fragmentSize == 0,
+ immediateSack: false,
+ payloadType: ppi,
+ streamSequenceNumber: s.sequenceNumber,
+ head: head,
+ }
+
+ if head == nil {
+ head = chunk
+ }
+
+ chunks = append(chunks, chunk)
+
+ remaining -= fragmentSize
+ i += fragmentSize
+ }
+
+ // RFC 4960 Sec 6.6
+ // Note: When transmitting ordered and unordered data, an endpoint does
+ // not increment its Stream Sequence Number when transmitting a DATA
+ // chunk with U flag set to 1.
+ if !unordered {
+ s.sequenceNumber++
+ }
+
+ s.bufferedAmount += uint64(len(raw))
+ s.log.Tracef("[%s] bufferedAmount = %d", s.name, s.bufferedAmount)
+
+ return chunks
+}
+
+// Close closes the write-direction of the stream.
+// Future calls to Write are not permitted after calling Close.
+func (s *Stream) Close() error {
+ if sid, isOpen := func() (uint16, bool) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ isOpen := true
+ if s.writeErr == nil {
+ s.writeErr = errors.New("Stream closed")
+ } else {
+ isOpen = false
+ }
+
+ if s.readErr == nil {
+ s.readErr = io.EOF
+ } else {
+ isOpen = false
+ }
+ s.readNotifier.Broadcast() // broadcast regardless
+
+ return s.streamIdentifier, isOpen
+ }(); isOpen {
+ // Reset the outgoing stream
+ // https://tools.ietf.org/html/rfc6525
+ return s.association.sendResetRequest(sid)
+ }
+
+ return nil
+}
+
+// BufferedAmount returns the number of bytes of data currently queued to be sent over this stream.
+func (s *Stream) BufferedAmount() uint64 {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+
+ return s.bufferedAmount
+}
+
+// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing data that is
+// considered "low." Defaults to 0.
+func (s *Stream) BufferedAmountLowThreshold() uint64 {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+
+ return s.bufferedAmountLow
+}
+
+// SetBufferedAmountLowThreshold is used to update the threshold.
+// See BufferedAmountLowThreshold().
+func (s *Stream) SetBufferedAmountLowThreshold(th uint64) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.bufferedAmountLow = th
+}
+
+// OnBufferedAmountLow sets the callback handler which would be called when the number of
+// bytes of outgoing data buffered is lower than the threshold.
+func (s *Stream) OnBufferedAmountLow(f func()) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+
+ s.onBufferedAmountLow = f
+}
+
+// This method is called by association's readLoop (go-)routine to notify this stream
+// of the specified amount of outgoing data has been delivered to the peer.
+func (s *Stream) onBufferReleased(nBytesReleased int) {
+ if nBytesReleased <= 0 {
+ return
+ }
+
+ s.lock.Lock()
+
+ fromAmount := s.bufferedAmount
+
+ if s.bufferedAmount < uint64(nBytesReleased) {
+ s.bufferedAmount = 0
+ s.log.Errorf("[%s] released buffer size %d should be <= %d",
+ s.name, nBytesReleased, s.bufferedAmount)
+ } else {
+ s.bufferedAmount -= uint64(nBytesReleased)
+ }
+
+ s.log.Tracef("[%s] bufferedAmount = %d", s.name, s.bufferedAmount)
+
+ if s.onBufferedAmountLow != nil && fromAmount > s.bufferedAmountLow && s.bufferedAmount <= s.bufferedAmountLow {
+ f := s.onBufferedAmountLow
+ s.lock.Unlock()
+ f()
+ return
+ }
+
+ s.lock.Unlock()
+}
+
+func (s *Stream) getNumBytesInReassemblyQueue() int {
+ // No lock is required as it reads the size with atomic load function.
+ return s.reassemblyQueue.getNumBytes()
+}
diff --git a/vendor/github.com/pion/sctp/util.go b/vendor/github.com/pion/sctp/util.go
new file mode 100644
index 0000000..e2e54ab
--- /dev/null
+++ b/vendor/github.com/pion/sctp/util.go
@@ -0,0 +1,58 @@
+package sctp
+
+const (
+ paddingMultiple = 4
+)
+
+func getPadding(len int) int {
+ return (paddingMultiple - (len % paddingMultiple)) % paddingMultiple
+}
+
+func padByte(in []byte, cnt int) []byte {
+ if cnt < 0 {
+ cnt = 0
+ }
+ padding := make([]byte, cnt)
+ return append(in, padding...)
+}
+
+// Serial Number Arithmetic (RFC 1982)
+func sna32LT(i1, i2 uint32) bool {
+ return (i1 < i2 && i2-i1 < 1<<31) || (i1 > i2 && i1-i2 > 1<<31)
+}
+
+func sna32LTE(i1, i2 uint32) bool {
+ return i1 == i2 || sna32LT(i1, i2)
+}
+
+func sna32GT(i1, i2 uint32) bool {
+ return (i1 < i2 && (i2-i1) >= 1<<31) || (i1 > i2 && (i1-i2) <= 1<<31)
+}
+
+func sna32GTE(i1, i2 uint32) bool {
+ return i1 == i2 || sna32GT(i1, i2)
+}
+
+func sna32EQ(i1, i2 uint32) bool {
+ return i1 == i2
+}
+
+func sna16LT(i1, i2 uint16) bool {
+ return (i1 < i2 && (i2-i1) < 1<<15) || (i1 > i2 && (i1-i2) > 1<<15)
+}
+
+func sna16LTE(i1, i2 uint16) bool {
+ return i1 == i2 || sna16LT(i1, i2)
+}
+
+func sna16GT(i1, i2 uint16) bool {
+ return (i1 < i2 && (i2-i1) >= 1<<15) || (i1 > i2 && (i1-i2) <= 1<<15)
+}
+
+func sna16GTE(i1, i2 uint16) bool {
+ return i1 == i2 || sna16GT(i1, i2)
+}
+
+func sna16EQ(i1, i2 uint16) bool {
+ return i1 == i2
+}
diff --git a/vendor/github.com/pion/sdp/v3/.gitignore b/vendor/github.com/pion/sdp/v3/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/sdp/v3/.golangci.yml b/vendor/github.com/pion/sdp/v3/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/sdp/v3/LICENSE b/vendor/github.com/pion/sdp/v3/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/sdp/v3/README.md b/vendor/github.com/pion/sdp/v3/README.md
new file mode 100644
index 0000000..e7d7ff6
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/README.md
@@ -0,0 +1,59 @@
+<h1 align="center">
+ <br>
+ Pion SDP
+ <br>
+</h1>
+<h4 align="center">A Go implementation of the SDP</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-sdp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion SDP"></a>
+ <a href="https://sourcegraph.com/github.com/pion/sdp?badge"><img src="https://sourcegraph.com/github.com/pion/sdp/-/badge.svg" alt="Sourcegraph Widget"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/sdp"><img src="https://travis-ci.org/pion/sdp.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/sdp/v2"><img src="https://godoc.org/github.com/pion/sdp?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/sdp"><img src="https://codecov.io/gh/pion/sdp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/sdp"><img src="https://goreportcard.com/badge/github.com/pion/sdp" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *Public API, Initialization*
+* [Konstantin Itskov](https://github.com/trivigy) - *Fix documentation*
+* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter*
+* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
+* [Michael MacDonald](https://github.com/mjmac)
+* [Max Hawkins](https://github.com/maxhawkins)
+* [mchlrhw](https://github.com/mchlrhw)
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Guilherme Souza](https://github.com/gqgs)
+* [adwpc](https://github.com/adwpc) - *extmap add transport-cc*
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Luke S](https://github.com/encounter)
+* [Jerko Steiner](https://github.com/jeremija)
+* [Roman Romanenko](https://github.com/r-novel)
+* [Jason Brady](https://github.com/jbrady42)
+* [Kory Miller](https://github.com/jbrady42/korymiller1489)
+* [ZHENK](https://github.com/scorpionknifes)
+* [Tarrence van As](https://github.com/tarrencev)
+* [Maxim Oransky](https://github.com/sdfsdhgjkbmnmxc)
+* [Graham King](https://github.com/grahamking/)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/sdp/v3/base_lexer.go b/vendor/github.com/pion/sdp/v3/base_lexer.go
new file mode 100644
index 0000000..45fe799
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/base_lexer.go
@@ -0,0 +1,231 @@
+package sdp
+
+import (
+ "errors"
+ "fmt"
+ "io"
+)
+
+var errDocumentStart = errors.New("already on document start")
+
+type syntaxError struct {
+ s string
+ i int
+}
+
+func (e syntaxError) Error() string {
+ if e.i < 0 {
+ e.i = 0
+ }
+ head, middle, tail := e.s[:e.i], e.s[e.i:e.i+1], e.s[e.i+1:]
+ return fmt.Sprintf("%s --> %s <-- %s", head, middle, tail)
+}
+
+type baseLexer struct {
+ value []byte
+ pos int
+}
+
+func (l baseLexer) syntaxError() error {
+ return syntaxError{s: string(l.value), i: l.pos - 1}
+}
+
+func (l *baseLexer) unreadByte() error {
+ if l.pos <= 0 {
+ return errDocumentStart
+ }
+ l.pos--
+ return nil
+}
+
+func (l *baseLexer) readByte() (byte, error) {
+ if l.pos >= len(l.value) {
+ return byte(0), io.EOF
+ }
+ ch := l.value[l.pos]
+ l.pos++
+ return ch, nil
+}
+
+func (l *baseLexer) nextLine() error {
+ for {
+ ch, err := l.readByte()
+ if err == io.EOF {
+ return nil
+ } else if err != nil {
+ return err
+ }
+ if !isNewline(ch) {
+ return l.unreadByte()
+ }
+ }
+}
+
+func (l *baseLexer) readWhitespace() error {
+ for {
+ ch, err := l.readByte()
+ if err == io.EOF {
+ return nil
+ } else if err != nil {
+ return err
+ }
+ if !isWhitespace(ch) {
+ return l.unreadByte()
+ }
+ }
+}
+
+func (l *baseLexer) readUint64Field() (i uint64, err error) {
+ for {
+ ch, err := l.readByte()
+ if err == io.EOF && i > 0 {
+ break
+ } else if err != nil {
+ return i, err
+ }
+
+ if isNewline(ch) {
+ if err := l.unreadByte(); err != nil {
+ return i, err
+ }
+ break
+ }
+
+ if isWhitespace(ch) {
+ if err := l.readWhitespace(); err != nil {
+ return i, err
+ }
+ break
+ }
+
+ switch ch {
+ case '0':
+ i *= 10
+ case '1':
+ i = i*10 + 1
+ case '2':
+ i = i*10 + 2
+ case '3':
+ i = i*10 + 3
+ case '4':
+ i = i*10 + 4
+ case '5':
+ i = i*10 + 5
+ case '6':
+ i = i*10 + 6
+ case '7':
+ i = i*10 + 7
+ case '8':
+ i = i*10 + 8
+ case '9':
+ i = i*10 + 9
+ default:
+ return i, l.syntaxError()
+ }
+ }
+
+ return i, nil
+}
+
+// Returns next field on this line or empty string if no more fields on line
+func (l *baseLexer) readField() (string, error) {
+ start := l.pos
+ stop := start
+ for {
+ stop = l.pos
+ ch, err := l.readByte()
+ if err == io.EOF && stop > start {
+ break
+ } else if err != nil {
+ return "", err
+ }
+
+ if isNewline(ch) {
+ if err := l.unreadByte(); err != nil {
+ return "", err
+ }
+ break
+ }
+
+ if isWhitespace(ch) {
+ if err := l.readWhitespace(); err != nil {
+ return "", err
+ }
+ break
+ }
+ }
+ return string(l.value[start:stop]), nil
+}
+
+// Returns symbols until line end
+func (l *baseLexer) readLine() (string, error) {
+ start := l.pos
+ trim := 1
+ for {
+ ch, err := l.readByte()
+ if err != nil {
+ return "", err
+ }
+ if ch == '\r' {
+ trim++
+ }
+ if ch == '\n' {
+ return string(l.value[start : l.pos-trim]), nil
+ }
+ }
+}
+
+func (l *baseLexer) readString(until byte) (string, error) {
+ start := l.pos
+ for {
+ ch, err := l.readByte()
+ if err != nil {
+ return "", err
+ }
+ if ch == until {
+ return string(l.value[start:l.pos]), nil
+ }
+ }
+}
+
+func (l *baseLexer) readType() (string, error) {
+ for {
+ b, err := l.readByte()
+ if err != nil {
+ return "", err
+ }
+
+ if isNewline(b) {
+ continue
+ }
+
+ err = l.unreadByte()
+ if err != nil {
+ return "", err
+ }
+
+ key, err := l.readString('=')
+ if err != nil {
+ return key, err
+ }
+
+ if len(key) == 2 {
+ return key, nil
+ }
+
+ return key, l.syntaxError()
+ }
+}
+
+func isNewline(ch byte) bool { return ch == '\n' || ch == '\r' }
+
+func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' }
+
+func anyOf(element string, data ...string) bool {
+ for _, v := range data {
+ if element == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/pion/sdp/v3/codecov.yml b/vendor/github.com/pion/sdp/v3/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/sdp/v3/common_description.go b/vendor/github.com/pion/sdp/v3/common_description.go
new file mode 100644
index 0000000..1174be4
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/common_description.go
@@ -0,0 +1,110 @@
+package sdp
+
+import (
+ "strconv"
+ "strings"
+)
+
+// Information describes the "i=" field which provides textual information
+// about the session.
+type Information string
+
+func (i Information) String() string {
+ return string(i)
+}
+
+// ConnectionInformation defines the representation for the "c=" field
+// containing connection data.
+type ConnectionInformation struct {
+ NetworkType string
+ AddressType string
+ Address *Address
+}
+
+func (c ConnectionInformation) String() string {
+ parts := []string{c.NetworkType, c.AddressType}
+ if c.Address != nil && c.Address.String() != "" {
+ parts = append(parts, c.Address.String())
+ }
+ return strings.Join(parts, " ")
+}
+
+// Address desribes a structured address token from within the "c=" field.
+type Address struct {
+ Address string
+ TTL *int
+ Range *int
+}
+
+func (c *Address) String() string {
+ var parts []string
+ parts = append(parts, c.Address)
+ if c.TTL != nil {
+ parts = append(parts, strconv.Itoa(*c.TTL))
+ }
+
+ if c.Range != nil {
+ parts = append(parts, strconv.Itoa(*c.Range))
+ }
+
+ return strings.Join(parts, "/")
+}
+
+// Bandwidth describes an optional field which denotes the proposed bandwidth
+// to be used by the session or media.
+type Bandwidth struct {
+ Experimental bool
+ Type string
+ Bandwidth uint64
+}
+
+func (b Bandwidth) String() string {
+ var output string
+ if b.Experimental {
+ output += "X-"
+ }
+ output += b.Type + ":" + strconv.FormatUint(b.Bandwidth, 10)
+ return output
+}
+
+// EncryptionKey describes the "k=" which conveys encryption key information.
+type EncryptionKey string
+
+func (s EncryptionKey) String() string {
+ return string(s)
+}
+
+// Attribute describes the "a=" field which represents the primary means for
+// extending SDP.
+type Attribute struct {
+ Key string
+ Value string
+}
+
+// NewPropertyAttribute constructs a new attribute
+func NewPropertyAttribute(key string) Attribute {
+ return Attribute{
+ Key: key,
+ }
+}
+
+// NewAttribute constructs a new attribute
+func NewAttribute(key, value string) Attribute {
+ return Attribute{
+ Key: key,
+ Value: value,
+ }
+}
+
+func (a Attribute) String() string {
+ output := a.Key
+ if len(a.Value) > 0 {
+ output += ":" + a.Value
+ }
+ return output
+}
+
+// IsICECandidate returns true if the attribute key equals "candidate".
+func (a Attribute) IsICECandidate() bool {
+ return a.Key == "candidate"
+}
diff --git a/vendor/github.com/pion/sdp/v3/direction.go b/vendor/github.com/pion/sdp/v3/direction.go
new file mode 100644
index 0000000..19ea92d
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/direction.go
@@ -0,0 +1,59 @@
+package sdp
+
+import "errors"
+
+// Direction is a marker for transmission directon of an endpoint
+type Direction int
+
+const (
+ // DirectionSendRecv is for bidirectional communication
+ DirectionSendRecv Direction = iota + 1
+ // DirectionSendOnly is for outgoing communication
+ DirectionSendOnly
+ // DirectionRecvOnly is for incoming communication
+ DirectionRecvOnly
+ // DirectionInactive is for no communication
+ DirectionInactive
+)
+
+const (
+ directionSendRecvStr = "sendrecv"
+ directionSendOnlyStr = "sendonly"
+ directionRecvOnlyStr = "recvonly"
+ directionInactiveStr = "inactive"
+ directionUnknownStr = ""
+)
+
+var errDirectionString = errors.New("invalid direction string")
+
+// NewDirection defines a procedure for creating a new direction from a raw
+// string.
+func NewDirection(raw string) (Direction, error) {
+ switch raw {
+ case directionSendRecvStr:
+ return DirectionSendRecv, nil
+ case directionSendOnlyStr:
+ return DirectionSendOnly, nil
+ case directionRecvOnlyStr:
+ return DirectionRecvOnly, nil
+ case directionInactiveStr:
+ return DirectionInactive, nil
+ default:
+ return Direction(unknown), errDirectionString
+ }
+}
+
+func (t Direction) String() string {
+ switch t {
+ case DirectionSendRecv:
+ return directionSendRecvStr
+ case DirectionSendOnly:
+ return directionSendOnlyStr
+ case DirectionRecvOnly:
+ return directionRecvOnlyStr
+ case DirectionInactive:
+ return directionInactiveStr
+ default:
+ return directionUnknownStr
+ }
+}
diff --git a/vendor/github.com/pion/sdp/v3/extmap.go b/vendor/github.com/pion/sdp/v3/extmap.go
new file mode 100644
index 0000000..1242a8c
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/extmap.go
@@ -0,0 +1,108 @@
+package sdp
+
+import (
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+)
+
+// Default ext values
+const (
+ DefExtMapValueABSSendTime = 1
+ DefExtMapValueTransportCC = 2
+ DefExtMapValueSDESMid = 3
+ DefExtMapValueSDESRTPStreamID = 4
+
+ ABSSendTimeURI = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time"
+ TransportCCURI = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
+ SDESMidURI = "urn:ietf:params:rtp-hdrext:sdes:mid"
+ SDESRTPStreamIDURI = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id"
+ AudioLevelURI = "urn:ietf:params:rtp-hdrext:ssrc-audio-level"
+)
+
+// ExtMap represents the activation of a single RTP header extension
+type ExtMap struct {
+ Value int
+ Direction Direction
+ URI *url.URL
+ ExtAttr *string
+}
+
+// Clone converts this object to an Attribute
+func (e *ExtMap) Clone() Attribute {
+ return Attribute{Key: "extmap", Value: e.string()}
+}
+
+// Unmarshal creates an Extmap from a string
+func (e *ExtMap) Unmarshal(raw string) error {
+ parts := strings.SplitN(raw, ":", 2)
+ if len(parts) != 2 {
+ return fmt.Errorf("%w: %v", errSyntaxError, raw)
+ }
+
+ fields := strings.Fields(parts[1])
+ if len(fields) < 2 {
+ return fmt.Errorf("%w: %v", errSyntaxError, raw)
+ }
+
+ valdir := strings.Split(fields[0], "/")
+ value, err := strconv.ParseInt(valdir[0], 10, 64)
+ if (value < 1) || (value > 246) {
+ return fmt.Errorf("%w: %v -- extmap key must be in the range 1-256", errSyntaxError, valdir[0])
+ }
+ if err != nil {
+ return fmt.Errorf("%w: %v", errSyntaxError, valdir[0])
+ }
+
+ var direction Direction
+ if len(valdir) == 2 {
+ direction, err = NewDirection(valdir[1])
+ if err != nil {
+ return err
+ }
+ }
+
+ uri, err := url.Parse(fields[1])
+ if err != nil {
+ return err
+ }
+
+ if len(fields) == 3 {
+ tmp := fields[2]
+ e.ExtAttr = &tmp
+ }
+
+ e.Value = int(value)
+ e.Direction = direction
+ e.URI = uri
+ return nil
+}
+
+// Marshal creates a string from an ExtMap
+func (e *ExtMap) Marshal() string {
+ return e.Name() + ":" + e.string()
+}
+
+func (e *ExtMap) string() string {
+ output := fmt.Sprintf("%d", e.Value)
+ dirstring := e.Direction.String()
+ if dirstring != directionUnknownStr {
+ output += "/" + dirstring
+ }
+
+ if e.URI != nil {
+ output += " " + e.URI.String()
+ }
+
+ if e.ExtAttr != nil {
+ output += " " + *e.ExtAttr
+ }
+
+ return output
+}
+
+// Name returns the constant name of this object
+func (e *ExtMap) Name() string {
+ return "extmap"
+}
diff --git a/vendor/github.com/pion/sdp/v3/fuzz.go b/vendor/github.com/pion/sdp/v3/fuzz.go
new file mode 100644
index 0000000..f41ef78
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/fuzz.go
@@ -0,0 +1,31 @@
+// +build gofuzz
+
+package sdp
+
+// Fuzz implements a randomized fuzz test of the sdp
+// parser using go-fuzz.
+//
+// To run the fuzzer, first download go-fuzz:
+// `go get github.com/dvyukov/go-fuzz/...`
+//
+// Then build the testing package:
+// `go-fuzz-build`
+//
+// And run the fuzzer on the corpus:
+// `go-fuzz`
+func Fuzz(data []byte) int {
+ // Check that unmarshalling any byte slice does not panic.
+ var sd SessionDescription
+ if err := sd.Unmarshal(data); err != nil {
+ return 0
+ }
+ // Check that we can marshal anything we unmarshalled.
+ _, err := sd.Marshal()
+ if err != nil {
+ panic("failed to marshal") // nolint
+ }
+ // It'd be nice to check that if we round trip Marshal then Unmarshal,
+ // we get the original back. Right now, though, we frequently don't,
+ // and we'd need to fix that first.
+ return 1
+}
diff --git a/vendor/github.com/pion/sdp/v3/go.mod b/vendor/github.com/pion/sdp/v3/go.mod
new file mode 100644
index 0000000..da7ceaa
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/go.mod
@@ -0,0 +1,8 @@
+module github.com/pion/sdp/v3
+
+go 1.13
+
+require (
+ github.com/pion/randutil v0.1.0
+ github.com/stretchr/testify v1.6.1
+)
diff --git a/vendor/github.com/pion/sdp/v3/go.sum b/vendor/github.com/pion/sdp/v3/go.sum
new file mode 100644
index 0000000..43316cb
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/go.sum
@@ -0,0 +1,13 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/sdp/v3/jsep.go b/vendor/github.com/pion/sdp/v3/jsep.go
new file mode 100644
index 0000000..efdef03
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/jsep.go
@@ -0,0 +1,205 @@
+package sdp
+
+import (
+ "fmt"
+ "net/url"
+ "strconv"
+ "time"
+)
+
+// Constants for SDP attributes used in JSEP
+const (
+ AttrKeyCandidate = "candidate"
+ AttrKeyEndOfCandidates = "end-of-candidates"
+ AttrKeyIdentity = "identity"
+ AttrKeyGroup = "group"
+ AttrKeySSRC = "ssrc"
+ AttrKeySSRCGroup = "ssrc-group"
+ AttrKeyMsid = "msid"
+ AttrKeyMsidSemantic = "msid-semantic"
+ AttrKeyConnectionSetup = "setup"
+ AttrKeyMID = "mid"
+ AttrKeyICELite = "ice-lite"
+ AttrKeyRTCPMux = "rtcp-mux"
+ AttrKeyRTCPRsize = "rtcp-rsize"
+ AttrKeyInactive = "inactive"
+ AttrKeyRecvOnly = "recvonly"
+ AttrKeySendOnly = "sendonly"
+ AttrKeySendRecv = "sendrecv"
+ AttrKeyExtMap = "extmap"
+)
+
+// Constants for semantic tokens used in JSEP
+const (
+ SemanticTokenLipSynchronization = "LS"
+ SemanticTokenFlowIdentification = "FID"
+ SemanticTokenForwardErrorCorrection = "FEC"
+ SemanticTokenWebRTCMediaStreams = "WMS"
+)
+
+// Constants for extmap key
+const (
+ ExtMapValueTransportCC = 3
+)
+
+func extMapURI() map[int]string {
+ return map[int]string{
+ ExtMapValueTransportCC: "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
+ }
+}
+
+// API to match draft-ietf-rtcweb-jsep
+// Move to webrtc or its own package?
+
+// NewJSEPSessionDescription creates a new SessionDescription with
+// some settings that are required by the JSEP spec.
+//
+// Note: Since v2.4.0, session ID has been fixed to use crypto random according to
+// JSEP spec, so that NewJSEPSessionDescription now returns error as a second
+// return value.
+func NewJSEPSessionDescription(identity bool) (*SessionDescription, error) {
+ sid, err := newSessionID()
+ if err != nil {
+ return nil, err
+ }
+ d := &SessionDescription{
+ Version: 0,
+ Origin: Origin{
+ Username: "-",
+ SessionID: sid,
+ SessionVersion: uint64(time.Now().Unix()),
+ NetworkType: "IN",
+ AddressType: "IP4",
+ UnicastAddress: "0.0.0.0",
+ },
+ SessionName: "-",
+ TimeDescriptions: []TimeDescription{
+ {
+ Timing: Timing{
+ StartTime: 0,
+ StopTime: 0,
+ },
+ RepeatTimes: nil,
+ },
+ },
+ Attributes: []Attribute{
+ // "Attribute(ice-options:trickle)", // TODO: implement trickle ICE
+ },
+ }
+
+ if identity {
+ d.WithPropertyAttribute(AttrKeyIdentity)
+ }
+
+ return d, nil
+}
+
+// WithPropertyAttribute adds a property attribute 'a=key' to the session description
+func (s *SessionDescription) WithPropertyAttribute(key string) *SessionDescription {
+ s.Attributes = append(s.Attributes, NewPropertyAttribute(key))
+ return s
+}
+
+// WithValueAttribute adds a value attribute 'a=key:value' to the session description
+func (s *SessionDescription) WithValueAttribute(key, value string) *SessionDescription {
+ s.Attributes = append(s.Attributes, NewAttribute(key, value))
+ return s
+}
+
+// WithFingerprint adds a fingerprint to the session description
+func (s *SessionDescription) WithFingerprint(algorithm, value string) *SessionDescription {
+ return s.WithValueAttribute("fingerprint", algorithm+" "+value)
+}
+
+// WithMedia adds a media description to the session description
+func (s *SessionDescription) WithMedia(md *MediaDescription) *SessionDescription {
+ s.MediaDescriptions = append(s.MediaDescriptions, md)
+ return s
+}
+
+// NewJSEPMediaDescription creates a new MediaName with
+// some settings that are required by the JSEP spec.
+func NewJSEPMediaDescription(codecType string, codecPrefs []string) *MediaDescription {
+ return &MediaDescription{
+ MediaName: MediaName{
+ Media: codecType,
+ Port: RangedPort{Value: 9},
+ Protos: []string{"UDP", "TLS", "RTP", "SAVPF"},
+ },
+ ConnectionInformation: &ConnectionInformation{
+ NetworkType: "IN",
+ AddressType: "IP4",
+ Address: &Address{
+ Address: "0.0.0.0",
+ },
+ },
+ }
+}
+
+// WithPropertyAttribute adds a property attribute 'a=key' to the media description
+func (d *MediaDescription) WithPropertyAttribute(key string) *MediaDescription {
+ d.Attributes = append(d.Attributes, NewPropertyAttribute(key))
+ return d
+}
+
+// WithValueAttribute adds a value attribute 'a=key:value' to the media description
+func (d *MediaDescription) WithValueAttribute(key, value string) *MediaDescription {
+ d.Attributes = append(d.Attributes, NewAttribute(key, value))
+ return d
+}
+
+// WithFingerprint adds a fingerprint to the media description
+func (d *MediaDescription) WithFingerprint(algorithm, value string) *MediaDescription {
+ return d.WithValueAttribute("fingerprint", algorithm+" "+value)
+}
+
+// WithICECredentials adds ICE credentials to the media description
+func (d *MediaDescription) WithICECredentials(username, password string) *MediaDescription {
+ return d.
+ WithValueAttribute("ice-ufrag", username).
+ WithValueAttribute("ice-pwd", password)
+}
+
+// WithCodec adds codec information to the media description
+func (d *MediaDescription) WithCodec(payloadType uint8, name string, clockrate uint32, channels uint16, fmtp string) *MediaDescription {
+ d.MediaName.Formats = append(d.MediaName.Formats, strconv.Itoa(int(payloadType)))
+ rtpmap := fmt.Sprintf("%d %s/%d", payloadType, name, clockrate)
+ if channels > 0 {
+ rtpmap += fmt.Sprintf("/%d", channels)
+ }
+ d.WithValueAttribute("rtpmap", rtpmap)
+ if fmtp != "" {
+ d.WithValueAttribute("fmtp", fmt.Sprintf("%d %s", payloadType, fmtp))
+ }
+ return d
+}
+
+// WithMediaSource adds media source information to the media description
+func (d *MediaDescription) WithMediaSource(ssrc uint32, cname, streamLabel, label string) *MediaDescription {
+ return d.
+ WithValueAttribute("ssrc", fmt.Sprintf("%d cname:%s", ssrc, cname)). // Deprecated but not phased out?
+ WithValueAttribute("ssrc", fmt.Sprintf("%d msid:%s %s", ssrc, streamLabel, label)).
+ WithValueAttribute("ssrc", fmt.Sprintf("%d mslabel:%s", ssrc, streamLabel)). // Deprecated but not phased out?
+ WithValueAttribute("ssrc", fmt.Sprintf("%d label:%s", ssrc, label)) // Deprecated but not phased out?
+}
+
+// WithCandidate adds an ICE candidate to the media description
+// Deprecated: use WithICECandidate instead
+func (d *MediaDescription) WithCandidate(value string) *MediaDescription {
+ return d.WithValueAttribute("candidate", value)
+}
+
+// WithExtMap adds an extmap to the media description
+func (d *MediaDescription) WithExtMap(e ExtMap) *MediaDescription {
+ return d.WithPropertyAttribute(e.Marshal())
+}
+
+// WithTransportCCExtMap adds an extmap to the media description
+func (d *MediaDescription) WithTransportCCExtMap() *MediaDescription {
+ uri, _ := url.Parse(extMapURI()[ExtMapValueTransportCC])
+ e := ExtMap{
+ Value: ExtMapValueTransportCC,
+ URI: uri,
+ }
+ return d.WithExtMap(e)
+}
diff --git a/vendor/github.com/pion/sdp/v3/marshal.go b/vendor/github.com/pion/sdp/v3/marshal.go
new file mode 100644
index 0000000..f029c4e
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/marshal.go
@@ -0,0 +1,136 @@
+package sdp
+
+import (
+ "strings"
+)
+
+// Marshal takes a SDP struct to text
+// https://tools.ietf.org/html/rfc4566#section-5
+// Session description
+// v= (protocol version)
+// o= (originator and session identifier)
+// s= (session name)
+// i=* (session information)
+// u=* (URI of description)
+// e=* (email address)
+// p=* (phone number)
+// c=* (connection information -- not required if included in
+// all media)
+// b=* (zero or more bandwidth information lines)
+// One or more time descriptions ("t=" and "r=" lines; see below)
+// z=* (time zone adjustments)
+// k=* (encryption key)
+// a=* (zero or more session attribute lines)
+// Zero or more media descriptions
+//
+// Time description
+// t= (time the session is active)
+// r=* (zero or more repeat times)
+//
+// Media description, if present
+// m= (media name and transport address)
+// i=* (media title)
+// c=* (connection information -- optional if included at
+// session level)
+// b=* (zero or more bandwidth information lines)
+// k=* (encryption key)
+// a=* (zero or more media attribute lines)
+func (s *SessionDescription) Marshal() ([]byte, error) {
+ m := make(marshaller, 0, 1024)
+
+ m.addKeyValue("v=", s.Version.String())
+ m.addKeyValue("o=", s.Origin.String())
+ m.addKeyValue("s=", s.SessionName.String())
+
+ if s.SessionInformation != nil {
+ m.addKeyValue("i=", s.SessionInformation.String())
+ }
+
+ if s.URI != nil {
+ m.addKeyValue("u=", s.URI.String())
+ }
+
+ if s.EmailAddress != nil {
+ m.addKeyValue("e=", s.EmailAddress.String())
+ }
+
+ if s.PhoneNumber != nil {
+ m.addKeyValue("p=", s.PhoneNumber.String())
+ }
+
+ if s.ConnectionInformation != nil {
+ m.addKeyValue("c=", s.ConnectionInformation.String())
+ }
+
+ for _, b := range s.Bandwidth {
+ m.addKeyValue("b=", b.String())
+ }
+
+ for _, td := range s.TimeDescriptions {
+ m.addKeyValue("t=", td.Timing.String())
+ for _, r := range td.RepeatTimes {
+ m.addKeyValue("r=", r.String())
+ }
+ }
+
+ if len(s.TimeZones) > 0 {
+ var b strings.Builder
+ for i, z := range s.TimeZones {
+ if i > 0 {
+ b.WriteString(" ")
+ }
+ b.WriteString(z.String())
+ }
+ m.addKeyValue("z=", b.String())
+ }
+
+ if s.EncryptionKey != nil {
+ m.addKeyValue("k=", s.EncryptionKey.String())
+ }
+
+ for _, a := range s.Attributes {
+ m.addKeyValue("a=", a.String())
+ }
+
+ for _, md := range s.MediaDescriptions {
+ m.addKeyValue("m=", md.MediaName.String())
+
+ if md.MediaTitle != nil {
+ m.addKeyValue("i=", md.MediaTitle.String())
+ }
+
+ if md.ConnectionInformation != nil {
+ m.addKeyValue("c=", md.ConnectionInformation.String())
+ }
+
+ for _, b := range md.Bandwidth {
+ m.addKeyValue("b=", b.String())
+ }
+
+ if md.EncryptionKey != nil {
+ m.addKeyValue("k=", md.EncryptionKey.String())
+ }
+
+ for _, a := range md.Attributes {
+ m.addKeyValue("a=", a.String())
+ }
+ }
+
+ return m.bytes(), nil
+}
+
+// marshaller contains state during marshaling.
+type marshaller []byte
+
+func (m *marshaller) addKeyValue(key, value string) {
+ if value == "" {
+ return
+ }
+ *m = append(*m, key...)
+ *m = append(*m, value...)
+ *m = append(*m, "\r\n"...)
+}
+
+func (m *marshaller) bytes() []byte {
+ return *m
+}
diff --git a/vendor/github.com/pion/sdp/v3/media_description.go b/vendor/github.com/pion/sdp/v3/media_description.go
new file mode 100644
index 0000000..c088507
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/media_description.go
@@ -0,0 +1,80 @@
+package sdp
+
+import (
+ "strconv"
+ "strings"
+)
+
+// MediaDescription represents a media type.
+// https://tools.ietf.org/html/rfc4566#section-5.14
+type MediaDescription struct {
+ // m=<media> <port>/<number of ports> <proto> <fmt> ...
+ // https://tools.ietf.org/html/rfc4566#section-5.14
+ MediaName MediaName
+
+ // i=<session description>
+ // https://tools.ietf.org/html/rfc4566#section-5.4
+ MediaTitle *Information
+
+ // c=<nettype> <addrtype> <connection-address>
+ // https://tools.ietf.org/html/rfc4566#section-5.7
+ ConnectionInformation *ConnectionInformation
+
+ // b=<bwtype>:<bandwidth>
+ // https://tools.ietf.org/html/rfc4566#section-5.8
+ Bandwidth []Bandwidth
+
+ // k=<method>
+ // k=<method>:<encryption key>
+ // https://tools.ietf.org/html/rfc4566#section-5.12
+ EncryptionKey *EncryptionKey
+
+ // a=<attribute>
+ // a=<attribute>:<value>
+ // https://tools.ietf.org/html/rfc4566#section-5.13
+ Attributes []Attribute
+}
+
+// Attribute returns the value of an attribute and if it exists
+func (d *MediaDescription) Attribute(key string) (string, bool) {
+ for _, a := range d.Attributes {
+ if a.Key == key {
+ return a.Value, true
+ }
+ }
+ return "", false
+}
+
+// RangedPort supports special format for the media field "m=" port value. If
+// it may be necessary to specify multiple transport ports, the protocol allows
+// to write it as: <port>/<number of ports> where number of ports is a an
+// offsetting range.
+type RangedPort struct {
+ Value int
+ Range *int
+}
+
+func (p *RangedPort) String() string {
+ output := strconv.Itoa(p.Value)
+ if p.Range != nil {
+ output += "/" + strconv.Itoa(*p.Range)
+ }
+ return output
+}
+
+// MediaName describes the "m=" field storage structure.
+type MediaName struct {
+ Media string
+ Port RangedPort
+ Protos []string
+ Formats []string
+}
+
+func (m MediaName) String() string {
+ return strings.Join([]string{
+ m.Media,
+ m.Port.String(),
+ strings.Join(m.Protos, "/"),
+ strings.Join(m.Formats, " "),
+ }, " ")
+}
diff --git a/vendor/github.com/pion/sdp/v3/renovate.json b/vendor/github.com/pion/sdp/v3/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/sdp/v3/sdp.go b/vendor/github.com/pion/sdp/v3/sdp.go
new file mode 100644
index 0000000..c92ac1a
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/sdp.go
@@ -0,0 +1,2 @@
+// Package sdp implements Session Description Protocol (SDP)
+package sdp
diff --git a/vendor/github.com/pion/sdp/v3/session_description.go b/vendor/github.com/pion/sdp/v3/session_description.go
new file mode 100644
index 0000000..c4aaf5f
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/session_description.go
@@ -0,0 +1,146 @@
+package sdp
+
+import (
+ "fmt"
+ "net/url"
+ "strconv"
+)
+
+// SessionDescription is a a well-defined format for conveying sufficient
+// information to discover and participate in a multimedia session.
+type SessionDescription struct {
+ // v=0
+ // https://tools.ietf.org/html/rfc4566#section-5.1
+ Version Version
+
+ // o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
+ // https://tools.ietf.org/html/rfc4566#section-5.2
+ Origin Origin
+
+ // s=<session name>
+ // https://tools.ietf.org/html/rfc4566#section-5.3
+ SessionName SessionName
+
+ // i=<session description>
+ // https://tools.ietf.org/html/rfc4566#section-5.4
+ SessionInformation *Information
+
+ // u=<uri>
+ // https://tools.ietf.org/html/rfc4566#section-5.5
+ URI *url.URL
+
+ // e=<email-address>
+ // https://tools.ietf.org/html/rfc4566#section-5.6
+ EmailAddress *EmailAddress
+
+ // p=<phone-number>
+ // https://tools.ietf.org/html/rfc4566#section-5.6
+ PhoneNumber *PhoneNumber
+
+ // c=<nettype> <addrtype> <connection-address>
+ // https://tools.ietf.org/html/rfc4566#section-5.7
+ ConnectionInformation *ConnectionInformation
+
+ // b=<bwtype>:<bandwidth>
+ // https://tools.ietf.org/html/rfc4566#section-5.8
+ Bandwidth []Bandwidth
+
+ // https://tools.ietf.org/html/rfc4566#section-5.9
+ // https://tools.ietf.org/html/rfc4566#section-5.10
+ TimeDescriptions []TimeDescription
+
+ // z=<adjustment time> <offset> <adjustment time> <offset> ...
+ // https://tools.ietf.org/html/rfc4566#section-5.11
+ TimeZones []TimeZone
+
+ // k=<method>
+ // k=<method>:<encryption key>
+ // https://tools.ietf.org/html/rfc4566#section-5.12
+ EncryptionKey *EncryptionKey
+
+ // a=<attribute>
+ // a=<attribute>:<value>
+ // https://tools.ietf.org/html/rfc4566#section-5.13
+ Attributes []Attribute
+
+ // https://tools.ietf.org/html/rfc4566#section-5.14
+ MediaDescriptions []*MediaDescription
+}
+
+// Attribute returns the value of an attribute and if it exists
+func (s *SessionDescription) Attribute(key string) (string, bool) {
+ for _, a := range s.Attributes {
+ if a.Key == key {
+ return a.Value, true
+ }
+ }
+ return "", false
+}
+
+// Version describes the value provided by the "v=" field which gives
+// the version of the Session Description Protocol.
+type Version int
+
+func (v Version) String() string {
+ return strconv.Itoa(int(v))
+}
+
+// Origin defines the structure for the "o=" field which provides the
+// originator of the session plus a session identifier and version number.
+type Origin struct {
+ Username string
+ SessionID uint64
+ SessionVersion uint64
+ NetworkType string
+ AddressType string
+ UnicastAddress string
+}
+
+func (o Origin) String() string {
+ return fmt.Sprintf(
+ "%v %d %d %v %v %v",
+ o.Username,
+ o.SessionID,
+ o.SessionVersion,
+ o.NetworkType,
+ o.AddressType,
+ o.UnicastAddress,
+ )
+}
+
+// SessionName describes a structured representations for the "s=" field
+// and is the textual session name.
+type SessionName string
+
+func (s SessionName) String() string {
+ return string(s)
+}
+
+// EmailAddress describes a structured representations for the "e=" line
+// which specifies email contact information for the person responsible for
+// the conference.
+type EmailAddress string
+
+func (e EmailAddress) String() string {
+ return string(e)
+}
+
+// PhoneNumber describes a structured representations for the "p=" line
+// specify phone contact information for the person responsible for the
+// conference.
+type PhoneNumber string
+
+func (p PhoneNumber) String() string {
+ return string(p)
+}
+
+// TimeZone defines the structured object for "z=" line which describes
+// repeated sessions scheduling.
+type TimeZone struct {
+ AdjustmentTime uint64
+ Offset int64
+}
+
+func (z TimeZone) String() string {
+ return strconv.FormatUint(z.AdjustmentTime, 10) + " " + strconv.FormatInt(z.Offset, 10)
+}
diff --git a/vendor/github.com/pion/sdp/v3/time_description.go b/vendor/github.com/pion/sdp/v3/time_description.go
new file mode 100644
index 0000000..8b24e13
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/time_description.go
@@ -0,0 +1,51 @@
+package sdp
+
+import (
+ "strconv"
+ "strings"
+)
+
+// TimeDescription describes "t=", "r=" fields of the session description
+// which are used to specify the start and stop times for a session as well as
+// repeat intervals and durations for the scheduled session.
+type TimeDescription struct {
+ // t=<start-time> <stop-time>
+ // https://tools.ietf.org/html/rfc4566#section-5.9
+ Timing Timing
+
+ // r=<repeat interval> <active duration> <offsets from start-time>
+ // https://tools.ietf.org/html/rfc4566#section-5.10
+ RepeatTimes []RepeatTime
+}
+
+// Timing defines the "t=" field's structured representation for the start and
+// stop times.
+type Timing struct {
+ StartTime uint64
+ StopTime uint64
+}
+
+func (t Timing) String() string {
+ output := strconv.FormatUint(t.StartTime, 10)
+ output += " " + strconv.FormatUint(t.StopTime, 10)
+ return output
+}
+
+// RepeatTime describes the "r=" fields of the session description which
+// represents the intervals and durations for repeated scheduled sessions.
+type RepeatTime struct {
+ Interval int64
+ Duration int64
+ Offsets []int64
+}
+
+func (r RepeatTime) String() string {
+ fields := make([]string, 0)
+ fields = append(fields, strconv.FormatInt(r.Interval, 10))
+ fields = append(fields, strconv.FormatInt(r.Duration, 10))
+ for _, value := range r.Offsets {
+ fields = append(fields, strconv.FormatInt(value, 10))
+ }
+
+ return strings.Join(fields, " ")
+}
diff --git a/vendor/github.com/pion/sdp/v3/unmarshal.go b/vendor/github.com/pion/sdp/v3/unmarshal.go
new file mode 100644
index 0000000..92e0dcf
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/unmarshal.go
@@ -0,0 +1,907 @@
+package sdp
+
+import (
+ "errors"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+)
+
+var (
+ errSDPInvalidSyntax = errors.New("sdp: invalid syntax")
+ errSDPInvalidNumericValue = errors.New("sdp: invalid numeric value")
+ errSDPInvalidValue = errors.New("sdp: invalid value")
+ errSDPInvalidPortValue = errors.New("sdp: invalid port value")
+)
+
+// Unmarshal is the primary function that deserializes the session description
+// message and stores it inside of a structured SessionDescription object.
+//
+// The States Transition Table describes the computation flow between functions
+// (namely s1, s2, s3, ...) for a parsing procedure that complies with the
+// specifications laid out by the rfc4566#section-5 as well as by JavaScript
+// Session Establishment Protocol draft. Links:
+// https://tools.ietf.org/html/rfc4566#section-5
+// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24
+//
+// https://tools.ietf.org/html/rfc4566#section-5
+// Session description
+// v= (protocol version)
+// o= (originator and session identifier)
+// s= (session name)
+// i=* (session information)
+// u=* (URI of description)
+// e=* (email address)
+// p=* (phone number)
+// c=* (connection information -- not required if included in
+// all media)
+// b=* (zero or more bandwidth information lines)
+// One or more time descriptions ("t=" and "r=" lines; see below)
+// z=* (time zone adjustments)
+// k=* (encryption key)
+// a=* (zero or more session attribute lines)
+// Zero or more media descriptions
+//
+// Time description
+// t= (time the session is active)
+// r=* (zero or more repeat times)
+//
+// Media description, if present
+// m= (media name and transport address)
+// i=* (media title)
+// c=* (connection information -- optional if included at
+// session level)
+// b=* (zero or more bandwidth information lines)
+// k=* (encryption key)
+// a=* (zero or more media attribute lines)
+//
+// In order to generate the following state table and draw subsequent
+// deterministic finite-state automota ("DFA") the following regex was used to
+// derive the DFA:
+// vosi?u?e?p?c?b*(tr*)+z?k?a*(mi?c?b*k?a*)*
+// possible place and state to exit:
+// ** * * * ** * * * *
+// 99 1 1 1 11 1 1 1 1
+// 3 1 1 26 5 5 4 4
+//
+// Please pay close attention to the `k`, and `a` parsing states. In the table
+// below in order to distinguish between the states belonging to the media
+// description as opposed to the session description, the states are marked
+// with an asterisk ("a*", "k*").
+// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
+// | STATES | a* | a*,k* | a | a,k | b | b,c | e | i | m | o | p | r,t | s | t | u | v | z |
+// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
+// | s1 | | | | | | | | | | | | | | | | 2 | |
+// | s2 | | | | | | | | | | 3 | | | | | | | |
+// | s3 | | | | | | | | | | | | | 4 | | | | |
+// | s4 | | | | | | 5 | 6 | 7 | | | 8 | | | 9 | 10 | | |
+// | s5 | | | | | 5 | | | | | | | | | 9 | | | |
+// | s6 | | | | | | 5 | | | | | 8 | | | 9 | | | |
+// | s7 | | | | | | 5 | 6 | | | | 8 | | | 9 | 10 | | |
+// | s8 | | | | | | 5 | | | | | | | | 9 | | | |
+// | s9 | | | | 11 | | | | | 12 | | | 9 | | | | | 13 |
+// | s10 | | | | | | 5 | 6 | | | | 8 | | | 9 | | | |
+// | s11 | | | 11 | | | | | | 12 | | | | | | | | |
+// | s12 | | 14 | | | | 15 | | 16 | 12 | | | | | | | | |
+// | s13 | | | | 11 | | | | | 12 | | | | | | | | |
+// | s14 | 14 | | | | | | | | 12 | | | | | | | | |
+// | s15 | | 14 | | | 15 | | | | 12 | | | | | | | | |
+// | s16 | | 14 | | | | 15 | | | 12 | | | | | | | | |
+// +--------+----+-------+----+-----+----+-----+---+----+----+---+---+-----+---+---+----+---+----+
+func (s *SessionDescription) Unmarshal(value []byte) error {
+ l := new(lexer)
+ l.desc = s
+ l.value = value
+ for state := s1; state != nil; {
+ var err error
+ state, err = state(l)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func s1(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ if key == "v=" {
+ return unmarshalProtocolVersion
+ }
+ return nil
+ })
+}
+
+func s2(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ if key == "o=" {
+ return unmarshalOrigin
+ }
+ return nil
+ })
+}
+
+func s3(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ if key == "s=" {
+ return unmarshalSessionName
+ }
+ return nil
+ })
+}
+
+func s4(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "i=":
+ return unmarshalSessionInformation
+ case "u=":
+ return unmarshalURI
+ case "e=":
+ return unmarshalEmail
+ case "p=":
+ return unmarshalPhone
+ case "c=":
+ return unmarshalSessionConnectionInformation
+ case "b=":
+ return unmarshalSessionBandwidth
+ case "t=":
+ return unmarshalTiming
+ }
+ return nil
+ })
+}
+
+func s5(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "b=":
+ return unmarshalSessionBandwidth
+ case "t=":
+ return unmarshalTiming
+ }
+ return nil
+ })
+}
+
+func s6(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "p=":
+ return unmarshalPhone
+ case "c=":
+ return unmarshalSessionConnectionInformation
+ case "b=":
+ return unmarshalSessionBandwidth
+ case "t=":
+ return unmarshalTiming
+ }
+ return nil
+ })
+}
+
+func s7(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "u=":
+ return unmarshalURI
+ case "e=":
+ return unmarshalEmail
+ case "p=":
+ return unmarshalPhone
+ case "c=":
+ return unmarshalSessionConnectionInformation
+ case "b=":
+ return unmarshalSessionBandwidth
+ case "t=":
+ return unmarshalTiming
+ }
+ return nil
+ })
+}
+
+func s8(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "c=":
+ return unmarshalSessionConnectionInformation
+ case "b=":
+ return unmarshalSessionBandwidth
+ case "t=":
+ return unmarshalTiming
+ }
+ return nil
+ })
+}
+
+func s9(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "z=":
+ return unmarshalTimeZones
+ case "k=":
+ return unmarshalSessionEncryptionKey
+ case "a=":
+ return unmarshalSessionAttribute
+ case "r=":
+ return unmarshalRepeatTimes
+ case "t=":
+ return unmarshalTiming
+ case "m=":
+ return unmarshalMediaDescription
+ }
+ return nil
+ })
+}
+
+func s10(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "e=":
+ return unmarshalEmail
+ case "p=":
+ return unmarshalPhone
+ case "c=":
+ return unmarshalSessionConnectionInformation
+ case "b=":
+ return unmarshalSessionBandwidth
+ case "t=":
+ return unmarshalTiming
+ }
+ return nil
+ })
+}
+
+func s11(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "a=":
+ return unmarshalSessionAttribute
+ case "m=":
+ return unmarshalMediaDescription
+ }
+ return nil
+ })
+}
+
+func s12(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "a=":
+ return unmarshalMediaAttribute
+ case "k=":
+ return unmarshalMediaEncryptionKey
+ case "b=":
+ return unmarshalMediaBandwidth
+ case "c=":
+ return unmarshalMediaConnectionInformation
+ case "i=":
+ return unmarshalMediaTitle
+ case "m=":
+ return unmarshalMediaDescription
+ }
+ return nil
+ })
+}
+
+func s13(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "a=":
+ return unmarshalSessionAttribute
+ case "k=":
+ return unmarshalSessionEncryptionKey
+ case "m=":
+ return unmarshalMediaDescription
+ }
+ return nil
+ })
+}
+
+func s14(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "a=":
+ return unmarshalMediaAttribute
+ case "k=":
+ // Non-spec ordering
+ return unmarshalMediaEncryptionKey
+ case "b=":
+ // Non-spec ordering
+ return unmarshalMediaBandwidth
+ case "c=":
+ // Non-spec ordering
+ return unmarshalMediaConnectionInformation
+ case "i=":
+ // Non-spec ordering
+ return unmarshalMediaTitle
+ case "m=":
+ return unmarshalMediaDescription
+ }
+ return nil
+ })
+}
+
+func s15(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "a=":
+ return unmarshalMediaAttribute
+ case "k=":
+ return unmarshalMediaEncryptionKey
+ case "b=":
+ return unmarshalMediaBandwidth
+ case "c=":
+ return unmarshalMediaConnectionInformation
+ case "i=":
+ // Non-spec ordering
+ return unmarshalMediaTitle
+ case "m=":
+ return unmarshalMediaDescription
+ }
+ return nil
+ })
+}
+
+func s16(l *lexer) (stateFn, error) {
+ return l.handleType(func(key string) stateFn {
+ switch key {
+ case "a=":
+ return unmarshalMediaAttribute
+ case "k=":
+ return unmarshalMediaEncryptionKey
+ case "c=":
+ return unmarshalMediaConnectionInformation
+ case "b=":
+ return unmarshalMediaBandwidth
+ case "i=":
+ // Non-spec ordering
+ return unmarshalMediaTitle
+ case "m=":
+ return unmarshalMediaDescription
+ }
+ return nil
+ })
+}
+
+func unmarshalProtocolVersion(l *lexer) (stateFn, error) {
+ version, err := l.readUint64Field()
+ if err != nil {
+ return nil, err
+ }
+
+ // As off the latest draft of the rfc this value is required to be 0.
+ // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-24#section-5.8.1
+ if version != 0 {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, version)
+ }
+
+ if err := l.nextLine(); err != nil {
+ return nil, err
+ }
+
+ return s2, nil
+}
+
+func unmarshalOrigin(l *lexer) (stateFn, error) {
+ var err error
+
+ l.desc.Origin.Username, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ l.desc.Origin.SessionID, err = l.readUint64Field()
+ if err != nil {
+ return nil, err
+ }
+
+ l.desc.Origin.SessionVersion, err = l.readUint64Field()
+ if err != nil {
+ return nil, err
+ }
+
+ l.desc.Origin.NetworkType, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ // Set according to currently registered with IANA
+ // https://tools.ietf.org/html/rfc4566#section-8.2.6
+ if !anyOf(l.desc.Origin.NetworkType, "IN") {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, l.desc.Origin.NetworkType)
+ }
+
+ l.desc.Origin.AddressType, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ // Set according to currently registered with IANA
+ // https://tools.ietf.org/html/rfc4566#section-8.2.7
+ if !anyOf(l.desc.Origin.AddressType, "IP4", "IP6") {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, l.desc.Origin.AddressType)
+ }
+
+ l.desc.Origin.UnicastAddress, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ if err := l.nextLine(); err != nil {
+ return nil, err
+ }
+
+ return s3, nil
+}
+
+func unmarshalSessionName(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ l.desc.SessionName = SessionName(value)
+ return s4, nil
+}
+
+func unmarshalSessionInformation(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ sessionInformation := Information(value)
+ l.desc.SessionInformation = &sessionInformation
+ return s7, nil
+}
+
+func unmarshalURI(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ l.desc.URI, err = url.Parse(value)
+ if err != nil {
+ return nil, err
+ }
+
+ return s10, nil
+}
+
+func unmarshalEmail(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ emailAddress := EmailAddress(value)
+ l.desc.EmailAddress = &emailAddress
+ return s6, nil
+}
+
+func unmarshalPhone(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ phoneNumber := PhoneNumber(value)
+ l.desc.PhoneNumber = &phoneNumber
+ return s8, nil
+}
+
+func unmarshalSessionConnectionInformation(l *lexer) (stateFn, error) {
+ var err error
+ l.desc.ConnectionInformation, err = l.unmarshalConnectionInformation()
+ if err != nil {
+ return nil, err
+ }
+ return s5, nil
+}
+
+func (l *lexer) unmarshalConnectionInformation() (*ConnectionInformation, error) {
+ var err error
+ var c ConnectionInformation
+
+ c.NetworkType, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ // Set according to currently registered with IANA
+ // https://tools.ietf.org/html/rfc4566#section-8.2.6
+ if !anyOf(c.NetworkType, "IN") {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, c.NetworkType)
+ }
+
+ c.AddressType, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ // Set according to currently registered with IANA
+ // https://tools.ietf.org/html/rfc4566#section-8.2.7
+ if !anyOf(c.AddressType, "IP4", "IP6") {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, c.AddressType)
+ }
+
+ address, err := l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ if address != "" {
+ c.Address = new(Address)
+ c.Address.Address = address
+ }
+
+ if err := l.nextLine(); err != nil {
+ return nil, err
+ }
+
+ return &c, nil
+}
+
+func unmarshalSessionBandwidth(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ bandwidth, err := unmarshalBandwidth(value)
+ if err != nil {
+ return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidValue, value)
+ }
+ l.desc.Bandwidth = append(l.desc.Bandwidth, *bandwidth)
+
+ return s5, nil
+}
+
+func unmarshalBandwidth(value string) (*Bandwidth, error) {
+ parts := strings.Split(value, ":")
+ if len(parts) != 2 {
+ return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidValue, parts)
+ }
+
+ experimental := strings.HasPrefix(parts[0], "X-")
+ if experimental {
+ parts[0] = strings.TrimPrefix(parts[0], "X-")
+ } else if !anyOf(parts[0], "CT", "AS") {
+ // Set according to currently registered with IANA
+ // https://tools.ietf.org/html/rfc4566#section-5.8
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, parts[0])
+ }
+
+ bandwidth, err := strconv.ParseUint(parts[1], 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, parts[1])
+ }
+
+ return &Bandwidth{
+ Experimental: experimental,
+ Type: parts[0],
+ Bandwidth: bandwidth,
+ }, nil
+}
+
+func unmarshalTiming(l *lexer) (stateFn, error) {
+ var err error
+ var td TimeDescription
+
+ td.Timing.StartTime, err = l.readUint64Field()
+ if err != nil {
+ return nil, err
+ }
+
+ td.Timing.StopTime, err = l.readUint64Field()
+ if err != nil {
+ return nil, err
+ }
+
+ if err := l.nextLine(); err != nil {
+ return nil, err
+ }
+
+ l.desc.TimeDescriptions = append(l.desc.TimeDescriptions, td)
+ return s9, nil
+}
+
+func unmarshalRepeatTimes(l *lexer) (stateFn, error) {
+ var err error
+ var newRepeatTime RepeatTime
+
+ latestTimeDesc := &l.desc.TimeDescriptions[len(l.desc.TimeDescriptions)-1]
+
+ field, err := l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ newRepeatTime.Interval, err = parseTimeUnits(field)
+ if err != nil {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
+ }
+
+ field, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ newRepeatTime.Duration, err = parseTimeUnits(field)
+ if err != nil {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
+ }
+
+ for {
+ field, err := l.readField()
+ if err != nil {
+ return nil, err
+ }
+ if field == "" {
+ break
+ }
+ offset, err := parseTimeUnits(field)
+ if err != nil {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
+ }
+ newRepeatTime.Offsets = append(newRepeatTime.Offsets, offset)
+ }
+
+ if err := l.nextLine(); err != nil {
+ return nil, err
+ }
+
+ latestTimeDesc.RepeatTimes = append(latestTimeDesc.RepeatTimes, newRepeatTime)
+ return s9, nil
+}
+
+func unmarshalTimeZones(l *lexer) (stateFn, error) {
+ // These fields are transimitted in pairs
+ // z=<adjustment time> <offset> <adjustment time> <offset> ....
+ // so we are making sure that there are actually multiple of 2 total.
+ for {
+ var err error
+ var timeZone TimeZone
+
+ timeZone.AdjustmentTime, err = l.readUint64Field()
+ if err != nil {
+ return nil, err
+ }
+
+ offset, err := l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ if offset == "" {
+ break
+ }
+
+ timeZone.Offset, err = parseTimeUnits(offset)
+ if err != nil {
+ return nil, err
+ }
+
+ l.desc.TimeZones = append(l.desc.TimeZones, timeZone)
+ }
+
+ if err := l.nextLine(); err != nil {
+ return nil, err
+ }
+
+ return s13, nil
+}
+
+func unmarshalSessionEncryptionKey(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ encryptionKey := EncryptionKey(value)
+ l.desc.EncryptionKey = &encryptionKey
+ return s11, nil
+}
+
+func unmarshalSessionAttribute(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ i := strings.IndexRune(value, ':')
+ var a Attribute
+ if i > 0 {
+ a = NewAttribute(value[:i], value[i+1:])
+ } else {
+ a = NewPropertyAttribute(value)
+ }
+
+ l.desc.Attributes = append(l.desc.Attributes, a)
+ return s11, nil
+}
+
+func unmarshalMediaDescription(l *lexer) (stateFn, error) {
+ var newMediaDesc MediaDescription
+
+ // <media>
+ field, err := l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ // Set according to currently registered with IANA
+ // https://tools.ietf.org/html/rfc4566#section-5.14
+ if !anyOf(field, "audio", "video", "text", "application", "message") {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, field)
+ }
+ newMediaDesc.MediaName.Media = field
+
+ // <port>
+ field, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+ parts := strings.Split(field, "/")
+ newMediaDesc.MediaName.Port.Value, err = parsePort(parts[0])
+ if err != nil {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, parts[0])
+ }
+
+ if len(parts) > 1 {
+ var portRange int
+ portRange, err = strconv.Atoi(parts[1])
+ if err != nil {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, parts)
+ }
+ newMediaDesc.MediaName.Port.Range = &portRange
+ }
+
+ // <proto>
+ field, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+
+ // Set according to currently registered with IANA
+ // https://tools.ietf.org/html/rfc4566#section-5.14
+ for _, proto := range strings.Split(field, "/") {
+ if !anyOf(proto, "UDP", "RTP", "AVP", "SAVP", "SAVPF", "TLS", "DTLS", "SCTP", "AVPF") {
+ return nil, fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, field)
+ }
+ newMediaDesc.MediaName.Protos = append(newMediaDesc.MediaName.Protos, proto)
+ }
+
+ // <fmt>...
+ for {
+ field, err = l.readField()
+ if err != nil {
+ return nil, err
+ }
+ if field == "" {
+ break
+ }
+ newMediaDesc.MediaName.Formats = append(newMediaDesc.MediaName.Formats, field)
+ }
+
+ if err := l.nextLine(); err != nil {
+ return nil, err
+ }
+
+ l.desc.MediaDescriptions = append(l.desc.MediaDescriptions, &newMediaDesc)
+ return s12, nil
+}
+
+func unmarshalMediaTitle(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
+ mediaTitle := Information(value)
+ latestMediaDesc.MediaTitle = &mediaTitle
+ return s16, nil
+}
+
+func unmarshalMediaConnectionInformation(l *lexer) (stateFn, error) {
+ var err error
+ latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
+ latestMediaDesc.ConnectionInformation, err = l.unmarshalConnectionInformation()
+ if err != nil {
+ return nil, err
+ }
+ return s15, nil
+}
+
+func unmarshalMediaBandwidth(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
+ bandwidth, err := unmarshalBandwidth(value)
+ if err != nil {
+ return nil, fmt.Errorf("%w `b=%v`", errSDPInvalidSyntax, value)
+ }
+ latestMediaDesc.Bandwidth = append(latestMediaDesc.Bandwidth, *bandwidth)
+ return s15, nil
+}
+
+func unmarshalMediaEncryptionKey(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
+ encryptionKey := EncryptionKey(value)
+ latestMediaDesc.EncryptionKey = &encryptionKey
+ return s14, nil
+}
+
+func unmarshalMediaAttribute(l *lexer) (stateFn, error) {
+ value, err := l.readLine()
+ if err != nil {
+ return nil, err
+ }
+
+ i := strings.IndexRune(value, ':')
+ var a Attribute
+ if i > 0 {
+ a = NewAttribute(value[:i], value[i+1:])
+ } else {
+ a = NewPropertyAttribute(value)
+ }
+
+ latestMediaDesc := l.desc.MediaDescriptions[len(l.desc.MediaDescriptions)-1]
+ latestMediaDesc.Attributes = append(latestMediaDesc.Attributes, a)
+ return s14, nil
+}
+
+func parseTimeUnits(value string) (num int64, err error) {
+ k := timeShorthand(value[len(value)-1])
+ if k > 0 {
+ num, err = strconv.ParseInt(value[:len(value)-1], 10, 64)
+ } else {
+ k = 1
+ num, err = strconv.ParseInt(value, 10, 64)
+ }
+ if err != nil {
+ return 0, fmt.Errorf("%w `%v`", errSDPInvalidValue, value)
+ }
+ return num * k, nil
+}
+
+func timeShorthand(b byte) int64 {
+ // Some time offsets in the protocol can be provided with a shorthand
+ // notation. This code ensures to convert it to NTP timestamp format.
+ switch b {
+ case 'd': // days
+ return 86400
+ case 'h': // hours
+ return 3600
+ case 'm': // minutes
+ return 60
+ case 's': // seconds (allowed for completeness)
+ return 1
+ default:
+ return 0
+ }
+}
+
+func parsePort(value string) (int, error) {
+ port, err := strconv.Atoi(value)
+ if err != nil {
+ return 0, fmt.Errorf("%w `%v`", errSDPInvalidPortValue, port)
+ }
+
+ if port < 0 || port > 65536 {
+ return 0, fmt.Errorf("%w -- out of range `%v`", errSDPInvalidPortValue, port)
+ }
+
+ return port, nil
+}
diff --git a/vendor/github.com/pion/sdp/v3/util.go b/vendor/github.com/pion/sdp/v3/util.go
new file mode 100644
index 0000000..74f47ed
--- /dev/null
+++ b/vendor/github.com/pion/sdp/v3/util.go
@@ -0,0 +1,318 @@
+package sdp
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/pion/randutil"
+)
+
+const (
+ attributeKey = "a="
+)
+
+var (
+ errExtractCodecRtpmap = errors.New("could not extract codec from rtpmap")
+ errExtractCodecFmtp = errors.New("could not extract codec from fmtp")
+ errExtractCodecRtcpFb = errors.New("could not extract codec from rtcp-fb")
+ errPayloadTypeNotFound = errors.New("payload type not found")
+ errCodecNotFound = errors.New("codec not found")
+ errSyntaxError = errors.New("SyntaxError")
+)
+
+// ConnectionRole indicates which of the end points should initiate the connection establishment
+type ConnectionRole int
+
+const (
+ // ConnectionRoleActive indicates the endpoint will initiate an outgoing connection.
+ ConnectionRoleActive ConnectionRole = iota + 1
+
+ // ConnectionRolePassive indicates the endpoint will accept an incoming connection.
+ ConnectionRolePassive
+
+ // ConnectionRoleActpass indicates the endpoint is willing to accept an incoming connection or to initiate an outgoing connection.
+ ConnectionRoleActpass
+
+ // ConnectionRoleHoldconn indicates the endpoint does not want the connection to be established for the time being.
+ ConnectionRoleHoldconn
+)
+
+func (t ConnectionRole) String() string {
+ switch t {
+ case ConnectionRoleActive:
+ return "active"
+ case ConnectionRolePassive:
+ return "passive"
+ case ConnectionRoleActpass:
+ return "actpass"
+ case ConnectionRoleHoldconn:
+ return "holdconn"
+ default:
+ return "Unknown"
+ }
+}
+
+func newSessionID() (uint64, error) {
+ // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-26#section-5.2.1
+ // Session ID is recommended to be constructed by generating a 64-bit
+ // quantity with the highest bit set to zero and the remaining 63-bits
+ // being cryptographically random.
+ id, err := randutil.CryptoUint64()
+ return id & (^(uint64(1) << 63)), err
+}
+
+// Codec represents a codec
+type Codec struct {
+ PayloadType uint8
+ Name string
+ ClockRate uint32
+ EncodingParameters string
+ Fmtp string
+ RTCPFeedback []string
+}
+
+const (
+ unknown = iota
+)
+
+func (c Codec) String() string {
+ return fmt.Sprintf("%d %s/%d/%s (%s) [%s]", c.PayloadType, c.Name, c.ClockRate, c.EncodingParameters, c.Fmtp, strings.Join(c.RTCPFeedback, ", "))
+}
+
+func parseRtpmap(rtpmap string) (Codec, error) {
+ var codec Codec
+ parsingFailed := errExtractCodecRtpmap
+
+ // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]
+ split := strings.Split(rtpmap, " ")
+ if len(split) != 2 {
+ return codec, parsingFailed
+ }
+
+ ptSplit := strings.Split(split[0], ":")
+ if len(ptSplit) != 2 {
+ return codec, parsingFailed
+ }
+
+ ptInt, err := strconv.Atoi(ptSplit[1])
+ if err != nil {
+ return codec, parsingFailed
+ }
+
+ codec.PayloadType = uint8(ptInt)
+
+ split = strings.Split(split[1], "/")
+ codec.Name = split[0]
+ parts := len(split)
+ if parts > 1 {
+ rate, err := strconv.Atoi(split[1])
+ if err != nil {
+ return codec, parsingFailed
+ }
+ codec.ClockRate = uint32(rate)
+ }
+ if parts > 2 {
+ codec.EncodingParameters = split[2]
+ }
+
+ return codec, nil
+}
+
+func parseFmtp(fmtp string) (Codec, error) {
+ var codec Codec
+ parsingFailed := errExtractCodecFmtp
+
+ // a=fmtp:<format> <format specific parameters>
+ split := strings.Split(fmtp, " ")
+ if len(split) != 2 {
+ return codec, parsingFailed
+ }
+
+ formatParams := split[1]
+
+ split = strings.Split(split[0], ":")
+ if len(split) != 2 {
+ return codec, parsingFailed
+ }
+
+ ptInt, err := strconv.Atoi(split[1])
+ if err != nil {
+ return codec, parsingFailed
+ }
+
+ codec.PayloadType = uint8(ptInt)
+ codec.Fmtp = formatParams
+
+ return codec, nil
+}
+
+func parseRtcpFb(rtcpFb string) (Codec, error) {
+ var codec Codec
+ parsingFailed := errExtractCodecRtcpFb
+
+ // a=ftcp-fb:<payload type> <RTCP feedback type> [<RTCP feedback parameter>]
+ split := strings.SplitN(rtcpFb, " ", 2)
+ if len(split) != 2 {
+ return codec, parsingFailed
+ }
+
+ ptSplit := strings.Split(split[0], ":")
+ if len(ptSplit) != 2 {
+ return codec, parsingFailed
+ }
+
+ ptInt, err := strconv.Atoi(ptSplit[1])
+ if err != nil {
+ return codec, parsingFailed
+ }
+
+ codec.PayloadType = uint8(ptInt)
+ codec.RTCPFeedback = append(codec.RTCPFeedback, split[1])
+
+ return codec, nil
+}
+
+func mergeCodecs(codec Codec, codecs map[uint8]Codec) {
+ savedCodec := codecs[codec.PayloadType]
+
+ if savedCodec.PayloadType == 0 {
+ savedCodec.PayloadType = codec.PayloadType
+ }
+ if savedCodec.Name == "" {
+ savedCodec.Name = codec.Name
+ }
+ if savedCodec.ClockRate == 0 {
+ savedCodec.ClockRate = codec.ClockRate
+ }
+ if savedCodec.EncodingParameters == "" {
+ savedCodec.EncodingParameters = codec.EncodingParameters
+ }
+ if savedCodec.Fmtp == "" {
+ savedCodec.Fmtp = codec.Fmtp
+ }
+ savedCodec.RTCPFeedback = append(savedCodec.RTCPFeedback, codec.RTCPFeedback...)
+
+ codecs[savedCodec.PayloadType] = savedCodec
+}
+
+func (s *SessionDescription) buildCodecMap() map[uint8]Codec {
+ codecs := make(map[uint8]Codec)
+
+ for _, m := range s.MediaDescriptions {
+ for _, a := range m.Attributes {
+ attr := a.String()
+ switch {
+ case strings.HasPrefix(attr, "rtpmap:"):
+ codec, err := parseRtpmap(attr)
+ if err == nil {
+ mergeCodecs(codec, codecs)
+ }
+ case strings.HasPrefix(attr, "fmtp:"):
+ codec, err := parseFmtp(attr)
+ if err == nil {
+ mergeCodecs(codec, codecs)
+ }
+ case strings.HasPrefix(attr, "rtcp-fb:"):
+ codec, err := parseRtcpFb(attr)
+ if err == nil {
+ mergeCodecs(codec, codecs)
+ }
+ }
+ }
+ }
+
+ return codecs
+}
+
+func equivalentFmtp(want, got string) bool {
+ wantSplit := strings.Split(want, ";")
+ gotSplit := strings.Split(got, ";")
+
+ if len(wantSplit) != len(gotSplit) {
+ return false
+ }
+
+ sort.Strings(wantSplit)
+ sort.Strings(gotSplit)
+
+ for i, wantPart := range wantSplit {
+ wantPart = strings.TrimSpace(wantPart)
+ gotPart := strings.TrimSpace(gotSplit[i])
+ if gotPart != wantPart {
+ return false
+ }
+ }
+
+ return true
+}
+
+func codecsMatch(wanted, got Codec) bool {
+ if wanted.Name != "" && !strings.EqualFold(wanted.Name, got.Name) {
+ return false
+ }
+ if wanted.ClockRate != 0 && wanted.ClockRate != got.ClockRate {
+ return false
+ }
+ if wanted.EncodingParameters != "" && wanted.EncodingParameters != got.EncodingParameters {
+ return false
+ }
+ if wanted.Fmtp != "" && !equivalentFmtp(wanted.Fmtp, got.Fmtp) {
+ return false
+ }
+
+ return true
+}
+
+// GetCodecForPayloadType scans the SessionDescription for the given payload type and returns the codec
+func (s *SessionDescription) GetCodecForPayloadType(payloadType uint8) (Codec, error) {
+ codecs := s.buildCodecMap()
+
+ codec, ok := codecs[payloadType]
+ if ok {
+ return codec, nil
+ }
+
+ return codec, errPayloadTypeNotFound
+}
+
+// GetPayloadTypeForCodec scans the SessionDescription for a codec that matches the provided codec
+// as closely as possible and returns its payload type
+func (s *SessionDescription) GetPayloadTypeForCodec(wanted Codec) (uint8, error) {
+ codecs := s.buildCodecMap()
+
+ for payloadType, codec := range codecs {
+ if codecsMatch(wanted, codec) {
+ return payloadType, nil
+ }
+ }
+
+ return 0, errCodecNotFound
+}
+
+type stateFn func(*lexer) (stateFn, error)
+
+type lexer struct {
+ desc *SessionDescription
+ baseLexer
+}
+
+type keyToState func(key string) stateFn
+
+func (l *lexer) handleType(fn keyToState) (stateFn, error) {
+ key, err := l.readType()
+ if err == io.EOF && key == "" {
+ return nil, nil
+ } else if err != nil {
+ return nil, err
+ }
+
+ if res := fn(key); res != nil {
+ return res, nil
+ }
+
+ return nil, l.syntaxError()
+}
diff --git a/vendor/github.com/pion/srtp/v2/.gitignore b/vendor/github.com/pion/srtp/v2/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/srtp/v2/.golangci.yml b/vendor/github.com/pion/srtp/v2/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/srtp/v2/DESIGN.md b/vendor/github.com/pion/srtp/v2/DESIGN.md
new file mode 100644
index 0000000..8742540
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/DESIGN.md
@@ -0,0 +1,20 @@
+<h1 align="center">
+ Design
+</h1>
+
+### Portable
+Pion SRTP is written in Go and extremely portable. Anywhere Golang runs, Pion SRTP should work as well! Instead of dealing with complicated
+cross-compiling of multiple libraries, you now can run anywhere with one `go build`
+
+### Simple API
+The API is based on an io.ReadWriteCloser.
+
+### Readable
+If code comes from an RFC we try to make sure everything is commented with a link to the spec.
+This makes learning and debugging easier, this library was written to also serve as a guide for others.
+
+### Tested
+Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on.
+
+### Shared libraries
+Every pion product is built using shared libraries, allowing others to review and reuse our libraries.
diff --git a/vendor/github.com/pion/srtp/v2/LICENSE b/vendor/github.com/pion/srtp/v2/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/srtp/v2/README.md b/vendor/github.com/pion/srtp/v2/README.md
new file mode 100644
index 0000000..19551e3
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/README.md
@@ -0,0 +1,54 @@
+<h1 align="center">
+ <br>
+ Pion SRTP
+ <br>
+</h1>
+<h4 align="center">A Go implementation of SRTP</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-srtp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion SRTP"></a>
+ <a href="https://sourcegraph.com/github.com/pion/srtp?badge"><img src="https://sourcegraph.com/github.com/pion/srtp/-/badge.svg" alt="Sourcegraph Widget"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/srtp"><img src="https://travis-ci.org/pion/srtp.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/srtp"><img src="https://godoc.org/github.com/pion/srtp?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/srtp"><img src="https://codecov.io/gh/pion/srtp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/srtp"><img src="https://goreportcard.com/badge/github.com/pion/srtp" alt="Go Report Card"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+See [DESIGN.md](DESIGN.md) for an overview of features and future goals.
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *io.Writer interfaces*
+* [Tobias Fridén](https://github.com/tobiasfriden) *SRTP authentication verification*
+* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter*
+* [Luke Curley](https://github.com/kixelated) - *Performance*
+* [Chris Hiszpanski](https://github.com/thinkski) - *Fix out-of-bounds access*
+* [Yutaka Takeda](https://github.com/enobufs) - *Fix log messages*
+* [Max Hawkins](https://github.com/maxhawkins)
+* [Woodrow Douglass](https://github.com/wdouglass)
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Novel Corpse](https://github.com/NovelCorpse)
+* [Jerko Steiner](https://github.com/jeremija)
+* [Juliusz Chroboczek](https://github.com/jech)
+* [Mission Liao](https://github.com/mission-liao)
+* [Orlando](https://github.com/OrlandoCo)
+* [Tarrence van As](https://github.com/tarrencev)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/srtp/v2/codecov.yml b/vendor/github.com/pion/srtp/v2/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/srtp/v2/context.go b/vendor/github.com/pion/srtp/v2/context.go
new file mode 100644
index 0000000..63566de
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/context.go
@@ -0,0 +1,196 @@
+package srtp
+
+import (
+ "fmt"
+
+ "github.com/pion/transport/replaydetector"
+)
+
+const (
+ labelSRTPEncryption = 0x00
+ labelSRTPAuthenticationTag = 0x01
+ labelSRTPSalt = 0x02
+
+ labelSRTCPEncryption = 0x03
+ labelSRTCPAuthenticationTag = 0x04
+ labelSRTCPSalt = 0x05
+
+ maxROCDisorder = 100
+ maxSequenceNumber = 65535
+
+ srtcpIndexSize = 4
+)
+
+// Encrypt/Decrypt state for a single SRTP SSRC
+type srtpSSRCState struct {
+ ssrc uint32
+ rolloverCounter uint32
+ rolloverHasProcessed bool
+ lastSequenceNumber uint16
+ replayDetector replaydetector.ReplayDetector
+}
+
+// Encrypt/Decrypt state for a single SRTCP SSRC
+type srtcpSSRCState struct {
+ srtcpIndex uint32
+ ssrc uint32
+ replayDetector replaydetector.ReplayDetector
+}
+
+// Context represents a SRTP cryptographic context.
+// Context can only be used for one-way operations.
+// it must either used ONLY for encryption or ONLY for decryption.
+type Context struct {
+ cipher srtpCipher
+
+ srtpSSRCStates map[uint32]*srtpSSRCState
+ srtcpSSRCStates map[uint32]*srtcpSSRCState
+
+ newSRTCPReplayDetector func() replaydetector.ReplayDetector
+ newSRTPReplayDetector func() replaydetector.ReplayDetector
+}
+
+// CreateContext creates a new SRTP Context.
+//
+// CreateContext receives variable number of ContextOption-s.
+// Passing multiple options which set the same parameter let the last one valid.
+// Following example create SRTP Context with replay protection with window size of 256.
+//
+// decCtx, err := srtp.CreateContext(key, salt, profile, srtp.SRTPReplayProtection(256))
+//
+func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts ...ContextOption) (c *Context, err error) {
+ keyLen, err := profile.keyLen()
+ if err != nil {
+ return nil, err
+ }
+
+ saltLen, err := profile.saltLen()
+ if err != nil {
+ return nil, err
+ }
+
+ if masterKeyLen := len(masterKey); masterKeyLen != keyLen {
+ return c, fmt.Errorf("%w expected(%d) actual(%d)", errShortSrtpMasterKey, masterKey, keyLen)
+ } else if masterSaltLen := len(masterSalt); masterSaltLen != saltLen {
+ return c, fmt.Errorf("%w expected(%d) actual(%d)", errShortSrtpMasterSalt, saltLen, masterSaltLen)
+ }
+
+ c = &Context{
+ srtpSSRCStates: map[uint32]*srtpSSRCState{},
+ srtcpSSRCStates: map[uint32]*srtcpSSRCState{},
+ }
+
+ switch profile {
+ case ProtectionProfileAeadAes128Gcm:
+ c.cipher, err = newSrtpCipherAeadAesGcm(masterKey, masterSalt)
+ case ProtectionProfileAes128CmHmacSha1_80:
+ c.cipher, err = newSrtpCipherAesCmHmacSha1(masterKey, masterSalt)
+ default:
+ return nil, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, profile)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ for _, o := range append(
+ []ContextOption{ // Default options
+ SRTPNoReplayProtection(),
+ SRTCPNoReplayProtection(),
+ },
+ opts..., // User specified options
+ ) {
+ if errOpt := o(c); errOpt != nil {
+ return nil, errOpt
+ }
+ }
+
+ return c, nil
+}
+
+// https://tools.ietf.org/html/rfc3550#appendix-A.1
+func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (uint32, func()) {
+ roc := s.rolloverCounter
+
+ switch {
+ case !s.rolloverHasProcessed:
+ case sequenceNumber == 0: // We exactly hit the rollover count
+ // Only update rolloverCounter if lastSequenceNumber is greater then maxROCDisorder
+ // otherwise we already incremented for disorder
+ if s.lastSequenceNumber > maxROCDisorder {
+ roc++
+ }
+ case s.lastSequenceNumber < maxROCDisorder &&
+ sequenceNumber > (maxSequenceNumber-maxROCDisorder):
+ // Our last sequence number incremented because we crossed 0, but then our current number was within maxROCDisorder of the max
+ // So we fell behind, drop to account for jitter
+ roc--
+ case sequenceNumber < maxROCDisorder &&
+ s.lastSequenceNumber > (maxSequenceNumber-maxROCDisorder):
+ // our current is within a maxROCDisorder of 0
+ // and our last sequence number was a high sequence number, increment to account for jitter
+ roc++
+ }
+ return roc, func() {
+ s.rolloverHasProcessed = true
+ s.lastSequenceNumber = sequenceNumber
+ s.rolloverCounter = roc
+ }
+}
+
+func (c *Context) getSRTPSSRCState(ssrc uint32) *srtpSSRCState {
+ s, ok := c.srtpSSRCStates[ssrc]
+ if ok {
+ return s
+ }
+
+ s = &srtpSSRCState{
+ ssrc: ssrc,
+ replayDetector: c.newSRTPReplayDetector(),
+ }
+ c.srtpSSRCStates[ssrc] = s
+ return s
+}
+
+func (c *Context) getSRTCPSSRCState(ssrc uint32) *srtcpSSRCState {
+ s, ok := c.srtcpSSRCStates[ssrc]
+ if ok {
+ return s
+ }
+
+ s = &srtcpSSRCState{
+ ssrc: ssrc,
+ replayDetector: c.newSRTCPReplayDetector(),
+ }
+ c.srtcpSSRCStates[ssrc] = s
+ return s
+}
+
+// ROC returns SRTP rollover counter value of specified SSRC.
+func (c *Context) ROC(ssrc uint32) (uint32, bool) {
+ s, ok := c.srtpSSRCStates[ssrc]
+ if !ok {
+ return 0, false
+ }
+ return s.rolloverCounter, true
+}
+
+// SetROC sets SRTP rollover counter value of specified SSRC.
+func (c *Context) SetROC(ssrc uint32, roc uint32) {
+ s := c.getSRTPSSRCState(ssrc)
+ s.rolloverCounter = roc
+}
+
+// Index returns SRTCP index value of specified SSRC.
+func (c *Context) Index(ssrc uint32) (uint32, bool) {
+ s, ok := c.srtcpSSRCStates[ssrc]
+ if !ok {
+ return 0, false
+ }
+ return s.srtcpIndex, true
+}
+
+// SetIndex sets SRTCP index value of specified SSRC.
+func (c *Context) SetIndex(ssrc uint32, index uint32) {
+ s := c.getSRTCPSSRCState(ssrc)
+ s.srtcpIndex = index % (maxSRTCPIndex + 1)
+}
diff --git a/vendor/github.com/pion/srtp/v2/errors.go b/vendor/github.com/pion/srtp/v2/errors.go
new file mode 100644
index 0000000..a702621
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/errors.go
@@ -0,0 +1,40 @@
+package srtp
+
+import (
+ "errors"
+ "fmt"
+)
+
+var (
+ errDuplicated = errors.New("duplicated packet")
+ errShortSrtpMasterKey = errors.New("SRTP master key is not long enough")
+ errShortSrtpMasterSalt = errors.New("SRTP master salt is not long enough")
+ errNoSuchSRTPProfile = errors.New("no such SRTP Profile")
+ errNonZeroKDRNotSupported = errors.New("indexOverKdr > 0 is not supported yet")
+ errExporterWrongLabel = errors.New("exporter called with wrong label")
+ errNoConfig = errors.New("no config provided")
+ errNoConn = errors.New("no conn provided")
+ errFailedToVerifyAuthTag = errors.New("failed to verify auth tag")
+ errTooShortRTCP = errors.New("packet is too short to be rtcp packet")
+ errPayloadDiffers = errors.New("payload differs")
+ errStartedChannelUsedIncorrectly = errors.New("started channel used incorrectly, should only be closed")
+
+ errStreamNotInited = errors.New("stream has not been inited, unable to close")
+ errStreamAlreadyClosed = errors.New("stream is already closed")
+ errStreamAlreadyInited = errors.New("stream is already inited")
+ errFailedTypeAssertion = errors.New("failed to cast child")
+)
+
+type errorDuplicated struct {
+ Proto string // srtp or srtcp
+ SSRC uint32
+ Index uint32 // sequence number or index
+}
+
+func (e *errorDuplicated) Error() string {
+ return fmt.Sprintf("%s ssrc=%d index=%d: %v", e.Proto, e.SSRC, e.Index, errDuplicated)
+}
+
+func (e *errorDuplicated) Unwrap() error {
+ return errDuplicated
+}
diff --git a/vendor/github.com/pion/srtp/v2/go.mod b/vendor/github.com/pion/srtp/v2/go.mod
new file mode 100644
index 0000000..bc04f63
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/go.mod
@@ -0,0 +1,11 @@
+module github.com/pion/srtp/v2
+
+go 1.14
+
+require (
+ github.com/pion/logging v0.2.2
+ github.com/pion/rtcp v1.2.6
+ github.com/pion/rtp v1.6.2
+ github.com/pion/transport v0.12.2
+ github.com/stretchr/testify v1.7.0
+)
diff --git a/vendor/github.com/pion/srtp/v2/go.sum b/vendor/github.com/pion/srtp/v2/go.sum
new file mode 100644
index 0000000..6066df9
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/go.sum
@@ -0,0 +1,34 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
+github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
+github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
+github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
+github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys=
+github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/srtp/v2/key_derivation.go b/vendor/github.com/pion/srtp/v2/key_derivation.go
new file mode 100644
index 0000000..5bbf3aa
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/key_derivation.go
@@ -0,0 +1,63 @@
+package srtp
+
+import (
+ "crypto/aes"
+ "encoding/binary"
+)
+
+func aesCmKeyDerivation(label byte, masterKey, masterSalt []byte, indexOverKdr int, outLen int) ([]byte, error) {
+ if indexOverKdr != 0 {
+ // 24-bit "index DIV kdr" must be xored to prf input.
+ return nil, errNonZeroKDRNotSupported
+ }
+
+ // https://tools.ietf.org/html/rfc3711#appendix-B.3
+ // The input block for AES-CM is generated by exclusive-oring the master salt with the
+ // concatenation of the encryption key label 0x00 with (index DIV kdr),
+ // - index is 'rollover count' and DIV is 'divided by'
+
+ nMasterKey := len(masterKey)
+ nMasterSalt := len(masterSalt)
+
+ prfIn := make([]byte, nMasterKey)
+ copy(prfIn[:nMasterSalt], masterSalt)
+
+ prfIn[7] ^= label
+
+ // The resulting value is then AES encrypted using the master key to get the cipher key.
+ block, err := aes.NewCipher(masterKey)
+ if err != nil {
+ return nil, err
+ }
+
+ out := make([]byte, ((outLen+nMasterKey)/nMasterKey)*nMasterKey)
+ var i uint16
+ for n := 0; n < outLen; n += nMasterKey {
+ binary.BigEndian.PutUint16(prfIn[nMasterKey-2:], i)
+ block.Encrypt(out[n:n+nMasterKey], prfIn)
+ i++
+ }
+ return out[:outLen], nil
+}
+
+// Generate IV https://tools.ietf.org/html/rfc3711#section-4.1.1
+// where the 128-bit integer value IV SHALL be defined by the SSRC, the
+// SRTP packet index i, and the SRTP session salting key k_s, as below.
+// - ROC = a 32-bit unsigned rollover counter (ROC), which records how many
+// - times the 16-bit RTP sequence number has been reset to zero after
+// - passing through 65,535
+// i = 2^16 * ROC + SEQ
+// IV = (salt*2 ^ 16) | (ssrc*2 ^ 64) | (i*2 ^ 16)
+func generateCounter(sequenceNumber uint16, rolloverCounter uint32, ssrc uint32, sessionSalt []byte) []byte {
+ counter := make([]byte, 16)
+
+ binary.BigEndian.PutUint32(counter[4:], ssrc)
+ binary.BigEndian.PutUint32(counter[8:], rolloverCounter)
+ binary.BigEndian.PutUint32(counter[12:], uint32(sequenceNumber)<<16)
+
+ for i := range sessionSalt {
+ counter[i] ^= sessionSalt[i]
+ }
+
+ return counter
+}
diff --git a/vendor/github.com/pion/srtp/v2/keying.go b/vendor/github.com/pion/srtp/v2/keying.go
new file mode 100644
index 0000000..82fd4d9
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/keying.go
@@ -0,0 +1,54 @@
+package srtp
+
+const labelExtractorDtlsSrtp = "EXTRACTOR-dtls_srtp"
+
+// KeyingMaterialExporter allows package SRTP to extract keying material
+type KeyingMaterialExporter interface {
+ ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error)
+}
+
+// ExtractSessionKeysFromDTLS allows setting the Config SessionKeys by
+// extracting them from DTLS. This behavior is defined in RFC5764:
+// https://tools.ietf.org/html/rfc5764
+func (c *Config) ExtractSessionKeysFromDTLS(exporter KeyingMaterialExporter, isClient bool) error {
+ keyLen, err := c.Profile.keyLen()
+ if err != nil {
+ return err
+ }
+
+ saltLen, err := c.Profile.saltLen()
+ if err != nil {
+ return err
+ }
+
+ keyingMaterial, err := exporter.ExportKeyingMaterial(labelExtractorDtlsSrtp, nil, (keyLen*2)+(saltLen*2))
+ if err != nil {
+ return err
+ }
+
+ offset := 0
+ clientWriteKey := append([]byte{}, keyingMaterial[offset:offset+keyLen]...)
+ offset += keyLen
+
+ serverWriteKey := append([]byte{}, keyingMaterial[offset:offset+keyLen]...)
+ offset += keyLen
+
+ clientWriteKey = append(clientWriteKey, keyingMaterial[offset:offset+saltLen]...)
+ offset += saltLen
+
+ serverWriteKey = append(serverWriteKey, keyingMaterial[offset:offset+saltLen]...)
+
+ if isClient {
+ c.Keys.LocalMasterKey = clientWriteKey[0:keyLen]
+ c.Keys.LocalMasterSalt = clientWriteKey[keyLen:]
+ c.Keys.RemoteMasterKey = serverWriteKey[0:keyLen]
+ c.Keys.RemoteMasterSalt = serverWriteKey[keyLen:]
+ return nil
+ }
+
+ c.Keys.LocalMasterKey = serverWriteKey[0:keyLen]
+ c.Keys.LocalMasterSalt = serverWriteKey[keyLen:]
+ c.Keys.RemoteMasterKey = clientWriteKey[0:keyLen]
+ c.Keys.RemoteMasterSalt = clientWriteKey[keyLen:]
+ return nil
+}
diff --git a/vendor/github.com/pion/srtp/v2/option.go b/vendor/github.com/pion/srtp/v2/option.go
new file mode 100644
index 0000000..d6159f1
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/option.go
@@ -0,0 +1,54 @@
+package srtp
+
+import (
+ "github.com/pion/transport/replaydetector"
+)
+
+// ContextOption represents option of Context using the functional options pattern.
+type ContextOption func(*Context) error
+
+// SRTPReplayProtection sets SRTP replay protection window size.
+func SRTPReplayProtection(windowSize uint) ContextOption { // nolint:golint
+ return func(c *Context) error {
+ c.newSRTPReplayDetector = func() replaydetector.ReplayDetector {
+ return replaydetector.WithWrap(windowSize, maxSequenceNumber)
+ }
+ return nil
+ }
+}
+
+// SRTCPReplayProtection sets SRTCP replay protection window size.
+func SRTCPReplayProtection(windowSize uint) ContextOption {
+ return func(c *Context) error {
+ c.newSRTCPReplayDetector = func() replaydetector.ReplayDetector {
+ return replaydetector.WithWrap(windowSize, maxSRTCPIndex)
+ }
+ return nil
+ }
+}
+
+// SRTPNoReplayProtection disables SRTP replay protection.
+func SRTPNoReplayProtection() ContextOption { // nolint:golint
+ return func(c *Context) error {
+ c.newSRTPReplayDetector = func() replaydetector.ReplayDetector {
+ return &nopReplayDetector{}
+ }
+ return nil
+ }
+}
+
+// SRTCPNoReplayProtection disables SRTCP replay protection.
+func SRTCPNoReplayProtection() ContextOption {
+ return func(c *Context) error {
+ c.newSRTCPReplayDetector = func() replaydetector.ReplayDetector {
+ return &nopReplayDetector{}
+ }
+ return nil
+ }
+}
+
+type nopReplayDetector struct{}
+
+func (s *nopReplayDetector) Check(uint64) (func(), bool) {
+ return func() {}, true
+}
diff --git a/vendor/github.com/pion/srtp/v2/protection_profile.go b/vendor/github.com/pion/srtp/v2/protection_profile.go
new file mode 100644
index 0000000..94476ad
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/protection_profile.go
@@ -0,0 +1,67 @@
+package srtp
+
+import "fmt"
+
+// ProtectionProfile specifies Cipher and AuthTag details, similar to TLS cipher suite
+type ProtectionProfile uint16
+
+// Supported protection profiles
+const (
+ ProtectionProfileAes128CmHmacSha1_80 ProtectionProfile = 0x0001
+ ProtectionProfileAeadAes128Gcm ProtectionProfile = 0x0007
+)
+
+func (p ProtectionProfile) keyLen() (int, error) {
+ switch p {
+ case ProtectionProfileAes128CmHmacSha1_80:
+ fallthrough
+ case ProtectionProfileAeadAes128Gcm:
+ return 16, nil
+ default:
+ return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p)
+ }
+}
+
+func (p ProtectionProfile) saltLen() (int, error) {
+ switch p {
+ case ProtectionProfileAes128CmHmacSha1_80:
+ return 14, nil
+ case ProtectionProfileAeadAes128Gcm:
+ return 12, nil
+ default:
+ return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p)
+ }
+}
+
+func (p ProtectionProfile) authTagLen() (int, error) {
+ switch p {
+ case ProtectionProfileAes128CmHmacSha1_80:
+ return (&srtpCipherAesCmHmacSha1{}).authTagLen(), nil
+ case ProtectionProfileAeadAes128Gcm:
+ return (&srtpCipherAeadAesGcm{}).authTagLen(), nil
+ default:
+ return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p)
+ }
+}
+
+func (p ProtectionProfile) aeadAuthTagLen() (int, error) {
+ switch p {
+ case ProtectionProfileAes128CmHmacSha1_80:
+ return (&srtpCipherAesCmHmacSha1{}).aeadAuthTagLen(), nil
+ case ProtectionProfileAeadAes128Gcm:
+ return (&srtpCipherAeadAesGcm{}).aeadAuthTagLen(), nil
+ default:
+ return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p)
+ }
+}
+
+func (p ProtectionProfile) authKeyLen() (int, error) {
+ switch p {
+ case ProtectionProfileAes128CmHmacSha1_80:
+ return 20, nil
+ case ProtectionProfileAeadAes128Gcm:
+ return 0, nil
+ default:
+ return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p)
+ }
+}
diff --git a/vendor/github.com/pion/srtp/v2/renovate.json b/vendor/github.com/pion/srtp/v2/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/srtp/v2/session.go b/vendor/github.com/pion/srtp/v2/session.go
new file mode 100644
index 0000000..95520f7
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/session.go
@@ -0,0 +1,150 @@
+package srtp
+
+import (
+ "io"
+ "net"
+ "sync"
+
+ "github.com/pion/logging"
+ "github.com/pion/transport/packetio"
+)
+
+type streamSession interface {
+ Close() error
+ write([]byte) (int, error)
+ decrypt([]byte) error
+}
+
+type session struct {
+ localContextMutex sync.Mutex
+ localContext, remoteContext *Context
+ localOptions, remoteOptions []ContextOption
+
+ newStream chan readStream
+
+ started chan interface{}
+ closed chan interface{}
+
+ readStreamsClosed bool
+ readStreams map[uint32]readStream
+ readStreamsLock sync.Mutex
+
+ log logging.LeveledLogger
+ bufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser
+
+ nextConn net.Conn
+}
+
+// Config is used to configure a session.
+// You can provide either a KeyingMaterialExporter to export keys
+// or directly pass the keys themselves.
+// After a Config is passed to a session it must not be modified.
+type Config struct {
+ Keys SessionKeys
+ Profile ProtectionProfile
+ BufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser
+ LoggerFactory logging.LoggerFactory
+
+ // List of local/remote context options.
+ // ReplayProtection is enabled on remote context by default.
+ // Default replay protection window size is 64.
+ LocalOptions, RemoteOptions []ContextOption
+}
+
+// SessionKeys bundles the keys required to setup an SRTP session
+type SessionKeys struct {
+ LocalMasterKey []byte
+ LocalMasterSalt []byte
+ RemoteMasterKey []byte
+ RemoteMasterSalt []byte
+}
+
+func (s *session) getOrCreateReadStream(ssrc uint32, child streamSession, proto func() readStream) (readStream, bool) {
+ s.readStreamsLock.Lock()
+ defer s.readStreamsLock.Unlock()
+
+ if s.readStreamsClosed {
+ return nil, false
+ }
+
+ r, ok := s.readStreams[ssrc]
+ if ok {
+ return r, false
+ }
+
+ // Create the readStream.
+ r = proto()
+
+ if err := r.init(child, ssrc); err != nil {
+ return nil, false
+ }
+
+ s.readStreams[ssrc] = r
+ return r, true
+}
+
+func (s *session) removeReadStream(ssrc uint32) {
+ s.readStreamsLock.Lock()
+ defer s.readStreamsLock.Unlock()
+
+ if s.readStreamsClosed {
+ return
+ }
+
+ delete(s.readStreams, ssrc)
+}
+
+func (s *session) close() error {
+ if s.nextConn == nil {
+ return nil
+ } else if err := s.nextConn.Close(); err != nil {
+ return err
+ }
+
+ <-s.closed
+ return nil
+}
+
+func (s *session) start(localMasterKey, localMasterSalt, remoteMasterKey, remoteMasterSalt []byte, profile ProtectionProfile, child streamSession) error {
+ var err error
+ s.localContext, err = CreateContext(localMasterKey, localMasterSalt, profile, s.localOptions...)
+ if err != nil {
+ return err
+ }
+
+ s.remoteContext, err = CreateContext(remoteMasterKey, remoteMasterSalt, profile, s.remoteOptions...)
+ if err != nil {
+ return err
+ }
+
+ go func() {
+ defer func() {
+ close(s.newStream)
+
+ s.readStreamsLock.Lock()
+ s.readStreamsClosed = true
+ s.readStreamsLock.Unlock()
+ close(s.closed)
+ }()
+
+ b := make([]byte, 8192)
+ for {
+ var i int
+ i, err = s.nextConn.Read(b)
+ if err != nil {
+ if err != io.EOF {
+ s.log.Error(err.Error())
+ }
+ return
+ }
+
+ if err = child.decrypt(b[:i]); err != nil {
+ s.log.Info(err.Error())
+ }
+ }
+ }()
+
+ close(s.started)
+
+ return nil
+}
diff --git a/vendor/github.com/pion/srtp/v2/session_srtcp.go b/vendor/github.com/pion/srtp/v2/session_srtcp.go
new file mode 100644
index 0000000..a5fb656
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/session_srtcp.go
@@ -0,0 +1,180 @@
+package srtp
+
+import (
+ "net"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/rtcp"
+)
+
+const defaultSessionSRTCPReplayProtectionWindow = 64
+
+// SessionSRTCP implements io.ReadWriteCloser and provides a bi-directional SRTCP session
+// SRTCP itself does not have a design like this, but it is common in most applications
+// for local/remote to each have their own keying material. This provides those patterns
+// instead of making everyone re-implement
+type SessionSRTCP struct {
+ session
+ writeStream *WriteStreamSRTCP
+}
+
+// NewSessionSRTCP creates a SRTCP session using conn as the underlying transport.
+func NewSessionSRTCP(conn net.Conn, config *Config) (*SessionSRTCP, error) { //nolint:dupl
+ if config == nil {
+ return nil, errNoConfig
+ } else if conn == nil {
+ return nil, errNoConn
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ localOpts := append(
+ []ContextOption{},
+ config.LocalOptions...,
+ )
+ remoteOpts := append(
+ []ContextOption{
+ // Default options
+ SRTCPReplayProtection(defaultSessionSRTCPReplayProtectionWindow),
+ },
+ config.RemoteOptions...,
+ )
+
+ s := &SessionSRTCP{
+ session: session{
+ nextConn: conn,
+ localOptions: localOpts,
+ remoteOptions: remoteOpts,
+ readStreams: map[uint32]readStream{},
+ newStream: make(chan readStream),
+ started: make(chan interface{}),
+ closed: make(chan interface{}),
+ bufferFactory: config.BufferFactory,
+ log: loggerFactory.NewLogger("srtp"),
+ },
+ }
+ s.writeStream = &WriteStreamSRTCP{s}
+
+ err := s.session.start(
+ config.Keys.LocalMasterKey, config.Keys.LocalMasterSalt,
+ config.Keys.RemoteMasterKey, config.Keys.RemoteMasterSalt,
+ config.Profile,
+ s,
+ )
+ if err != nil {
+ return nil, err
+ }
+ return s, nil
+}
+
+// OpenWriteStream returns the global write stream for the Session
+func (s *SessionSRTCP) OpenWriteStream() (*WriteStreamSRTCP, error) {
+ return s.writeStream, nil
+}
+
+// OpenReadStream opens a read stream for the given SSRC, it can be used
+// if you want a certain SSRC, but don't want to wait for AcceptStream
+func (s *SessionSRTCP) OpenReadStream(ssrc uint32) (*ReadStreamSRTCP, error) {
+ r, _ := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTCP)
+
+ if readStream, ok := r.(*ReadStreamSRTCP); ok {
+ return readStream, nil
+ }
+ return nil, errFailedTypeAssertion
+}
+
+// AcceptStream returns a stream to handle RTCP for a single SSRC
+func (s *SessionSRTCP) AcceptStream() (*ReadStreamSRTCP, uint32, error) {
+ stream, ok := <-s.newStream
+ if !ok {
+ return nil, 0, errStreamAlreadyClosed
+ }
+
+ readStream, ok := stream.(*ReadStreamSRTCP)
+ if !ok {
+ return nil, 0, errFailedTypeAssertion
+ }
+
+ return readStream, stream.GetSSRC(), nil
+}
+
+// Close ends the session
+func (s *SessionSRTCP) Close() error {
+ return s.session.close()
+}
+
+// Private
+
+func (s *SessionSRTCP) write(buf []byte) (int, error) {
+ if _, ok := <-s.session.started; ok {
+ return 0, errStartedChannelUsedIncorrectly
+ }
+
+ s.session.localContextMutex.Lock()
+ encrypted, err := s.localContext.EncryptRTCP(nil, buf, nil)
+ s.session.localContextMutex.Unlock()
+
+ if err != nil {
+ return 0, err
+ }
+ return s.session.nextConn.Write(encrypted)
+}
+
+func (s *SessionSRTCP) setWriteDeadline(t time.Time) error {
+ return s.session.nextConn.SetWriteDeadline(t)
+}
+
+// create a list of Destination SSRCs
+// that's a superset of all Destinations in the slice.
+func destinationSSRC(pkts []rtcp.Packet) []uint32 {
+ ssrcSet := make(map[uint32]struct{})
+ for _, p := range pkts {
+ for _, ssrc := range p.DestinationSSRC() {
+ ssrcSet[ssrc] = struct{}{}
+ }
+ }
+
+ out := make([]uint32, 0, len(ssrcSet))
+ for ssrc := range ssrcSet {
+ out = append(out, ssrc)
+ }
+
+ return out
+}
+
+func (s *SessionSRTCP) decrypt(buf []byte) error {
+ decrypted, err := s.remoteContext.DecryptRTCP(buf, buf, nil)
+ if err != nil {
+ return err
+ }
+
+ pkt, err := rtcp.Unmarshal(decrypted)
+ if err != nil {
+ return err
+ }
+
+ for _, ssrc := range destinationSSRC(pkt) {
+ r, isNew := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTCP)
+ if r == nil {
+ return nil // Session has been closed
+ } else if isNew {
+ s.session.newStream <- r // Notify AcceptStream
+ }
+
+ readStream, ok := r.(*ReadStreamSRTCP)
+ if !ok {
+ return errFailedTypeAssertion
+ }
+
+ _, err = readStream.write(decrypted)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/srtp/v2/session_srtp.go b/vendor/github.com/pion/srtp/v2/session_srtp.go
new file mode 100644
index 0000000..dc815af
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/session_srtp.go
@@ -0,0 +1,171 @@
+package srtp
+
+import (
+ "net"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/rtp"
+)
+
+const defaultSessionSRTPReplayProtectionWindow = 64
+
+// SessionSRTP implements io.ReadWriteCloser and provides a bi-directional SRTP session
+// SRTP itself does not have a design like this, but it is common in most applications
+// for local/remote to each have their own keying material. This provides those patterns
+// instead of making everyone re-implement
+type SessionSRTP struct {
+ session
+ writeStream *WriteStreamSRTP
+}
+
+// NewSessionSRTP creates a SRTP session using conn as the underlying transport.
+func NewSessionSRTP(conn net.Conn, config *Config) (*SessionSRTP, error) { //nolint:dupl
+ if config == nil {
+ return nil, errNoConfig
+ } else if conn == nil {
+ return nil, errNoConn
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ localOpts := append(
+ []ContextOption{},
+ config.LocalOptions...,
+ )
+ remoteOpts := append(
+ []ContextOption{
+ // Default options
+ SRTPReplayProtection(defaultSessionSRTPReplayProtectionWindow),
+ },
+ config.RemoteOptions...,
+ )
+
+ s := &SessionSRTP{
+ session: session{
+ nextConn: conn,
+ localOptions: localOpts,
+ remoteOptions: remoteOpts,
+ readStreams: map[uint32]readStream{},
+ newStream: make(chan readStream),
+ started: make(chan interface{}),
+ closed: make(chan interface{}),
+ bufferFactory: config.BufferFactory,
+ log: loggerFactory.NewLogger("srtp"),
+ },
+ }
+ s.writeStream = &WriteStreamSRTP{s}
+
+ err := s.session.start(
+ config.Keys.LocalMasterKey, config.Keys.LocalMasterSalt,
+ config.Keys.RemoteMasterKey, config.Keys.RemoteMasterSalt,
+ config.Profile,
+ s,
+ )
+ if err != nil {
+ return nil, err
+ }
+ return s, nil
+}
+
+// OpenWriteStream returns the global write stream for the Session
+func (s *SessionSRTP) OpenWriteStream() (*WriteStreamSRTP, error) {
+ return s.writeStream, nil
+}
+
+// OpenReadStream opens a read stream for the given SSRC, it can be used
+// if you want a certain SSRC, but don't want to wait for AcceptStream
+func (s *SessionSRTP) OpenReadStream(ssrc uint32) (*ReadStreamSRTP, error) {
+ r, _ := s.session.getOrCreateReadStream(ssrc, s, newReadStreamSRTP)
+
+ if readStream, ok := r.(*ReadStreamSRTP); ok {
+ return readStream, nil
+ }
+
+ return nil, errFailedTypeAssertion
+}
+
+// AcceptStream returns a stream to handle RTCP for a single SSRC
+func (s *SessionSRTP) AcceptStream() (*ReadStreamSRTP, uint32, error) {
+ stream, ok := <-s.newStream
+ if !ok {
+ return nil, 0, errStreamAlreadyClosed
+ }
+
+ readStream, ok := stream.(*ReadStreamSRTP)
+ if !ok {
+ return nil, 0, errFailedTypeAssertion
+ }
+
+ return readStream, stream.GetSSRC(), nil
+}
+
+// Close ends the session
+func (s *SessionSRTP) Close() error {
+ return s.session.close()
+}
+
+func (s *SessionSRTP) write(b []byte) (int, error) {
+ packet := &rtp.Packet{}
+
+ err := packet.Unmarshal(b)
+ if err != nil {
+ return 0, nil
+ }
+
+ return s.writeRTP(&packet.Header, packet.Payload)
+}
+
+func (s *SessionSRTP) writeRTP(header *rtp.Header, payload []byte) (int, error) {
+ if _, ok := <-s.session.started; ok {
+ return 0, errStartedChannelUsedIncorrectly
+ }
+
+ s.session.localContextMutex.Lock()
+ encrypted, err := s.localContext.encryptRTP(nil, header, payload)
+ s.session.localContextMutex.Unlock()
+
+ if err != nil {
+ return 0, err
+ }
+
+ return s.session.nextConn.Write(encrypted)
+}
+
+func (s *SessionSRTP) setWriteDeadline(t time.Time) error {
+ return s.session.nextConn.SetWriteDeadline(t)
+}
+
+func (s *SessionSRTP) decrypt(buf []byte) error {
+ h := &rtp.Header{}
+ if err := h.Unmarshal(buf); err != nil {
+ return err
+ }
+
+ r, isNew := s.session.getOrCreateReadStream(h.SSRC, s, newReadStreamSRTP)
+ if r == nil {
+ return nil // Session has been closed
+ } else if isNew {
+ s.session.newStream <- r // Notify AcceptStream
+ }
+
+ readStream, ok := r.(*ReadStreamSRTP)
+ if !ok {
+ return errFailedTypeAssertion
+ }
+
+ decrypted, err := s.remoteContext.decryptRTP(buf, buf, h)
+ if err != nil {
+ return err
+ }
+
+ _, err = readStream.write(decrypted)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/srtp/v2/srtcp.go b/vendor/github.com/pion/srtp/v2/srtcp.go
new file mode 100644
index 0000000..dbf5125
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/srtcp.go
@@ -0,0 +1,77 @@
+package srtp
+
+import (
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pion/rtcp"
+)
+
+const maxSRTCPIndex = 0x7FFFFFFF
+
+func (c *Context) decryptRTCP(dst, encrypted []byte) ([]byte, error) {
+ out := allocateIfMismatch(dst, encrypted)
+ tailOffset := len(encrypted) - (c.cipher.authTagLen() + srtcpIndexSize)
+
+ if tailOffset < 0 {
+ return nil, fmt.Errorf("%w: %d", errTooShortRTCP, len(encrypted))
+ } else if isEncrypted := encrypted[tailOffset] >> 7; isEncrypted == 0 {
+ return out, nil
+ }
+
+ index := c.cipher.getRTCPIndex(encrypted)
+ ssrc := binary.BigEndian.Uint32(encrypted[4:])
+
+ s := c.getSRTCPSSRCState(ssrc)
+ markAsValid, ok := s.replayDetector.Check(uint64(index))
+ if !ok {
+ return nil, &errorDuplicated{Proto: "srtcp", SSRC: ssrc, Index: index}
+ }
+
+ out, err := c.cipher.decryptRTCP(out, encrypted, index, ssrc)
+ if err != nil {
+ return nil, err
+ }
+
+ markAsValid()
+ return out, nil
+}
+
+// DecryptRTCP decrypts a buffer that contains a RTCP packet
+func (c *Context) DecryptRTCP(dst, encrypted []byte, header *rtcp.Header) ([]byte, error) {
+ if header == nil {
+ header = &rtcp.Header{}
+ }
+
+ if err := header.Unmarshal(encrypted); err != nil {
+ return nil, err
+ }
+
+ return c.decryptRTCP(dst, encrypted)
+}
+
+func (c *Context) encryptRTCP(dst, decrypted []byte) ([]byte, error) {
+ ssrc := binary.BigEndian.Uint32(decrypted[4:])
+ s := c.getSRTCPSSRCState(ssrc)
+
+ // We roll over early because MSB is used for marking as encrypted
+ s.srtcpIndex++
+ if s.srtcpIndex > maxSRTCPIndex {
+ s.srtcpIndex = 0
+ }
+
+ return c.cipher.encryptRTCP(dst, decrypted, s.srtcpIndex, ssrc)
+}
+
+// EncryptRTCP Encrypts a RTCP packet
+func (c *Context) EncryptRTCP(dst, decrypted []byte, header *rtcp.Header) ([]byte, error) {
+ if header == nil {
+ header = &rtcp.Header{}
+ }
+
+ if err := header.Unmarshal(decrypted); err != nil {
+ return nil, err
+ }
+
+ return c.encryptRTCP(dst, decrypted)
+}
diff --git a/vendor/github.com/pion/srtp/v2/srtp.go b/vendor/github.com/pion/srtp/v2/srtp.go
new file mode 100644
index 0000000..c4ed3ac
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/srtp.go
@@ -0,0 +1,68 @@
+// Package srtp implements Secure Real-time Transport Protocol
+package srtp
+
+import (
+ "github.com/pion/rtp"
+)
+
+func (c *Context) decryptRTP(dst, ciphertext []byte, header *rtp.Header) ([]byte, error) {
+ s := c.getSRTPSSRCState(header.SSRC)
+
+ markAsValid, ok := s.replayDetector.Check(uint64(header.SequenceNumber))
+ if !ok {
+ return nil, &errorDuplicated{
+ Proto: "srtp", SSRC: header.SSRC, Index: uint32(header.SequenceNumber),
+ }
+ }
+
+ dst = growBufferSize(dst, len(ciphertext)-c.cipher.authTagLen())
+ roc, updateROC := s.nextRolloverCount(header.SequenceNumber)
+
+ dst, err := c.cipher.decryptRTP(dst, ciphertext, header, roc)
+ if err != nil {
+ return nil, err
+ }
+
+ markAsValid()
+ updateROC()
+ return dst, nil
+}
+
+// DecryptRTP decrypts a RTP packet with an encrypted payload
+func (c *Context) DecryptRTP(dst, encrypted []byte, header *rtp.Header) ([]byte, error) {
+ if header == nil {
+ header = &rtp.Header{}
+ }
+
+ if err := header.Unmarshal(encrypted); err != nil {
+ return nil, err
+ }
+
+ return c.decryptRTP(dst, encrypted, header)
+}
+
+// EncryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided.
+// If the dst buffer does not have the capacity to hold `len(plaintext) + 10` bytes, a new one will be allocated and returned.
+// If a rtp.Header is provided, it will be Unmarshaled using the plaintext.
+func (c *Context) EncryptRTP(dst []byte, plaintext []byte, header *rtp.Header) ([]byte, error) {
+ if header == nil {
+ header = &rtp.Header{}
+ }
+
+ if err := header.Unmarshal(plaintext); err != nil {
+ return nil, err
+ }
+
+ return c.encryptRTP(dst, header, plaintext[header.PayloadOffset:])
+}
+
+// encryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided.
+// If the dst buffer does not have the capacity, a new one will be allocated and returned.
+// Similar to above but faster because it can avoid unmarshaling the header and marshaling the payload.
+func (c *Context) encryptRTP(dst []byte, header *rtp.Header, payload []byte) (ciphertext []byte, err error) {
+ s := c.getSRTPSSRCState(header.SSRC)
+ roc, updateROC := s.nextRolloverCount(header.SequenceNumber)
+ updateROC()
+
+ return c.cipher.encryptRTP(dst, header, payload, roc)
+}
diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher.go b/vendor/github.com/pion/srtp/v2/srtp_cipher.go
new file mode 100644
index 0000000..4c5cd88
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/srtp_cipher.go
@@ -0,0 +1,46 @@
+package srtp
+
+import "github.com/pion/rtp"
+
+// cipher represents a implementation of one
+// of the SRTP Specific ciphers
+type srtpCipher interface {
+ // authTagLen returns auth key length of the cipher.
+ // See the note below.
+ authTagLen() int
+ // aeadAuthTagLen returns AEAD auth key length of the cipher.
+ // See the note below.
+ aeadAuthTagLen() int
+ getRTCPIndex([]byte) uint32
+
+ encryptRTP([]byte, *rtp.Header, []byte, uint32) ([]byte, error)
+ encryptRTCP([]byte, []byte, uint32, uint32) ([]byte, error)
+
+ decryptRTP([]byte, []byte, *rtp.Header, uint32) ([]byte, error)
+ decryptRTCP([]byte, []byte, uint32, uint32) ([]byte, error)
+}
+
+/*
+NOTE: Auth tag and AEAD auth tag are placed at the different position in SRTCP
+
+In non-AEAD cipher, the authentication tag is placed *after* the ESRTCP word
+(Encrypted-flag and SRTCP index).
+
+> AES_128_CM_HMAC_SHA1_80
+> | RTCP Header | Encrypted payload |E| SRTCP Index | Auth tag |
+> ^ |----------|
+> | ^
+> | authTagLen=10
+> aeadAuthTagLen=0
+
+In AEAD cipher, the AEAD authentication tag is embedded in the ciphertext.
+It is *before* the ESRTCP word (Encrypted-flag and SRTCP index).
+
+> AEAD_AES_128_GCM
+> | RTCP Header | Encrypted payload | AEAD auth tag |E| SRTCP Index |
+> |---------------| ^
+> ^ authTagLen=0
+> aeadAuthTagLen=16
+
+See https://tools.ietf.org/html/rfc7714 for the full specifications.
+*/
diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go b/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go
new file mode 100644
index 0000000..2720679
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go
@@ -0,0 +1,198 @@
+package srtp
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "encoding/binary"
+
+ "github.com/pion/rtp"
+)
+
+const (
+ rtcpEncryptionFlag = 0x80
+)
+
+type srtpCipherAeadAesGcm struct {
+ srtpCipher, srtcpCipher cipher.AEAD
+
+ srtpSessionSalt, srtcpSessionSalt []byte
+}
+
+func newSrtpCipherAeadAesGcm(masterKey, masterSalt []byte) (*srtpCipherAeadAesGcm, error) {
+ s := &srtpCipherAeadAesGcm{}
+
+ srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey))
+ if err != nil {
+ return nil, err
+ }
+
+ srtpBlock, err := aes.NewCipher(srtpSessionKey)
+ if err != nil {
+ return nil, err
+ }
+
+ s.srtpCipher, err = cipher.NewGCM(srtpBlock)
+ if err != nil {
+ return nil, err
+ }
+
+ srtcpSessionKey, err := aesCmKeyDerivation(labelSRTCPEncryption, masterKey, masterSalt, 0, len(masterKey))
+ if err != nil {
+ return nil, err
+ }
+
+ srtcpBlock, err := aes.NewCipher(srtcpSessionKey)
+ if err != nil {
+ return nil, err
+ }
+
+ s.srtcpCipher, err = cipher.NewGCM(srtcpBlock)
+ if err != nil {
+ return nil, err
+ }
+
+ if s.srtpSessionSalt, err = aesCmKeyDerivation(labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil {
+ return nil, err
+ } else if s.srtcpSessionSalt, err = aesCmKeyDerivation(labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil {
+ return nil, err
+ }
+
+ return s, nil
+}
+
+func (s *srtpCipherAeadAesGcm) authTagLen() int {
+ return 0
+}
+
+func (s *srtpCipherAeadAesGcm) aeadAuthTagLen() int {
+ return 16
+}
+
+func (s *srtpCipherAeadAesGcm) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) {
+ // Grow the given buffer to fit the output.
+ dst = growBufferSize(dst, header.MarshalSize()+len(payload)+s.aeadAuthTagLen())
+
+ hdr, err := header.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ iv := s.rtpInitializationVector(header, roc)
+ nHdr := len(hdr)
+ s.srtpCipher.Seal(dst[nHdr:nHdr], iv, payload, hdr)
+ copy(dst[:nHdr], hdr)
+ return dst, nil
+}
+
+func (s *srtpCipherAeadAesGcm) decryptRTP(dst, ciphertext []byte, header *rtp.Header, roc uint32) ([]byte, error) {
+ // Grow the given buffer to fit the output.
+ nDst := len(ciphertext) - s.aeadAuthTagLen()
+ if nDst < 0 {
+ // Size of ciphertext is shorter than AEAD auth tag len.
+ return nil, errFailedToVerifyAuthTag
+ }
+ dst = growBufferSize(dst, nDst)
+
+ iv := s.rtpInitializationVector(header, roc)
+
+ if _, err := s.srtpCipher.Open(
+ dst[header.PayloadOffset:header.PayloadOffset], iv, ciphertext[header.PayloadOffset:], ciphertext[:header.PayloadOffset],
+ ); err != nil {
+ return nil, err
+ }
+
+ copy(dst[:header.PayloadOffset], ciphertext[:header.PayloadOffset])
+ return dst, nil
+}
+
+func (s *srtpCipherAeadAesGcm) encryptRTCP(dst, decrypted []byte, srtcpIndex uint32, ssrc uint32) ([]byte, error) {
+ aadPos := len(decrypted) + s.aeadAuthTagLen()
+ // Grow the given buffer to fit the output.
+ dst = growBufferSize(dst, aadPos+srtcpIndexSize)
+
+ iv := s.rtcpInitializationVector(srtcpIndex, ssrc)
+ aad := s.rtcpAdditionalAuthenticatedData(decrypted, srtcpIndex)
+
+ s.srtcpCipher.Seal(dst[8:8], iv, decrypted[8:], aad)
+
+ copy(dst[:8], decrypted[:8])
+ copy(dst[aadPos:aadPos+4], aad[8:12])
+ return dst, nil
+}
+
+func (s *srtpCipherAeadAesGcm) decryptRTCP(dst, encrypted []byte, srtcpIndex, ssrc uint32) ([]byte, error) {
+ aadPos := len(encrypted) - srtcpIndexSize
+ // Grow the given buffer to fit the output.
+ nDst := aadPos - s.aeadAuthTagLen()
+ if nDst < 0 {
+ // Size of ciphertext is shorter than AEAD auth tag len.
+ return nil, errFailedToVerifyAuthTag
+ }
+ dst = growBufferSize(dst, nDst)
+
+ iv := s.rtcpInitializationVector(srtcpIndex, ssrc)
+ aad := s.rtcpAdditionalAuthenticatedData(encrypted, srtcpIndex)
+
+ if _, err := s.srtcpCipher.Open(dst[8:8], iv, encrypted[8:aadPos], aad); err != nil {
+ return nil, err
+ }
+
+ copy(dst[:8], encrypted[:8])
+ return dst, nil
+}
+
+// The 12-octet IV used by AES-GCM SRTP is formed by first concatenating
+// 2 octets of zeroes, the 4-octet SSRC, the 4-octet rollover counter
+// (ROC), and the 2-octet sequence number (SEQ). The resulting 12-octet
+// value is then XORed to the 12-octet salt to form the 12-octet IV.
+//
+// https://tools.ietf.org/html/rfc7714#section-8.1
+func (s *srtpCipherAeadAesGcm) rtpInitializationVector(header *rtp.Header, roc uint32) []byte {
+ iv := make([]byte, 12)
+ binary.BigEndian.PutUint32(iv[2:], header.SSRC)
+ binary.BigEndian.PutUint32(iv[6:], roc)
+ binary.BigEndian.PutUint16(iv[10:], header.SequenceNumber)
+
+ for i := range iv {
+ iv[i] ^= s.srtpSessionSalt[i]
+ }
+ return iv
+}
+
+// The 12-octet IV used by AES-GCM SRTCP is formed by first
+// concatenating 2 octets of zeroes, the 4-octet SSRC identifier,
+// 2 octets of zeroes, a single "0" bit, and the 31-bit SRTCP index.
+// The resulting 12-octet value is then XORed to the 12-octet salt to
+// form the 12-octet IV.
+//
+// https://tools.ietf.org/html/rfc7714#section-9.1
+func (s *srtpCipherAeadAesGcm) rtcpInitializationVector(srtcpIndex uint32, ssrc uint32) []byte {
+ iv := make([]byte, 12)
+
+ binary.BigEndian.PutUint32(iv[2:], ssrc)
+ binary.BigEndian.PutUint32(iv[8:], srtcpIndex)
+
+ for i := range iv {
+ iv[i] ^= s.srtcpSessionSalt[i]
+ }
+ return iv
+}
+
+// In an SRTCP packet, a 1-bit Encryption flag is prepended to the
+// 31-bit SRTCP index to form a 32-bit value we shall call the
+// "ESRTCP word"
+//
+// https://tools.ietf.org/html/rfc7714#section-17
+func (s *srtpCipherAeadAesGcm) rtcpAdditionalAuthenticatedData(rtcpPacket []byte, srtcpIndex uint32) []byte {
+ aad := make([]byte, 12)
+
+ copy(aad, rtcpPacket[:8])
+ binary.BigEndian.PutUint32(aad[8:], srtcpIndex)
+ aad[8] |= rtcpEncryptionFlag
+
+ return aad
+}
+
+func (s *srtpCipherAeadAesGcm) getRTCPIndex(in []byte) uint32 {
+ return binary.BigEndian.Uint32(in[len(in)-4:]) &^ (rtcpEncryptionFlag << 24)
+}
diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go b/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go
new file mode 100644
index 0000000..9d783d1
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go
@@ -0,0 +1,228 @@
+package srtp
+
+import ( //nolint:gci
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha1" //nolint:gosec
+ "crypto/subtle"
+ "encoding/binary"
+ "hash"
+
+ "github.com/pion/rtp"
+)
+
+type srtpCipherAesCmHmacSha1 struct {
+ srtpSessionSalt []byte
+ srtpSessionAuth hash.Hash
+ srtpBlock cipher.Block
+
+ srtcpSessionSalt []byte
+ srtcpSessionAuth hash.Hash
+ srtcpBlock cipher.Block
+}
+
+func newSrtpCipherAesCmHmacSha1(masterKey, masterSalt []byte) (*srtpCipherAesCmHmacSha1, error) {
+ s := &srtpCipherAesCmHmacSha1{}
+ srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey))
+ if err != nil {
+ return nil, err
+ } else if s.srtpBlock, err = aes.NewCipher(srtpSessionKey); err != nil {
+ return nil, err
+ }
+
+ srtcpSessionKey, err := aesCmKeyDerivation(labelSRTCPEncryption, masterKey, masterSalt, 0, len(masterKey))
+ if err != nil {
+ return nil, err
+ } else if s.srtcpBlock, err = aes.NewCipher(srtcpSessionKey); err != nil {
+ return nil, err
+ }
+
+ if s.srtpSessionSalt, err = aesCmKeyDerivation(labelSRTPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil {
+ return nil, err
+ } else if s.srtcpSessionSalt, err = aesCmKeyDerivation(labelSRTCPSalt, masterKey, masterSalt, 0, len(masterSalt)); err != nil {
+ return nil, err
+ }
+
+ authKeyLen, err := ProtectionProfileAes128CmHmacSha1_80.authKeyLen()
+ if err != nil {
+ return nil, err
+ }
+
+ srtpSessionAuthTag, err := aesCmKeyDerivation(labelSRTPAuthenticationTag, masterKey, masterSalt, 0, authKeyLen)
+ if err != nil {
+ return nil, err
+ }
+
+ srtcpSessionAuthTag, err := aesCmKeyDerivation(labelSRTCPAuthenticationTag, masterKey, masterSalt, 0, authKeyLen)
+ if err != nil {
+ return nil, err
+ }
+
+ s.srtcpSessionAuth = hmac.New(sha1.New, srtcpSessionAuthTag)
+ s.srtpSessionAuth = hmac.New(sha1.New, srtpSessionAuthTag)
+ return s, nil
+}
+
+func (s *srtpCipherAesCmHmacSha1) authTagLen() int {
+ return 10
+}
+
+func (s *srtpCipherAesCmHmacSha1) aeadAuthTagLen() int {
+ return 0
+}
+
+func (s *srtpCipherAesCmHmacSha1) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) {
+ // Grow the given buffer to fit the output.
+ dst = growBufferSize(dst, header.MarshalSize()+len(payload)+s.authTagLen())
+
+ // Copy the header unencrypted.
+ n, err := header.MarshalTo(dst)
+ if err != nil {
+ return nil, err
+ }
+
+ // Encrypt the payload
+ counter := generateCounter(header.SequenceNumber, roc, header.SSRC, s.srtpSessionSalt)
+ stream := cipher.NewCTR(s.srtpBlock, counter)
+ stream.XORKeyStream(dst[n:], payload)
+ n += len(payload)
+
+ // Generate the auth tag.
+ authTag, err := s.generateSrtpAuthTag(dst[:n], roc)
+ if err != nil {
+ return nil, err
+ }
+
+ // Write the auth tag to the dest.
+ copy(dst[n:], authTag)
+
+ return dst, nil
+}
+
+func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp.Header, roc uint32) ([]byte, error) {
+ // Split the auth tag and the cipher text into two parts.
+ actualTag := ciphertext[len(ciphertext)-s.authTagLen():]
+ ciphertext = ciphertext[:len(ciphertext)-s.authTagLen()]
+
+ // Generate the auth tag we expect to see from the ciphertext.
+ expectedTag, err := s.generateSrtpAuthTag(ciphertext, roc)
+ if err != nil {
+ return nil, err
+ }
+
+ // See if the auth tag actually matches.
+ // We use a constant time comparison to prevent timing attacks.
+ if subtle.ConstantTimeCompare(actualTag, expectedTag) != 1 {
+ return nil, errFailedToVerifyAuthTag
+ }
+
+ // Write the plaintext header to the destination buffer.
+ copy(dst, ciphertext[:header.PayloadOffset])
+
+ // Decrypt the ciphertext for the payload.
+ counter := generateCounter(header.SequenceNumber, roc, header.SSRC, s.srtpSessionSalt)
+ stream := cipher.NewCTR(s.srtpBlock, counter)
+ stream.XORKeyStream(dst[header.PayloadOffset:], ciphertext[header.PayloadOffset:])
+ return dst, nil
+}
+
+func (s *srtpCipherAesCmHmacSha1) encryptRTCP(dst, decrypted []byte, srtcpIndex uint32, ssrc uint32) ([]byte, error) {
+ dst = allocateIfMismatch(dst, decrypted)
+
+ // Encrypt everything after header
+ stream := cipher.NewCTR(s.srtcpBlock, generateCounter(uint16(srtcpIndex&0xffff), srtcpIndex>>16, ssrc, s.srtcpSessionSalt))
+ stream.XORKeyStream(dst[8:], dst[8:])
+
+ // Add SRTCP Index and set Encryption bit
+ dst = append(dst, make([]byte, 4)...)
+ binary.BigEndian.PutUint32(dst[len(dst)-4:], srtcpIndex)
+ dst[len(dst)-4] |= 0x80
+
+ authTag, err := s.generateSrtcpAuthTag(dst)
+ if err != nil {
+ return nil, err
+ }
+ return append(dst, authTag...), nil
+}
+
+func (s *srtpCipherAesCmHmacSha1) decryptRTCP(out, encrypted []byte, index, ssrc uint32) ([]byte, error) {
+ tailOffset := len(encrypted) - (s.authTagLen() + srtcpIndexSize)
+ out = out[0:tailOffset]
+
+ expectedTag, err := s.generateSrtcpAuthTag(encrypted[:len(encrypted)-s.authTagLen()])
+ if err != nil {
+ return nil, err
+ }
+
+ actualTag := encrypted[len(encrypted)-s.authTagLen():]
+ if subtle.ConstantTimeCompare(actualTag, expectedTag) != 1 {
+ return nil, errFailedToVerifyAuthTag
+ }
+
+ stream := cipher.NewCTR(s.srtcpBlock, generateCounter(uint16(index&0xffff), index>>16, ssrc, s.srtcpSessionSalt))
+ stream.XORKeyStream(out[8:], out[8:])
+
+ return out, nil
+}
+
+func (s *srtpCipherAesCmHmacSha1) generateSrtpAuthTag(buf []byte, roc uint32) ([]byte, error) {
+ // https://tools.ietf.org/html/rfc3711#section-4.2
+ // In the case of SRTP, M SHALL consist of the Authenticated
+ // Portion of the packet (as specified in Figure 1) concatenated with
+ // the ROC, M = Authenticated Portion || ROC;
+ //
+ // The pre-defined authentication transform for SRTP is HMAC-SHA1
+ // [RFC2104]. With HMAC-SHA1, the SRTP_PREFIX_LENGTH (Figure 3) SHALL
+ // be 0. For SRTP (respectively SRTCP), the HMAC SHALL be applied to
+ // the session authentication key and M as specified above, i.e.,
+ // HMAC(k_a, M). The HMAC output SHALL then be truncated to the n_tag
+ // left-most bits.
+ // - Authenticated portion of the packet is everything BEFORE MKI
+ // - k_a is the session message authentication key
+ // - n_tag is the bit-length of the output authentication tag
+ s.srtpSessionAuth.Reset()
+
+ if _, err := s.srtpSessionAuth.Write(buf); err != nil {
+ return nil, err
+ }
+
+ // For SRTP only, we need to hash the rollover counter as well.
+ rocRaw := [4]byte{}
+ binary.BigEndian.PutUint32(rocRaw[:], roc)
+
+ _, err := s.srtpSessionAuth.Write(rocRaw[:])
+ if err != nil {
+ return nil, err
+ }
+
+ // Truncate the hash to the first 10 bytes.
+ return s.srtpSessionAuth.Sum(nil)[0:s.authTagLen()], nil
+}
+
+func (s *srtpCipherAesCmHmacSha1) generateSrtcpAuthTag(buf []byte) ([]byte, error) {
+ // https://tools.ietf.org/html/rfc3711#section-4.2
+ //
+ // The pre-defined authentication transform for SRTP is HMAC-SHA1
+ // [RFC2104]. With HMAC-SHA1, the SRTP_PREFIX_LENGTH (Figure 3) SHALL
+ // be 0. For SRTP (respectively SRTCP), the HMAC SHALL be applied to
+ // the session authentication key and M as specified above, i.e.,
+ // HMAC(k_a, M). The HMAC output SHALL then be truncated to the n_tag
+ // left-most bits.
+ // - Authenticated portion of the packet is everything BEFORE MKI
+ // - k_a is the session message authentication key
+ // - n_tag is the bit-length of the output authentication tag
+ s.srtcpSessionAuth.Reset()
+
+ if _, err := s.srtcpSessionAuth.Write(buf); err != nil {
+ return nil, err
+ }
+
+ return s.srtcpSessionAuth.Sum(nil)[0:s.authTagLen()], nil
+}
+
+func (s *srtpCipherAesCmHmacSha1) getRTCPIndex(in []byte) uint32 {
+ tailOffset := len(in) - (s.authTagLen() + srtcpIndexSize)
+ srtcpIndexBuffer := in[tailOffset : tailOffset+srtcpIndexSize]
+ return binary.BigEndian.Uint32(srtcpIndexBuffer) &^ (1 << 31)
+}
diff --git a/vendor/github.com/pion/srtp/v2/stream.go b/vendor/github.com/pion/srtp/v2/stream.go
new file mode 100644
index 0000000..7b7a0cf
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/stream.go
@@ -0,0 +1,8 @@
+package srtp
+
+type readStream interface {
+ init(child streamSession, ssrc uint32) error
+
+ Read(buf []byte) (int, error)
+ GetSSRC() uint32
+}
diff --git a/vendor/github.com/pion/srtp/v2/stream_srtcp.go b/vendor/github.com/pion/srtp/v2/stream_srtcp.go
new file mode 100644
index 0000000..e335937
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/stream_srtcp.go
@@ -0,0 +1,157 @@
+package srtp
+
+import (
+ "errors"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/pion/rtcp"
+ "github.com/pion/transport/packetio"
+)
+
+// Limit the buffer size to 100KB
+const srtcpBufferSize = 100 * 1000
+
+// ReadStreamSRTCP handles decryption for a single RTCP SSRC
+type ReadStreamSRTCP struct {
+ mu sync.Mutex
+
+ isInited bool
+ isClosed chan bool
+
+ session *SessionSRTCP
+ ssrc uint32
+
+ buffer io.ReadWriteCloser
+}
+
+func (r *ReadStreamSRTCP) write(buf []byte) (n int, err error) {
+ n, err = r.buffer.Write(buf)
+
+ if errors.Is(err, packetio.ErrFull) {
+ // Silently drop data when the buffer is full.
+ return len(buf), nil
+ }
+
+ return n, err
+}
+
+// Used by getOrCreateReadStream
+func newReadStreamSRTCP() readStream {
+ return &ReadStreamSRTCP{}
+}
+
+// ReadRTCP reads and decrypts full RTCP packet and its header from the nextConn
+func (r *ReadStreamSRTCP) ReadRTCP(buf []byte) (int, *rtcp.Header, error) {
+ n, err := r.Read(buf)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ header := &rtcp.Header{}
+ err = header.Unmarshal(buf[:n])
+ if err != nil {
+ return 0, nil, err
+ }
+
+ return n, header, nil
+}
+
+// Read reads and decrypts full RTCP packet from the nextConn
+func (r *ReadStreamSRTCP) Read(buf []byte) (int, error) {
+ return r.buffer.Read(buf)
+}
+
+// SetReadDeadline sets the deadline for the Read operation.
+// Setting to zero means no deadline.
+func (r *ReadStreamSRTCP) SetReadDeadline(t time.Time) error {
+ if b, ok := r.buffer.(interface {
+ SetReadDeadline(time.Time) error
+ }); ok {
+ return b.SetReadDeadline(t)
+ }
+ return nil
+}
+
+// Close removes the ReadStream from the session and cleans up any associated state
+func (r *ReadStreamSRTCP) Close() error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if !r.isInited {
+ return errStreamNotInited
+ }
+
+ select {
+ case <-r.isClosed:
+ return errStreamAlreadyClosed
+ default:
+ err := r.buffer.Close()
+ if err != nil {
+ return err
+ }
+
+ r.session.removeReadStream(r.ssrc)
+ return nil
+ }
+}
+
+func (r *ReadStreamSRTCP) init(child streamSession, ssrc uint32) error {
+ sessionSRTCP, ok := child.(*SessionSRTCP)
+
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ if !ok {
+ return errFailedTypeAssertion
+ } else if r.isInited {
+ return errStreamAlreadyInited
+ }
+
+ r.session = sessionSRTCP
+ r.ssrc = ssrc
+ r.isInited = true
+ r.isClosed = make(chan bool)
+
+ if r.session.bufferFactory != nil {
+ r.buffer = r.session.bufferFactory(packetio.RTCPBufferPacket, ssrc)
+ } else {
+ // Create a buffer and limit it to 100KB
+ buff := packetio.NewBuffer()
+ buff.SetLimitSize(srtcpBufferSize)
+ r.buffer = buff
+ }
+
+ return nil
+}
+
+// GetSSRC returns the SSRC we are demuxing for
+func (r *ReadStreamSRTCP) GetSSRC() uint32 {
+ return r.ssrc
+}
+
+// WriteStreamSRTCP is stream for a single Session that is used to encrypt RTCP
+type WriteStreamSRTCP struct {
+ session *SessionSRTCP
+}
+
+// WriteRTCP encrypts a RTCP header and its payload to the nextConn
+func (w *WriteStreamSRTCP) WriteRTCP(header *rtcp.Header, payload []byte) (int, error) {
+ headerRaw, err := header.Marshal()
+ if err != nil {
+ return 0, err
+ }
+
+ return w.session.write(append(headerRaw, payload...))
+}
+
+// Write encrypts and writes a full RTCP packets to the nextConn
+func (w *WriteStreamSRTCP) Write(b []byte) (int, error) {
+ return w.session.write(b)
+}
+
+// SetWriteDeadline sets the deadline for the Write operation.
+// Setting to zero means no deadline.
+func (w *WriteStreamSRTCP) SetWriteDeadline(t time.Time) error {
+ return w.session.setWriteDeadline(t)
+}
diff --git a/vendor/github.com/pion/srtp/v2/stream_srtp.go b/vendor/github.com/pion/srtp/v2/stream_srtp.go
new file mode 100644
index 0000000..c391adb
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/stream_srtp.go
@@ -0,0 +1,154 @@
+package srtp
+
+import (
+ "errors"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/pion/rtp"
+ "github.com/pion/transport/packetio"
+)
+
+// Limit the buffer size to 1MB
+const srtpBufferSize = 1000 * 1000
+
+// ReadStreamSRTP handles decryption for a single RTP SSRC
+type ReadStreamSRTP struct {
+ mu sync.Mutex
+
+ isInited bool
+ isClosed chan bool
+
+ session *SessionSRTP
+ ssrc uint32
+
+ buffer io.ReadWriteCloser
+}
+
+// Used by getOrCreateReadStream
+func newReadStreamSRTP() readStream {
+ return &ReadStreamSRTP{}
+}
+
+func (r *ReadStreamSRTP) init(child streamSession, ssrc uint32) error {
+ sessionSRTP, ok := child.(*SessionSRTP)
+
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if !ok {
+ return errFailedTypeAssertion
+ } else if r.isInited {
+ return errStreamAlreadyInited
+ }
+
+ r.session = sessionSRTP
+ r.ssrc = ssrc
+ r.isInited = true
+ r.isClosed = make(chan bool)
+
+ // Create a buffer with a 1MB limit
+ if r.session.bufferFactory != nil {
+ r.buffer = r.session.bufferFactory(packetio.RTPBufferPacket, ssrc)
+ } else {
+ buff := packetio.NewBuffer()
+ buff.SetLimitSize(srtpBufferSize)
+ r.buffer = buff
+ }
+
+ return nil
+}
+
+func (r *ReadStreamSRTP) write(buf []byte) (n int, err error) {
+ n, err = r.buffer.Write(buf)
+
+ if errors.Is(err, packetio.ErrFull) {
+ // Silently drop data when the buffer is full.
+ return len(buf), nil
+ }
+
+ return n, err
+}
+
+// Read reads and decrypts full RTP packet from the nextConn
+func (r *ReadStreamSRTP) Read(buf []byte) (int, error) {
+ return r.buffer.Read(buf)
+}
+
+// ReadRTP reads and decrypts full RTP packet and its header from the nextConn
+func (r *ReadStreamSRTP) ReadRTP(buf []byte) (int, *rtp.Header, error) {
+ n, err := r.Read(buf)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ header := &rtp.Header{}
+
+ err = header.Unmarshal(buf[:n])
+ if err != nil {
+ return 0, nil, err
+ }
+
+ return n, header, nil
+}
+
+// SetReadDeadline sets the deadline for the Read operation.
+// Setting to zero means no deadline.
+func (r *ReadStreamSRTP) SetReadDeadline(t time.Time) error {
+ if b, ok := r.buffer.(interface {
+ SetReadDeadline(time.Time) error
+ }); ok {
+ return b.SetReadDeadline(t)
+ }
+ return nil
+}
+
+// Close removes the ReadStream from the session and cleans up any associated state
+func (r *ReadStreamSRTP) Close() error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if !r.isInited {
+ return errStreamNotInited
+ }
+
+ select {
+ case <-r.isClosed:
+ return errStreamAlreadyClosed
+ default:
+ err := r.buffer.Close()
+ if err != nil {
+ return err
+ }
+
+ r.session.removeReadStream(r.ssrc)
+ return nil
+ }
+}
+
+// GetSSRC returns the SSRC we are demuxing for
+func (r *ReadStreamSRTP) GetSSRC() uint32 {
+ return r.ssrc
+}
+
+// WriteStreamSRTP is stream for a single Session that is used to encrypt RTP
+type WriteStreamSRTP struct {
+ session *SessionSRTP
+}
+
+// WriteRTP encrypts a RTP packet and writes to the connection
+func (w *WriteStreamSRTP) WriteRTP(header *rtp.Header, payload []byte) (int, error) {
+ return w.session.writeRTP(header, payload)
+}
+
+// Write encrypts and writes a full RTP packets to the nextConn
+func (w *WriteStreamSRTP) Write(b []byte) (int, error) {
+ return w.session.write(b)
+}
+
+// SetWriteDeadline sets the deadline for the Write operation.
+// Setting to zero means no deadline.
+func (w *WriteStreamSRTP) SetWriteDeadline(t time.Time) error {
+ return w.session.setWriteDeadline(t)
+}
diff --git a/vendor/github.com/pion/srtp/v2/util.go b/vendor/github.com/pion/srtp/v2/util.go
new file mode 100644
index 0000000..1ae34a6
--- /dev/null
+++ b/vendor/github.com/pion/srtp/v2/util.go
@@ -0,0 +1,33 @@
+package srtp
+
+import "bytes"
+
+// Grow the buffer size to the given number of bytes.
+func growBufferSize(buf []byte, size int) []byte {
+ if size <= cap(buf) {
+ return buf[:size]
+ }
+
+ buf2 := make([]byte, size)
+ copy(buf2, buf)
+ return buf2
+}
+
+// Check if buffers match, if not allocate a new buffer and return it
+func allocateIfMismatch(dst, src []byte) []byte {
+ if dst == nil {
+ dst = make([]byte, len(src))
+ copy(dst, src)
+ } else if !bytes.Equal(dst, src) { // bytes.Equal returns on ref equality, no optimization needed
+ extraNeeded := len(src) - len(dst)
+ if extraNeeded > 0 {
+ dst = append(dst, make([]byte, extraNeeded)...)
+ } else if extraNeeded < 0 {
+ dst = dst[:len(dst)+extraNeeded]
+ }
+
+ copy(dst, src)
+ }
+
+ return dst
+}
diff --git a/vendor/github.com/pion/stun/.codecov.yml b/vendor/github.com/pion/stun/.codecov.yml
new file mode 100644
index 0000000..7fa67fe
--- /dev/null
+++ b/vendor/github.com/pion/stun/.codecov.yml
@@ -0,0 +1,9 @@
+coverage:
+ status:
+ patch: off
+ project:
+ default:
+ # basic
+ target: 98
+ threshold: null
+ base: auto
diff --git a/vendor/github.com/pion/stun/.gitignore b/vendor/github.com/pion/stun/.gitignore
new file mode 100644
index 0000000..b9420d9
--- /dev/null
+++ b/vendor/github.com/pion/stun/.gitignore
@@ -0,0 +1,17 @@
+*-fuzz.zip
+.idea
+benchmark.*.write
+*.test
+*.out
+*.sw[poe]
+bench.go-*
+PACKAGES
+cmd/stun-cli/stun-cli
+cmd/stun-decode/stun-decode
+cmd/stun-bench/stun-bench
+cmd/stun-nat-behaviour/stun-nat-behaviour
+
+coverage.txt
+
+e2e/dump.pcap
+e2e/log-*.txt
diff --git a/vendor/github.com/pion/stun/.golangci.yml b/vendor/github.com/pion/stun/.golangci.yml
new file mode 100644
index 0000000..40ca69c
--- /dev/null
+++ b/vendor/github.com/pion/stun/.golangci.yml
@@ -0,0 +1,93 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ golint:
+ min-confidence: 0
+ gocyclo:
+ min-complexity: 15
+ maligned:
+ suggest-new: true
+ dupl:
+ threshold: 100
+ goconst:
+ min-len: 2
+ min-occurrences: 2
+ misspell:
+ locale: US
+ lll:
+ line-length: 140
+ goimports:
+ local-prefixes: github.com/pion
+ gocritic:
+ enabled-tags:
+ - performance
+ - style
+ - experimental
+ disabled-checks:
+ - commentedOutCode
+ - sloppyReassign
+
+issues:
+ exclude:
+ - "`assertHMACSize` - `blocksize` always receives `64`"
+ exclude-rules:
+ - text: "string `<nil>`"
+ linters:
+ - goconst
+
+ # Exclude some linters from running on tests files.
+ - path: _test\.go
+ linters:
+ - gocyclo
+ - errcheck
+ - dupl
+ - gosec
+ - goconst
+
+ # Ease some gocritic warnings on test files.
+ - path: _test\.go
+ text: "(unnamedResult|exitAfterDefer|unlambda)"
+ linters:
+ - gocritic
+
+ # Exclude known linters from partially hard-vendored code,
+ # which is impossible to exclude via "nolint" comments.
+ - path: internal/hmac/
+ text: "weak cryptographic primitive"
+ linters:
+ - gosec
+ - path: internal/hmac/
+ text: "Write\\` is not checked"
+ linters:
+ - errcheck
+
+ # Ease linting on benchmarking code.
+ - path: cmd/stun-bench/
+ linters:
+ - gosec
+ - errcheck
+ - unparam
+
+ - path: ^cmd/
+ linters:
+ - gocyclo
+ - path: ^cmd/
+ text: "(unnamedResult|exitAfterDefer)"
+ linters:
+ - gocritic
+
+linters:
+ enable-all: true
+ disable:
+ - funlen
+ - gochecknoglobals
+ - godox
+ - prealloc
+ - scopelint
+
+run:
+ skip-dirs:
+ - e2e
+ - fuzz
+ - testdata
+ - api
diff --git a/vendor/github.com/pion/stun/.goreleaser.yml b/vendor/github.com/pion/stun/.goreleaser.yml
new file mode 100644
index 0000000..d72bde6
--- /dev/null
+++ b/vendor/github.com/pion/stun/.goreleaser.yml
@@ -0,0 +1,39 @@
+before:
+ hooks:
+ - go mod tidy
+
+archives:
+- replacements:
+ darwin: Darwin
+ linux: Linux
+ windows: Windows
+ 386: i386
+ amd64: x86_64
+
+checksum:
+ name_template: 'checksums.txt'
+
+snapshot:
+ name_template: "{{ .Tag }}-next"
+
+changelog:
+ sort: asc
+ filters:
+ exclude:
+ - '^docs:'
+ - '^test:'
+
+builds:
+ - binary: stun-not-behavior
+ id: stun-not-behavior
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./cmd/stun-nat-behavior
diff --git a/vendor/github.com/pion/stun/.travis.yml b/vendor/github.com/pion/stun/.travis.yml
new file mode 100644
index 0000000..e464e84
--- /dev/null
+++ b/vendor/github.com/pion/stun/.travis.yml
@@ -0,0 +1,135 @@
+#
+# DO NOT EDIT THIS FILE DIRECTLY
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+# If this repository should have package specific CI config,
+# remove the repository name from .goassets/.github/workflows/assets-sync.yml.
+#
+
+dist: bionic
+language: go
+
+
+branches:
+ only:
+ - master
+
+env:
+ global:
+ - GO111MODULE=on
+ - GOLANGCI_LINT_VERSION=1.19.1
+
+cache:
+ directories:
+ - ${HOME}/.cache/go-build
+ - ${GOPATH}/pkg/mod
+ npm: true
+ yarn: true
+
+_lint_job: &lint_job
+ env: CACHE_NAME=lint
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ install: skip
+ before_script:
+ - |
+ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
+ | bash -s - -b $GOPATH/bin v${GOLANGCI_LINT_VERSION}
+ script:
+ - bash .github/assert-contributors.sh
+ - bash .github/lint-disallowed-functions-in-library.sh
+ - bash .github/lint-commit-message.sh
+ - bash .github/lint-filename.sh
+ - golangci-lint run ./...
+_test_job: &test_job
+ env: CACHE_NAME=test
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ - go mod download
+ install:
+ - go build ./...
+ script:
+ - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+ - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
+ - |
+ go test \
+ -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
+ ${TEST_EXTRA_ARGS:-} \
+ -v -race ${testpkgs}
+ - if [ -n "${TEST_HOOK}" ]; then ${TEST_HOOK}; fi
+ after_success:
+ - travis_retry bash <(curl -s https://codecov.io/bash) -c -F go
+_test_i386_job: &test_i386_job
+ env: CACHE_NAME=test386
+ services: docker
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ script:
+ - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+ - |
+ docker run \
+ -u $(id -u):$(id -g) \
+ -e "GO111MODULE=on" \
+ -e "CGO_ENABLED=0" \
+ -v ${PWD}:/go/src/github.com/pion/$(basename ${PWD}) \
+ -v ${HOME}/gopath/pkg/mod:/go/pkg/mod \
+ -v ${HOME}/.cache/go-build:/.cache/go-build \
+ -w /go/src/github.com/pion/$(basename ${PWD}) \
+ -it i386/golang:${GO_VERSION}-alpine \
+ /usr/local/go/bin/go test \
+ ${TEST_EXTRA_ARGS:-} \
+ -v ${testpkgs}
+_test_wasm_job: &test_wasm_job
+ env: CACHE_NAME=wasm
+ language: node_js
+ node_js: 12
+ before_install:
+ - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi
+ - if ${SKIP_WASM_TEST:-false}; then exit 0; fi
+ install:
+ # Manually download and install Go instead of using gimme.
+ # It looks like gimme Go causes some errors on go-test for Wasm.
+ - curl -sSfL https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz | tar -C ~ -xzf -
+ - export GOROOT=${HOME}/go
+ - export PATH=${GOROOT}/bin:${PATH}
+ - yarn install
+ - export GO_JS_WASM_EXEC=${GO_JS_WASM_EXEC:-${GOROOT}/misc/wasm/go_js_wasm_exec}
+ script:
+ - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)}
+ - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',')
+ - |
+ GOOS=js GOARCH=wasm go test \
+ -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \
+ -exec="${GO_JS_WASM_EXEC}" \
+ -v ${testpkgs}
+ after_success:
+ - travis_retry bash <(curl -s https://codecov.io/bash) -c -F wasm
+
+jobs:
+ include:
+ - <<: *lint_job
+ name: Lint 1.14
+ go: 1.14
+ - <<: *test_job
+ name: Test 1.13
+ go: 1.13
+ - <<: *test_job
+ name: Test 1.14
+ go: 1.14
+ - <<: *test_i386_job
+ name: Test i386 1.13
+ env: GO_VERSION=1.13
+ go: 1.14 # version for host environment used to go list
+ - <<: *test_i386_job
+ name: Test i386 1.14
+ env: GO_VERSION=1.14
+ go: 1.14 # version for host environment used to go list
+ - <<: *test_wasm_job
+ name: Test WASM 1.13
+ env: GO_VERSION=1.13
+ - <<: *test_wasm_job
+ name: Test WASM 1.14
+ env: GO_VERSION=1.14
+
+notifications:
+ email: false
diff --git a/vendor/github.com/pion/stun/AUTHORS b/vendor/github.com/pion/stun/AUTHORS
new file mode 100644
index 0000000..717af8f
--- /dev/null
+++ b/vendor/github.com/pion/stun/AUTHORS
@@ -0,0 +1,10 @@
+Sean DuBois <https://github.com/Sean-Der>
+Raphael Randschau <https://github.com/nicolai86>
+Aleksandr Razumov <ar@cydev.ru>
+Aliaksandr Valialkin <valyala@gmail.com>
+Michiel De Backker <https://github.com/backkem>
+Y.Horie <https://github.com/u5surf>
+songjiayang <https://github.com/songjiayang>
+The gortc project
+The IETF Trust
+The Go Authors
diff --git a/vendor/github.com/pion/stun/Dockerfile b/vendor/github.com/pion/stun/Dockerfile
new file mode 100644
index 0000000..429239a
--- /dev/null
+++ b/vendor/github.com/pion/stun/Dockerfile
@@ -0,0 +1,5 @@
+FROM golang:1.14
+
+COPY . /go/src/github.com/pion/stun
+
+RUN go test github.com/pion/stun
diff --git a/vendor/github.com/pion/stun/LICENSE.md b/vendor/github.com/pion/stun/LICENSE.md
new file mode 100644
index 0000000..5cc9cbd
--- /dev/null
+++ b/vendor/github.com/pion/stun/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright 2018 Pion LLC
+
+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.
diff --git a/vendor/github.com/pion/stun/Makefile b/vendor/github.com/pion/stun/Makefile
new file mode 100644
index 0000000..43de8d0
--- /dev/null
+++ b/vendor/github.com/pion/stun/Makefile
@@ -0,0 +1,61 @@
+VERSION := $(shell git describe --tags | sed -e 's/^v//g' | awk -F "-" '{print $$1}')
+ITERATION := $(shell git describe --tags --long | awk -F "-" '{print $$2}')
+GO_VERSION=$(shell gobuild -v)
+GO := $(or $(GOROOT),/usr/lib/go)/bin/go
+PROCS := $(shell nproc)
+cores:
+ @echo "cores: $(PROCS)"
+bench:
+ go test -bench .
+bench-record:
+ $(GO) test -bench . > "benchmarks/stun-go-$(GO_VERSION).txt"
+fuzz-prepare-msg:
+ go-fuzz-build -func FuzzMessage -o stun-msg-fuzz.zip github.com/pion/stun
+fuzz-prepare-typ:
+ go-fuzz-build -func FuzzType -o stun-typ-fuzz.zip github.com/pion/stun
+fuzz-prepare-setters:
+ go-fuzz-build -func FuzzSetters -o stun-setters-fuzz.zip github.com/pion/stun
+fuzz-msg:
+ go-fuzz -bin=./stun-msg-fuzz.zip -workdir=fuzz/stun-msg
+fuzz-typ:
+ go-fuzz -bin=./stun-typ-fuzz.zip -workdir=fuzz/stun-typ
+fuzz-setters:
+ go-fuzz -bin=./stun-setters-fuzz.zip -workdir=fuzz/stun-setters
+fuzz-test:
+ go test -tags gofuzz -run TestFuzz -v .
+fuzz-reset-setters:
+ rm -f -v -r stun-setters-fuzz.zip fuzz/stun-setters
+lint:
+ @golangci-lint run ./...
+ @echo "ok"
+escape:
+ @echo "Not escapes, except autogenerated:"
+ @go build -gcflags '-m -l' 2>&1 \
+ | grep -v "<autogenerated>" \
+ | grep escapes
+format:
+ goimports -w .
+bench-compare:
+ go test -bench . > bench.go-16
+ go-tip test -bench . > bench.go-tip
+ @benchcmp bench.go-16 bench.go-tip
+install-fuzz:
+ go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
+ go get github.com/dvyukov/go-fuzz/go-fuzz
+install:
+ go get gortc.io/api
+ go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
+docker-build:
+ docker build -t pion/stun .
+test-integration:
+ @cd e2e && bash ./test.sh
+prepush: assert test lint test-integration
+check-api:
+ @cd api && bash ./check.sh
+assert:
+ bash .github/assert-contributors.sh
+ bash .github/lint-disallowed-functions-in-library.sh
+ bash .github/lint-commit-message.sh
+test:
+ @./go.test.sh
+clean:
diff --git a/vendor/github.com/pion/stun/README.md b/vendor/github.com/pion/stun/README.md
new file mode 100644
index 0000000..6dd50d0
--- /dev/null
+++ b/vendor/github.com/pion/stun/README.md
@@ -0,0 +1,184 @@
+<h1 align="center">
+ <br>
+ Pion STUN
+ <br>
+</h1>
+<h4 align="center">A Go implementation of STUN</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-stun-gray.svg?longCache=true&colorB=brightgreen" alt="Pion stun"></a>
+ <!--<a href="https://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/badge.svg" alt="Sourcegraph Widget"></a>-->
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/stun"><img src="https://travis-ci.org/pion/stun.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/stun"><img src="https://godoc.org/github.com/pion/stun?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/stun"><img src="https://codecov.io/gh/pion/stun/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/stun"><img src="https://goreportcard.com/badge/github.com/pion/stun" alt="Go Report Card"></a>
+ <!--<a href="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>-->
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Raphael Randschau](https://github.com/nicolai86) - *STUN client*
+* [Michiel De Backker](https://github.com/backkem) - *Minor fixes*
+* [Y.Horie](https://github.com/u5surf) - *Fix lint issues*
+* [Aleksandr Razumov](https://github.com/ernado) - *The v0.3 version*
+* [songjiayang](https://github.com/songjiayang)
+* [Adam Kiss](https://github.com/masterada)
+* [Moises Marangoni](https://github.com/Moisesbr)
+* [Yutaka Takeda](https://github.com/enobufs)
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Maanas Royy](https://github.com/maanas)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Cecylia Bocovich](https://github.com/cohosh)
+* [Christian Muehlhaeuser](https://github.com/muesli)
+
+# STUN
+Package stun implements Session Traversal Utilities for NAT (STUN) [[RFC5389](https://tools.ietf.org/html/rfc5389)]
+protocol and [client](https://pkg.go.dev/github.com/pion/stun#Client) with no external dependencies and zero allocations in hot paths.
+Client [supports](https://pkg.go.dev/github.com/pion/stun#WithRTO) automatic request retransmissions.
+
+# Example
+You can get your current IP address from any STUN server by sending
+binding request. See more idiomatic example at `cmd/stun-client`.
+```go
+package main
+
+import (
+ "fmt"
+
+ "github.com/pion/stun"
+)
+
+func main() {
+ // Creating a "connection" to STUN server.
+ c, err := stun.Dial("udp", "stun.l.google.com:19302")
+ if err != nil {
+ panic(err)
+ }
+ // Building binding request with random transaction id.
+ message := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
+ // Sending request to STUN server, waiting for response message.
+ if err := c.Do(message, func(res stun.Event) {
+ if res.Error != nil {
+ panic(res.Error)
+ }
+ // Decoding XOR-MAPPED-ADDRESS attribute from message.
+ var xorAddr stun.XORMappedAddress
+ if err := xorAddr.GetFrom(res.Message); err != nil {
+ panic(err)
+ }
+ fmt.Println("your IP is", xorAddr.IP)
+ }); err != nil {
+ panic(err)
+ }
+}
+```
+
+## Supported RFCs
+- [x] [RFC 5389](https://tools.ietf.org/html/rfc5389) — Session Traversal Utilities for NAT
+- [x] [RFC 5769](https://tools.ietf.org/html/rfc5769) — Test Vectors for STUN
+- [x] [RFC 6062](https://tools.ietf.org/html/rfc6062) — TURN extensions for TCP allocations
+- [x] [RFC 7064](https://tools.ietf.org/html/rfc7064) — STUN URI
+- [x] (TLS-over-)TCP client support
+- [ ] [ALTERNATE-SERVER](https://tools.ietf.org/html/rfc5389#section-11) support [#48](https://github.com/pion/stun/issues/48)
+- [ ] [RFC 5780](https://tools.ietf.org/html/rfc5780) — NAT Behavior Discovery Using STUN [#49](https://github.com/pion/stun/issues/49)
+
+# Stability
+Package is currently stable, no backward incompatible changes are expected
+with exception of critical bugs or security fixes.
+
+Additional attributes are unlikely to be implemented in scope of stun package,
+the only exception is constants for attribute or message types.
+
+# RFC 3489 notes
+RFC 5389 obsoletes RFC 3489, so implementation was ignored by purpose, however,
+RFC 3489 can be easily implemented as separate package.
+
+# Requirements
+Go 1.12 is currently supported and tested in CI.
+
+# Testing
+Client behavior is tested and verified in many ways:
+ * End-To-End with long-term credentials
+ * **coturn**: The coturn [server](https://github.com/coturn/coturn/wiki/turnserver) (linux)
+ * Bunch of code static checkers (linters)
+ * Standard unit-tests with coverage reporting (linux {amd64, **arm**64}, windows and darwin)
+ * Explicit API backward compatibility [check](https://github.com/gortc/api), see `api` directory
+
+See [TeamCity project](https://tc.gortc.io/project.html?projectId=stun&guest=1) and `e2e` directory
+for more information. Also the Wireshark `.pcap` files are available for e2e test in
+artifacts for build.
+
+# Benchmarks
+
+Intel(R) Core(TM) i7-8700K:
+
+```
+version: 1.16.5
+goos: linux
+goarch: amd64
+pkg: github.com/pion/stun
+PASS
+benchmark iter time/iter throughput bytes alloc allocs
+--------- ---- --------- ---------- ----------- ------
+BenchmarkMappedAddress_AddTo-12 30000000 36.40 ns/op 0 B/op 0 allocs/op
+BenchmarkAlternateServer_AddTo-12 50000000 36.70 ns/op 0 B/op 0 allocs/op
+BenchmarkAgent_GC-12 500000 2552.00 ns/op 0 B/op 0 allocs/op
+BenchmarkAgent_Process-12 50000000 38.00 ns/op 0 B/op 0 allocs/op
+BenchmarkMessage_GetNotFound-12 200000000 6.90 ns/op 0 B/op 0 allocs/op
+BenchmarkMessage_Get-12 200000000 7.61 ns/op 0 B/op 0 allocs/op
+BenchmarkClient_Do-12 2000000 1072.00 ns/op 0 B/op 0 allocs/op
+BenchmarkErrorCode_AddTo-12 20000000 67.00 ns/op 0 B/op 0 allocs/op
+BenchmarkErrorCodeAttribute_AddTo-12 30000000 52.20 ns/op 0 B/op 0 allocs/op
+BenchmarkErrorCodeAttribute_GetFrom-12 100000000 12.00 ns/op 0 B/op 0 allocs/op
+BenchmarkFingerprint_AddTo-12 20000000 102.00 ns/op 430.08 MB/s 0 B/op 0 allocs/op
+BenchmarkFingerprint_Check-12 30000000 54.80 ns/op 948.38 MB/s 0 B/op 0 allocs/op
+BenchmarkBuildOverhead/Build-12 5000000 333.00 ns/op 0 B/op 0 allocs/op
+BenchmarkBuildOverhead/BuildNonPointer-12 3000000 536.00 ns/op 100 B/op 4 allocs/op
+BenchmarkBuildOverhead/Raw-12 10000000 181.00 ns/op 0 B/op 0 allocs/op
+BenchmarkMessageIntegrity_AddTo-12 1000000 1053.00 ns/op 18.98 MB/s 0 B/op 0 allocs/op
+BenchmarkMessageIntegrity_Check-12 1000000 1135.00 ns/op 28.17 MB/s 0 B/op 0 allocs/op
+BenchmarkMessage_Write-12 100000000 27.70 ns/op 1011.09 MB/s 0 B/op 0 allocs/op
+BenchmarkMessageType_Value-12 2000000000 0.49 ns/op 0 B/op 0 allocs/op
+BenchmarkMessage_WriteTo-12 100000000 12.80 ns/op 0 B/op 0 allocs/op
+BenchmarkMessage_ReadFrom-12 50000000 25.00 ns/op 801.19 MB/s 0 B/op 0 allocs/op
+BenchmarkMessage_ReadBytes-12 100000000 18.00 ns/op 1113.03 MB/s 0 B/op 0 allocs/op
+BenchmarkIsMessage-12 2000000000 1.08 ns/op 18535.57 MB/s 0 B/op 0 allocs/op
+BenchmarkMessage_NewTransactionID-12 2000000 673.00 ns/op 0 B/op 0 allocs/op
+BenchmarkMessageFull-12 5000000 316.00 ns/op 0 B/op 0 allocs/op
+BenchmarkMessageFullHardcore-12 20000000 88.90 ns/op 0 B/op 0 allocs/op
+BenchmarkMessage_WriteHeader-12 200000000 8.18 ns/op 0 B/op 0 allocs/op
+BenchmarkMessage_CloneTo-12 30000000 37.90 ns/op 1795.32 MB/s 0 B/op 0 allocs/op
+BenchmarkMessage_AddTo-12 300000000 4.77 ns/op 0 B/op 0 allocs/op
+BenchmarkDecode-12 100000000 22.00 ns/op 0 B/op 0 allocs/op
+BenchmarkUsername_AddTo-12 50000000 23.20 ns/op 0 B/op 0 allocs/op
+BenchmarkUsername_GetFrom-12 100000000 17.90 ns/op 0 B/op 0 allocs/op
+BenchmarkNonce_AddTo-12 50000000 34.40 ns/op 0 B/op 0 allocs/op
+BenchmarkNonce_AddTo_BadLength-12 200000000 8.29 ns/op 0 B/op 0 allocs/op
+BenchmarkNonce_GetFrom-12 100000000 17.50 ns/op 0 B/op 0 allocs/op
+BenchmarkUnknownAttributes/AddTo-12 30000000 48.10 ns/op 0 B/op 0 allocs/op
+BenchmarkUnknownAttributes/GetFrom-12 100000000 20.90 ns/op 0 B/op 0 allocs/op
+BenchmarkXOR-12 50000000 25.80 ns/op 39652.86 MB/s 0 B/op 0 allocs/op
+BenchmarkXORSafe-12 3000000 515.00 ns/op 1988.04 MB/s 0 B/op 0 allocs/op
+BenchmarkXORFast-12 20000000 73.40 ns/op 13959.30 MB/s 0 B/op 0 allocs/op
+BenchmarkXORMappedAddress_AddTo-12 20000000 56.70 ns/op 0 B/op 0 allocs/op
+BenchmarkXORMappedAddress_GetFrom-12 50000000 37.40 ns/op 0 B/op 0 allocs/op
+ok github.com/pion/stun 76.868s
+```
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/stun/addr.go b/vendor/github.com/pion/stun/addr.go
new file mode 100644
index 0000000..c4d9653
--- /dev/null
+++ b/vendor/github.com/pion/stun/addr.go
@@ -0,0 +1,134 @@
+package stun
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+)
+
+// MappedAddress represents MAPPED-ADDRESS attribute.
+//
+// This attribute is used only by servers for achieving backwards
+// compatibility with RFC 3489 clients.
+//
+// RFC 5389 Section 15.1
+type MappedAddress struct {
+ IP net.IP
+ Port int
+}
+
+// AlternateServer represents ALTERNATE-SERVER attribute.
+//
+// RFC 5389 Section 15.11
+type AlternateServer struct {
+ IP net.IP
+ Port int
+}
+
+// OtherAddress represents OTHER-ADDRESS attribute.
+//
+// RFC 5780 Section 7.4
+type OtherAddress struct {
+ IP net.IP
+ Port int
+}
+
+// AddTo adds ALTERNATE-SERVER attribute to message.
+func (s *AlternateServer) AddTo(m *Message) error {
+ a := (*MappedAddress)(s)
+ return a.addAs(m, AttrAlternateServer)
+}
+
+// GetFrom decodes ALTERNATE-SERVER from message.
+func (s *AlternateServer) GetFrom(m *Message) error {
+ a := (*MappedAddress)(s)
+ return a.getAs(m, AttrAlternateServer)
+}
+
+func (a MappedAddress) String() string {
+ return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
+}
+
+func (a *MappedAddress) getAs(m *Message, t AttrType) error {
+ v, err := m.Get(t)
+ if err != nil {
+ return err
+ }
+ if len(v) <= 4 {
+ return io.ErrUnexpectedEOF
+ }
+ family := bin.Uint16(v[0:2])
+ if family != familyIPv6 && family != familyIPv4 {
+ return newDecodeErr("xor-mapped address", "family",
+ fmt.Sprintf("bad value %d", family),
+ )
+ }
+ ipLen := net.IPv4len
+ if family == familyIPv6 {
+ ipLen = net.IPv6len
+ }
+ // Ensuring len(a.IP) == ipLen and reusing a.IP.
+ if len(a.IP) < ipLen {
+ a.IP = a.IP[:cap(a.IP)]
+ for len(a.IP) < ipLen {
+ a.IP = append(a.IP, 0)
+ }
+ }
+ a.IP = a.IP[:ipLen]
+ for i := range a.IP {
+ a.IP[i] = 0
+ }
+ a.Port = int(bin.Uint16(v[2:4]))
+ copy(a.IP, v[4:])
+ return nil
+}
+
+func (a *MappedAddress) addAs(m *Message, t AttrType) error {
+ var (
+ family = familyIPv4
+ ip = a.IP
+ )
+ if len(a.IP) == net.IPv6len {
+ if isIPv4(ip) {
+ ip = ip[12:16] // like in ip.To4()
+ } else {
+ family = familyIPv6
+ }
+ } else if len(ip) != net.IPv4len {
+ return ErrBadIPLength
+ }
+ value := make([]byte, 128)
+ value[0] = 0 // first 8 bits are zeroes
+ bin.PutUint16(value[0:2], family)
+ bin.PutUint16(value[2:4], uint16(a.Port))
+ copy(value[4:], ip)
+ m.Add(t, value[:4+len(ip)])
+ return nil
+}
+
+// AddTo adds MAPPED-ADDRESS to message.
+func (a *MappedAddress) AddTo(m *Message) error {
+ return a.addAs(m, AttrMappedAddress)
+}
+
+// GetFrom decodes MAPPED-ADDRESS from message.
+func (a *MappedAddress) GetFrom(m *Message) error {
+ return a.getAs(m, AttrMappedAddress)
+}
+
+// AddTo adds OTHER-ADDRESS attribute to message.
+func (o *OtherAddress) AddTo(m *Message) error {
+ a := (*MappedAddress)(o)
+ return a.addAs(m, AttrOtherAddress)
+}
+
+// GetFrom decodes OTHER-ADDRESS from message.
+func (o *OtherAddress) GetFrom(m *Message) error {
+ a := (*MappedAddress)(o)
+ return a.getAs(m, AttrOtherAddress)
+}
+
+func (o OtherAddress) String() string {
+ return net.JoinHostPort(o.IP.String(), strconv.Itoa(o.Port))
+}
diff --git a/vendor/github.com/pion/stun/agent.go b/vendor/github.com/pion/stun/agent.go
new file mode 100644
index 0000000..6a8a473
--- /dev/null
+++ b/vendor/github.com/pion/stun/agent.go
@@ -0,0 +1,228 @@
+package stun
+
+import (
+ "errors"
+ "sync"
+ "time"
+)
+
+// NoopHandler just discards any event.
+var NoopHandler Handler = func(e Event) {}
+
+// NewAgent initializes and returns new Agent with provided handler.
+// If h is nil, the NoopHandler will be used.
+func NewAgent(h Handler) *Agent {
+ if h == nil {
+ h = NoopHandler
+ }
+ a := &Agent{
+ transactions: make(map[transactionID]agentTransaction),
+ handler: h,
+ }
+ return a
+}
+
+// Agent is low-level abstraction over transaction list that
+// handles concurrency (all calls are goroutine-safe) and
+// time outs (via Collect call).
+type Agent struct {
+ // transactions is map of transactions that are currently
+ // in progress. Event handling is done in such way when
+ // transaction is unregistered before agentTransaction access,
+ // minimizing mux lock and protecting agentTransaction from
+ // data races via unexpected concurrent access.
+ transactions map[transactionID]agentTransaction
+ closed bool // all calls are invalid if true
+ mux sync.Mutex // protects transactions and closed
+ handler Handler // handles transactions
+}
+
+// Handler handles state changes of transaction.
+//
+// Handler is called on transaction state change.
+// Usage of e is valid only during call, user must
+// copy needed fields explicitly.
+type Handler func(e Event)
+
+// Event is passed to Handler describing the transaction event.
+// Do not reuse outside Handler.
+type Event struct {
+ TransactionID [TransactionIDSize]byte
+ Message *Message
+ Error error
+}
+
+// agentTransaction represents transaction in progress.
+// Concurrent access is invalid.
+type agentTransaction struct {
+ id transactionID
+ deadline time.Time
+}
+
+var (
+ // ErrTransactionStopped indicates that transaction was manually stopped.
+ ErrTransactionStopped = errors.New("transaction is stopped")
+ // ErrTransactionNotExists indicates that agent failed to find transaction.
+ ErrTransactionNotExists = errors.New("transaction not exists")
+ // ErrTransactionExists indicates that transaction with same id is already
+ // registered.
+ ErrTransactionExists = errors.New("transaction exists with same id")
+)
+
+// StopWithError removes transaction from list and calls handler with
+// provided error. Can return ErrTransactionNotExists and ErrAgentClosed.
+func (a *Agent) StopWithError(id [TransactionIDSize]byte, err error) error {
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ t, exists := a.transactions[id]
+ delete(a.transactions, id)
+ h := a.handler
+ a.mux.Unlock()
+ if !exists {
+ return ErrTransactionNotExists
+ }
+ h(Event{
+ TransactionID: t.id,
+ Error: err,
+ })
+ return nil
+}
+
+// Stop stops transaction by id with ErrTransactionStopped, blocking
+// until handler returns.
+func (a *Agent) Stop(id [TransactionIDSize]byte) error {
+ return a.StopWithError(id, ErrTransactionStopped)
+}
+
+// ErrAgentClosed indicates that agent is in closed state and is unable
+// to handle transactions.
+var ErrAgentClosed = errors.New("agent is closed")
+
+// Start registers transaction with provided id and deadline.
+// Could return ErrAgentClosed, ErrTransactionExists.
+//
+// Agent handler is guaranteed to be eventually called.
+func (a *Agent) Start(id [TransactionIDSize]byte, deadline time.Time) error {
+ a.mux.Lock()
+ defer a.mux.Unlock()
+ if a.closed {
+ return ErrAgentClosed
+ }
+ _, exists := a.transactions[id]
+ if exists {
+ return ErrTransactionExists
+ }
+ a.transactions[id] = agentTransaction{
+ id: id,
+ deadline: deadline,
+ }
+ return nil
+}
+
+// agentCollectCap is initial capacity for Agent.Collect slices,
+// sufficient to make function zero-alloc in most cases.
+const agentCollectCap = 100
+
+// ErrTransactionTimeOut indicates that transaction has reached deadline.
+var ErrTransactionTimeOut = errors.New("transaction is timed out")
+
+// Collect terminates all transactions that have deadline before provided
+// time, blocking until all handlers will process ErrTransactionTimeOut.
+// Will return ErrAgentClosed if agent is already closed.
+//
+// It is safe to call Collect concurrently but makes no sense.
+func (a *Agent) Collect(gcTime time.Time) error {
+ toRemove := make([]transactionID, 0, agentCollectCap)
+ a.mux.Lock()
+ if a.closed {
+ // Doing nothing if agent is closed.
+ // All transactions should be already closed
+ // during Close() call.
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ // Adding all transactions with deadline before gcTime
+ // to toCall and toRemove slices.
+ // No allocs if there are less than agentCollectCap
+ // timed out transactions.
+ for id, t := range a.transactions {
+ if t.deadline.Before(gcTime) {
+ toRemove = append(toRemove, id)
+ }
+ }
+ // Un-registering timed out transactions.
+ for _, id := range toRemove {
+ delete(a.transactions, id)
+ }
+ // Calling handler does not require locked mutex,
+ // reducing lock time.
+ h := a.handler
+ a.mux.Unlock()
+ // Sending ErrTransactionTimeOut to handler for all transactions,
+ // blocking until last one.
+ event := Event{
+ Error: ErrTransactionTimeOut,
+ }
+ for _, id := range toRemove {
+ event.TransactionID = id
+ h(event)
+ }
+ return nil
+}
+
+// Process incoming message, synchronously passing it to handler.
+func (a *Agent) Process(m *Message) error {
+ e := Event{
+ TransactionID: m.TransactionID,
+ Message: m,
+ }
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ h := a.handler
+ delete(a.transactions, m.TransactionID)
+ a.mux.Unlock()
+ h(e)
+ return nil
+}
+
+// SetHandler sets agent handler to h.
+func (a *Agent) SetHandler(h Handler) error {
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ a.handler = h
+ a.mux.Unlock()
+ return nil
+}
+
+// Close terminates all transactions with ErrAgentClosed and renders Agent to
+// closed state.
+func (a *Agent) Close() error {
+ e := Event{
+ Error: ErrAgentClosed,
+ }
+ a.mux.Lock()
+ if a.closed {
+ a.mux.Unlock()
+ return ErrAgentClosed
+ }
+ for _, t := range a.transactions {
+ e.TransactionID = t.id
+ a.handler(e)
+ }
+ a.transactions = nil
+ a.closed = true
+ a.handler = nil
+ a.mux.Unlock()
+ return nil
+}
+
+type transactionID [TransactionIDSize]byte
diff --git a/vendor/github.com/pion/stun/appveyor.yml b/vendor/github.com/pion/stun/appveyor.yml
new file mode 100644
index 0000000..664099d
--- /dev/null
+++ b/vendor/github.com/pion/stun/appveyor.yml
@@ -0,0 +1,22 @@
+version: "{build}"
+
+platform: x64
+
+branches:
+ only:
+ - master
+
+skip_tags: true
+
+clone_folder: c:\gopath\src\github.com\pion\stun
+
+environment:
+ GOPATH: c:\gopath
+ GOVERSION: 1.12
+
+install:
+ - go version
+ - go get -v -t .
+
+build_script:
+ - go test -v .
diff --git a/vendor/github.com/pion/stun/attributes.go b/vendor/github.com/pion/stun/attributes.go
new file mode 100644
index 0000000..7238234
--- /dev/null
+++ b/vendor/github.com/pion/stun/attributes.go
@@ -0,0 +1,226 @@
+package stun
+
+import (
+ "errors"
+ "fmt"
+)
+
+// Attributes is list of message attributes.
+type Attributes []RawAttribute
+
+// Get returns first attribute from list by the type.
+// If attribute is present the RawAttribute is returned and the
+// boolean is true. Otherwise the returned RawAttribute will be
+// empty and boolean will be false.
+func (a Attributes) Get(t AttrType) (RawAttribute, bool) {
+ for _, candidate := range a {
+ if candidate.Type == t {
+ return candidate, true
+ }
+ }
+ return RawAttribute{}, false
+}
+
+// AttrType is attribute type.
+type AttrType uint16
+
+// Required returns true if type is from comprehension-required range (0x0000-0x7FFF).
+func (t AttrType) Required() bool {
+ return t <= 0x7FFF
+}
+
+// Optional returns true if type is from comprehension-optional range (0x8000-0xFFFF).
+func (t AttrType) Optional() bool {
+ return t >= 0x8000
+}
+
+// Attributes from comprehension-required range (0x0000-0x7FFF).
+const (
+ AttrMappedAddress AttrType = 0x0001 // MAPPED-ADDRESS
+ AttrUsername AttrType = 0x0006 // USERNAME
+ AttrMessageIntegrity AttrType = 0x0008 // MESSAGE-INTEGRITY
+ AttrErrorCode AttrType = 0x0009 // ERROR-CODE
+ AttrUnknownAttributes AttrType = 0x000A // UNKNOWN-ATTRIBUTES
+ AttrRealm AttrType = 0x0014 // REALM
+ AttrNonce AttrType = 0x0015 // NONCE
+ AttrXORMappedAddress AttrType = 0x0020 // XOR-MAPPED-ADDRESS
+)
+
+// Attributes from comprehension-optional range (0x8000-0xFFFF).
+const (
+ AttrSoftware AttrType = 0x8022 // SOFTWARE
+ AttrAlternateServer AttrType = 0x8023 // ALTERNATE-SERVER
+ AttrFingerprint AttrType = 0x8028 // FINGERPRINT
+)
+
+// Attributes from RFC 5245 ICE.
+const (
+ AttrPriority AttrType = 0x0024 // PRIORITY
+ AttrUseCandidate AttrType = 0x0025 // USE-CANDIDATE
+ AttrICEControlled AttrType = 0x8029 // ICE-CONTROLLED
+ AttrICEControlling AttrType = 0x802A // ICE-CONTROLLING
+)
+
+// Attributes from RFC 5766 TURN.
+const (
+ AttrChannelNumber AttrType = 0x000C // CHANNEL-NUMBER
+ AttrLifetime AttrType = 0x000D // LIFETIME
+ AttrXORPeerAddress AttrType = 0x0012 // XOR-PEER-ADDRESS
+ AttrData AttrType = 0x0013 // DATA
+ AttrXORRelayedAddress AttrType = 0x0016 // XOR-RELAYED-ADDRESS
+ AttrEvenPort AttrType = 0x0018 // EVEN-PORT
+ AttrRequestedTransport AttrType = 0x0019 // REQUESTED-TRANSPORT
+ AttrDontFragment AttrType = 0x001A // DONT-FRAGMENT
+ AttrReservationToken AttrType = 0x0022 // RESERVATION-TOKEN
+)
+
+// Attributes from RFC 5780 NAT Behavior Discovery
+const (
+ AttrOtherAddress AttrType = 0x802C // OTHER-ADDRESS
+ AttrChangeRequest AttrType = 0x0003 // CHANGE-REQUEST
+)
+
+// Attributes from RFC 6062 TURN Extensions for TCP Allocations.
+const (
+ AttrConnectionID AttrType = 0x002a // CONNECTION-ID
+)
+
+// Attributes from RFC 6156 TURN IPv6.
+const (
+ AttrRequestedAddressFamily AttrType = 0x0017 // REQUESTED-ADDRESS-FAMILY
+)
+
+// Attributes from An Origin Attribute for the STUN Protocol.
+const (
+ AttrOrigin AttrType = 0x802F
+)
+
+// Value returns uint16 representation of attribute type.
+func (t AttrType) Value() uint16 {
+ return uint16(t)
+}
+
+var attrNames = map[AttrType]string{
+ AttrMappedAddress: "MAPPED-ADDRESS",
+ AttrUsername: "USERNAME",
+ AttrErrorCode: "ERROR-CODE",
+ AttrMessageIntegrity: "MESSAGE-INTEGRITY",
+ AttrUnknownAttributes: "UNKNOWN-ATTRIBUTES",
+ AttrRealm: "REALM",
+ AttrNonce: "NONCE",
+ AttrXORMappedAddress: "XOR-MAPPED-ADDRESS",
+ AttrSoftware: "SOFTWARE",
+ AttrAlternateServer: "ALTERNATE-SERVER",
+ AttrOtherAddress: "OTHER-ADDRESS",
+ AttrChangeRequest: "CHANGE-REQUEST",
+ AttrFingerprint: "FINGERPRINT",
+ AttrPriority: "PRIORITY",
+ AttrUseCandidate: "USE-CANDIDATE",
+ AttrICEControlled: "ICE-CONTROLLED",
+ AttrICEControlling: "ICE-CONTROLLING",
+ AttrChannelNumber: "CHANNEL-NUMBER",
+ AttrLifetime: "LIFETIME",
+ AttrXORPeerAddress: "XOR-PEER-ADDRESS",
+ AttrData: "DATA",
+ AttrXORRelayedAddress: "XOR-RELAYED-ADDRESS",
+ AttrEvenPort: "EVEN-PORT",
+ AttrRequestedTransport: "REQUESTED-TRANSPORT",
+ AttrDontFragment: "DONT-FRAGMENT",
+ AttrReservationToken: "RESERVATION-TOKEN",
+ AttrConnectionID: "CONNECTION-ID",
+ AttrRequestedAddressFamily: "REQUESTED-ADDRESS-FAMILY",
+ AttrOrigin: "ORIGIN",
+}
+
+func (t AttrType) String() string {
+ s, ok := attrNames[t]
+ if !ok {
+ // Just return hex representation of unknown attribute type.
+ return fmt.Sprintf("0x%x", uint16(t))
+ }
+ return s
+}
+
+// RawAttribute is a Type-Length-Value (TLV) object that
+// can be added to a STUN message. Attributes are divided into two
+// types: comprehension-required and comprehension-optional. STUN
+// agents can safely ignore comprehension-optional attributes they
+// don't understand, but cannot successfully process a message if it
+// contains comprehension-required attributes that are not
+// understood.
+type RawAttribute struct {
+ Type AttrType
+ Length uint16 // ignored while encoding
+ Value []byte
+}
+
+// AddTo implements Setter, adding attribute as a.Type with a.Value and ignoring
+// the Length field.
+func (a RawAttribute) AddTo(m *Message) error {
+ m.Add(a.Type, a.Value)
+ return nil
+}
+
+// Equal returns true if a == b.
+func (a RawAttribute) Equal(b RawAttribute) bool {
+ if a.Type != b.Type {
+ return false
+ }
+ if a.Length != b.Length {
+ return false
+ }
+ if len(b.Value) != len(a.Value) {
+ return false
+ }
+ for i, v := range a.Value {
+ if b.Value[i] != v {
+ return false
+ }
+ }
+ return true
+}
+
+func (a RawAttribute) String() string {
+ return fmt.Sprintf("%s: 0x%x", a.Type, a.Value)
+}
+
+// ErrAttributeNotFound means that attribute with provided attribute
+// type does not exist in message.
+var ErrAttributeNotFound = errors.New("attribute not found")
+
+// Get returns byte slice that represents attribute value,
+// if there is no attribute with such type,
+// ErrAttributeNotFound is returned.
+func (m *Message) Get(t AttrType) ([]byte, error) {
+ v, ok := m.Attributes.Get(t)
+ if !ok {
+ return nil, ErrAttributeNotFound
+ }
+ return v.Value, nil
+}
+
+// STUN aligns attributes on 32-bit boundaries, attributes whose content
+// is not a multiple of 4 bytes are padded with 1, 2, or 3 bytes of
+// padding so that its value contains a multiple of 4 bytes. The
+// padding bits are ignored, and may be any value.
+//
+// https://tools.ietf.org/html/rfc5389#section-15
+const padding = 4
+
+func nearestPaddedValueLength(l int) int {
+ n := padding * (l / padding)
+ if n < l {
+ n += padding
+ }
+ return n
+}
+
+// This method converts uint16 vlue to AttrType. If it finds an old attribute
+// type value, it also translates it to the new value to enable backward
+// compatibility. (See: https://github.com/pion/stun/issues/21)
+func compatAttrType(val uint16) AttrType {
+ if val == 0x8020 {
+ return AttrXORMappedAddress // new: 0x0020
+ }
+ return AttrType(val)
+}
diff --git a/vendor/github.com/pion/stun/attributes_debug.go b/vendor/github.com/pion/stun/attributes_debug.go
new file mode 100644
index 0000000..7bf09af
--- /dev/null
+++ b/vendor/github.com/pion/stun/attributes_debug.go
@@ -0,0 +1,33 @@
+// +build debug
+
+package stun
+
+import "fmt"
+
+// AttrOverflowErr occurs when len(v) > Max.
+type AttrOverflowErr struct {
+ Type AttrType
+ Max int
+ Got int
+}
+
+func (e AttrOverflowErr) Error() string {
+ return fmt.Sprintf("incorrect length of %s attribute: %d exceeds maximum %d",
+ e.Type, e.Got, e.Max,
+ )
+}
+
+// AttrLengthErr means that length for attribute is invalid.
+type AttrLengthErr struct {
+ Attr AttrType
+ Got int
+ Expected int
+}
+
+func (e AttrLengthErr) Error() string {
+ return fmt.Sprintf("incorrect length of %s attribute: got %d, expected %d",
+ e.Attr,
+ e.Got,
+ e.Expected,
+ )
+}
diff --git a/vendor/github.com/pion/stun/checks.go b/vendor/github.com/pion/stun/checks.go
new file mode 100644
index 0000000..a760997
--- /dev/null
+++ b/vendor/github.com/pion/stun/checks.go
@@ -0,0 +1,45 @@
+// +build !debug
+
+package stun
+
+import "github.com/pion/stun/internal/hmac"
+
+// CheckSize returns ErrAttrSizeInvalid if got is not equal to expected.
+func CheckSize(_ AttrType, got, expected int) error {
+ if got == expected {
+ return nil
+ }
+ return ErrAttributeSizeInvalid
+}
+
+func checkHMAC(got, expected []byte) error {
+ if hmac.Equal(got, expected) {
+ return nil
+ }
+ return ErrIntegrityMismatch
+}
+
+func checkFingerprint(got, expected uint32) error {
+ if got == expected {
+ return nil
+ }
+ return ErrFingerprintMismatch
+}
+
+// IsAttrSizeInvalid returns true if error means that attribute size is invalid.
+func IsAttrSizeInvalid(err error) bool {
+ return err == ErrAttributeSizeInvalid
+}
+
+// CheckOverflow returns ErrAttributeSizeOverflow if got is bigger that max.
+func CheckOverflow(_ AttrType, got, max int) error {
+ if got <= max {
+ return nil
+ }
+ return ErrAttributeSizeOverflow
+}
+
+// IsAttrSizeOverflow returns true if error means that attribute size is too big.
+func IsAttrSizeOverflow(err error) bool {
+ return err == ErrAttributeSizeOverflow
+}
diff --git a/vendor/github.com/pion/stun/checks_debug.go b/vendor/github.com/pion/stun/checks_debug.go
new file mode 100644
index 0000000..955f555
--- /dev/null
+++ b/vendor/github.com/pion/stun/checks_debug.go
@@ -0,0 +1,61 @@
+// +build debug
+
+package stun
+
+import "github.com/pion/stun/internal/hmac"
+
+// CheckSize returns *AttrLengthError if got is not equal to expected.
+func CheckSize(a AttrType, got, expected int) error {
+ if got == expected {
+ return nil
+ }
+ return &AttrLengthErr{
+ Got: got,
+ Expected: expected,
+ Attr: a,
+ }
+}
+
+func checkHMAC(got, expected []byte) error {
+ if hmac.Equal(got, expected) {
+ return nil
+ }
+ return &IntegrityErr{
+ Expected: expected,
+ Actual: got,
+ }
+}
+
+func checkFingerprint(got, expected uint32) error {
+ if got == expected {
+ return nil
+ }
+ return &CRCMismatch{
+ Actual: got,
+ Expected: expected,
+ }
+}
+
+// IsAttrSizeInvalid returns true if error means that attribute size is invalid.
+func IsAttrSizeInvalid(err error) bool {
+ _, ok := err.(*AttrLengthErr)
+ return ok
+}
+
+// CheckOverflow returns *AttrOverflowErr if got is bigger that max.
+func CheckOverflow(t AttrType, got, max int) error {
+ if got <= max {
+ return nil
+ }
+ return &AttrOverflowErr{
+ Type: t,
+ Got: got,
+ Max: max,
+ }
+}
+
+// IsAttrSizeOverflow returns true if error means that attribute size is too big.
+func IsAttrSizeOverflow(err error) bool {
+ _, ok := err.(*AttrOverflowErr)
+ return ok
+}
diff --git a/vendor/github.com/pion/stun/client.go b/vendor/github.com/pion/stun/client.go
new file mode 100644
index 0000000..62a0b6e
--- /dev/null
+++ b/vendor/github.com/pion/stun/client.go
@@ -0,0 +1,631 @@
+package stun
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "runtime"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// Dial connects to the address on the named network and then
+// initializes Client on that connection, returning error if any.
+func Dial(network, address string) (*Client, error) {
+ conn, err := net.Dial(network, address)
+ if err != nil {
+ return nil, err
+ }
+ return NewClient(conn)
+}
+
+// ErrNoConnection means that ClientOptions.Connection is nil.
+var ErrNoConnection = errors.New("no connection provided")
+
+// ClientOption sets some client option.
+type ClientOption func(c *Client)
+
+// WithHandler sets client handler which is called if Agent emits the Event
+// with TransactionID that is not currently registered by Client.
+// Useful for handling Data indications from TURN server.
+func WithHandler(h Handler) ClientOption {
+ return func(c *Client) {
+ c.handler = h
+ }
+}
+
+// WithRTO sets client RTO as defined in STUN RFC.
+func WithRTO(rto time.Duration) ClientOption {
+ return func(c *Client) {
+ c.rto = int64(rto)
+ }
+}
+
+// WithClock sets Clock of client, the source of current time.
+// Also clock is passed to default collector if set.
+func WithClock(clock Clock) ClientOption {
+ return func(c *Client) {
+ c.clock = clock
+ }
+}
+
+// WithTimeoutRate sets RTO timer minimum resolution.
+func WithTimeoutRate(d time.Duration) ClientOption {
+ return func(c *Client) {
+ c.rtoRate = d
+ }
+}
+
+// WithAgent sets client STUN agent.
+//
+// Defaults to agent implementation in current package,
+// see agent.go.
+func WithAgent(a ClientAgent) ClientOption {
+ return func(c *Client) {
+ c.a = a
+ }
+}
+
+// WithCollector rests client timeout collector, the implementation
+// of ticker which calls function on each tick.
+func WithCollector(coll Collector) ClientOption {
+ return func(c *Client) {
+ c.collector = coll
+ }
+}
+
+// WithNoConnClose prevents client from closing underlying connection when
+// the Close() method is called.
+var WithNoConnClose ClientOption = func(c *Client) {
+ c.closeConn = false
+}
+
+// WithNoRetransmit disables retransmissions and sets RTO to
+// defaultMaxAttempts * defaultRTO which will be effectively time out
+// if not set.
+//
+// Useful for TCP connections where transport handles RTO.
+func WithNoRetransmit(c *Client) {
+ c.maxAttempts = 0
+ if c.rto == 0 {
+ c.rto = defaultMaxAttempts * int64(defaultRTO)
+ }
+}
+
+const (
+ defaultTimeoutRate = time.Millisecond * 5
+ defaultRTO = time.Millisecond * 300
+ defaultMaxAttempts = 7
+)
+
+// NewClient initializes new Client from provided options,
+// starting internal goroutines and using default options fields
+// if necessary. Call Close method after using Client to close conn and
+// release resources.
+//
+// The conn will be closed on Close call. Use WithNoConnClose option to
+// prevent that.
+//
+// Note that user should handle the protocol multiplexing, client does not
+// provide any API for it, so if you need to read application data, wrap the
+// connection with your (de-)multiplexer and pass the wrapper as conn.
+func NewClient(conn Connection, options ...ClientOption) (*Client, error) {
+ c := &Client{
+ close: make(chan struct{}),
+ c: conn,
+ clock: systemClock,
+ rto: int64(defaultRTO),
+ rtoRate: defaultTimeoutRate,
+ t: make(map[transactionID]*clientTransaction, 100),
+ maxAttempts: defaultMaxAttempts,
+ closeConn: true,
+ }
+ for _, o := range options {
+ o(c)
+ }
+ if c.c == nil {
+ return nil, ErrNoConnection
+ }
+ if c.a == nil {
+ c.a = NewAgent(nil)
+ }
+ if err := c.a.SetHandler(c.handleAgentCallback); err != nil {
+ return nil, err
+ }
+ if c.collector == nil {
+ c.collector = &tickerCollector{
+ close: make(chan struct{}),
+ clock: c.clock,
+ }
+ }
+ if err := c.collector.Start(c.rtoRate, func(t time.Time) {
+ closedOrPanic(c.a.Collect(t))
+ }); err != nil {
+ return nil, err
+ }
+ c.wg.Add(1)
+ go c.readUntilClosed()
+ runtime.SetFinalizer(c, clientFinalizer)
+ return c, nil
+}
+
+func clientFinalizer(c *Client) {
+ if c == nil {
+ return
+ }
+ err := c.Close()
+ if err == ErrClientClosed {
+ return
+ }
+ if err == nil {
+ log.Println("client: called finalizer on non-closed client") // nolint
+ return
+ }
+ log.Println("client: called finalizer on non-closed client:", err) // nolint
+}
+
+// Connection wraps Reader, Writer and Closer interfaces.
+type Connection interface {
+ io.Reader
+ io.Writer
+ io.Closer
+}
+
+// ClientAgent is Agent implementation that is used by Client to
+// process transactions.
+type ClientAgent interface {
+ Process(*Message) error
+ Close() error
+ Start(id [TransactionIDSize]byte, deadline time.Time) error
+ Stop(id [TransactionIDSize]byte) error
+ Collect(time.Time) error
+ SetHandler(h Handler) error
+}
+
+// Client simulates "connection" to STUN server.
+type Client struct {
+ rto int64 // time.Duration
+ a ClientAgent
+ c Connection
+ close chan struct{}
+ rtoRate time.Duration
+ maxAttempts int32
+ closed bool
+ closeConn bool // should call c.Close() while closing
+ wg sync.WaitGroup
+ clock Clock
+ handler Handler
+ collector Collector
+ t map[transactionID]*clientTransaction
+
+ // mux guards closed and t
+ mux sync.RWMutex
+}
+
+// clientTransaction represents transaction in progress.
+// If transaction is succeed or failed, f will be called
+// provided by event.
+// Concurrent access is invalid.
+type clientTransaction struct {
+ id transactionID
+ attempt int32
+ calls int32
+ h Handler
+ start time.Time
+ rto time.Duration
+ raw []byte
+}
+
+func (t *clientTransaction) handle(e Event) {
+ if atomic.AddInt32(&t.calls, 1) == 1 {
+ t.h(e)
+ }
+}
+
+var clientTransactionPool = &sync.Pool{
+ New: func() interface{} {
+ return &clientTransaction{
+ raw: make([]byte, 1500),
+ }
+ },
+}
+
+func acquireClientTransaction() *clientTransaction {
+ return clientTransactionPool.Get().(*clientTransaction)
+}
+
+func putClientTransaction(t *clientTransaction) {
+ t.raw = t.raw[:0]
+ t.start = time.Time{}
+ t.attempt = 0
+ t.id = transactionID{}
+ clientTransactionPool.Put(t)
+}
+
+func (t *clientTransaction) nextTimeout(now time.Time) time.Time {
+ return now.Add(time.Duration(t.attempt+1) * t.rto)
+}
+
+// start registers transaction.
+//
+// Could return ErrClientClosed, ErrTransactionExists.
+func (c *Client) start(t *clientTransaction) error {
+ c.mux.Lock()
+ defer c.mux.Unlock()
+ if c.closed {
+ return ErrClientClosed
+ }
+ _, exists := c.t[t.id]
+ if exists {
+ return ErrTransactionExists
+ }
+ c.t[t.id] = t
+ return nil
+}
+
+// Clock abstracts the source of current time.
+type Clock interface {
+ Now() time.Time
+}
+
+type systemClockService struct{}
+
+func (systemClockService) Now() time.Time { return time.Now() }
+
+var systemClock = systemClockService{}
+
+// SetRTO sets current RTO value.
+func (c *Client) SetRTO(rto time.Duration) {
+ atomic.StoreInt64(&c.rto, int64(rto))
+}
+
+// StopErr occurs when Client fails to stop transaction while
+// processing error.
+type StopErr struct {
+ Err error // value returned by Stop()
+ Cause error // error that caused Stop() call
+}
+
+func (e StopErr) Error() string {
+ return fmt.Sprintf("error while stopping due to %s: %s", sprintErr(e.Cause), sprintErr(e.Err))
+}
+
+// CloseErr indicates client close failure.
+type CloseErr struct {
+ AgentErr error
+ ConnectionErr error
+}
+
+func sprintErr(err error) string {
+ if err == nil {
+ return "<nil>"
+ }
+ return err.Error()
+}
+
+func (c CloseErr) Error() string {
+ return fmt.Sprintf("failed to close: %s (connection), %s (agent)", sprintErr(c.ConnectionErr), sprintErr(c.AgentErr))
+}
+
+func (c *Client) readUntilClosed() {
+ defer c.wg.Done()
+ m := new(Message)
+ m.Raw = make([]byte, 1024)
+ for {
+ select {
+ case <-c.close:
+ return
+ default:
+ }
+ _, err := m.ReadFrom(c.c)
+ if err == nil {
+ if pErr := c.a.Process(m); pErr == ErrAgentClosed {
+ return
+ }
+ }
+ }
+}
+
+func closedOrPanic(err error) {
+ if err == nil || err == ErrAgentClosed {
+ return
+ }
+ panic(err) // nolint
+}
+
+type tickerCollector struct {
+ close chan struct{}
+ wg sync.WaitGroup
+ clock Clock
+}
+
+// Collector calls function f with constant rate.
+//
+// The simple Collector is ticker which calls function on each tick.
+type Collector interface {
+ Start(rate time.Duration, f func(now time.Time)) error
+ Close() error
+}
+
+func (a *tickerCollector) Start(rate time.Duration, f func(now time.Time)) error {
+ t := time.NewTicker(rate)
+ a.wg.Add(1)
+ go func() {
+ defer a.wg.Done()
+ for {
+ select {
+ case <-a.close:
+ t.Stop()
+ return
+ case <-t.C:
+ f(a.clock.Now())
+ }
+ }
+ }()
+ return nil
+}
+
+func (a *tickerCollector) Close() error {
+ close(a.close)
+ a.wg.Wait()
+ return nil
+}
+
+// ErrClientClosed indicates that client is closed.
+var ErrClientClosed = errors.New("client is closed")
+
+// Close stops internal connection and agent, returning CloseErr on error.
+func (c *Client) Close() error {
+ if err := c.checkInit(); err != nil {
+ return err
+ }
+ c.mux.Lock()
+ if c.closed {
+ c.mux.Unlock()
+ return ErrClientClosed
+ }
+ c.closed = true
+ c.mux.Unlock()
+ if closeErr := c.collector.Close(); closeErr != nil {
+ return closeErr
+ }
+ var connErr error
+ agentErr := c.a.Close()
+ if c.closeConn {
+ connErr = c.c.Close()
+ }
+ close(c.close)
+ c.wg.Wait()
+ if agentErr == nil && connErr == nil {
+ return nil
+ }
+ return CloseErr{
+ AgentErr: agentErr,
+ ConnectionErr: connErr,
+ }
+}
+
+// Indicate sends indication m to server. Shorthand to Start call
+// with zero deadline and callback.
+func (c *Client) Indicate(m *Message) error {
+ return c.Start(m, nil)
+}
+
+// callbackWaitHandler blocks on wait() call until callback is called.
+type callbackWaitHandler struct {
+ handler Handler
+ callback func(event Event)
+ cond *sync.Cond
+ processed bool
+}
+
+func (s *callbackWaitHandler) HandleEvent(e Event) {
+ s.cond.L.Lock()
+ if s.callback == nil {
+ panic("s.callback is nil") // nolint
+ }
+ s.callback(e)
+ s.processed = true
+ s.cond.Broadcast()
+ s.cond.L.Unlock()
+}
+
+func (s *callbackWaitHandler) wait() {
+ s.cond.L.Lock()
+ for !s.processed {
+ s.cond.Wait()
+ }
+ s.processed = false
+ s.callback = nil
+ s.cond.L.Unlock()
+}
+
+func (s *callbackWaitHandler) setCallback(f func(event Event)) {
+ if f == nil {
+ panic("f is nil") // nolint
+ }
+ s.cond.L.Lock()
+ s.callback = f
+ if s.handler == nil {
+ s.handler = s.HandleEvent
+ }
+ s.cond.L.Unlock()
+}
+
+var callbackWaitHandlerPool = sync.Pool{
+ New: func() interface{} {
+ return &callbackWaitHandler{
+ cond: sync.NewCond(new(sync.Mutex)),
+ }
+ },
+}
+
+// ErrClientNotInitialized means that client connection or agent is nil.
+var ErrClientNotInitialized = errors.New("client not initialized")
+
+func (c *Client) checkInit() error {
+ if c == nil || c.c == nil || c.a == nil || c.close == nil {
+ return ErrClientNotInitialized
+ }
+ return nil
+}
+
+// Do is Start wrapper that waits until callback is called. If no callback
+// provided, Indicate is called instead.
+//
+// Do has cpu overhead due to blocking, see BenchmarkClient_Do.
+// Use Start method for less overhead.
+func (c *Client) Do(m *Message, f func(Event)) error {
+ if err := c.checkInit(); err != nil {
+ return err
+ }
+ if f == nil {
+ return c.Indicate(m)
+ }
+ h := callbackWaitHandlerPool.Get().(*callbackWaitHandler)
+ h.setCallback(f)
+ defer func() {
+ callbackWaitHandlerPool.Put(h)
+ }()
+ if err := c.Start(m, h.handler); err != nil {
+ return err
+ }
+ h.wait()
+ return nil
+}
+
+func (c *Client) delete(id transactionID) {
+ c.mux.Lock()
+ if c.t != nil {
+ delete(c.t, id)
+ }
+ c.mux.Unlock()
+}
+
+type buffer struct {
+ buf []byte
+}
+
+var bufferPool = &sync.Pool{
+ New: func() interface{} {
+ return &buffer{buf: make([]byte, 2048)}
+ },
+}
+
+func (c *Client) handleAgentCallback(e Event) {
+ c.mux.Lock()
+ if c.closed {
+ c.mux.Unlock()
+ return
+ }
+ t, found := c.t[e.TransactionID]
+ if found {
+ delete(c.t, t.id)
+ }
+ c.mux.Unlock()
+ if !found {
+ if c.handler != nil && e.Error != ErrTransactionStopped {
+ c.handler(e)
+ }
+ // Ignoring.
+ return
+ }
+ if atomic.LoadInt32(&c.maxAttempts) <= t.attempt || e.Error == nil {
+ // Transaction completed.
+ t.handle(e)
+ putClientTransaction(t)
+ return
+ }
+ // Doing re-transmission.
+ t.attempt++
+ b := bufferPool.Get().(*buffer)
+ b.buf = b.buf[:copy(b.buf[:cap(b.buf)], t.raw)]
+ defer bufferPool.Put(b)
+ var (
+ now = c.clock.Now()
+ timeOut = t.nextTimeout(now)
+ id = t.id
+ )
+ // Starting client transaction.
+ if startErr := c.start(t); startErr != nil {
+ c.delete(id)
+ e.Error = startErr
+ t.handle(e)
+ putClientTransaction(t)
+ return
+ }
+ // Starting agent transaction.
+ if startErr := c.a.Start(id, timeOut); startErr != nil {
+ c.delete(id)
+ e.Error = startErr
+ t.handle(e)
+ putClientTransaction(t)
+ return
+ }
+ // Writing message to connection again.
+ _, writeErr := c.c.Write(b.buf)
+ if writeErr != nil {
+ c.delete(id)
+ e.Error = writeErr
+ // Stopping agent transaction instead of waiting until it's deadline.
+ // This will call handleAgentCallback with "ErrTransactionStopped" error
+ // which will be ignored.
+ if stopErr := c.a.Stop(id); stopErr != nil {
+ // Failed to stop agent transaction. Wrapping the error in StopError.
+ e.Error = StopErr{
+ Err: stopErr,
+ Cause: writeErr,
+ }
+ }
+ t.handle(e)
+ putClientTransaction(t)
+ return
+ }
+}
+
+// Start starts transaction (if h set) and writes message to server, handler
+// is called asynchronously.
+func (c *Client) Start(m *Message, h Handler) error {
+ if err := c.checkInit(); err != nil {
+ return err
+ }
+ c.mux.RLock()
+ closed := c.closed
+ c.mux.RUnlock()
+ if closed {
+ return ErrClientClosed
+ }
+ if h != nil {
+ // Starting transaction only if h is set. Useful for indications.
+ t := acquireClientTransaction()
+ t.id = m.TransactionID
+ t.start = c.clock.Now()
+ t.h = h
+ t.rto = time.Duration(atomic.LoadInt64(&c.rto))
+ t.attempt = 0
+ t.raw = append(t.raw[:0], m.Raw...)
+ t.calls = 0
+ d := t.nextTimeout(t.start)
+ if err := c.start(t); err != nil {
+ return err
+ }
+ if err := c.a.Start(m.TransactionID, d); err != nil {
+ return err
+ }
+ }
+ _, err := m.WriteTo(c.c)
+ if err != nil && h != nil {
+ c.delete(m.TransactionID)
+ // Stopping transaction instead of waiting until deadline.
+ if stopErr := c.a.Stop(m.TransactionID); stopErr != nil {
+ return StopErr{
+ Err: stopErr,
+ Cause: err,
+ }
+ }
+ }
+ return err
+}
diff --git a/vendor/github.com/pion/stun/errorcode.go b/vendor/github.com/pion/stun/errorcode.go
new file mode 100644
index 0000000..8095048
--- /dev/null
+++ b/vendor/github.com/pion/stun/errorcode.go
@@ -0,0 +1,158 @@
+package stun
+
+import (
+ "errors"
+ "fmt"
+ "io"
+)
+
+// ErrorCodeAttribute represents ERROR-CODE attribute.
+//
+// RFC 5389 Section 15.6
+type ErrorCodeAttribute struct {
+ Code ErrorCode
+ Reason []byte
+}
+
+func (c ErrorCodeAttribute) String() string {
+ return fmt.Sprintf("%d: %s", c.Code, c.Reason)
+}
+
+// constants for ERROR-CODE encoding.
+const (
+ errorCodeReasonStart = 4
+ errorCodeClassByte = 2
+ errorCodeNumberByte = 3
+ errorCodeReasonMaxB = 763
+ errorCodeModulo = 100
+)
+
+// AddTo adds ERROR-CODE to m.
+func (c ErrorCodeAttribute) AddTo(m *Message) error {
+ value := make([]byte, 0, errorCodeReasonMaxB)
+ if err := CheckOverflow(AttrErrorCode,
+ len(c.Reason)+errorCodeReasonStart,
+ errorCodeReasonMaxB+errorCodeReasonStart,
+ ); err != nil {
+ return err
+ }
+ value = value[:errorCodeReasonStart+len(c.Reason)]
+ number := byte(c.Code % errorCodeModulo) // error code modulo 100
+ class := byte(c.Code / errorCodeModulo) // hundred digit
+ value[errorCodeClassByte] = class
+ value[errorCodeNumberByte] = number
+ copy(value[errorCodeReasonStart:], c.Reason)
+ m.Add(AttrErrorCode, value)
+ return nil
+}
+
+// GetFrom decodes ERROR-CODE from m. Reason is valid until m.Raw is valid.
+func (c *ErrorCodeAttribute) GetFrom(m *Message) error {
+ v, err := m.Get(AttrErrorCode)
+ if err != nil {
+ return err
+ }
+ if len(v) < errorCodeReasonStart {
+ return io.ErrUnexpectedEOF
+ }
+ var (
+ class = uint16(v[errorCodeClassByte])
+ number = uint16(v[errorCodeNumberByte])
+ code = int(class*errorCodeModulo + number)
+ )
+ c.Code = ErrorCode(code)
+ c.Reason = v[errorCodeReasonStart:]
+ return nil
+}
+
+// ErrorCode is code for ERROR-CODE attribute.
+type ErrorCode int
+
+// ErrNoDefaultReason means that default reason for provided error code
+// is not defined in RFC.
+var ErrNoDefaultReason = errors.New("no default reason for ErrorCode")
+
+// AddTo adds ERROR-CODE with default reason to m. If there
+// is no default reason, returns ErrNoDefaultReason.
+func (c ErrorCode) AddTo(m *Message) error {
+ reason := errorReasons[c]
+ if reason == nil {
+ return ErrNoDefaultReason
+ }
+ a := &ErrorCodeAttribute{
+ Code: c,
+ Reason: reason,
+ }
+ return a.AddTo(m)
+}
+
+// Possible error codes.
+const (
+ CodeTryAlternate ErrorCode = 300
+ CodeBadRequest ErrorCode = 400
+ CodeUnauthorized ErrorCode = 401
+ CodeUnknownAttribute ErrorCode = 420
+ CodeStaleNonce ErrorCode = 438
+ CodeRoleConflict ErrorCode = 487
+ CodeServerError ErrorCode = 500
+)
+
+// DEPRECATED constants.
+const (
+ // DEPRECATED, use CodeUnauthorized.
+ CodeUnauthorised = CodeUnauthorized
+)
+
+// Error codes from RFC 5766.
+//
+// RFC 5766 Section 15
+const (
+ CodeForbidden ErrorCode = 403 // Forbidden
+ CodeAllocMismatch ErrorCode = 437 // Allocation Mismatch
+ CodeWrongCredentials ErrorCode = 441 // Wrong Credentials
+ CodeUnsupportedTransProto ErrorCode = 442 // Unsupported Transport Protocol
+ CodeAllocQuotaReached ErrorCode = 486 // Allocation Quota Reached
+ CodeInsufficientCapacity ErrorCode = 508 // Insufficient Capacity
+)
+
+// Error codes from RFC 6062.
+//
+// RFC 6062 Section 6.3
+const (
+ CodeConnAlreadyExists ErrorCode = 446
+ CodeConnTimeoutOrFailure ErrorCode = 447
+)
+
+// Error codes from RFC 6156.
+//
+// RFC 6156 Section 10.2
+const (
+ CodeAddrFamilyNotSupported ErrorCode = 440 // Address Family not Supported
+ CodePeerAddrFamilyMismatch ErrorCode = 443 // Peer Address Family Mismatch
+)
+
+var errorReasons = map[ErrorCode][]byte{
+ CodeTryAlternate: []byte("Try Alternate"),
+ CodeBadRequest: []byte("Bad Request"),
+ CodeUnauthorized: []byte("Unauthorized"),
+ CodeUnknownAttribute: []byte("Unknown Attribute"),
+ CodeStaleNonce: []byte("Stale Nonce"),
+ CodeServerError: []byte("Server Error"),
+ CodeRoleConflict: []byte("Role Conflict"),
+
+ // RFC 5766.
+ CodeForbidden: []byte("Forbidden"),
+ CodeAllocMismatch: []byte("Allocation Mismatch"),
+ CodeWrongCredentials: []byte("Wrong Credentials"),
+ CodeUnsupportedTransProto: []byte("Unsupported Transport Protocol"),
+ CodeAllocQuotaReached: []byte("Allocation Quota Reached"),
+ CodeInsufficientCapacity: []byte("Insufficient Capacity"),
+
+ // RFC 6062.
+ CodeConnAlreadyExists: []byte("Connection Already Exists"),
+ CodeConnTimeoutOrFailure: []byte("Connection Timeout or Failure"),
+
+ // RFC 6156.
+ CodeAddrFamilyNotSupported: []byte("Address Family not Supported"),
+ CodePeerAddrFamilyMismatch: []byte("Peer Address Family Mismatch"),
+}
diff --git a/vendor/github.com/pion/stun/errors.go b/vendor/github.com/pion/stun/errors.go
new file mode 100644
index 0000000..029c9e4
--- /dev/null
+++ b/vendor/github.com/pion/stun/errors.go
@@ -0,0 +1,62 @@
+package stun
+
+import "errors"
+
+// DecodeErr records an error and place when it is occurred.
+type DecodeErr struct {
+ Place DecodeErrPlace
+ Message string
+}
+
+// IsInvalidCookie returns true if error means that magic cookie
+// value is invalid.
+func (e DecodeErr) IsInvalidCookie() bool {
+ return e.Place == DecodeErrPlace{"message", "cookie"}
+}
+
+// IsPlaceParent reports if error place parent is p.
+func (e DecodeErr) IsPlaceParent(p string) bool {
+ return e.Place.Parent == p
+}
+
+// IsPlaceChildren reports if error place children is c.
+func (e DecodeErr) IsPlaceChildren(c string) bool {
+ return e.Place.Children == c
+}
+
+// IsPlace reports if error place is p.
+func (e DecodeErr) IsPlace(p DecodeErrPlace) bool {
+ return e.Place == p
+}
+
+// DecodeErrPlace records a place where error is occurred.
+type DecodeErrPlace struct {
+ Parent string
+ Children string
+}
+
+func (p DecodeErrPlace) String() string {
+ return p.Parent + "/" + p.Children
+}
+
+func (e DecodeErr) Error() string {
+ return "BadFormat for " + e.Place.String() + ": " + e.Message
+}
+
+func newDecodeErr(parent, children, message string) *DecodeErr {
+ return &DecodeErr{
+ Place: DecodeErrPlace{Parent: parent, Children: children},
+ Message: message,
+ }
+}
+
+// TODO(ar): rewrite errors to be more precise.
+func newAttrDecodeErr(children, message string) *DecodeErr {
+ return newDecodeErr("attribute", children, message)
+}
+
+// ErrAttributeSizeInvalid means that decoded attribute size is invalid.
+var ErrAttributeSizeInvalid = errors.New("attribute size is invalid")
+
+// ErrAttributeSizeOverflow means that decoded attribute size is too big.
+var ErrAttributeSizeOverflow = errors.New("attribute size overflow")
diff --git a/vendor/github.com/pion/stun/fingerprint.go b/vendor/github.com/pion/stun/fingerprint.go
new file mode 100644
index 0000000..aef80a2
--- /dev/null
+++ b/vendor/github.com/pion/stun/fingerprint.go
@@ -0,0 +1,67 @@
+package stun
+
+import (
+ "errors"
+ "hash/crc32"
+)
+
+// FingerprintAttr represents FINGERPRINT attribute.
+//
+// RFC 5389 Section 15.5
+type FingerprintAttr struct{}
+
+// ErrFingerprintMismatch means that computed fingerprint differs from expected.
+var ErrFingerprintMismatch = errors.New("fingerprint check failed")
+
+// Fingerprint is shorthand for FingerprintAttr.
+//
+// Example:
+//
+// m := New()
+// Fingerprint.AddTo(m)
+var Fingerprint FingerprintAttr
+
+const (
+ fingerprintXORValue uint32 = 0x5354554e //nolint:staticcheck
+ fingerprintSize = 4 // 32 bit
+)
+
+// FingerprintValue returns CRC-32 of b XOR-ed by 0x5354554e.
+//
+// The value of the attribute is computed as the CRC-32 of the STUN message
+// up to (but excluding) the FINGERPRINT attribute itself, XOR'ed with
+// the 32-bit value 0x5354554e (the XOR helps in cases where an
+// application packet is also using CRC-32 in it).
+func FingerprintValue(b []byte) uint32 {
+ return crc32.ChecksumIEEE(b) ^ fingerprintXORValue // XOR
+}
+
+// AddTo adds fingerprint to message.
+func (FingerprintAttr) AddTo(m *Message) error {
+ l := m.Length
+ // length in header should include size of fingerprint attribute
+ m.Length += fingerprintSize + attributeHeaderSize // increasing length
+ m.WriteLength() // writing Length to Raw
+ b := make([]byte, fingerprintSize)
+ val := FingerprintValue(m.Raw)
+ bin.PutUint32(b, val)
+ m.Length = l
+ m.Add(AttrFingerprint, b)
+ return nil
+}
+
+// Check reads fingerprint value from m and checks it, returning error if any.
+// Can return *AttrLengthErr, ErrAttributeNotFound, and *CRCMismatch.
+func (FingerprintAttr) Check(m *Message) error {
+ b, err := m.Get(AttrFingerprint)
+ if err != nil {
+ return err
+ }
+ if err = CheckSize(AttrFingerprint, len(b), fingerprintSize); err != nil {
+ return err
+ }
+ val := bin.Uint32(b)
+ attrStart := len(m.Raw) - (fingerprintSize + attributeHeaderSize)
+ expected := FingerprintValue(m.Raw[:attrStart])
+ return checkFingerprint(val, expected)
+}
diff --git a/vendor/github.com/pion/stun/fingerprint_debug.go b/vendor/github.com/pion/stun/fingerprint_debug.go
new file mode 100644
index 0000000..6da074c
--- /dev/null
+++ b/vendor/github.com/pion/stun/fingerprint_debug.go
@@ -0,0 +1,18 @@
+// +build debug
+
+package stun
+
+import "fmt"
+
+// CRCMismatch represents CRC check error.
+type CRCMismatch struct {
+ Expected uint32
+ Actual uint32
+}
+
+func (m CRCMismatch) Error() string {
+ return fmt.Sprintf("CRC mismatch: %x (expected) != %x (actual)",
+ m.Expected,
+ m.Actual,
+ )
+}
diff --git a/vendor/github.com/pion/stun/fuzz.go b/vendor/github.com/pion/stun/fuzz.go
new file mode 100644
index 0000000..debfa8d
--- /dev/null
+++ b/vendor/github.com/pion/stun/fuzz.go
@@ -0,0 +1,140 @@
+// +build gofuzz
+
+package stun
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+var (
+ m = New()
+)
+
+// FuzzMessage is go-fuzz endpoint for message.
+func FuzzMessage(data []byte) int {
+ m.Reset()
+ // fuzzer dont know about cookies
+ binary.BigEndian.PutUint32(data[4:8], magicCookie)
+ // trying to read data as message
+ if _, err := m.Write(data); err != nil {
+ return 0
+ }
+ m2 := New()
+ if _, err := m2.Write(m.Raw); err != nil {
+ panic(err) // nolint
+ }
+ if m2.TransactionID != m.TransactionID {
+ panic("transaction ID mismatch") // nolint
+ }
+ if m2.Type != m.Type {
+ panic("type missmatch") // nolint
+ }
+ if len(m2.Attributes) != len(m.Attributes) {
+ panic("attributes length missmatch") // nolint
+ }
+ return 1
+}
+
+// FuzzType is go-fuzz endpoint for message type.
+func FuzzType(data []byte) int {
+ t := MessageType{}
+ vt, _ := binary.Uvarint(data)
+ v := uint16(vt) & 0x1fff // first 3 bits are empty
+ t.ReadValue(v)
+ v2 := t.Value()
+ if v != v2 {
+ panic("v != v2") // nolint
+ }
+ t2 := MessageType{}
+ t2.ReadValue(v2)
+ if t2 != t {
+ panic("t2 != t") // nolint
+ }
+ return 0
+}
+
+type attr interface {
+ Getter
+ Setter
+}
+
+type attrs []struct {
+ g attr
+ t AttrType
+}
+
+func (a attrs) pick(v byte) struct {
+ g attr
+ t AttrType
+} {
+ idx := int(v) % len(a)
+ return a[idx]
+}
+
+func FuzzSetters(data []byte) int {
+ var (
+ m1 = &Message{
+ Raw: make([]byte, 0, 2048),
+ }
+ m2 = &Message{
+ Raw: make([]byte, 0, 2048),
+ }
+ m3 = &Message{
+ Raw: make([]byte, 0, 2048),
+ }
+ )
+ attributes := attrs{
+ {new(Realm), AttrRealm},
+ {new(XORMappedAddress), AttrXORMappedAddress},
+ {new(Nonce), AttrNonce},
+ {new(Software), AttrSoftware},
+ {new(AlternateServer), AttrAlternateServer},
+ {new(ErrorCodeAttribute), AttrErrorCode},
+ {new(UnknownAttributes), AttrUnknownAttributes},
+ {new(Username), AttrUsername},
+ {new(MappedAddress), AttrMappedAddress},
+ {new(Realm), AttrRealm},
+ }
+ var firstByte = byte(0)
+ if len(data) > 0 {
+ firstByte = data[0]
+ }
+ a := attributes.pick(firstByte)
+ value := data
+ if len(data) > 1 {
+ value = value[1:]
+ }
+ m1.WriteHeader()
+ m1.Add(a.t, value)
+ err := a.g.GetFrom(m1)
+ if err == ErrAttributeNotFound {
+ fmt.Println("unexpected 404") // nolint
+ panic(err) // nolint
+ }
+ if err != nil {
+ return 1
+ }
+ m2.WriteHeader()
+ if err = a.g.AddTo(m2); err != nil {
+ // We allow decoding some text attributes
+ // when their length is too big, but
+ // not encoding.
+ if !IsAttrSizeOverflow(err) {
+ panic(err) // nolint
+ }
+ return 1
+ }
+ m3.WriteHeader()
+ v, err := m2.Get(a.t)
+ if err != nil {
+ panic(err) // nolint
+ }
+ m3.Add(a.t, v)
+
+ if !m2.Equal(m3) {
+ fmt.Println(m2, "not equal", m3) // nolint
+ panic("not equal") // nolint
+ }
+ return 1
+}
diff --git a/vendor/github.com/pion/stun/go.mod b/vendor/github.com/pion/stun/go.mod
new file mode 100644
index 0000000..72b5671
--- /dev/null
+++ b/vendor/github.com/pion/stun/go.mod
@@ -0,0 +1,3 @@
+module github.com/pion/stun
+
+go 1.12
diff --git a/vendor/github.com/pion/stun/go.sum b/vendor/github.com/pion/stun/go.sum
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/github.com/pion/stun/go.sum
diff --git a/vendor/github.com/pion/stun/go.test.sh b/vendor/github.com/pion/stun/go.test.sh
new file mode 100644
index 0000000..25234e4
--- /dev/null
+++ b/vendor/github.com/pion/stun/go.test.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+set -e
+touch coverage.txt
+
+# test fuzz inputs
+go test -tags gofuzz -run TestFuzz -v .
+
+# quick-test without -race
+go test ./...
+
+# test with "debug" tag
+go test -tags debug ./...
+
+# test concurrency
+go test -race -cpu=1,2,4 -run TestClient_DoConcurrent
+
+for d in $(go list ./... | grep -v vendor); do
+ go test -race -coverprofile=profile.out -covermode=atomic "$d"
+ if [[ -f profile.out ]]; then
+ cat profile.out >> coverage.txt
+ rm profile.out
+ fi
+done
diff --git a/vendor/github.com/pion/stun/helpers.go b/vendor/github.com/pion/stun/helpers.go
new file mode 100644
index 0000000..158a51d
--- /dev/null
+++ b/vendor/github.com/pion/stun/helpers.go
@@ -0,0 +1,105 @@
+package stun
+
+// Interfaces that are implemented by message attributes, shorthands for them,
+// or helpers for message fields as type or transaction id.
+type (
+ // Setter sets *Message attribute.
+ Setter interface {
+ AddTo(m *Message) error
+ }
+ // Getter parses attribute from *Message.
+ Getter interface {
+ GetFrom(m *Message) error
+ }
+ // Checker checks *Message attribute.
+ Checker interface {
+ Check(m *Message) error
+ }
+)
+
+// Build resets message and applies setters to it in batch, returning on
+// first error. To prevent allocations, pass pointers to values.
+//
+// Example:
+// var (
+// t = BindingRequest
+// username = NewUsername("username")
+// nonce = NewNonce("nonce")
+// realm = NewRealm("example.org")
+// )
+// m := new(Message)
+// m.Build(t, username, nonce, realm) // 4 allocations
+// m.Build(&t, &username, &nonce, &realm) // 0 allocations
+//
+// See BenchmarkBuildOverhead.
+func (m *Message) Build(setters ...Setter) error {
+ m.Reset()
+ m.WriteHeader()
+ for _, s := range setters {
+ if err := s.AddTo(m); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Check applies checkers to message in batch, returning on first error.
+func (m *Message) Check(checkers ...Checker) error {
+ for _, c := range checkers {
+ if err := c.Check(m); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Parse applies getters to message in batch, returning on first error.
+func (m *Message) Parse(getters ...Getter) error {
+ for _, c := range getters {
+ if err := c.GetFrom(m); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// MustBuild wraps Build call and panics on error.
+func MustBuild(setters ...Setter) *Message {
+ m, err := Build(setters...)
+ if err != nil {
+ panic(err) // nolint
+ }
+ return m
+}
+
+// Build wraps Message.Build method.
+func Build(setters ...Setter) (*Message, error) {
+ m := new(Message)
+ if err := m.Build(setters...); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// ForEach is helper that iterates over message attributes allowing to call
+// Getter in f callback to get all attributes of type t and returning on first
+// f error.
+//
+// The m.Get method inside f will be returning next attribute on each f call.
+// Does not error if there are no results.
+func (m *Message) ForEach(t AttrType, f func(m *Message) error) error {
+ attrs := m.Attributes
+ defer func() {
+ m.Attributes = attrs
+ }()
+ for i, a := range attrs {
+ if a.Type != t {
+ continue
+ }
+ m.Attributes = attrs[i:]
+ if err := f(m); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/stun/integrity.go b/vendor/github.com/pion/stun/integrity.go
new file mode 100644
index 0000000..39b0d50
--- /dev/null
+++ b/vendor/github.com/pion/stun/integrity.go
@@ -0,0 +1,124 @@
+package stun
+
+import (
+ "crypto/md5" // #nosec
+ "crypto/sha1" // #nosec
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/pion/stun/internal/hmac"
+)
+
+// separator for credentials.
+const credentialsSep = ":"
+
+// NewLongTermIntegrity returns new MessageIntegrity with key for long-term
+// credentials. Password, username, and realm must be SASL-prepared.
+func NewLongTermIntegrity(username, realm, password string) MessageIntegrity {
+ k := strings.Join([]string{username, realm, password}, credentialsSep)
+ // #nosec
+ h := md5.New()
+ fmt.Fprint(h, k)
+ return MessageIntegrity(h.Sum(nil))
+}
+
+// NewShortTermIntegrity returns new MessageIntegrity with key for short-term
+// credentials. Password must be SASL-prepared.
+func NewShortTermIntegrity(password string) MessageIntegrity {
+ return MessageIntegrity(password)
+}
+
+// MessageIntegrity represents MESSAGE-INTEGRITY attribute.
+//
+// AddTo and Check methods are using zero-allocation version of hmac, see
+// newHMAC function and internal/hmac/pool.go.
+//
+// RFC 5389 Section 15.4
+type MessageIntegrity []byte
+
+func newHMAC(key, message, buf []byte) []byte {
+ mac := hmac.AcquireSHA1(key)
+ writeOrPanic(mac, message)
+ defer hmac.PutSHA1(mac)
+ return mac.Sum(buf)
+}
+
+func (i MessageIntegrity) String() string {
+ return fmt.Sprintf("KEY: 0x%x", []byte(i))
+}
+
+const messageIntegritySize = 20
+
+// ErrFingerprintBeforeIntegrity means that FINGERPRINT attribute is already in
+// message, so MESSAGE-INTEGRITY attribute cannot be added.
+var ErrFingerprintBeforeIntegrity = errors.New("FINGERPRINT before MESSAGE-INTEGRITY attribute")
+
+// AddTo adds MESSAGE-INTEGRITY attribute to message.
+//
+// CPU costly, see BenchmarkMessageIntegrity_AddTo.
+func (i MessageIntegrity) AddTo(m *Message) error {
+ for _, a := range m.Attributes {
+ // Message should not contain FINGERPRINT attribute
+ // before MESSAGE-INTEGRITY.
+ if a.Type == AttrFingerprint {
+ return ErrFingerprintBeforeIntegrity
+ }
+ }
+ // The text used as input to HMAC is the STUN message,
+ // including the header, up to and including the attribute preceding the
+ // MESSAGE-INTEGRITY attribute.
+ length := m.Length
+ // Adjusting m.Length to contain MESSAGE-INTEGRITY TLV.
+ m.Length += messageIntegritySize + attributeHeaderSize
+ m.WriteLength() // writing length to m.Raw
+ v := newHMAC(i, m.Raw, m.Raw[len(m.Raw):]) // calculating HMAC for adjusted m.Raw
+ m.Length = length // changing m.Length back
+
+ // Copy hmac value to temporary variable to protect it from resetting
+ // while processing m.Add call.
+ vBuf := make([]byte, sha1.Size)
+ copy(vBuf, v)
+
+ m.Add(AttrMessageIntegrity, vBuf)
+ return nil
+}
+
+// ErrIntegrityMismatch means that computed HMAC differs from expected.
+var ErrIntegrityMismatch = errors.New("integrity check failed")
+
+// Check checks MESSAGE-INTEGRITY attribute.
+//
+// CPU costly, see BenchmarkMessageIntegrity_Check.
+func (i MessageIntegrity) Check(m *Message) error {
+ v, err := m.Get(AttrMessageIntegrity)
+ if err != nil {
+ return err
+ }
+
+ // Adjusting length in header to match m.Raw that was
+ // used when computing HMAC.
+ var (
+ length = m.Length
+ afterIntegrity = false
+ sizeReduced int
+ )
+ for _, a := range m.Attributes {
+ if afterIntegrity {
+ sizeReduced += nearestPaddedValueLength(int(a.Length))
+ sizeReduced += attributeHeaderSize
+ }
+ if a.Type == AttrMessageIntegrity {
+ afterIntegrity = true
+ }
+ }
+ m.Length -= uint32(sizeReduced)
+ m.WriteLength()
+ // startOfHMAC should be first byte of integrity attribute.
+ startOfHMAC := messageHeaderSize + m.Length - (attributeHeaderSize + messageIntegritySize)
+ b := m.Raw[:startOfHMAC] // data before integrity attribute
+ expected := newHMAC(i, b, m.Raw[len(m.Raw):])
+ m.Length = length
+ m.WriteLength() // writing length back
+ return checkHMAC(v, expected)
+}
diff --git a/vendor/github.com/pion/stun/integrity_debug.go b/vendor/github.com/pion/stun/integrity_debug.go
new file mode 100644
index 0000000..6b8a303
--- /dev/null
+++ b/vendor/github.com/pion/stun/integrity_debug.go
@@ -0,0 +1,18 @@
+// +build debug
+
+package stun
+
+import "fmt"
+
+// IntegrityErr occurs when computed HMAC differs from expected.
+type IntegrityErr struct {
+ Expected []byte
+ Actual []byte
+}
+
+func (i *IntegrityErr) Error() string {
+ return fmt.Sprintf(
+ "Integrity check failed: 0x%x (expected) !- 0x%x (actual)",
+ i.Expected, i.Actual,
+ )
+}
diff --git a/vendor/github.com/pion/stun/internal/hmac/hmac.go b/vendor/github.com/pion/stun/internal/hmac/hmac.go
new file mode 100644
index 0000000..801ece6
--- /dev/null
+++ b/vendor/github.com/pion/stun/internal/hmac/hmac.go
@@ -0,0 +1,101 @@
+// Copyright 2009 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.
+
+/*
+Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as
+defined in U.S. Federal Information Processing Standards Publication 198.
+An HMAC is a cryptographic hash that uses a key to sign a message.
+The receiver verifies the hash by recomputing it using the same key.
+
+Receivers should be careful to use Equal to compare MACs in order to avoid
+timing side-channels:
+
+ // ValidMAC reports whether messageMAC is a valid HMAC tag for message.
+ func ValidMAC(message, messageMAC, key []byte) bool {
+ mac := hmac.New(sha256.New, key)
+ mac.Write(message)
+ expectedMAC := mac.Sum(nil)
+ return hmac.Equal(messageMAC, expectedMAC)
+ }
+*/
+package hmac
+
+import (
+ "crypto/subtle"
+ "hash"
+)
+
+// FIPS 198-1:
+// https://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf
+
+// key is zero padded to the block size of the hash function
+// ipad = 0x36 byte repeated for key length
+// opad = 0x5c byte repeated for key length
+// hmac = H([key ^ opad] H([key ^ ipad] text))
+
+type hmac struct {
+ size int
+ blocksize int
+ opad, ipad []byte
+ outer, inner hash.Hash
+}
+
+func (h *hmac) Sum(in []byte) []byte {
+ origLen := len(in)
+ in = h.inner.Sum(in)
+ h.outer.Reset()
+ h.outer.Write(h.opad)
+ h.outer.Write(in[origLen:])
+ return h.outer.Sum(in[:origLen])
+}
+
+func (h *hmac) Write(p []byte) (n int, err error) {
+ return h.inner.Write(p)
+}
+
+func (h *hmac) Size() int { return h.size }
+
+func (h *hmac) BlockSize() int { return h.blocksize }
+
+func (h *hmac) Reset() {
+ h.inner.Reset()
+ h.inner.Write(h.ipad)
+}
+
+// New returns a new HMAC hash using the given hash.Hash type and key.
+// Note that unlike other hash implementations in the standard library,
+// the returned Hash does not implement encoding.BinaryMarshaler
+// or encoding.BinaryUnmarshaler.
+func New(h func() hash.Hash, key []byte) hash.Hash {
+ hm := new(hmac)
+ hm.outer = h()
+ hm.inner = h()
+ hm.size = hm.inner.Size()
+ hm.blocksize = hm.inner.BlockSize()
+ hm.ipad = make([]byte, hm.blocksize)
+ hm.opad = make([]byte, hm.blocksize)
+ if len(key) > hm.blocksize {
+ // If key is too big, hash it.
+ hm.outer.Write(key)
+ key = hm.outer.Sum(nil)
+ }
+ copy(hm.ipad, key)
+ copy(hm.opad, key)
+ for i := range hm.ipad {
+ hm.ipad[i] ^= 0x36
+ }
+ for i := range hm.opad {
+ hm.opad[i] ^= 0x5c
+ }
+ hm.inner.Write(hm.ipad)
+ return hm
+}
+
+// Equal compares two MACs for equality without leaking timing information.
+func Equal(mac1, mac2 []byte) bool {
+ // We don't have to be constant time if the lengths of the MACs are
+ // different as that suggests that a completely different hash function
+ // was used.
+ return subtle.ConstantTimeCompare(mac1, mac2) == 1
+}
diff --git a/vendor/github.com/pion/stun/internal/hmac/pool.go b/vendor/github.com/pion/stun/internal/hmac/pool.go
new file mode 100644
index 0000000..4db61e5
--- /dev/null
+++ b/vendor/github.com/pion/stun/internal/hmac/pool.go
@@ -0,0 +1,92 @@
+package hmac
+
+import (
+ "crypto/sha1"
+ "crypto/sha256"
+ "hash"
+ "sync"
+)
+
+// setZeroes sets all bytes from b to zeroes.
+//
+// See https://github.com/golang/go/issues/5373
+func setZeroes(b []byte) {
+ for i := range b {
+ b[i] = 0
+ }
+}
+
+func (h *hmac) resetTo(key []byte) {
+ h.outer.Reset()
+ h.inner.Reset()
+ setZeroes(h.ipad)
+ setZeroes(h.opad)
+ if len(key) > h.blocksize {
+ // If key is too big, hash it.
+ h.outer.Write(key)
+ key = h.outer.Sum(nil)
+ }
+ copy(h.ipad, key)
+ copy(h.opad, key)
+ for i := range h.ipad {
+ h.ipad[i] ^= 0x36
+ }
+ for i := range h.opad {
+ h.opad[i] ^= 0x5c
+ }
+ h.inner.Write(h.ipad)
+}
+
+var hmacSHA1Pool = &sync.Pool{
+ New: func() interface{} {
+ h := New(sha1.New, make([]byte, sha1.BlockSize))
+ return h
+ },
+}
+
+// AcquireSHA1 returns new HMAC from pool.
+func AcquireSHA1(key []byte) hash.Hash {
+ h := hmacSHA1Pool.Get().(*hmac)
+ assertHMACSize(h, sha1.Size, sha1.BlockSize)
+ h.resetTo(key)
+ return h
+}
+
+// PutSHA1 puts h to pool.
+func PutSHA1(h hash.Hash) {
+ hm := h.(*hmac)
+ assertHMACSize(hm, sha1.Size, sha1.BlockSize)
+ hmacSHA1Pool.Put(hm)
+}
+
+var hmacSHA256Pool = &sync.Pool{
+ New: func() interface{} {
+ h := New(sha256.New, make([]byte, sha256.BlockSize))
+ return h
+ },
+}
+
+// AcquireSHA256 returns new HMAC from SHA256 pool.
+func AcquireSHA256(key []byte) hash.Hash {
+ h := hmacSHA256Pool.Get().(*hmac)
+ assertHMACSize(h, sha256.Size, sha256.BlockSize)
+ h.resetTo(key)
+ return h
+}
+
+// PutSHA256 puts h to SHA256 pool.
+func PutSHA256(h hash.Hash) {
+ hm := h.(*hmac)
+ assertHMACSize(hm, sha256.Size, sha256.BlockSize)
+ hmacSHA256Pool.Put(hm)
+}
+
+// assertHMACSize panics if h.size != size or h.blocksize != blocksize.
+//
+// Put and Acquire functions are internal functions to project, so
+// checking it via such assert is optimal.
+func assertHMACSize(h *hmac, size, blocksize int) {
+ if h.size != size || h.blocksize != blocksize {
+ panic("BUG: hmac size invalid") // nolint
+ }
+}
diff --git a/vendor/github.com/pion/stun/internal/hmac/vendor.sh b/vendor/github.com/pion/stun/internal/hmac/vendor.sh
new file mode 100644
index 0000000..83a2b32
--- /dev/null
+++ b/vendor/github.com/pion/stun/internal/hmac/vendor.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+cp -v $GOROOT/src/crypto/hmac/{hmac,hmac_test}.go .
+git diff {hmac,hmac_test}.go
+
diff --git a/vendor/github.com/pion/stun/message.go b/vendor/github.com/pion/stun/message.go
new file mode 100644
index 0000000..3819235
--- /dev/null
+++ b/vendor/github.com/pion/stun/message.go
@@ -0,0 +1,588 @@
+package stun
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "io"
+)
+
+const (
+ // magicCookie is fixed value that aids in distinguishing STUN packets
+ // from packets of other protocols when STUN is multiplexed with those
+ // other protocols on the same Port.
+ //
+ // The magic cookie field MUST contain the fixed value 0x2112A442 in
+ // network byte order.
+ //
+ // Defined in "STUN Message Structure", section 6.
+ magicCookie = 0x2112A442
+ attributeHeaderSize = 4
+ messageHeaderSize = 20
+
+ // TransactionIDSize is length of transaction id array (in bytes).
+ TransactionIDSize = 12 // 96 bit
+)
+
+// NewTransactionID returns new random transaction ID using crypto/rand
+// as source.
+func NewTransactionID() (b [TransactionIDSize]byte) {
+ readFullOrPanic(rand.Reader, b[:])
+ return b
+}
+
+// IsMessage returns true if b looks like STUN message.
+// Useful for multiplexing. IsMessage does not guarantee
+// that decoding will be successful.
+func IsMessage(b []byte) bool {
+ return len(b) >= messageHeaderSize && bin.Uint32(b[4:8]) == magicCookie
+}
+
+// New returns *Message with pre-allocated Raw.
+func New() *Message {
+ const defaultRawCapacity = 120
+ return &Message{
+ Raw: make([]byte, messageHeaderSize, defaultRawCapacity),
+ }
+}
+
+// ErrDecodeToNil occurs on Decode(data, nil) call.
+var ErrDecodeToNil = errors.New("attempt to decode to nil message")
+
+// Decode decodes Message from data to m, returning error if any.
+func Decode(data []byte, m *Message) error {
+ if m == nil {
+ return ErrDecodeToNil
+ }
+ m.Raw = append(m.Raw[:0], data...)
+ return m.Decode()
+}
+
+// Message represents a single STUN packet. It uses aggressive internal
+// buffering to enable zero-allocation encoding and decoding,
+// so there are some usage constraints:
+//
+// Message, its fields, results of m.Get or any attribute a.GetFrom
+// are valid only until Message.Raw is not modified.
+type Message struct {
+ Type MessageType
+ Length uint32 // len(Raw) not including header
+ TransactionID [TransactionIDSize]byte
+ Attributes Attributes
+ Raw []byte
+}
+
+// AddTo sets b.TransactionID to m.TransactionID.
+//
+// Implements Setter to aid in crafting responses.
+func (m *Message) AddTo(b *Message) error {
+ b.TransactionID = m.TransactionID
+ b.WriteTransactionID()
+ return nil
+}
+
+// NewTransactionID sets m.TransactionID to random value from crypto/rand
+// and returns error if any.
+func (m *Message) NewTransactionID() error {
+ _, err := io.ReadFull(rand.Reader, m.TransactionID[:])
+ if err == nil {
+ m.WriteTransactionID()
+ }
+ return err
+}
+
+func (m *Message) String() string {
+ tID := base64.StdEncoding.EncodeToString(m.TransactionID[:])
+ return fmt.Sprintf("%s l=%d attrs=%d id=%s", m.Type, m.Length, len(m.Attributes), tID)
+}
+
+// Reset resets Message, attributes and underlying buffer length.
+func (m *Message) Reset() {
+ m.Raw = m.Raw[:0]
+ m.Length = 0
+ m.Attributes = m.Attributes[:0]
+}
+
+// grow ensures that internal buffer has n length.
+func (m *Message) grow(n int) {
+ if len(m.Raw) >= n {
+ return
+ }
+ if cap(m.Raw) >= n {
+ m.Raw = m.Raw[:n]
+ return
+ }
+ m.Raw = append(m.Raw, make([]byte, n-len(m.Raw))...)
+}
+
+// Add appends new attribute to message. Not goroutine-safe.
+//
+// Value of attribute is copied to internal buffer so
+// it is safe to reuse v.
+func (m *Message) Add(t AttrType, v []byte) {
+ // Allocating buffer for TLV (type-length-value).
+ // T = t, L = len(v), V = v.
+ // m.Raw will look like:
+ // [0:20] <- message header
+ // [20:20+m.Length] <- existing message attributes
+ // [20+m.Length:20+m.Length+len(v) + 4] <- allocated buffer for new TLV
+ // [first:last] <- same as previous
+ // [0 1|2 3|4 4 + len(v)] <- mapping for allocated buffer
+ // T L V
+ allocSize := attributeHeaderSize + len(v) // ~ len(TLV) = len(TL) + len(V)
+ first := messageHeaderSize + int(m.Length) // first byte number
+ last := first + allocSize // last byte number
+ m.grow(last) // growing cap(Raw) to fit TLV
+ m.Raw = m.Raw[:last] // now len(Raw) = last
+ m.Length += uint32(allocSize) // rendering length change
+
+ // Sub-slicing internal buffer to simplify encoding.
+ buf := m.Raw[first:last] // slice for TLV
+ value := buf[attributeHeaderSize:] // slice for V
+ attr := RawAttribute{
+ Type: t, // T
+ Length: uint16(len(v)), // L
+ Value: value, // V
+ }
+
+ // Encoding attribute TLV to allocated buffer.
+ bin.PutUint16(buf[0:2], attr.Type.Value()) // T
+ bin.PutUint16(buf[2:4], attr.Length) // L
+ copy(value, v) // V
+
+ // Checking that attribute value needs padding.
+ if attr.Length%padding != 0 {
+ // Performing padding.
+ bytesToAdd := nearestPaddedValueLength(len(v)) - len(v)
+ last += bytesToAdd
+ m.grow(last)
+ // setting all padding bytes to zero
+ // to prevent data leak from previous
+ // data in next bytesToAdd bytes
+ buf = m.Raw[last-bytesToAdd : last]
+ for i := range buf {
+ buf[i] = 0
+ }
+ m.Raw = m.Raw[:last] // increasing buffer length
+ m.Length += uint32(bytesToAdd) // rendering length change
+ }
+ m.Attributes = append(m.Attributes, attr)
+ m.WriteLength()
+}
+
+func attrSliceEqual(a, b Attributes) bool {
+ for _, attr := range a {
+ found := false
+ for _, attrB := range b {
+ if attrB.Type != attr.Type {
+ continue
+ }
+ if attrB.Equal(attr) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}
+
+func attrEqual(a, b Attributes) bool {
+ if a == nil && b == nil {
+ return true
+ }
+ if a == nil || b == nil {
+ return false
+ }
+ if len(a) != len(b) {
+ return false
+ }
+ if !attrSliceEqual(a, b) {
+ return false
+ }
+ if !attrSliceEqual(b, a) {
+ return false
+ }
+ return true
+}
+
+// Equal returns true if Message b equals to m.
+// Ignores m.Raw.
+func (m *Message) Equal(b *Message) bool {
+ if m == nil && b == nil {
+ return true
+ }
+ if m == nil || b == nil {
+ return false
+ }
+ if m.Type != b.Type {
+ return false
+ }
+ if m.TransactionID != b.TransactionID {
+ return false
+ }
+ if m.Length != b.Length {
+ return false
+ }
+ if !attrEqual(m.Attributes, b.Attributes) {
+ return false
+ }
+ return true
+}
+
+// WriteLength writes m.Length to m.Raw.
+func (m *Message) WriteLength() {
+ m.grow(4)
+ bin.PutUint16(m.Raw[2:4], uint16(m.Length))
+}
+
+// WriteHeader writes header to underlying buffer. Not goroutine-safe.
+func (m *Message) WriteHeader() {
+ m.grow(messageHeaderSize)
+ _ = m.Raw[:messageHeaderSize] // early bounds check to guarantee safety of writes below
+
+ m.WriteType()
+ m.WriteLength()
+ bin.PutUint32(m.Raw[4:8], magicCookie) // magic cookie
+ copy(m.Raw[8:messageHeaderSize], m.TransactionID[:]) // transaction ID
+}
+
+// WriteTransactionID writes m.TransactionID to m.Raw.
+func (m *Message) WriteTransactionID() {
+ copy(m.Raw[8:messageHeaderSize], m.TransactionID[:]) // transaction ID
+}
+
+// WriteAttributes encodes all m.Attributes to m.
+func (m *Message) WriteAttributes() {
+ attributes := m.Attributes
+ m.Attributes = attributes[:0]
+ for _, a := range attributes {
+ m.Add(a.Type, a.Value)
+ }
+ m.Attributes = attributes
+}
+
+// WriteType writes m.Type to m.Raw.
+func (m *Message) WriteType() {
+ m.grow(2)
+ bin.PutUint16(m.Raw[0:2], m.Type.Value()) // message type
+}
+
+// SetType sets m.Type and writes it to m.Raw.
+func (m *Message) SetType(t MessageType) {
+ m.Type = t
+ m.WriteType()
+}
+
+// Encode re-encodes message into m.Raw.
+func (m *Message) Encode() {
+ m.Raw = m.Raw[:0]
+ m.WriteHeader()
+ m.Length = 0
+ m.WriteAttributes()
+}
+
+// WriteTo implements WriterTo via calling Write(m.Raw) on w and returning
+// call result.
+func (m *Message) WriteTo(w io.Writer) (int64, error) {
+ n, err := w.Write(m.Raw)
+ return int64(n), err
+}
+
+// ReadFrom implements ReaderFrom. Reads message from r into m.Raw,
+// Decodes it and return error if any. If m.Raw is too small, will return
+// ErrUnexpectedEOF, ErrUnexpectedHeaderEOF or *DecodeErr.
+//
+// Can return *DecodeErr while decoding too.
+func (m *Message) ReadFrom(r io.Reader) (int64, error) {
+ tBuf := m.Raw[:cap(m.Raw)]
+ var (
+ n int
+ err error
+ )
+ if n, err = r.Read(tBuf); err != nil {
+ return int64(n), err
+ }
+ m.Raw = tBuf[:n]
+ return int64(n), m.Decode()
+}
+
+// ErrUnexpectedHeaderEOF means that there were not enough bytes in
+// m.Raw to read header.
+var ErrUnexpectedHeaderEOF = errors.New("unexpected EOF: not enough bytes to read header")
+
+// Decode decodes m.Raw into m.
+func (m *Message) Decode() error {
+ // decoding message header
+ buf := m.Raw
+ if len(buf) < messageHeaderSize {
+ return ErrUnexpectedHeaderEOF
+ }
+ var (
+ t = bin.Uint16(buf[0:2]) // first 2 bytes
+ size = int(bin.Uint16(buf[2:4])) // second 2 bytes
+ cookie = bin.Uint32(buf[4:8]) // last 4 bytes
+ fullSize = messageHeaderSize + size // len(m.Raw)
+ )
+ if cookie != magicCookie {
+ msg := fmt.Sprintf("%x is invalid magic cookie (should be %x)", cookie, magicCookie)
+ return newDecodeErr("message", "cookie", msg)
+ }
+ if len(buf) < fullSize {
+ msg := fmt.Sprintf("buffer length %d is less than %d (expected message size)", len(buf), fullSize)
+ return newAttrDecodeErr("message", msg)
+ }
+ // saving header data
+ m.Type.ReadValue(t)
+ m.Length = uint32(size)
+ copy(m.TransactionID[:], buf[8:messageHeaderSize])
+
+ m.Attributes = m.Attributes[:0]
+ var (
+ offset = 0
+ b = buf[messageHeaderSize:fullSize]
+ )
+ for offset < size {
+ // checking that we have enough bytes to read header
+ if len(b) < attributeHeaderSize {
+ msg := fmt.Sprintf("buffer length %d is less than %d (expected header size)", len(b), attributeHeaderSize)
+ return newAttrDecodeErr("header", msg)
+ }
+ var (
+ a = RawAttribute{
+ Type: compatAttrType(bin.Uint16(b[0:2])), // first 2 bytes
+ Length: bin.Uint16(b[2:4]), // second 2 bytes
+ }
+ aL = int(a.Length) // attribute length
+ aBuffL = nearestPaddedValueLength(aL) // expected buffer length (with padding)
+ )
+ b = b[attributeHeaderSize:] // slicing again to simplify value read
+ offset += attributeHeaderSize
+ if len(b) < aBuffL { // checking size
+ msg := fmt.Sprintf("buffer length %d is less than %d (expected value size for %s)", len(b), aBuffL, a.Type)
+ return newAttrDecodeErr("value", msg)
+ }
+ a.Value = b[:aL]
+ offset += aBuffL
+ b = b[aBuffL:]
+
+ m.Attributes = append(m.Attributes, a)
+ }
+ return nil
+}
+
+// Write decodes message and return error if any.
+//
+// Any error is unrecoverable, but message could be partially decoded.
+func (m *Message) Write(tBuf []byte) (int, error) {
+ m.Raw = append(m.Raw[:0], tBuf...)
+ return len(tBuf), m.Decode()
+}
+
+// CloneTo clones m to b securing any further m mutations.
+func (m *Message) CloneTo(b *Message) error {
+ // TODO(ar): implement low-level copy.
+ b.Raw = append(b.Raw[:0], m.Raw...)
+ return b.Decode()
+}
+
+// MessageClass is 8-bit representation of 2-bit class of STUN Message Class.
+type MessageClass byte
+
+// Possible values for message class in STUN Message Type.
+const (
+ ClassRequest MessageClass = 0x00 // 0b00
+ ClassIndication MessageClass = 0x01 // 0b01
+ ClassSuccessResponse MessageClass = 0x02 // 0b10
+ ClassErrorResponse MessageClass = 0x03 // 0b11
+)
+
+// Common STUN message types.
+var (
+ // Binding request message type.
+ BindingRequest = NewType(MethodBinding, ClassRequest)
+ // Binding success response message type
+ BindingSuccess = NewType(MethodBinding, ClassSuccessResponse)
+ // Binding error response message type.
+ BindingError = NewType(MethodBinding, ClassErrorResponse)
+)
+
+func (c MessageClass) String() string {
+ switch c {
+ case ClassRequest:
+ return "request"
+ case ClassIndication:
+ return "indication"
+ case ClassSuccessResponse:
+ return "success response"
+ case ClassErrorResponse:
+ return "error response"
+ default:
+ panic("unknown message class") // nolint: never happens unless wrongly casted
+ }
+}
+
+// Method is uint16 representation of 12-bit STUN method.
+type Method uint16
+
+// Possible methods for STUN Message.
+const (
+ MethodBinding Method = 0x001
+ MethodAllocate Method = 0x003
+ MethodRefresh Method = 0x004
+ MethodSend Method = 0x006
+ MethodData Method = 0x007
+ MethodCreatePermission Method = 0x008
+ MethodChannelBind Method = 0x009
+)
+
+// Methods from RFC 6062.
+const (
+ MethodConnect Method = 0x000a
+ MethodConnectionBind Method = 0x000b
+ MethodConnectionAttempt Method = 0x000c
+)
+
+var methodName = map[Method]string{
+ MethodBinding: "Binding",
+ MethodAllocate: "Allocate",
+ MethodRefresh: "Refresh",
+ MethodSend: "Send",
+ MethodData: "Data",
+ MethodCreatePermission: "CreatePermission",
+ MethodChannelBind: "ChannelBind",
+
+ // RFC 6062.
+ MethodConnect: "Connect",
+ MethodConnectionBind: "ConnectionBind",
+ MethodConnectionAttempt: "ConnectionAttempt",
+}
+
+func (m Method) String() string {
+ s, ok := methodName[m]
+ if !ok {
+ // Falling back to hex representation.
+ s = fmt.Sprintf("0x%x", uint16(m))
+ }
+ return s
+}
+
+// MessageType is STUN Message Type Field.
+type MessageType struct {
+ Method Method // e.g. binding
+ Class MessageClass // e.g. request
+}
+
+// AddTo sets m type to t.
+func (t MessageType) AddTo(m *Message) error {
+ m.SetType(t)
+ return nil
+}
+
+// NewType returns new message type with provided method and class.
+func NewType(method Method, class MessageClass) MessageType {
+ return MessageType{
+ Method: method,
+ Class: class,
+ }
+}
+
+const (
+ methodABits = 0xf // 0b0000000000001111
+ methodBBits = 0x70 // 0b0000000001110000
+ methodDBits = 0xf80 // 0b0000111110000000
+
+ methodBShift = 1
+ methodDShift = 2
+
+ firstBit = 0x1
+ secondBit = 0x2
+
+ c0Bit = firstBit
+ c1Bit = secondBit
+
+ classC0Shift = 4
+ classC1Shift = 7
+)
+
+// Value returns bit representation of messageType.
+func (t MessageType) Value() uint16 {
+ // 0 1
+ // 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ // +--+--+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |M |M |M|M|M|C|M|M|M|C|M|M|M|M|
+ // |11|10|9|8|7|1|6|5|4|0|3|2|1|0|
+ // +--+--+-+-+-+-+-+-+-+-+-+-+-+-+
+ // Figure 3: Format of STUN Message Type Field
+
+ // Warning: Abandon all hope ye who enter here.
+ // Splitting M into A(M0-M3), B(M4-M6), D(M7-M11).
+ m := uint16(t.Method)
+ a := m & methodABits // A = M * 0b0000000000001111 (right 4 bits)
+ b := m & methodBBits // B = M * 0b0000000001110000 (3 bits after A)
+ d := m & methodDBits // D = M * 0b0000111110000000 (5 bits after B)
+
+ // Shifting to add "holes" for C0 (at 4 bit) and C1 (8 bit).
+ m = a + (b << methodBShift) + (d << methodDShift)
+
+ // C0 is zero bit of C, C1 is first bit.
+ // C0 = C * 0b01, C1 = (C * 0b10) >> 1
+ // Ct = C0 << 4 + C1 << 8.
+ // Optimizations: "((C * 0b10) >> 1) << 8" as "(C * 0b10) << 7"
+ // We need C0 shifted by 4, and C1 by 8 to fit "11" and "7" positions
+ // (see figure 3).
+ c := uint16(t.Class)
+ c0 := (c & c0Bit) << classC0Shift
+ c1 := (c & c1Bit) << classC1Shift
+ class := c0 + c1
+
+ return m + class
+}
+
+// ReadValue decodes uint16 into MessageType.
+func (t *MessageType) ReadValue(v uint16) {
+ // Decoding class.
+ // We are taking first bit from v >> 4 and second from v >> 7.
+ c0 := (v >> classC0Shift) & c0Bit
+ c1 := (v >> classC1Shift) & c1Bit
+ class := c0 + c1
+ t.Class = MessageClass(class)
+
+ // Decoding method.
+ a := v & methodABits // A(M0-M3)
+ b := (v >> methodBShift) & methodBBits // B(M4-M6)
+ d := (v >> methodDShift) & methodDBits // D(M7-M11)
+ m := a + b + d
+ t.Method = Method(m)
+}
+
+func (t MessageType) String() string {
+ return fmt.Sprintf("%s %s", t.Method, t.Class)
+}
+
+// Contains return true if message contain t attribute.
+func (m *Message) Contains(t AttrType) bool {
+ for _, a := range m.Attributes {
+ if a.Type == t {
+ return true
+ }
+ }
+ return false
+}
+
+type transactionIDValueSetter [TransactionIDSize]byte
+
+// NewTransactionIDSetter returns new Setter that sets message transaction id
+// to provided value.
+func NewTransactionIDSetter(value [TransactionIDSize]byte) Setter {
+ return transactionIDValueSetter(value)
+}
+
+func (t transactionIDValueSetter) AddTo(m *Message) error {
+ m.TransactionID = t
+ m.WriteTransactionID()
+ return nil
+}
diff --git a/vendor/github.com/pion/stun/renovate.json b/vendor/github.com/pion/stun/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/stun/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/stun/stun.go b/vendor/github.com/pion/stun/stun.go
new file mode 100644
index 0000000..9f804d2
--- /dev/null
+++ b/vendor/github.com/pion/stun/stun.go
@@ -0,0 +1,51 @@
+// Package stun implements Session Traversal Utilities for NAT (STUN) RFC 5389.
+//
+// The stun package is intended to use by package that implements extension
+// to STUN (e.g. TURN) or client/server applications.
+//
+// Most methods are designed to be zero allocations. If it is not enough,
+// low-level methods are available. On other hand, there are helpers that
+// reduce code repeat.
+//
+// See examples for Message for basic usage, or https://github.com/pion/turn
+// package for example of stun extension implementation.
+package stun
+
+import (
+ "encoding/binary"
+ "io"
+)
+
+// bin is shorthand to binary.BigEndian.
+var bin = binary.BigEndian
+
+func readFullOrPanic(r io.Reader, v []byte) int {
+ n, err := io.ReadFull(r, v)
+ if err != nil {
+ panic(err) // nolint
+ }
+ return n
+}
+
+func writeOrPanic(w io.Writer, v []byte) int {
+ n, err := w.Write(v)
+ if err != nil {
+ panic(err) // nolint
+ }
+ return n
+}
+
+// IANA assigned ports for "stun" protocol.
+const (
+ DefaultPort = 3478
+ DefaultTLSPort = 5349
+)
+
+type transactionIDSetter struct{}
+
+func (transactionIDSetter) AddTo(m *Message) error {
+ return m.NewTransactionID()
+}
+
+// TransactionID is Setter for m.TransactionID.
+var TransactionID Setter = transactionIDSetter{}
diff --git a/vendor/github.com/pion/stun/textattrs.go b/vendor/github.com/pion/stun/textattrs.go
new file mode 100644
index 0000000..efdfbd9
--- /dev/null
+++ b/vendor/github.com/pion/stun/textattrs.go
@@ -0,0 +1,129 @@
+package stun
+
+// NewUsername returns Username with provided value.
+func NewUsername(username string) Username {
+ return Username(username)
+}
+
+// Username represents USERNAME attribute.
+//
+// RFC 5389 Section 15.3
+type Username []byte
+
+func (u Username) String() string {
+ return string(u)
+}
+
+const maxUsernameB = 513
+
+// AddTo adds USERNAME attribute to message.
+func (u Username) AddTo(m *Message) error {
+ return TextAttribute(u).AddToAs(m, AttrUsername, maxUsernameB)
+}
+
+// GetFrom gets USERNAME from message.
+func (u *Username) GetFrom(m *Message) error {
+ return (*TextAttribute)(u).GetFromAs(m, AttrUsername)
+}
+
+// NewRealm returns Realm with provided value.
+// Must be SASL-prepared.
+func NewRealm(realm string) Realm {
+ return Realm(realm)
+}
+
+// Realm represents REALM attribute.
+//
+// RFC 5389 Section 15.7
+type Realm []byte
+
+func (n Realm) String() string {
+ return string(n)
+}
+
+const maxRealmB = 763
+
+// AddTo adds NONCE to message.
+func (n Realm) AddTo(m *Message) error {
+ return TextAttribute(n).AddToAs(m, AttrRealm, maxRealmB)
+}
+
+// GetFrom gets REALM from message.
+func (n *Realm) GetFrom(m *Message) error {
+ return (*TextAttribute)(n).GetFromAs(m, AttrRealm)
+}
+
+const softwareRawMaxB = 763
+
+// Software is SOFTWARE attribute.
+//
+// RFC 5389 Section 15.10
+type Software []byte
+
+func (s Software) String() string {
+ return string(s)
+}
+
+// NewSoftware returns *Software from string.
+func NewSoftware(software string) Software {
+ return Software(software)
+}
+
+// AddTo adds Software attribute to m.
+func (s Software) AddTo(m *Message) error {
+ return TextAttribute(s).AddToAs(m, AttrSoftware, softwareRawMaxB)
+}
+
+// GetFrom decodes Software from m.
+func (s *Software) GetFrom(m *Message) error {
+ return (*TextAttribute)(s).GetFromAs(m, AttrSoftware)
+}
+
+// Nonce represents NONCE attribute.
+//
+// RFC 5389 Section 15.8
+type Nonce []byte
+
+// NewNonce returns new Nonce from string.
+func NewNonce(nonce string) Nonce {
+ return Nonce(nonce)
+}
+
+func (n Nonce) String() string {
+ return string(n)
+}
+
+const maxNonceB = 763
+
+// AddTo adds NONCE to message.
+func (n Nonce) AddTo(m *Message) error {
+ return TextAttribute(n).AddToAs(m, AttrNonce, maxNonceB)
+}
+
+// GetFrom gets NONCE from message.
+func (n *Nonce) GetFrom(m *Message) error {
+ return (*TextAttribute)(n).GetFromAs(m, AttrNonce)
+}
+
+// TextAttribute is helper for adding and getting text attributes.
+type TextAttribute []byte
+
+// AddToAs adds attribute with type t to m, checking maximum length. If maxLen
+// is less than 0, no check is performed.
+func (v TextAttribute) AddToAs(m *Message, t AttrType, maxLen int) error {
+ if err := CheckOverflow(t, len(v), maxLen); err != nil {
+ return err
+ }
+ m.Add(t, v)
+ return nil
+}
+
+// GetFromAs gets t attribute from m and appends its value to reseted v.
+func (v *TextAttribute) GetFromAs(m *Message, t AttrType) error {
+ a, err := m.Get(t)
+ if err != nil {
+ return err
+ }
+ *v = a
+ return nil
+}
diff --git a/vendor/github.com/pion/stun/uattrs.go b/vendor/github.com/pion/stun/uattrs.go
new file mode 100644
index 0000000..238d32d
--- /dev/null
+++ b/vendor/github.com/pion/stun/uattrs.go
@@ -0,0 +1,63 @@
+package stun
+
+import "errors"
+
+// UnknownAttributes represents UNKNOWN-ATTRIBUTES attribute.
+//
+// RFC 5389 Section 15.9
+type UnknownAttributes []AttrType
+
+func (a UnknownAttributes) String() string {
+ s := ""
+ if len(a) == 0 {
+ return "<nil>"
+ }
+ last := len(a) - 1
+ for i, t := range a {
+ s += t.String()
+ if i != last {
+ s += ", "
+ }
+ }
+ return s
+}
+
+// type size is 16 bit.
+const attrTypeSize = 4
+
+// AddTo adds UNKNOWN-ATTRIBUTES attribute to message.
+func (a UnknownAttributes) AddTo(m *Message) error {
+ v := make([]byte, 0, attrTypeSize*20) // 20 should be enough
+ // If len(a.Types) > 20, there will be allocations.
+ for i, t := range a {
+ v = append(v, 0, 0, 0, 0) // 4 times by 0 (16 bits)
+ first := attrTypeSize * i
+ last := first + attrTypeSize
+ bin.PutUint16(v[first:last], t.Value())
+ }
+ m.Add(AttrUnknownAttributes, v)
+ return nil
+}
+
+// ErrBadUnknownAttrsSize means that UNKNOWN-ATTRIBUTES attribute value
+// has invalid length.
+var ErrBadUnknownAttrsSize = errors.New("bad UNKNOWN-ATTRIBUTES size")
+
+// GetFrom parses UNKNOWN-ATTRIBUTES from message.
+func (a *UnknownAttributes) GetFrom(m *Message) error {
+ v, err := m.Get(AttrUnknownAttributes)
+ if err != nil {
+ return err
+ }
+ if len(v)%attrTypeSize != 0 {
+ return ErrBadUnknownAttrsSize
+ }
+ *a = (*a)[:0]
+ first := 0
+ for first < len(v) {
+ last := first + attrTypeSize
+ *a = append(*a, AttrType(bin.Uint16(v[first:last])))
+ first = last
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/stun/xor.go b/vendor/github.com/pion/stun/xor.go
new file mode 100644
index 0000000..34365eb
--- /dev/null
+++ b/vendor/github.com/pion/stun/xor.go
@@ -0,0 +1,62 @@
+// Copyright 2013 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.
+
+package stun
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+// #nosec
+const wordSize = int(unsafe.Sizeof(uintptr(0)))
+
+var supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64"
+
+// fastXORBytes xors in bulk. It only works on architectures that
+// support unaligned read/writes.
+//
+// #nosec
+func fastXORBytes(dst, a, b []byte) int {
+ n := len(a)
+ if len(b) < n {
+ n = len(b)
+ }
+
+ w := n / wordSize
+ if w > 0 {
+ dw := *(*[]uintptr)(unsafe.Pointer(&dst))
+ aw := *(*[]uintptr)(unsafe.Pointer(&a))
+ bw := *(*[]uintptr)(unsafe.Pointer(&b))
+ for i := 0; i < w; i++ {
+ dw[i] = aw[i] ^ bw[i]
+ }
+ }
+
+ for i := n - n%wordSize; i < n; i++ {
+ dst[i] = a[i] ^ b[i]
+ }
+
+ return n
+}
+
+func safeXORBytes(dst, a, b []byte) int {
+ n := len(a)
+ if len(b) < n {
+ n = len(b)
+ }
+ for i := 0; i < n; i++ {
+ dst[i] = a[i] ^ b[i]
+ }
+ return n
+}
+
+// xorBytes xors the bytes in a and b. The destination is assumed to have enough
+// space. Returns the number of bytes xor'd.
+func xorBytes(dst, a, b []byte) int {
+ if supportsUnaligned {
+ return fastXORBytes(dst, a, b)
+ }
+ return safeXORBytes(dst, a, b)
+}
diff --git a/vendor/github.com/pion/stun/xoraddr.go b/vendor/github.com/pion/stun/xoraddr.go
new file mode 100644
index 0000000..23f7777
--- /dev/null
+++ b/vendor/github.com/pion/stun/xoraddr.go
@@ -0,0 +1,145 @@
+package stun
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+)
+
+const (
+ familyIPv4 uint16 = 0x01
+ familyIPv6 uint16 = 0x02
+)
+
+// XORMappedAddress implements XOR-MAPPED-ADDRESS attribute.
+//
+// RFC 5389 Section 15.2
+type XORMappedAddress struct {
+ IP net.IP
+ Port int
+}
+
+func (a XORMappedAddress) String() string {
+ return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
+}
+
+// isIPv4 returns true if ip with len of net.IPv6Len seems to be ipv4.
+func isIPv4(ip net.IP) bool {
+ // Optimized for performance. Copied from net.IP.To4.
+ return isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff
+}
+
+// Is p all zeros?
+func isZeros(p net.IP) bool {
+ for i := 0; i < len(p); i++ {
+ if p[i] != 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// ErrBadIPLength means that len(IP) is not net.{IPv6len,IPv4len}.
+var ErrBadIPLength = errors.New("invalid length of IP value")
+
+// AddToAs adds XOR-MAPPED-ADDRESS value to m as t attribute.
+func (a XORMappedAddress) AddToAs(m *Message, t AttrType) error {
+ var (
+ family = familyIPv4
+ ip = a.IP
+ )
+ if len(a.IP) == net.IPv6len {
+ if isIPv4(ip) {
+ ip = ip[12:16] // like in ip.To4()
+ } else {
+ family = familyIPv6
+ }
+ } else if len(ip) != net.IPv4len {
+ return ErrBadIPLength
+ }
+ value := make([]byte, 32+128)
+ value[0] = 0 // first 8 bits are zeroes
+ xorValue := make([]byte, net.IPv6len)
+ copy(xorValue[4:], m.TransactionID[:])
+ bin.PutUint32(xorValue[0:4], magicCookie)
+ bin.PutUint16(value[0:2], family)
+ bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16))
+ xorBytes(value[4:4+len(ip)], ip, xorValue)
+ m.Add(t, value[:4+len(ip)])
+ return nil
+}
+
+// AddTo adds XOR-MAPPED-ADDRESS to m. Can return ErrBadIPLength
+// if len(a.IP) is invalid.
+func (a XORMappedAddress) AddTo(m *Message) error {
+ return a.AddToAs(m, AttrXORMappedAddress)
+}
+
+// GetFromAs decodes XOR-MAPPED-ADDRESS attribute value in message
+// getting it as for t type.
+func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error {
+ v, err := m.Get(t)
+ if err != nil {
+ return err
+ }
+ family := bin.Uint16(v[0:2])
+ if family != familyIPv6 && family != familyIPv4 {
+ return newDecodeErr("xor-mapped address", "family",
+ fmt.Sprintf("bad value %d", family),
+ )
+ }
+ ipLen := net.IPv4len
+ if family == familyIPv6 {
+ ipLen = net.IPv6len
+ }
+ // Ensuring len(a.IP) == ipLen and reusing a.IP.
+ if len(a.IP) < ipLen {
+ a.IP = a.IP[:cap(a.IP)]
+ for len(a.IP) < ipLen {
+ a.IP = append(a.IP, 0)
+ }
+ }
+ a.IP = a.IP[:ipLen]
+ for i := range a.IP {
+ a.IP[i] = 0
+ }
+ if len(v) <= 4 {
+ return io.ErrUnexpectedEOF
+ }
+ if err := CheckOverflow(t, len(v[4:]), len(a.IP)); err != nil {
+ return err
+ }
+ a.Port = int(bin.Uint16(v[2:4])) ^ (magicCookie >> 16)
+ xorValue := make([]byte, 4+TransactionIDSize)
+ bin.PutUint32(xorValue[0:4], magicCookie)
+ copy(xorValue[4:], m.TransactionID[:])
+ xorBytes(a.IP, v[4:], xorValue)
+ return nil
+}
+
+// GetFrom decodes XOR-MAPPED-ADDRESS attribute in message and returns
+// error if any. While decoding, a.IP is reused if possible and can be
+// rendered to invalid state (e.g. if a.IP was set to IPv6 and then
+// IPv4 value were decoded into it), be careful.
+//
+// Example:
+//
+// expectedIP := net.ParseIP("213.141.156.236")
+// expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes
+// expectedPort := 21254
+// addr := &XORMappedAddress{
+// IP: expectedIP,
+// Port: expectedPort,
+// }
+// // addr were added to message that is decoded as newMessage
+// // ...
+//
+// addr.GetFrom(newMessage)
+// addr.IP.String() // 213.141.156.236, net.IPv4Len
+// expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4
+// // now we have len(expectedIP) = 16 and len(addr.IP) = 4.
+func (a *XORMappedAddress) GetFrom(m *Message) error {
+ return a.GetFromAs(m, AttrXORMappedAddress)
+}
diff --git a/vendor/github.com/pion/transport/LICENSE b/vendor/github.com/pion/transport/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/transport/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/transport/connctx/connctx.go b/vendor/github.com/pion/transport/connctx/connctx.go
new file mode 100644
index 0000000..20510f2
--- /dev/null
+++ b/vendor/github.com/pion/transport/connctx/connctx.go
@@ -0,0 +1,172 @@
+// Package connctx wraps net.Conn using context.Context.
+package connctx
+
+import (
+ "context"
+ "errors"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// ErrClosing is returned on Write to closed connection.
+var ErrClosing = errors.New("use of closed network connection")
+
+// Reader is an interface for context controlled reader.
+type Reader interface {
+ ReadContext(context.Context, []byte) (int, error)
+}
+
+// Writer is an interface for context controlled writer.
+type Writer interface {
+ WriteContext(context.Context, []byte) (int, error)
+}
+
+// ReadWriter is a composite of ReadWriter.
+type ReadWriter interface {
+ Reader
+ Writer
+}
+
+// ConnCtx is a wrapper of net.Conn using context.Context.
+type ConnCtx interface {
+ Reader
+ Writer
+ io.Closer
+ LocalAddr() net.Addr
+ RemoteAddr() net.Addr
+ Conn() net.Conn
+}
+
+type connCtx struct {
+ nextConn net.Conn
+ closed chan struct{}
+ closeOnce sync.Once
+ readMu sync.Mutex
+ writeMu sync.Mutex
+}
+
+var veryOld = time.Unix(0, 1) //nolint:gochecknoglobals
+
+// New creates a new ConnCtx wrapping given net.Conn.
+func New(conn net.Conn) ConnCtx {
+ c := &connCtx{
+ nextConn: conn,
+ closed: make(chan struct{}),
+ }
+ return c
+}
+
+func (c *connCtx) ReadContext(ctx context.Context, b []byte) (int, error) {
+ c.readMu.Lock()
+ defer c.readMu.Unlock()
+
+ select {
+ case <-c.closed:
+ return 0, io.EOF
+ default:
+ }
+
+ done := make(chan struct{})
+ var wg sync.WaitGroup
+ var errSetDeadline atomic.Value
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ select {
+ case <-ctx.Done():
+ // context canceled
+ if err := c.nextConn.SetReadDeadline(veryOld); err != nil {
+ errSetDeadline.Store(err)
+ return
+ }
+ <-done
+ if err := c.nextConn.SetReadDeadline(time.Time{}); err != nil {
+ errSetDeadline.Store(err)
+ }
+ case <-done:
+ }
+ }()
+
+ n, err := c.nextConn.Read(b)
+
+ close(done)
+ wg.Wait()
+ if e := ctx.Err(); e != nil && n == 0 {
+ err = e
+ }
+ if err2 := errSetDeadline.Load(); err == nil && err2 != nil {
+ err = err2.(error)
+ }
+ return n, err
+}
+
+func (c *connCtx) WriteContext(ctx context.Context, b []byte) (int, error) {
+ c.writeMu.Lock()
+ defer c.writeMu.Unlock()
+
+ select {
+ case <-c.closed:
+ return 0, ErrClosing
+ default:
+ }
+
+ done := make(chan struct{})
+ var wg sync.WaitGroup
+ var errSetDeadline atomic.Value
+ wg.Add(1)
+ go func() {
+ select {
+ case <-ctx.Done():
+ // context canceled
+ if err := c.nextConn.SetWriteDeadline(veryOld); err != nil {
+ errSetDeadline.Store(err)
+ return
+ }
+ <-done
+ if err := c.nextConn.SetWriteDeadline(time.Time{}); err != nil {
+ errSetDeadline.Store(err)
+ }
+ case <-done:
+ }
+ wg.Done()
+ }()
+
+ n, err := c.nextConn.Write(b)
+
+ close(done)
+ wg.Wait()
+ if e := ctx.Err(); e != nil && n == 0 {
+ err = e
+ }
+ if err2 := errSetDeadline.Load(); err == nil && err2 != nil {
+ err = err2.(error)
+ }
+ return n, err
+}
+
+func (c *connCtx) Close() error {
+ err := c.nextConn.Close()
+ c.closeOnce.Do(func() {
+ c.writeMu.Lock()
+ c.readMu.Lock()
+ close(c.closed)
+ c.readMu.Unlock()
+ c.writeMu.Unlock()
+ })
+ return err
+}
+
+func (c *connCtx) LocalAddr() net.Addr {
+ return c.nextConn.LocalAddr()
+}
+
+func (c *connCtx) RemoteAddr() net.Addr {
+ return c.nextConn.RemoteAddr()
+}
+
+func (c *connCtx) Conn() net.Conn {
+ return c.nextConn
+}
diff --git a/vendor/github.com/pion/transport/connctx/pipe.go b/vendor/github.com/pion/transport/connctx/pipe.go
new file mode 100644
index 0000000..e2f0409
--- /dev/null
+++ b/vendor/github.com/pion/transport/connctx/pipe.go
@@ -0,0 +1,11 @@
+package connctx
+
+import (
+ "net"
+)
+
+// Pipe creates piped pair of ConnCtx.
+func Pipe() (ConnCtx, ConnCtx) {
+ ca, cb := net.Pipe()
+ return New(ca), New(cb)
+}
diff --git a/vendor/github.com/pion/transport/deadline/deadline.go b/vendor/github.com/pion/transport/deadline/deadline.go
new file mode 100644
index 0000000..6f97386
--- /dev/null
+++ b/vendor/github.com/pion/transport/deadline/deadline.go
@@ -0,0 +1,110 @@
+// Package deadline provides deadline timer used to implement
+// net.Conn compatible connection
+package deadline
+
+import (
+ "context"
+ "sync"
+ "time"
+)
+
+// Deadline signals updatable deadline timer.
+// Also, it implements context.Context.
+type Deadline struct {
+ exceeded chan struct{}
+ stop chan struct{}
+ stopped chan bool
+ deadline time.Time
+ mu sync.RWMutex
+}
+
+// New creates new deadline timer.
+func New() *Deadline {
+ d := &Deadline{
+ exceeded: make(chan struct{}),
+ stop: make(chan struct{}),
+ stopped: make(chan bool, 1),
+ }
+ d.stopped <- true
+ return d
+}
+
+// Set new deadline. Zero value means no deadline.
+func (d *Deadline) Set(t time.Time) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ d.deadline = t
+
+ close(d.stop)
+
+ select {
+ case <-d.exceeded:
+ d.exceeded = make(chan struct{})
+ default:
+ stopped := <-d.stopped
+ if !stopped {
+ d.exceeded = make(chan struct{})
+ }
+ }
+ d.stop = make(chan struct{})
+ d.stopped = make(chan bool, 1)
+
+ if t.IsZero() {
+ d.stopped <- true
+ return
+ }
+
+ if dur := time.Until(t); dur > 0 {
+ exceeded := d.exceeded
+ stopped := d.stopped
+ go func() {
+ select {
+ case <-time.After(dur):
+ close(exceeded)
+ stopped <- false
+ case <-d.stop:
+ stopped <- true
+ }
+ }()
+ return
+ }
+
+ close(d.exceeded)
+ d.stopped <- false
+}
+
+// Done receives deadline signal.
+func (d *Deadline) Done() <-chan struct{} {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ return d.exceeded
+}
+
+// Err returns context.DeadlineExceeded if the deadline is exceeded.
+// Otherwise, it returns nil.
+func (d *Deadline) Err() error {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ select {
+ case <-d.exceeded:
+ return context.DeadlineExceeded
+ default:
+ return nil
+ }
+}
+
+// Deadline returns current deadline.
+func (d *Deadline) Deadline() (time.Time, bool) {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ if d.deadline.IsZero() {
+ return d.deadline, false
+ }
+ return d.deadline, true
+}
+
+// Value returns nil.
+func (d *Deadline) Value(interface{}) interface{} {
+ return nil
+}
diff --git a/vendor/github.com/pion/transport/packetio/buffer.go b/vendor/github.com/pion/transport/packetio/buffer.go
new file mode 100644
index 0000000..97d86f8
--- /dev/null
+++ b/vendor/github.com/pion/transport/packetio/buffer.go
@@ -0,0 +1,347 @@
+// Package packetio provides packet buffer
+package packetio
+
+import (
+ "errors"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/pion/transport/deadline"
+)
+
+var errPacketTooBig = errors.New("packet too big")
+
+// BufferPacketType allow the Buffer to know which packet protocol is writing.
+type BufferPacketType int
+
+const (
+ // RTPBufferPacket indicates the Buffer that is handling RTP packets
+ RTPBufferPacket BufferPacketType = 1
+ // RTCPBufferPacket indicates the Buffer that is handling RTCP packets
+ RTCPBufferPacket BufferPacketType = 2
+)
+
+// Buffer allows writing packets to an intermediate buffer, which can then be read form.
+// This is verify similar to bytes.Buffer but avoids combining multiple writes into a single read.
+type Buffer struct {
+ mutex sync.Mutex
+
+ // this is a circular buffer. If head <= tail, then the useful
+ // data is in the interval [head, tail[. If tail < head, then
+ // the useful data is the union of [head, len[ and [0, tail[.
+ // In order to avoid ambiguity when head = tail, we always leave
+ // an unused byte in the buffer.
+ data []byte
+ head, tail int
+
+ notify chan struct{}
+ subs bool
+ closed bool
+
+ count int
+ limitCount, limitSize int
+
+ readDeadline *deadline.Deadline
+}
+
+const (
+ minSize = 2048
+ cutoffSize = 128 * 1024
+ maxSize = 4 * 1024 * 1024
+)
+
+// NewBuffer creates a new Buffer.
+func NewBuffer() *Buffer {
+ return &Buffer{
+ notify: make(chan struct{}),
+ readDeadline: deadline.New(),
+ }
+}
+
+// available returns true if the buffer is large enough to fit a packet
+// of the given size, taking overhead into account.
+func (b *Buffer) available(size int) bool {
+ available := b.head - b.tail
+ if available <= 0 {
+ available += len(b.data)
+ }
+ // we interpret head=tail as empty, so always keep a byte free
+ if size+2+1 > available {
+ return false
+ }
+
+ return true
+}
+
+// grow increases the size of the buffer. If it returns nil, then the
+// buffer has been grown. It returns ErrFull if hits a limit.
+func (b *Buffer) grow() error {
+ var newsize int
+ if len(b.data) < cutoffSize {
+ newsize = 2 * len(b.data)
+ } else {
+ newsize = 5 * len(b.data) / 4
+ }
+ if newsize < minSize {
+ newsize = minSize
+ }
+ if (b.limitSize <= 0 || sizeHardlimit) && newsize > maxSize {
+ newsize = maxSize
+ }
+
+ // one byte slack
+ if b.limitSize > 0 && newsize > b.limitSize+1 {
+ newsize = b.limitSize + 1
+ }
+
+ if newsize <= len(b.data) {
+ return ErrFull
+ }
+
+ newdata := make([]byte, newsize)
+
+ var n int
+ if b.head <= b.tail {
+ // data was contiguous
+ n = copy(newdata, b.data[b.head:b.tail])
+ } else {
+ // data was discontiguous
+ n = copy(newdata, b.data[b.head:])
+ n += copy(newdata[n:], b.data[:b.tail])
+ }
+ b.head = 0
+ b.tail = n
+ b.data = newdata
+
+ return nil
+}
+
+// Write appends a copy of the packet data to the buffer.
+// Returns ErrFull if the packet doesn't fit.
+//
+// Note that the packet size is limited to 65536 bytes since v0.11.0 due to the internal data structure.
+func (b *Buffer) Write(packet []byte) (int, error) {
+ if len(packet) >= 0x10000 {
+ return 0, errPacketTooBig
+ }
+
+ b.mutex.Lock()
+
+ if b.closed {
+ b.mutex.Unlock()
+ return 0, io.ErrClosedPipe
+ }
+
+ if (b.limitCount > 0 && b.count >= b.limitCount) ||
+ (b.limitSize > 0 && b.size()+2+len(packet) > b.limitSize) {
+ b.mutex.Unlock()
+ return 0, ErrFull
+ }
+
+ // grow the buffer until the packet fits
+ for !b.available(len(packet)) {
+ err := b.grow()
+ if err != nil {
+ b.mutex.Unlock()
+ return 0, err
+ }
+ }
+
+ var notify chan struct{}
+
+ if b.subs {
+ // readers are waiting. Prepare to notify, but only
+ // actually do it after we release the lock.
+ notify = b.notify
+ b.notify = make(chan struct{})
+ b.subs = false
+ }
+
+ // store the length of the packet
+ b.data[b.tail] = uint8(len(packet) >> 8)
+ b.tail++
+ if b.tail >= len(b.data) {
+ b.tail = 0
+ }
+ b.data[b.tail] = uint8(len(packet))
+ b.tail++
+ if b.tail >= len(b.data) {
+ b.tail = 0
+ }
+
+ // store the packet
+ n := copy(b.data[b.tail:], packet)
+ b.tail += n
+ if b.tail >= len(b.data) {
+ // we reached the end, wrap around
+ m := copy(b.data, packet[n:])
+ b.tail = m
+ }
+ b.count++
+ b.mutex.Unlock()
+
+ if notify != nil {
+ close(notify)
+ }
+
+ return len(packet), nil
+}
+
+// Read populates the given byte slice, returning the number of bytes read.
+// Blocks until data is available or the buffer is closed.
+// Returns io.ErrShortBuffer is the packet is too small to copy the Write.
+// Returns io.EOF if the buffer is closed.
+func (b *Buffer) Read(packet []byte) (n int, err error) {
+ // Return immediately if the deadline is already exceeded.
+ select {
+ case <-b.readDeadline.Done():
+ return 0, &netError{ErrTimeout, true, true}
+ default:
+ }
+
+ for {
+ b.mutex.Lock()
+
+ if b.head != b.tail {
+ // decode the packet size
+ n1 := b.data[b.head]
+ b.head++
+ if b.head >= len(b.data) {
+ b.head = 0
+ }
+ n2 := b.data[b.head]
+ b.head++
+ if b.head >= len(b.data) {
+ b.head = 0
+ }
+ count := int((uint16(n1) << 8) | uint16(n2))
+
+ // determine the number of bytes we'll actually copy
+ copied := count
+ if copied > len(packet) {
+ copied = len(packet)
+ }
+
+ // copy the data
+ if b.head+copied < len(b.data) {
+ copy(packet, b.data[b.head:b.head+copied])
+ } else {
+ k := copy(packet, b.data[b.head:])
+ copy(packet[k:], b.data[:copied-k])
+ }
+
+ // advance head, discarding any data that wasn't copied
+ b.head += count
+ if b.head >= len(b.data) {
+ b.head -= len(b.data)
+ }
+
+ if b.head == b.tail {
+ // the buffer is empty, reset to beginning
+ // in order to improve cache locality.
+ b.head = 0
+ b.tail = 0
+ }
+
+ b.count--
+
+ b.mutex.Unlock()
+
+ if copied < count {
+ return copied, io.ErrShortBuffer
+ }
+ return copied, nil
+ }
+
+ if b.closed {
+ b.mutex.Unlock()
+ return 0, io.EOF
+ }
+
+ notify := b.notify
+ b.subs = true
+ b.mutex.Unlock()
+
+ select {
+ case <-b.readDeadline.Done():
+ return 0, &netError{ErrTimeout, true, true}
+ case <-notify:
+ }
+ }
+}
+
+// Close the buffer, unblocking any pending reads.
+// Data in the buffer can still be read, Read will return io.EOF only when empty.
+func (b *Buffer) Close() (err error) {
+ b.mutex.Lock()
+
+ if b.closed {
+ b.mutex.Unlock()
+ return nil
+ }
+
+ notify := b.notify
+ b.closed = true
+
+ b.mutex.Unlock()
+
+ close(notify)
+
+ return nil
+}
+
+// Count returns the number of packets in the buffer.
+func (b *Buffer) Count() int {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+ return b.count
+}
+
+// SetLimitCount controls the maximum number of packets that can be buffered.
+// Causes Write to return ErrFull when this limit is reached.
+// A zero value will disable this limit.
+func (b *Buffer) SetLimitCount(limit int) {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ b.limitCount = limit
+}
+
+// Size returns the total byte size of packets in the buffer, including
+// a small amount of administrative overhead.
+func (b *Buffer) Size() int {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ return b.size()
+}
+
+func (b *Buffer) size() int {
+ size := b.tail - b.head
+ if size < 0 {
+ size += len(b.data)
+ }
+ return size
+}
+
+// SetLimitSize controls the maximum number of bytes that can be buffered.
+// Causes Write to return ErrFull when this limit is reached.
+// A zero value means 4MB since v0.11.0.
+//
+// User can set packetioSizeHardlimit build tag to enable 4MB hardlimit.
+// When packetioSizeHardlimit build tag is set, SetLimitSize exceeding
+// the hardlimit will be silently discarded.
+func (b *Buffer) SetLimitSize(limit int) {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ b.limitSize = limit
+}
+
+// SetReadDeadline sets the deadline for the Read operation.
+// Setting to zero means no deadline.
+func (b *Buffer) SetReadDeadline(t time.Time) error {
+ b.readDeadline.Set(t)
+ return nil
+}
diff --git a/vendor/github.com/pion/transport/packetio/errors.go b/vendor/github.com/pion/transport/packetio/errors.go
new file mode 100644
index 0000000..06f1b9d
--- /dev/null
+++ b/vendor/github.com/pion/transport/packetio/errors.go
@@ -0,0 +1,27 @@
+package packetio
+
+import (
+ "errors"
+)
+
+// netError implements net.Error
+type netError struct {
+ error
+ timeout, temporary bool
+}
+
+func (e *netError) Timeout() bool {
+ return e.timeout
+}
+
+func (e *netError) Temporary() bool {
+ return e.temporary
+}
+
+var (
+ // ErrFull is returned when the buffer has hit the configured limits.
+ ErrFull = errors.New("packetio.Buffer is full, discarding write")
+
+ // ErrTimeout is returned when a deadline has expired
+ ErrTimeout = errors.New("i/o timeout")
+)
diff --git a/vendor/github.com/pion/transport/packetio/hardlimit.go b/vendor/github.com/pion/transport/packetio/hardlimit.go
new file mode 100644
index 0000000..5ddacc7
--- /dev/null
+++ b/vendor/github.com/pion/transport/packetio/hardlimit.go
@@ -0,0 +1,5 @@
+// +build packetioSizeHardlimit
+
+package packetio
+
+const sizeHardlimit = true
diff --git a/vendor/github.com/pion/transport/packetio/no_hardlimit.go b/vendor/github.com/pion/transport/packetio/no_hardlimit.go
new file mode 100644
index 0000000..55ea308
--- /dev/null
+++ b/vendor/github.com/pion/transport/packetio/no_hardlimit.go
@@ -0,0 +1,5 @@
+// +build !packetioSizeHardlimit
+
+package packetio
+
+const sizeHardlimit = false
diff --git a/vendor/github.com/pion/transport/replaydetector/fixedbig.go b/vendor/github.com/pion/transport/replaydetector/fixedbig.go
new file mode 100644
index 0000000..a571a1a
--- /dev/null
+++ b/vendor/github.com/pion/transport/replaydetector/fixedbig.go
@@ -0,0 +1,78 @@
+package replaydetector
+
+import (
+ "fmt"
+)
+
+// fixedBigInt is the fix-sized multi-word integer.
+type fixedBigInt struct {
+ bits []uint64
+ n uint
+ msbMask uint64
+}
+
+// newFixedBigInt creates a new fix-sized multi-word int.
+func newFixedBigInt(n uint) *fixedBigInt {
+ chunkSize := (n + 63) / 64
+ if chunkSize == 0 {
+ chunkSize = 1
+ }
+ return &fixedBigInt{
+ bits: make([]uint64, chunkSize),
+ n: n,
+ msbMask: (1 << (64 - n%64)) - 1,
+ }
+}
+
+// Lsh is the left shift operation.
+func (s *fixedBigInt) Lsh(n uint) {
+ if n == 0 {
+ return
+ }
+ nChunk := int(n / 64)
+ nN := n % 64
+
+ for i := len(s.bits) - 1; i >= 0; i-- {
+ var carry uint64
+ if i-nChunk >= 0 {
+ carry = s.bits[i-nChunk] << nN
+ if i-nChunk-1 >= 0 {
+ carry |= s.bits[i-nChunk-1] >> (64 - nN)
+ }
+ }
+ s.bits[i] = (s.bits[i] << n) | carry
+ }
+ s.bits[len(s.bits)-1] &= s.msbMask
+}
+
+// Bit returns i-th bit of the fixedBigInt.
+func (s *fixedBigInt) Bit(i uint) uint {
+ if i >= s.n {
+ return 0
+ }
+ chunk := i / 64
+ pos := i % 64
+ if s.bits[chunk]&(1<<pos) != 0 {
+ return 1
+ }
+ return 0
+}
+
+// SetBit sets i-th bit to 1.
+func (s *fixedBigInt) SetBit(i uint) {
+ if i >= s.n {
+ return
+ }
+ chunk := i / 64
+ pos := i % 64
+ s.bits[chunk] |= 1 << pos
+}
+
+// String returns string representation of fixedBigInt.
+func (s *fixedBigInt) String() string {
+ var out string
+ for i := len(s.bits) - 1; i >= 0; i-- {
+ out += fmt.Sprintf("%016X", s.bits[i])
+ }
+ return out
+}
diff --git a/vendor/github.com/pion/transport/replaydetector/replaydetector.go b/vendor/github.com/pion/transport/replaydetector/replaydetector.go
new file mode 100644
index 0000000..d942002
--- /dev/null
+++ b/vendor/github.com/pion/transport/replaydetector/replaydetector.go
@@ -0,0 +1,116 @@
+// Package replaydetector provides packet replay detection algorithm.
+package replaydetector
+
+// ReplayDetector is the interface of sequence replay detector.
+type ReplayDetector interface {
+ // Check returns true if given sequence number is not replayed.
+ // Call accept() to mark the packet is received properly.
+ Check(seq uint64) (accept func(), ok bool)
+}
+
+type slidingWindowDetector struct {
+ latestSeq uint64
+ maxSeq uint64
+ windowSize uint
+ mask *fixedBigInt
+}
+
+// New creates ReplayDetector.
+// Created ReplayDetector doesn't allow wrapping.
+// It can handle monotonically increasing sequence number up to
+// full 64bit number. It is suitable for DTLS replay protection.
+func New(windowSize uint, maxSeq uint64) ReplayDetector {
+ return &slidingWindowDetector{
+ maxSeq: maxSeq,
+ windowSize: windowSize,
+ mask: newFixedBigInt(windowSize),
+ }
+}
+
+func (d *slidingWindowDetector) Check(seq uint64) (accept func(), ok bool) {
+ if seq > d.maxSeq {
+ // Exceeded upper limit.
+ return func() {}, false
+ }
+
+ if seq <= d.latestSeq {
+ if d.latestSeq >= uint64(d.windowSize)+seq {
+ return func() {}, false
+ }
+ if d.mask.Bit(uint(d.latestSeq-seq)) != 0 {
+ // The sequence number is duplicated.
+ return func() {}, false
+ }
+ }
+
+ return func() {
+ if seq > d.latestSeq {
+ // Update the head of the window.
+ d.mask.Lsh(uint(seq - d.latestSeq))
+ d.latestSeq = seq
+ }
+ diff := (d.latestSeq - seq) % d.maxSeq
+ d.mask.SetBit(uint(diff))
+ }, true
+}
+
+// WithWrap creates ReplayDetector allowing sequence wrapping.
+// This is suitable for short bitwidth counter like SRTP and SRTCP.
+func WithWrap(windowSize uint, maxSeq uint64) ReplayDetector {
+ return &wrappedSlidingWindowDetector{
+ maxSeq: maxSeq,
+ windowSize: windowSize,
+ mask: newFixedBigInt(windowSize),
+ }
+}
+
+type wrappedSlidingWindowDetector struct {
+ latestSeq uint64
+ maxSeq uint64
+ windowSize uint
+ mask *fixedBigInt
+ init bool
+}
+
+func (d *wrappedSlidingWindowDetector) Check(seq uint64) (accept func(), ok bool) {
+ if seq > d.maxSeq {
+ // Exceeded upper limit.
+ return func() {}, false
+ }
+ if !d.init {
+ if seq != 0 {
+ d.latestSeq = seq - 1
+ } else {
+ d.latestSeq = d.maxSeq
+ }
+ d.init = true
+ }
+
+ diff := int64(d.latestSeq) - int64(seq)
+ // Wrap the number.
+ if diff > int64(d.maxSeq)/2 {
+ diff -= int64(d.maxSeq + 1)
+ } else if diff <= -int64(d.maxSeq)/2 {
+ diff += int64(d.maxSeq + 1)
+ }
+
+ if diff >= int64(d.windowSize) {
+ // Too old.
+ return func() {}, false
+ }
+ if diff >= 0 {
+ if d.mask.Bit(uint(diff)) != 0 {
+ // The sequence number is duplicated.
+ return func() {}, false
+ }
+ }
+
+ return func() {
+ if diff < 0 {
+ // Update the head of the window.
+ d.mask.Lsh(uint(-diff))
+ d.latestSeq = seq
+ }
+ d.mask.SetBit(uint(d.latestSeq - seq))
+ }, true
+}
diff --git a/vendor/github.com/pion/transport/vnet/.gitignore b/vendor/github.com/pion/transport/vnet/.gitignore
new file mode 100644
index 0000000..d39fb86
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/.gitignore
@@ -0,0 +1 @@
+*.sw[poe]
diff --git a/vendor/github.com/pion/transport/vnet/README.md b/vendor/github.com/pion/transport/vnet/README.md
new file mode 100644
index 0000000..b502f9f
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/README.md
@@ -0,0 +1,239 @@
+# vnet
+A virtual network layer for pion.
+
+## Overview
+
+### Goals
+* To make NAT traversal tests easy.
+* To emulate packet impairment at application level for testing.
+* To monitor packets at specified arbitrary interfaces.
+
+### Features
+* Configurable virtual LAN and WAN
+* Virtually hosted ICE servers
+
+### Virtual network components
+
+#### Top View
+```
+ ......................................
+ : Virtual Network (vnet) :
+ : :
+ +-------+ * 1 +----+ +--------+ :
+ | :App |------------>|:Net|--o<-----|:Router | :
+ +-------+ +----+ | | :
+ +-----------+ * 1 +----+ | | :
+ |:STUNServer|-------->|:Net|--o<-----| | :
+ +-----------+ +----+ | | :
+ +-----------+ * 1 +----+ | | :
+ |:TURNServer|-------->|:Net|--o<-----| | :
+ +-----------+ +----+ [1] | | :
+ : 1 | | 1 <<has>> :
+ : +---<>| |<>----+ [2] :
+ : | +--------+ | :
+ To form | *| v 0..1 :
+ a subnet tree | o [3] +-----+ :
+ : | ^ |:NAT | :
+ : | | +-----+ :
+ : +-------+ :
+ ......................................
+ Note:
+ o: NIC (Netork Interface Controller)
+ [1]: Net implments NIC interface.
+ [2]: Root router has no NAT. All child routers have a NAT always.
+ [3]: Router implements NIC interface for accesses from the
+ parent router.
+```
+
+#### Net
+Net provides 3 interfaces:
+* Configuration API (direct)
+* Network API via Net (equivalent to net.Xxx())
+* Router access via NIC interface
+```
+ (Pion module/app, ICE servers, etc.)
+ +-----------+
+ | :App |
+ +-----------+
+ * |
+ | <<uses>>
+ 1 v
+ +---------+ 1 * +-----------+ 1 * +-----------+ 1 * +------+
+ ..| :Router |----+------>o--| :Net |<>------|:Interface |<>------|:Addr |
+ +---------+ | NIC +-----------+ +-----------+ +------+
+ | <<interface>> (vnet.Interface) (net.Addr)
+ |
+ | * +-----------+ 1 * +-----------+ 1 * +------+
+ +------>o--| :Router |<>------|:Interface |<>------|:Addr |
+ NIC +-----------+ +-----------+ +------+
+ <<interface>> (vnet.Interface) (net.Addr)
+```
+
+> The instance of `Net` will be the one passed around the project.
+> Net class has public methods for configuration and for application use.
+
+
+## Implementation
+
+### Design Policy
+* Each pion package should have config object which has `Net` (of type vnet.Net) property. (just like how
+ we distribute `LoggerFactory` throughout the pion project.
+* DNS => a simple dictionary (global)?
+* Each Net has routing capability (a goroutine)
+* Use interface provided net package as much as possible
+* Routers are connected in a tree structure (no loop is allowed)
+ - To simplify routing
+ - Easy to control / monitor (stats, etc)
+* Root router has no NAT (== Internet / WAN)
+* Non-root router has a NAT always
+* When a Net is instantiated, it will automatically add `lo0` and `eth0` interface, and `lo0` will
+have one IP address, 127.0.0.1. (this is not used in pion/ice, however)
+* When a Net is added to a router, the router automatically assign an IP address for `eth0`
+interface.
+ - For simplicity
+* User data won't fragment, but optionally drop chunk larger than MTU
+* IPv6 is not supported
+
+### Basic steps for setting up virtual network
+1. Create a root router (WAN)
+1. Create child routers and add to its parent (forms a tree, don't create a loop!)
+1. Add instances of Net to each routers
+1. Call Stop(), or Stop(), on the top router, which propages all other routers
+
+#### Example: WAN with one endpoint (vnet)
+```go
+import (
+ "net"
+
+ "github.com/pion/transport/vnet"
+ "github.com/pion/logging"
+)
+
+// Create WAN (a root router).
+wan, err := vnet.NewRouter(&RouterConfig{
+ CIDR: "0.0.0.0/0",
+ LoggerFactory: logging.NewDefaultLoggerFactory(),
+})
+
+// Create a network.
+// You can specify a static IP for the instance of Net to use. If not specified,
+// router will assign an IP address that is contained in the router's CIDR.
+nw := vnet.NewNet(&vnet.NetConfig{
+ StaticIP: "27.1.2.3",
+})
+
+// Add the network to the router.
+// The router will assign an IP address to `nw`.
+if err = wan.AddNet(nw); err != nil {
+ // handle error
+}
+
+// Start router.
+// This will start internal goroutine to route packets.
+// If you set child routers (using AddRouter), the call on the root router
+// will start the rest of routers for you.
+if err = wan.Start(); err != nil {
+ // handle error
+}
+
+//
+// Your application runs here using `nw`.
+//
+
+// Stop the router.
+// This will stop all internal goroutines in the router tree.
+// (No need to call Stop() on child routers)
+if err = wan.Stop(); err != nil {
+ // handle error
+}
+```
+
+#### Example of how to pass around the instance of vnet.Net
+The instance of vnet.Net wraps a subset of net package to enable operations
+on the virtual network. Your project must be able to pass the instance to
+all your routines that do network operation with net package. A typical way
+is to use a config param to create your instances with the virtual network
+instance (`nw` in the above example) like this:
+
+```go
+type AgentConfig struct {
+ :
+ Net: *vnet.Net,
+}
+
+type Agent struct {
+ :
+ net: *vnet.Net,
+}
+
+func NetAgent(config *AgentConfig) *Agent {
+ if config.Net == nil {
+ config.Net = vnet.NewNet(nil) // defaults to native operation
+ }
+
+ return &Agent {
+ :
+ net: config.Net,
+ }
+}
+```
+
+```go
+// a.net is the instance of vnet.Net class
+func (a *Agent) listenUDP(...) error {
+ conn, err := a.net.ListenPacket(udpString, ...)
+ if err != nil {
+ return nil, err
+ }
+ :
+}
+```
+
+
+### Compatibility and Support Status
+
+|`net`<br>(built-in)|`vnet`|Note|
+|---|---|---|
+|net.Interfaces()|a.net.Interfaces()||
+|net.InterfaceByName()|a.net.InterfaceByName()||
+|net.ResolveUDPAddr()|a.net.ResolveUDPAddr()||
+|net.ListenPacket()|a.net.ListenPacket()||
+|net.ListenUDP()|a.net.ListenUDP()|(ListenPacket() is recommended)|
+|net.Listen()|a.net.Listen()|(TODO)|
+|net.ListenTCP()|(not supported)|(Listen() would be recommended)|
+|net.Dial()|a.net.Dial()||
+|net.DialUDP()|a.net.DialUDP()||
+|net.DialTCP()|(not supported)||
+|net.Interface|vnet.Interface||
+|net.PacketConn|(use it as-is)||
+|net.UDPConn|vnet.UDPConn|Use vnet.UDPPacketConn in your code|
+|net.TCPConn|vnet.TCPConn|(TODO)|Use net.Conn in your code|
+|net.Dialer|vnet.Dialer|Use a.net.CreateDialer() to create it.<br>The use of vnet.Dialer is currently experimental.|
+
+> `a.net` is an instance of Net class, and types are defined under the package name `vnet`
+
+> Most of other `interface` types in net package can be used as is.
+
+> Please post a github issue when other types/methods need to be added to vnet/vnet.Net.
+
+## TODO / Next Step
+* Implement TCP (TCPConn, Listen)
+* Support of IPv6
+* Write a bunch of examples for building virtual networks.
+* Add network impairment features (on Router)
+ - Introduce lantecy / jitter
+ - Packet filtering handler (allow selectively drop packets, etc.)
+* Add statistics data retrieval
+ - Total number of packets forward by each router
+ - Total number of packet loss
+ - Total number of connection failure (TCP)
+
+## References
+* [Comparing Simulated Packet Loss and RealWorld Network Congestion](https://www.riverbed.com/document/fpo/WhitePaper-Riverbed-SimulatedPacketLoss.pdf)
+
+### Code experiments
+* [CIDR and IPMask](https://play.golang.org/p/B7OBhkZqjmj)
+* [Test with net.IP](https://play.golang.org/p/AgXd23wKY4W)
+* [ListenPacket](https://play.golang.org/p/d4vasbnRimQ)
+* [isDottedIP()](https://play.golang.org/p/t4aZ47TgJfO)
+* [SplitHostPort](https://play.golang.org/p/JtvurlcMbhn)
diff --git a/vendor/github.com/pion/transport/vnet/chunk.go b/vendor/github.com/pion/transport/vnet/chunk.go
new file mode 100644
index 0000000..7a87a2f
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/chunk.go
@@ -0,0 +1,283 @@
+package vnet
+
+import (
+ "fmt"
+ "net"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "time"
+)
+
+type tcpFlag uint8
+
+const (
+ tcpFIN tcpFlag = 0x01
+ tcpSYN tcpFlag = 0x02
+ tcpRST tcpFlag = 0x04
+ tcpPSH tcpFlag = 0x08
+ tcpACK tcpFlag = 0x10
+)
+
+func (f tcpFlag) String() string {
+ var sa []string
+ if f&tcpFIN != 0 {
+ sa = append(sa, "FIN")
+ }
+ if f&tcpSYN != 0 {
+ sa = append(sa, "SYN")
+ }
+ if f&tcpRST != 0 {
+ sa = append(sa, "RST")
+ }
+ if f&tcpPSH != 0 {
+ sa = append(sa, "PSH")
+ }
+ if f&tcpACK != 0 {
+ sa = append(sa, "ACK")
+ }
+
+ return strings.Join(sa, "-")
+}
+
+// Generate a base36-encoded unique tag
+// See: https://play.golang.org/p/0ZaAID1q-HN
+var assignChunkTag = func() func() string { //nolint:gochecknoglobals
+ var tagCtr uint64
+
+ return func() string {
+ n := atomic.AddUint64(&tagCtr, 1)
+ return strconv.FormatUint(n, 36)
+ }
+}()
+
+// Chunk represents a packet passed around in the vnet
+type Chunk interface {
+ setTimestamp() time.Time // used by router
+ getTimestamp() time.Time // used by router
+ getSourceIP() net.IP // used by router
+ getDestinationIP() net.IP // used by router
+ setSourceAddr(address string) error // used by nat
+ setDestinationAddr(address string) error // used by nat
+
+ SourceAddr() net.Addr
+ DestinationAddr() net.Addr
+ UserData() []byte
+ Tag() string
+ Clone() Chunk
+ Network() string // returns "udp" or "tcp"
+ String() string
+}
+
+type chunkIP struct {
+ timestamp time.Time
+ sourceIP net.IP
+ destinationIP net.IP
+ tag string
+}
+
+func (c *chunkIP) setTimestamp() time.Time {
+ c.timestamp = time.Now()
+ return c.timestamp
+}
+
+func (c *chunkIP) getTimestamp() time.Time {
+ return c.timestamp
+}
+
+func (c *chunkIP) getDestinationIP() net.IP {
+ return c.destinationIP
+}
+
+func (c *chunkIP) getSourceIP() net.IP {
+ return c.sourceIP
+}
+
+func (c *chunkIP) Tag() string {
+ return c.tag
+}
+
+type chunkUDP struct {
+ chunkIP
+ sourcePort int
+ destinationPort int
+ userData []byte
+}
+
+func newChunkUDP(srcAddr, dstAddr *net.UDPAddr) *chunkUDP {
+ return &chunkUDP{
+ chunkIP: chunkIP{
+ sourceIP: srcAddr.IP,
+ destinationIP: dstAddr.IP,
+ tag: assignChunkTag(),
+ },
+ sourcePort: srcAddr.Port,
+ destinationPort: dstAddr.Port,
+ }
+}
+
+func (c *chunkUDP) SourceAddr() net.Addr {
+ return &net.UDPAddr{
+ IP: c.sourceIP,
+ Port: c.sourcePort,
+ }
+}
+
+func (c *chunkUDP) DestinationAddr() net.Addr {
+ return &net.UDPAddr{
+ IP: c.destinationIP,
+ Port: c.destinationPort,
+ }
+}
+
+func (c *chunkUDP) UserData() []byte {
+ return c.userData
+}
+
+func (c *chunkUDP) Clone() Chunk {
+ var userData []byte
+ if c.userData != nil {
+ userData = make([]byte, len(c.userData))
+ copy(userData, c.userData)
+ }
+
+ return &chunkUDP{
+ chunkIP: chunkIP{
+ timestamp: c.timestamp,
+ sourceIP: c.sourceIP,
+ destinationIP: c.destinationIP,
+ tag: c.tag,
+ },
+ sourcePort: c.sourcePort,
+ destinationPort: c.destinationPort,
+ userData: userData,
+ }
+}
+
+func (c *chunkUDP) Network() string {
+ return udpString
+}
+
+func (c *chunkUDP) String() string {
+ src := c.SourceAddr()
+ dst := c.DestinationAddr()
+ return fmt.Sprintf("%s chunk %s %s => %s",
+ src.Network(),
+ c.tag,
+ src.String(),
+ dst.String(),
+ )
+}
+
+func (c *chunkUDP) setSourceAddr(address string) error {
+ addr, err := net.ResolveUDPAddr(udpString, address)
+ if err != nil {
+ return err
+ }
+ c.sourceIP = addr.IP
+ c.sourcePort = addr.Port
+ return nil
+}
+
+func (c *chunkUDP) setDestinationAddr(address string) error {
+ addr, err := net.ResolveUDPAddr(udpString, address)
+ if err != nil {
+ return err
+ }
+ c.destinationIP = addr.IP
+ c.destinationPort = addr.Port
+ return nil
+}
+
+type chunkTCP struct {
+ chunkIP
+ sourcePort int
+ destinationPort int
+ flags tcpFlag // control bits
+ userData []byte // only with PSH flag
+ // seq uint32 // always starts with 0
+ // ack uint32 // always starts with 0
+}
+
+func newChunkTCP(srcAddr, dstAddr *net.TCPAddr, flags tcpFlag) *chunkTCP {
+ return &chunkTCP{
+ chunkIP: chunkIP{
+ sourceIP: srcAddr.IP,
+ destinationIP: dstAddr.IP,
+ tag: assignChunkTag(),
+ },
+ sourcePort: srcAddr.Port,
+ destinationPort: dstAddr.Port,
+ flags: flags,
+ }
+}
+
+func (c *chunkTCP) SourceAddr() net.Addr {
+ return &net.TCPAddr{
+ IP: c.sourceIP,
+ Port: c.sourcePort,
+ }
+}
+
+func (c *chunkTCP) DestinationAddr() net.Addr {
+ return &net.TCPAddr{
+ IP: c.destinationIP,
+ Port: c.destinationPort,
+ }
+}
+
+func (c *chunkTCP) UserData() []byte {
+ return c.userData
+}
+
+func (c *chunkTCP) Clone() Chunk {
+ userData := make([]byte, len(c.userData))
+ copy(userData, c.userData)
+
+ return &chunkTCP{
+ chunkIP: chunkIP{
+ timestamp: c.timestamp,
+ sourceIP: c.sourceIP,
+ destinationIP: c.destinationIP,
+ },
+ sourcePort: c.sourcePort,
+ destinationPort: c.destinationPort,
+ userData: userData,
+ }
+}
+
+func (c *chunkTCP) Network() string {
+ return "tcp"
+}
+
+func (c *chunkTCP) String() string {
+ src := c.SourceAddr()
+ dst := c.DestinationAddr()
+ return fmt.Sprintf("%s %s chunk %s %s => %s",
+ src.Network(),
+ c.flags.String(),
+ c.tag,
+ src.String(),
+ dst.String(),
+ )
+}
+
+func (c *chunkTCP) setSourceAddr(address string) error {
+ addr, err := net.ResolveTCPAddr("tcp", address)
+ if err != nil {
+ return err
+ }
+ c.sourceIP = addr.IP
+ c.sourcePort = addr.Port
+ return nil
+}
+
+func (c *chunkTCP) setDestinationAddr(address string) error {
+ addr, err := net.ResolveTCPAddr("tcp", address)
+ if err != nil {
+ return err
+ }
+ c.destinationIP = addr.IP
+ c.destinationPort = addr.Port
+ return nil
+}
diff --git a/vendor/github.com/pion/transport/vnet/chunk_queue.go b/vendor/github.com/pion/transport/vnet/chunk_queue.go
new file mode 100644
index 0000000..7b24462
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/chunk_queue.go
@@ -0,0 +1,52 @@
+package vnet
+
+import (
+ "sync"
+)
+
+type chunkQueue struct {
+ chunks []Chunk
+ maxSize int // 0 or negative value: unlimited
+ mutex sync.RWMutex
+}
+
+func newChunkQueue(maxSize int) *chunkQueue {
+ return &chunkQueue{maxSize: maxSize}
+}
+
+func (q *chunkQueue) push(c Chunk) bool {
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+
+ if q.maxSize > 0 && len(q.chunks) >= q.maxSize {
+ return false // dropped
+ }
+
+ q.chunks = append(q.chunks, c)
+ return true
+}
+
+func (q *chunkQueue) pop() (Chunk, bool) {
+ q.mutex.Lock()
+ defer q.mutex.Unlock()
+
+ if len(q.chunks) == 0 {
+ return nil, false
+ }
+
+ c := q.chunks[0]
+ q.chunks = q.chunks[1:]
+
+ return c, true
+}
+
+func (q *chunkQueue) peek() Chunk {
+ q.mutex.RLock()
+ defer q.mutex.RUnlock()
+
+ if len(q.chunks) == 0 {
+ return nil
+ }
+
+ return q.chunks[0]
+}
diff --git a/vendor/github.com/pion/transport/vnet/conn.go b/vendor/github.com/pion/transport/vnet/conn.go
new file mode 100644
index 0000000..f4b8b92
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/conn.go
@@ -0,0 +1,246 @@
+package vnet
+
+import (
+ "errors"
+ "io"
+ "math"
+ "net"
+ "sync"
+ "time"
+)
+
+const (
+ maxReadQueueSize = 1024
+)
+
+var (
+ errObsCannotBeNil = errors.New("obs cannot be nil")
+ errUseClosedNetworkConn = errors.New("use of closed network connection")
+ errAddrNotUDPAddr = errors.New("addr is not a net.UDPAddr")
+ errLocAddr = errors.New("something went wrong with locAddr")
+ errAlreadyClosed = errors.New("already closed")
+ errNoRemAddr = errors.New("no remAddr defined")
+)
+
+// UDPPacketConn is packet-oriented connection for UDP.
+type UDPPacketConn interface {
+ net.PacketConn
+ Read(b []byte) (int, error)
+ RemoteAddr() net.Addr
+ Write(b []byte) (int, error)
+}
+
+// vNet implements this
+type connObserver interface {
+ write(c Chunk) error
+ onClosed(addr net.Addr)
+ determineSourceIP(locIP, dstIP net.IP) net.IP
+}
+
+// UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections.
+// comatible with net.PacketConn and net.Conn
+type UDPConn struct {
+ locAddr *net.UDPAddr // read-only
+ remAddr *net.UDPAddr // read-only
+ obs connObserver // read-only
+ readCh chan Chunk // thread-safe
+ closed bool // requires mutex
+ mu sync.Mutex // to mutex closed flag
+ readTimer *time.Timer // thread-safe
+}
+
+func newUDPConn(locAddr, remAddr *net.UDPAddr, obs connObserver) (*UDPConn, error) {
+ if obs == nil {
+ return nil, errObsCannotBeNil
+ }
+
+ return &UDPConn{
+ locAddr: locAddr,
+ remAddr: remAddr,
+ obs: obs,
+ readCh: make(chan Chunk, maxReadQueueSize),
+ readTimer: time.NewTimer(time.Duration(math.MaxInt64)),
+ }, nil
+}
+
+// ReadFrom reads a packet from the connection,
+// copying the payload into p. It returns the number of
+// bytes copied into p and the return address that
+// was on the packet.
+// It returns the number of bytes read (0 <= n <= len(p))
+// and any error encountered. Callers should always process
+// the n > 0 bytes returned before considering the error err.
+// ReadFrom can be made to time out and return
+// an Error with Timeout() == true after a fixed time limit;
+// see SetDeadline and SetReadDeadline.
+func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
+loop:
+ for {
+ select {
+ case chunk, ok := <-c.readCh:
+ if !ok {
+ break loop
+ }
+ var err error
+ n := copy(p, chunk.UserData())
+ addr := chunk.SourceAddr()
+ if n < len(chunk.UserData()) {
+ err = io.ErrShortBuffer
+ }
+
+ if c.remAddr != nil {
+ if addr.String() != c.remAddr.String() {
+ break // discard (shouldn't happen)
+ }
+ }
+ return n, addr, err
+
+ case <-c.readTimer.C:
+ return 0, nil, &net.OpError{
+ Op: "read",
+ Net: c.locAddr.Network(),
+ Addr: c.locAddr,
+ Err: newTimeoutError("i/o timeout"),
+ }
+ }
+ }
+
+ return 0, nil, &net.OpError{
+ Op: "read",
+ Net: c.locAddr.Network(),
+ Addr: c.locAddr,
+ Err: errUseClosedNetworkConn,
+ }
+}
+
+// WriteTo writes a packet with payload p to addr.
+// WriteTo can be made to time out and return
+// an Error with Timeout() == true after a fixed time limit;
+// see SetDeadline and SetWriteDeadline.
+// On packet-oriented connections, write timeouts are rare.
+func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
+ dstAddr, ok := addr.(*net.UDPAddr)
+ if !ok {
+ return 0, errAddrNotUDPAddr
+ }
+
+ srcIP := c.obs.determineSourceIP(c.locAddr.IP, dstAddr.IP)
+ if srcIP == nil {
+ return 0, errLocAddr
+ }
+ srcAddr := &net.UDPAddr{
+ IP: srcIP,
+ Port: c.locAddr.Port,
+ }
+
+ chunk := newChunkUDP(srcAddr, dstAddr)
+ chunk.userData = make([]byte, len(p))
+ copy(chunk.userData, p)
+ if err := c.obs.write(chunk); err != nil {
+ return 0, err
+ }
+ return len(p), nil
+}
+
+// Close closes the connection.
+// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
+func (c *UDPConn) Close() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.closed {
+ return errAlreadyClosed
+ }
+ c.closed = true
+ close(c.readCh)
+
+ c.obs.onClosed(c.locAddr)
+ return nil
+}
+
+// LocalAddr returns the local network address.
+func (c *UDPConn) LocalAddr() net.Addr {
+ return c.locAddr
+}
+
+// SetDeadline sets the read and write deadlines associated
+// with the connection. It is equivalent to calling both
+// SetReadDeadline and SetWriteDeadline.
+//
+// A deadline is an absolute time after which I/O operations
+// fail with a timeout (see type Error) instead of
+// blocking. The deadline applies to all future and pending
+// I/O, not just the immediately following call to ReadFrom or
+// WriteTo. After a deadline has been exceeded, the connection
+// can be refreshed by setting a deadline in the future.
+//
+// An idle timeout can be implemented by repeatedly extending
+// the deadline after successful ReadFrom or WriteTo calls.
+//
+// A zero value for t means I/O operations will not time out.
+func (c *UDPConn) SetDeadline(t time.Time) error {
+ return c.SetReadDeadline(t)
+}
+
+// SetReadDeadline sets the deadline for future ReadFrom calls
+// and any currently-blocked ReadFrom call.
+// A zero value for t means ReadFrom will not time out.
+func (c *UDPConn) SetReadDeadline(t time.Time) error {
+ var d time.Duration
+ var noDeadline time.Time
+ if t == noDeadline {
+ d = time.Duration(math.MaxInt64)
+ } else {
+ d = time.Until(t)
+ }
+ c.readTimer.Reset(d)
+ return nil
+}
+
+// SetWriteDeadline sets the deadline for future WriteTo calls
+// and any currently-blocked WriteTo call.
+// Even if write times out, it may return n > 0, indicating that
+// some of the data was successfully written.
+// A zero value for t means WriteTo will not time out.
+func (c *UDPConn) SetWriteDeadline(t time.Time) error {
+ // Write never blocks.
+ return nil
+}
+
+// Read reads data from the connection.
+// Read can be made to time out and return an Error with Timeout() == true
+// after a fixed time limit; see SetDeadline and SetReadDeadline.
+func (c *UDPConn) Read(b []byte) (int, error) {
+ n, _, err := c.ReadFrom(b)
+ return n, err
+}
+
+// RemoteAddr returns the remote network address.
+func (c *UDPConn) RemoteAddr() net.Addr {
+ return c.remAddr
+}
+
+// Write writes data to the connection.
+// Write can be made to time out and return an Error with Timeout() == true
+// after a fixed time limit; see SetDeadline and SetWriteDeadline.
+func (c *UDPConn) Write(b []byte) (int, error) {
+ if c.remAddr == nil {
+ return 0, errNoRemAddr
+ }
+
+ return c.WriteTo(b, c.remAddr)
+}
+
+func (c *UDPConn) onInboundChunk(chunk Chunk) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.closed {
+ return
+ }
+
+ select {
+ case c.readCh <- chunk:
+ default:
+ }
+}
diff --git a/vendor/github.com/pion/transport/vnet/conn_map.go b/vendor/github.com/pion/transport/vnet/conn_map.go
new file mode 100644
index 0000000..d52818d
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/conn_map.go
@@ -0,0 +1,136 @@
+package vnet
+
+import (
+ "errors"
+ "net"
+ "sync"
+)
+
+var (
+ errAddressAlreadyInUse = errors.New("address already in use")
+ errNoSuchUDPConn = errors.New("no such UDPConn")
+ errCannotRemoveUnspecifiedIP = errors.New("cannot remove unspecified IP by the specified IP")
+)
+
+type udpConnMap struct {
+ portMap map[int][]*UDPConn
+ mutex sync.RWMutex
+}
+
+func newUDPConnMap() *udpConnMap {
+ return &udpConnMap{
+ portMap: map[int][]*UDPConn{},
+ }
+}
+
+func (m *udpConnMap) insert(conn *UDPConn) error {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ udpAddr := conn.LocalAddr().(*net.UDPAddr)
+
+ // check if the port has a listener
+ conns, ok := m.portMap[udpAddr.Port]
+ if ok {
+ if udpAddr.IP.IsUnspecified() {
+ return errAddressAlreadyInUse
+ }
+
+ for _, conn := range conns {
+ laddr := conn.LocalAddr().(*net.UDPAddr)
+ if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) {
+ return errAddressAlreadyInUse
+ }
+ }
+
+ conns = append(conns, conn)
+ } else {
+ conns = []*UDPConn{conn}
+ }
+
+ m.portMap[udpAddr.Port] = conns
+ return nil
+}
+
+func (m *udpConnMap) find(addr net.Addr) (*UDPConn, bool) {
+ m.mutex.Lock() // could be RLock, but we have delete() op
+ defer m.mutex.Unlock()
+
+ udpAddr := addr.(*net.UDPAddr)
+
+ if conns, ok := m.portMap[udpAddr.Port]; ok {
+ if udpAddr.IP.IsUnspecified() {
+ // pick the first one appears in the iteration
+ if len(conns) == 0 {
+ // This can't happen!
+ delete(m.portMap, udpAddr.Port)
+ return nil, false
+ }
+ return conns[0], true
+ }
+
+ for _, conn := range conns {
+ laddr := conn.LocalAddr().(*net.UDPAddr)
+ if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) {
+ return conn, ok
+ }
+ }
+ }
+
+ return nil, false
+}
+
+func (m *udpConnMap) delete(addr net.Addr) error {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ udpAddr := addr.(*net.UDPAddr)
+
+ conns, ok := m.portMap[udpAddr.Port]
+ if !ok {
+ return errNoSuchUDPConn
+ }
+
+ if udpAddr.IP.IsUnspecified() {
+ // remove all from this port
+ delete(m.portMap, udpAddr.Port)
+ return nil
+ }
+
+ newConns := []*UDPConn{}
+
+ for _, conn := range conns {
+ laddr := conn.LocalAddr().(*net.UDPAddr)
+ if laddr.IP.IsUnspecified() {
+ // This can't happen!
+ return errCannotRemoveUnspecifiedIP
+ }
+
+ if laddr.IP.Equal(udpAddr.IP) {
+ continue
+ }
+
+ newConns = append(newConns, conn)
+ }
+
+ if len(newConns) == 0 {
+ delete(m.portMap, udpAddr.Port)
+ } else {
+ m.portMap[udpAddr.Port] = newConns
+ }
+
+ return nil
+}
+
+// size returns the number of UDPConns (UDP listeners)
+func (m *udpConnMap) size() int {
+ m.mutex.RLock()
+ defer m.mutex.RUnlock()
+
+ n := 0
+ for _, conns := range m.portMap {
+ n += len(conns)
+ }
+
+ return n
+}
diff --git a/vendor/github.com/pion/transport/vnet/errors.go b/vendor/github.com/pion/transport/vnet/errors.go
new file mode 100644
index 0000000..d0e9394
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/errors.go
@@ -0,0 +1,19 @@
+package vnet
+
+type timeoutError struct {
+ msg string
+}
+
+func newTimeoutError(msg string) error {
+ return &timeoutError{
+ msg: msg,
+ }
+}
+
+func (e *timeoutError) Error() string {
+ return e.msg
+}
+
+func (e *timeoutError) Timeout() bool {
+ return true
+}
diff --git a/vendor/github.com/pion/transport/vnet/interface.go b/vendor/github.com/pion/transport/vnet/interface.go
new file mode 100644
index 0000000..ec80c0b
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/interface.go
@@ -0,0 +1,40 @@
+package vnet
+
+import (
+ "errors"
+ "net"
+)
+
+var errNoAddressAssigned = errors.New("no address assigned")
+
+// See: https://play.golang.org/p/nBO9KGYEziv
+
+// InterfaceBase ...
+type InterfaceBase net.Interface
+
+// Interface ...
+type Interface struct {
+ InterfaceBase
+ addrs []net.Addr
+}
+
+// NewInterface ...
+func NewInterface(ifc net.Interface) *Interface {
+ return &Interface{
+ InterfaceBase: InterfaceBase(ifc),
+ addrs: nil,
+ }
+}
+
+// AddAddr ...
+func (ifc *Interface) AddAddr(addr net.Addr) {
+ ifc.addrs = append(ifc.addrs, addr)
+}
+
+// Addrs ...
+func (ifc *Interface) Addrs() ([]net.Addr, error) {
+ if len(ifc.addrs) == 0 {
+ return nil, errNoAddressAssigned
+ }
+ return ifc.addrs, nil
+}
diff --git a/vendor/github.com/pion/transport/vnet/nat.go b/vendor/github.com/pion/transport/vnet/nat.go
new file mode 100644
index 0000000..4ece5fa
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/nat.go
@@ -0,0 +1,338 @@
+package vnet
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+var (
+ errNATRequriesMapping = errors.New("1:1 NAT requires more than one mapping")
+ errMismatchLengthIP = errors.New("length mismtach between mappedIPs and localIPs")
+ errNonUDPTranslationNotSupported = errors.New("non-udp translation is not supported yet")
+ errNoAssociatedLocalAddress = errors.New("no associated local address")
+ errNoNATBindingFound = errors.New("no NAT binding found")
+ errHasNoPermission = errors.New("has no permission")
+)
+
+// EndpointDependencyType defines a type of behavioral dependendency on the
+// remote endpoint's IP address or port number. This is used for the two
+// kinds of behaviors:
+// - Port mapping behavior
+// - Filtering behavior
+// See: https://tools.ietf.org/html/rfc4787
+type EndpointDependencyType uint8
+
+const (
+ // EndpointIndependent means the behavior is independent of the endpoint's address or port
+ EndpointIndependent EndpointDependencyType = iota
+ // EndpointAddrDependent means the behavior is dependent on the endpoint's address
+ EndpointAddrDependent
+ // EndpointAddrPortDependent means the behavior is dependent on the endpoint's address and port
+ EndpointAddrPortDependent
+)
+
+// NATMode defines basic behavior of the NAT
+type NATMode uint8
+
+const (
+ // NATModeNormal means the NAT behaves as a standard NAPT (RFC 2663).
+ NATModeNormal NATMode = iota
+ // NATModeNAT1To1 exhibits 1:1 DNAT where the external IP address is statically mapped to
+ // a specific local IP address with port number is preserved always between them.
+ // When this mode is selected, MappingBehavior, FilteringBehavior, PortPreservation and
+ // MappingLifeTime of NATType are ignored.
+ NATModeNAT1To1
+)
+
+const (
+ defaultNATMappingLifeTime = 30 * time.Second
+)
+
+// NATType has a set of parameters that define the behavior of NAT.
+type NATType struct {
+ Mode NATMode
+ MappingBehavior EndpointDependencyType
+ FilteringBehavior EndpointDependencyType
+ Hairpining bool // Not implemented yet
+ PortPreservation bool // Not implemented yet
+ MappingLifeTime time.Duration
+}
+
+type natConfig struct {
+ name string
+ natType NATType
+ mappedIPs []net.IP // mapped IPv4
+ localIPs []net.IP // local IPv4, required only when the mode is NATModeNAT1To1
+ loggerFactory logging.LoggerFactory
+}
+
+type mapping struct {
+ proto string // "udp" or "tcp"
+ local string // "<local-ip>:<local-port>"
+ mapped string // "<mapped-ip>:<mapped-port>"
+ bound string // key: "[<remote-ip>[:<remote-port>]]"
+ filters map[string]struct{} // key: "[<remote-ip>[:<remote-port>]]"
+ expires time.Time // time to expire
+}
+
+type networkAddressTranslator struct {
+ name string
+ natType NATType
+ mappedIPs []net.IP // mapped IPv4
+ localIPs []net.IP // local IPv4, required only when the mode is NATModeNAT1To1
+ outboundMap map[string]*mapping // key: "<proto>:<local-ip>:<local-port>[:remote-ip[:remote-port]]
+ inboundMap map[string]*mapping // key: "<proto>:<mapped-ip>:<mapped-port>"
+ udpPortCounter int
+ mutex sync.RWMutex
+ log logging.LeveledLogger
+}
+
+func newNAT(config *natConfig) (*networkAddressTranslator, error) {
+ natType := config.natType
+
+ if natType.Mode == NATModeNAT1To1 {
+ // 1:1 NAT behavior
+ natType.MappingBehavior = EndpointIndependent
+ natType.FilteringBehavior = EndpointIndependent
+ natType.PortPreservation = true
+ natType.MappingLifeTime = 0
+
+ if len(config.mappedIPs) == 0 {
+ return nil, errNATRequriesMapping
+ }
+ if len(config.mappedIPs) != len(config.localIPs) {
+ return nil, errMismatchLengthIP
+ }
+ } else {
+ // Normal (NAPT) behavior
+ natType.Mode = NATModeNormal
+ if natType.MappingLifeTime == 0 {
+ natType.MappingLifeTime = defaultNATMappingLifeTime
+ }
+ }
+
+ return &networkAddressTranslator{
+ name: config.name,
+ natType: natType,
+ mappedIPs: config.mappedIPs,
+ localIPs: config.localIPs,
+ outboundMap: map[string]*mapping{},
+ inboundMap: map[string]*mapping{},
+ log: config.loggerFactory.NewLogger("vnet"),
+ }, nil
+}
+
+func (n *networkAddressTranslator) getPairedMappedIP(locIP net.IP) net.IP {
+ for i, ip := range n.localIPs {
+ if ip.Equal(locIP) {
+ return n.mappedIPs[i]
+ }
+ }
+ return nil
+}
+
+func (n *networkAddressTranslator) getPairedLocalIP(mappedIP net.IP) net.IP {
+ for i, ip := range n.mappedIPs {
+ if ip.Equal(mappedIP) {
+ return n.localIPs[i]
+ }
+ }
+ return nil
+}
+
+func (n *networkAddressTranslator) translateOutbound(from Chunk) (Chunk, error) {
+ n.mutex.Lock()
+ defer n.mutex.Unlock()
+
+ to := from.Clone()
+
+ if from.Network() == udpString {
+ if n.natType.Mode == NATModeNAT1To1 {
+ // 1:1 NAT behavior
+ srcAddr := from.SourceAddr().(*net.UDPAddr)
+ srcIP := n.getPairedMappedIP(srcAddr.IP)
+ if srcIP == nil {
+ n.log.Debugf("[%s] drop outbound chunk %s with not route", n.name, from.String())
+ return nil, nil // silently discard
+ }
+ srcPort := srcAddr.Port
+ if err := to.setSourceAddr(fmt.Sprintf("%s:%d", srcIP.String(), srcPort)); err != nil {
+ return nil, err
+ }
+ } else {
+ // Normal (NAPT) behavior
+ var bound, filterKey string
+ switch n.natType.MappingBehavior {
+ case EndpointIndependent:
+ bound = ""
+ case EndpointAddrDependent:
+ bound = from.getDestinationIP().String()
+ case EndpointAddrPortDependent:
+ bound = from.DestinationAddr().String()
+ }
+
+ switch n.natType.FilteringBehavior {
+ case EndpointIndependent:
+ filterKey = ""
+ case EndpointAddrDependent:
+ filterKey = from.getDestinationIP().String()
+ case EndpointAddrPortDependent:
+ filterKey = from.DestinationAddr().String()
+ }
+
+ oKey := fmt.Sprintf("udp:%s:%s", from.SourceAddr().String(), bound)
+
+ m := n.findOutboundMapping(oKey)
+ if m == nil {
+ // Create a new mapping
+ mappedPort := 0xC000 + n.udpPortCounter
+ n.udpPortCounter++
+
+ m = &mapping{
+ proto: from.SourceAddr().Network(),
+ local: from.SourceAddr().String(),
+ bound: bound,
+ mapped: fmt.Sprintf("%s:%d", n.mappedIPs[0].String(), mappedPort),
+ filters: map[string]struct{}{},
+ expires: time.Now().Add(n.natType.MappingLifeTime),
+ }
+
+ n.outboundMap[oKey] = m
+
+ iKey := fmt.Sprintf("udp:%s", m.mapped)
+
+ n.log.Debugf("[%s] created a new NAT binding oKey=%s iKey=%s\n",
+ n.name,
+ oKey,
+ iKey)
+
+ m.filters[filterKey] = struct{}{}
+ n.log.Debugf("[%s] permit access from %s to %s\n", n.name, filterKey, m.mapped)
+ n.inboundMap[iKey] = m
+ } else if _, ok := m.filters[filterKey]; !ok {
+ n.log.Debugf("[%s] permit access from %s to %s\n", n.name, filterKey, m.mapped)
+ m.filters[filterKey] = struct{}{}
+ }
+
+ if err := to.setSourceAddr(m.mapped); err != nil {
+ return nil, err
+ }
+ }
+
+ n.log.Debugf("[%s] translate outbound chunk from %s to %s", n.name, from.String(), to.String())
+
+ return to, nil
+ }
+
+ return nil, errNonUDPTranslationNotSupported
+}
+
+func (n *networkAddressTranslator) translateInbound(from Chunk) (Chunk, error) {
+ n.mutex.Lock()
+ defer n.mutex.Unlock()
+
+ to := from.Clone()
+
+ if from.Network() == udpString {
+ if n.natType.Mode == NATModeNAT1To1 {
+ // 1:1 NAT behavior
+ dstAddr := from.DestinationAddr().(*net.UDPAddr)
+ dstIP := n.getPairedLocalIP(dstAddr.IP)
+ if dstIP == nil {
+ return nil, fmt.Errorf("drop %s as %w", from.String(), errNoAssociatedLocalAddress)
+ }
+ dstPort := from.DestinationAddr().(*net.UDPAddr).Port
+ if err := to.setDestinationAddr(fmt.Sprintf("%s:%d", dstIP, dstPort)); err != nil {
+ return nil, err
+ }
+ } else {
+ // Normal (NAPT) behavior
+ iKey := fmt.Sprintf("udp:%s", from.DestinationAddr().String())
+ m := n.findInboundMapping(iKey)
+ if m == nil {
+ return nil, fmt.Errorf("drop %s as %w", from.String(), errNoNATBindingFound)
+ }
+
+ var filterKey string
+ switch n.natType.FilteringBehavior {
+ case EndpointIndependent:
+ filterKey = ""
+ case EndpointAddrDependent:
+ filterKey = from.getSourceIP().String()
+ case EndpointAddrPortDependent:
+ filterKey = from.SourceAddr().String()
+ }
+
+ if _, ok := m.filters[filterKey]; !ok {
+ return nil, fmt.Errorf("drop %s as the remote %s %w", from.String(), filterKey, errHasNoPermission)
+ }
+
+ // See RFC 4847 Section 4.3. Mapping Refresh
+ // a) Inbound refresh may be useful for applications with no outgoing
+ // UDP traffic. However, allowing inbound refresh may allow an
+ // external attacker or misbehaving application to keep a mapping
+ // alive indefinitely. This may be a security risk. Also, if the
+ // process is repeated with different ports, over time, it could
+ // use up all the ports on the NAT.
+
+ if err := to.setDestinationAddr(m.local); err != nil {
+ return nil, err
+ }
+ }
+
+ n.log.Debugf("[%s] translate inbound chunk from %s to %s", n.name, from.String(), to.String())
+
+ return to, nil
+ }
+
+ return nil, errNonUDPTranslationNotSupported
+}
+
+// caller must hold the mutex
+func (n *networkAddressTranslator) findOutboundMapping(oKey string) *mapping {
+ now := time.Now()
+
+ m, ok := n.outboundMap[oKey]
+ if ok {
+ // check if this mapping is expired
+ if now.After(m.expires) {
+ n.removeMapping(m)
+ m = nil // expired
+ } else {
+ m.expires = time.Now().Add(n.natType.MappingLifeTime)
+ }
+ }
+
+ return m
+}
+
+// caller must hold the mutex
+func (n *networkAddressTranslator) findInboundMapping(iKey string) *mapping {
+ now := time.Now()
+ m, ok := n.inboundMap[iKey]
+ if !ok {
+ return nil
+ }
+
+ // check if this mapping is expired
+ if now.After(m.expires) {
+ n.removeMapping(m)
+ return nil
+ }
+
+ return m
+}
+
+// caller must hold the mutex
+func (n *networkAddressTranslator) removeMapping(m *mapping) {
+ oKey := fmt.Sprintf("%s:%s:%s", m.proto, m.local, m.bound)
+ iKey := fmt.Sprintf("%s:%s", m.proto, m.mapped)
+
+ delete(n.outboundMap, oKey)
+ delete(n.inboundMap, iKey)
+}
diff --git a/vendor/github.com/pion/transport/vnet/net.go b/vendor/github.com/pion/transport/vnet/net.go
new file mode 100644
index 0000000..4dc6a2a
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/net.go
@@ -0,0 +1,677 @@
+package vnet
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math/rand"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+const (
+ lo0String = "lo0String"
+ udpString = "udp"
+)
+
+var (
+ macAddrCounter uint64 = 0xBEEFED910200 //nolint:gochecknoglobals
+ errNoInterface = errors.New("no interface is available")
+ errNotFound = errors.New("not found")
+ errUnexpectedNetwork = errors.New("unexpected network")
+ errCantAssignRequestedAddr = errors.New("can't assign requested address")
+ errUnknownNetwork = errors.New("unknown network")
+ errNoRouterLinked = errors.New("no router linked")
+ errInvalidPortNumber = errors.New("invalid port number")
+ errUnexpectedTypeSwitchFailure = errors.New("unexpected type-switch failure")
+ errBindFailerFor = errors.New("bind failed for")
+ errEndPortLessThanStart = errors.New("end port is less than the start")
+ errPortSpaceExhausted = errors.New("port space exhausted")
+ errVNetDisabled = errors.New("vnet is not enabled")
+)
+
+func newMACAddress() net.HardwareAddr {
+ b := make([]byte, 8)
+ binary.BigEndian.PutUint64(b, macAddrCounter)
+ macAddrCounter++
+ return b[2:]
+}
+
+type vNet struct {
+ interfaces []*Interface // read-only
+ staticIPs []net.IP // read-only
+ router *Router // read-only
+ udpConns *udpConnMap // read-only
+ mutex sync.RWMutex
+}
+
+func (v *vNet) _getInterfaces() ([]*Interface, error) {
+ if len(v.interfaces) == 0 {
+ return nil, errNoInterface
+ }
+
+ return v.interfaces, nil
+}
+
+func (v *vNet) getInterfaces() ([]*Interface, error) {
+ v.mutex.RLock()
+ defer v.mutex.RUnlock()
+
+ return v._getInterfaces()
+}
+
+// caller must hold the mutex (read)
+func (v *vNet) _getInterface(ifName string) (*Interface, error) {
+ ifs, err := v._getInterfaces()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifc := range ifs {
+ if ifc.Name == ifName {
+ return ifc, nil
+ }
+ }
+
+ return nil, fmt.Errorf("interface %s %w", ifName, errNotFound)
+}
+
+func (v *vNet) getInterface(ifName string) (*Interface, error) {
+ v.mutex.RLock()
+ defer v.mutex.RUnlock()
+
+ return v._getInterface(ifName)
+}
+
+// caller must hold the mutex
+func (v *vNet) getAllIPAddrs(ipv6 bool) []net.IP {
+ ips := []net.IP{}
+
+ for _, ifc := range v.interfaces {
+ addrs, err := ifc.Addrs()
+ if err != nil {
+ continue
+ }
+
+ for _, addr := range addrs {
+ var ip net.IP
+ if ipNet, ok := addr.(*net.IPNet); ok {
+ ip = ipNet.IP
+ } else if ipAddr, ok := addr.(*net.IPAddr); ok {
+ ip = ipAddr.IP
+ } else {
+ continue
+ }
+
+ if !ipv6 {
+ if ip.To4() != nil {
+ ips = append(ips, ip)
+ }
+ }
+ }
+ }
+
+ return ips
+}
+
+func (v *vNet) setRouter(r *Router) error {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+
+ v.router = r
+ return nil
+}
+
+func (v *vNet) onInboundChunk(c Chunk) {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+
+ if c.Network() == udpString {
+ if conn, ok := v.udpConns.find(c.DestinationAddr()); ok {
+ conn.onInboundChunk(c)
+ }
+ }
+}
+
+// caller must hold the mutex
+func (v *vNet) _dialUDP(network string, locAddr, remAddr *net.UDPAddr) (UDPPacketConn, error) {
+ // validate network
+ if network != udpString && network != "udp4" {
+ return nil, fmt.Errorf("%w: %s", errUnexpectedNetwork, network)
+ }
+
+ if locAddr == nil {
+ locAddr = &net.UDPAddr{
+ IP: net.IPv4zero,
+ }
+ } else if locAddr.IP == nil {
+ locAddr.IP = net.IPv4zero
+ }
+
+ // validate address. do we have that address?
+ if !v.hasIPAddr(locAddr.IP) {
+ return nil, &net.OpError{
+ Op: "listen",
+ Net: network,
+ Addr: locAddr,
+ Err: fmt.Errorf("bind: %w", errCantAssignRequestedAddr),
+ }
+ }
+
+ if locAddr.Port == 0 {
+ // choose randomly from the range between 5000 and 5999
+ port, err := v.assignPort(locAddr.IP, 5000, 5999)
+ if err != nil {
+ return nil, &net.OpError{
+ Op: "listen",
+ Net: network,
+ Addr: locAddr,
+ Err: err,
+ }
+ }
+ locAddr.Port = port
+ } else if _, ok := v.udpConns.find(locAddr); ok {
+ return nil, &net.OpError{
+ Op: "listen",
+ Net: network,
+ Addr: locAddr,
+ Err: fmt.Errorf("bind: %w", errAddressAlreadyInUse),
+ }
+ }
+
+ conn, err := newUDPConn(locAddr, remAddr, v)
+ if err != nil {
+ return nil, err
+ }
+
+ err = v.udpConns.insert(conn)
+ if err != nil {
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+func (v *vNet) listenPacket(network string, address string) (UDPPacketConn, error) {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+
+ locAddr, err := v.resolveUDPAddr(network, address)
+ if err != nil {
+ return nil, err
+ }
+
+ return v._dialUDP(network, locAddr, nil)
+}
+
+func (v *vNet) listenUDP(network string, locAddr *net.UDPAddr) (UDPPacketConn, error) {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+
+ return v._dialUDP(network, locAddr, nil)
+}
+
+func (v *vNet) dialUDP(network string, locAddr, remAddr *net.UDPAddr) (UDPPacketConn, error) {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+
+ return v._dialUDP(network, locAddr, remAddr)
+}
+
+func (v *vNet) dial(network string, address string) (UDPPacketConn, error) {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+
+ remAddr, err := v.resolveUDPAddr(network, address)
+ if err != nil {
+ return nil, err
+ }
+
+ // Determine source address
+ srcIP := v.determineSourceIP(nil, remAddr.IP)
+
+ locAddr := &net.UDPAddr{IP: srcIP, Port: 0}
+
+ return v._dialUDP(network, locAddr, remAddr)
+}
+
+func (v *vNet) resolveUDPAddr(network, address string) (*net.UDPAddr, error) {
+ if network != udpString && network != "udp4" {
+ return nil, fmt.Errorf("%w %s", errUnknownNetwork, network)
+ }
+
+ host, sPort, err := net.SplitHostPort(address)
+ if err != nil {
+ return nil, err
+ }
+
+ // Check if host is a domain name
+ ip := net.ParseIP(host)
+ if ip == nil {
+ host = strings.ToLower(host)
+ if host == "localhost" {
+ ip = net.IPv4(127, 0, 0, 1)
+ } else {
+ // host is a domain name. resolve IP address by the name
+ if v.router == nil {
+ return nil, errNoRouterLinked
+ }
+
+ ip, err = v.router.resolver.lookUp(host)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ port, err := strconv.Atoi(sPort)
+ if err != nil {
+ return nil, errInvalidPortNumber
+ }
+
+ udpAddr := &net.UDPAddr{
+ IP: ip,
+ Port: port,
+ }
+
+ return udpAddr, nil
+}
+
+func (v *vNet) write(c Chunk) error {
+ if c.Network() == udpString {
+ if udp, ok := c.(*chunkUDP); ok {
+ if c.getDestinationIP().IsLoopback() {
+ if conn, ok := v.udpConns.find(udp.DestinationAddr()); ok {
+ conn.onInboundChunk(udp)
+ }
+ return nil
+ }
+ } else {
+ return errUnexpectedTypeSwitchFailure
+ }
+ }
+
+ if v.router == nil {
+ return errNoRouterLinked
+ }
+
+ v.router.push(c)
+ return nil
+}
+
+func (v *vNet) onClosed(addr net.Addr) {
+ if addr.Network() == udpString {
+ //nolint:errcheck
+ v.udpConns.delete(addr) // #nosec
+ }
+}
+
+// This method determines the srcIP based on the dstIP when locIP
+// is any IP address ("0.0.0.0" or "::"). If locIP is a non-any addr,
+// this method simply returns locIP.
+// caller must hold the mutex
+func (v *vNet) determineSourceIP(locIP, dstIP net.IP) net.IP {
+ if locIP != nil && !locIP.IsUnspecified() {
+ return locIP
+ }
+
+ var srcIP net.IP
+
+ if dstIP.IsLoopback() {
+ srcIP = net.ParseIP("127.0.0.1")
+ } else {
+ ifc, err2 := v._getInterface("eth0")
+ if err2 != nil {
+ return nil
+ }
+
+ addrs, err2 := ifc.Addrs()
+ if err2 != nil {
+ return nil
+ }
+
+ if len(addrs) == 0 {
+ return nil
+ }
+
+ var findIPv4 bool
+ if locIP != nil {
+ findIPv4 = (locIP.To4() != nil)
+ } else {
+ findIPv4 = (dstIP.To4() != nil)
+ }
+
+ for _, addr := range addrs {
+ ip := addr.(*net.IPNet).IP
+ if findIPv4 {
+ if ip.To4() != nil {
+ srcIP = ip
+ break
+ }
+ } else {
+ if ip.To4() == nil {
+ srcIP = ip
+ break
+ }
+ }
+ }
+ }
+
+ return srcIP
+}
+
+// caller must hold the mutex
+func (v *vNet) hasIPAddr(ip net.IP) bool { //nolint:gocognit
+ for _, ifc := range v.interfaces {
+ if addrs, err := ifc.Addrs(); err == nil {
+ for _, addr := range addrs {
+ var locIP net.IP
+ if ipNet, ok := addr.(*net.IPNet); ok {
+ locIP = ipNet.IP
+ } else if ipAddr, ok := addr.(*net.IPAddr); ok {
+ locIP = ipAddr.IP
+ } else {
+ continue
+ }
+
+ switch ip.String() {
+ case "0.0.0.0":
+ if locIP.To4() != nil {
+ return true
+ }
+ case "::":
+ if locIP.To4() == nil {
+ return true
+ }
+ default:
+ if locIP.Equal(ip) {
+ return true
+ }
+ }
+ }
+ }
+ }
+
+ return false
+}
+
+// caller must hold the mutex
+func (v *vNet) allocateLocalAddr(ip net.IP, port int) error {
+ // gather local IP addresses to bind
+ var ips []net.IP
+ if ip.IsUnspecified() {
+ ips = v.getAllIPAddrs(ip.To4() == nil)
+ } else if v.hasIPAddr(ip) {
+ ips = []net.IP{ip}
+ }
+
+ if len(ips) == 0 {
+ return fmt.Errorf("%w %s", errBindFailerFor, ip.String())
+ }
+
+ // check if all these transport addresses are not in use
+ for _, ip2 := range ips {
+ addr := &net.UDPAddr{
+ IP: ip2,
+ Port: port,
+ }
+ if _, ok := v.udpConns.find(addr); ok {
+ return &net.OpError{
+ Op: "bind",
+ Net: udpString,
+ Addr: addr,
+ Err: fmt.Errorf("bind: %w", errAddressAlreadyInUse),
+ }
+ }
+ }
+
+ return nil
+}
+
+// caller must hold the mutex
+func (v *vNet) assignPort(ip net.IP, start, end int) (int, error) {
+ // choose randomly from the range between start and end (inclusive)
+ if end < start {
+ return -1, errEndPortLessThanStart
+ }
+
+ space := end + 1 - start
+ offset := rand.Intn(space) //nolint:gosec
+ for i := 0; i < space; i++ {
+ port := ((offset + i) % space) + start
+
+ err := v.allocateLocalAddr(ip, port)
+ if err == nil {
+ return port, nil
+ }
+ }
+
+ return -1, errPortSpaceExhausted
+}
+
+// NetConfig is a bag of configuration parameters passed to NewNet().
+type NetConfig struct {
+ // StaticIPs is an array of static IP addresses to be assigned for this Net.
+ // If no static IP address is given, the router will automatically assign
+ // an IP address.
+ StaticIPs []string
+
+ // StaticIP is deprecated. Use StaticIPs.
+ StaticIP string
+}
+
+// Net represents a local network stack euivalent to a set of layers from NIC
+// up to the transport (UDP / TCP) layer.
+type Net struct {
+ v *vNet
+ ifs []*Interface
+}
+
+// NewNet creates an instance of Net.
+// If config is nil, the virtual network is disabled. (uses corresponding
+// net.Xxxx() operations.
+// By design, it always have lo0 and eth0 interfaces.
+// The lo0 has the address 127.0.0.1 assigned by default.
+// IP address for eth0 will be assigned when this Net is added to a router.
+func NewNet(config *NetConfig) *Net {
+ if config == nil {
+ ifs := []*Interface{}
+ if orgIfs, err := net.Interfaces(); err == nil {
+ for _, orgIfc := range orgIfs {
+ ifc := NewInterface(orgIfc)
+ if addrs, err := orgIfc.Addrs(); err == nil {
+ for _, addr := range addrs {
+ ifc.AddAddr(addr)
+ }
+ }
+
+ ifs = append(ifs, ifc)
+ }
+ }
+
+ return &Net{ifs: ifs}
+ }
+
+ lo0 := NewInterface(net.Interface{
+ Index: 1,
+ MTU: 16384,
+ Name: lo0String,
+ HardwareAddr: nil,
+ Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast,
+ })
+ lo0.AddAddr(&net.IPNet{
+ IP: net.ParseIP("127.0.0.1"),
+ Mask: net.CIDRMask(8, 32),
+ })
+
+ eth0 := NewInterface(net.Interface{
+ Index: 2,
+ MTU: 1500,
+ Name: "eth0",
+ HardwareAddr: newMACAddress(),
+ Flags: net.FlagUp | net.FlagMulticast,
+ })
+
+ var staticIPs []net.IP
+ for _, ipStr := range config.StaticIPs {
+ if ip := net.ParseIP(ipStr); ip != nil {
+ staticIPs = append(staticIPs, ip)
+ }
+ }
+ if len(config.StaticIP) > 0 {
+ if ip := net.ParseIP(config.StaticIP); ip != nil {
+ staticIPs = append(staticIPs, ip)
+ }
+ }
+
+ v := &vNet{
+ interfaces: []*Interface{lo0, eth0},
+ staticIPs: staticIPs,
+ udpConns: newUDPConnMap(),
+ }
+
+ return &Net{
+ v: v,
+ }
+}
+
+// Interfaces returns a list of the system's network interfaces.
+func (n *Net) Interfaces() ([]*Interface, error) {
+ if n.v == nil {
+ return n.ifs, nil
+ }
+
+ return n.v.getInterfaces()
+}
+
+// InterfaceByName returns the interface specified by name.
+func (n *Net) InterfaceByName(name string) (*Interface, error) {
+ if n.v == nil {
+ for _, ifc := range n.ifs {
+ if ifc.Name == name {
+ return ifc, nil
+ }
+ }
+
+ return nil, fmt.Errorf("interface %s %w", name, errNotFound)
+ }
+
+ return n.v.getInterface(name)
+}
+
+// ListenPacket announces on the local network address.
+func (n *Net) ListenPacket(network string, address string) (net.PacketConn, error) {
+ if n.v == nil {
+ return net.ListenPacket(network, address)
+ }
+
+ return n.v.listenPacket(network, address)
+}
+
+// ListenUDP acts like ListenPacket for UDP networks.
+func (n *Net) ListenUDP(network string, locAddr *net.UDPAddr) (UDPPacketConn, error) {
+ if n.v == nil {
+ return net.ListenUDP(network, locAddr)
+ }
+
+ return n.v.listenUDP(network, locAddr)
+}
+
+// Dial connects to the address on the named network.
+func (n *Net) Dial(network, address string) (net.Conn, error) {
+ if n.v == nil {
+ return net.Dial(network, address)
+ }
+
+ return n.v.dial(network, address)
+}
+
+// CreateDialer creates an instance of vnet.Dialer
+func (n *Net) CreateDialer(dialer *net.Dialer) Dialer {
+ if n.v == nil {
+ return &vDialer{
+ dialer: dialer,
+ }
+ }
+
+ return &vDialer{
+ dialer: dialer,
+ v: n.v,
+ }
+}
+
+// DialUDP acts like Dial for UDP networks.
+func (n *Net) DialUDP(network string, laddr, raddr *net.UDPAddr) (UDPPacketConn, error) {
+ if n.v == nil {
+ return net.DialUDP(network, laddr, raddr)
+ }
+
+ return n.v.dialUDP(network, laddr, raddr)
+}
+
+// ResolveUDPAddr returns an address of UDP end point.
+func (n *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) {
+ if n.v == nil {
+ return net.ResolveUDPAddr(network, address)
+ }
+
+ return n.v.resolveUDPAddr(network, address)
+}
+
+func (n *Net) getInterface(ifName string) (*Interface, error) {
+ if n.v == nil {
+ return nil, errVNetDisabled
+ }
+
+ return n.v.getInterface(ifName)
+}
+
+func (n *Net) setRouter(r *Router) error {
+ if n.v == nil {
+ return errVNetDisabled
+ }
+
+ return n.v.setRouter(r)
+}
+
+func (n *Net) onInboundChunk(c Chunk) {
+ if n.v == nil {
+ return
+ }
+
+ n.v.onInboundChunk(c)
+}
+
+func (n *Net) getStaticIPs() []net.IP {
+ if n.v == nil {
+ return nil
+ }
+
+ return n.v.staticIPs
+}
+
+// IsVirtual tests if the virtual network is enabled.
+func (n *Net) IsVirtual() bool {
+ return n.v != nil
+}
+
+// Dialer is identical to net.Dialer excepts that its methods
+// (Dial, DialContext) are overridden to use virtual network.
+// Use vnet.CreateDialer() to create an instance of this Dialer.
+type Dialer interface {
+ Dial(network, address string) (net.Conn, error)
+}
+
+type vDialer struct {
+ dialer *net.Dialer
+ v *vNet
+}
+
+func (d *vDialer) Dial(network, address string) (net.Conn, error) {
+ if d.v == nil {
+ return d.dialer.Dial(network, address)
+ }
+
+ return d.v.dial(network, address)
+}
diff --git a/vendor/github.com/pion/transport/vnet/resolver.go b/vendor/github.com/pion/transport/vnet/resolver.go
new file mode 100644
index 0000000..e5166e3
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/resolver.go
@@ -0,0 +1,89 @@
+package vnet
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+
+ "github.com/pion/logging"
+)
+
+var (
+ errHostnameEmpty = errors.New("host name must not be empty")
+ errFailedtoParseIPAddr = errors.New("failed to parse IP address")
+)
+
+type resolverConfig struct {
+ LoggerFactory logging.LoggerFactory
+}
+
+type resolver struct {
+ parent *resolver // read-only
+ hosts map[string]net.IP // requires mutex
+ mutex sync.RWMutex // thread-safe
+ log logging.LeveledLogger // read-only
+}
+
+func newResolver(config *resolverConfig) *resolver {
+ r := &resolver{
+ hosts: map[string]net.IP{},
+ log: config.LoggerFactory.NewLogger("vnet"),
+ }
+
+ if err := r.addHost("localhost", "127.0.0.1"); err != nil {
+ r.log.Warn("failed to add localhost to resolver")
+ }
+ return r
+}
+
+func (r *resolver) setParent(parent *resolver) {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ r.parent = parent
+}
+
+func (r *resolver) addHost(name string, ipAddr string) error {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ if len(name) == 0 {
+ return errHostnameEmpty
+ }
+ ip := net.ParseIP(ipAddr)
+ if ip == nil {
+ return fmt.Errorf("%w \"%s\"", errFailedtoParseIPAddr, ipAddr)
+ }
+ r.hosts[name] = ip
+ return nil
+}
+
+func (r *resolver) lookUp(hostName string) (net.IP, error) {
+ ip := func() net.IP {
+ r.mutex.RLock()
+ defer r.mutex.RUnlock()
+
+ if ip2, ok := r.hosts[hostName]; ok {
+ return ip2
+ }
+ return nil
+ }()
+ if ip != nil {
+ return ip, nil
+ }
+
+ // mutex must be unlocked before calling into parent resolver
+
+ if r.parent != nil {
+ return r.parent.lookUp(hostName)
+ }
+
+ return nil, &net.DNSError{
+ Err: "host not found",
+ Name: hostName,
+ Server: "vnet resolver",
+ IsTimeout: false,
+ IsTemporary: false,
+ }
+}
diff --git a/vendor/github.com/pion/transport/vnet/router.go b/vendor/github.com/pion/transport/vnet/router.go
new file mode 100644
index 0000000..616d2c9
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/router.go
@@ -0,0 +1,605 @@
+package vnet
+
+import (
+ "errors"
+ "fmt"
+ "math/rand"
+ "net"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+const (
+ defaultRouterQueueSize = 0 // unlimited
+)
+
+var (
+ errInvalidLocalIPinStaticIPs = errors.New("invalid local IP in StaticIPs")
+ errLocalIPBeyondStaticIPsSubset = errors.New("mapped in StaticIPs is beyond subnet")
+ errLocalIPNoStaticsIPsAssociated = errors.New("all StaticIPs must have associated local IPs")
+ errRouterAlreadyStarted = errors.New("router already started")
+ errRouterAlreadyStopped = errors.New("router already stopped")
+ errStaticIPisBeyondSubnet = errors.New("static IP is beyond subnet")
+ errAddressSpaceExhausted = errors.New("address space exhausted")
+ errNoIPAddrEth0 = errors.New("no IP address is assigned for eth0")
+)
+
+// Generate a unique router name
+var assignRouterName = func() func() string { //nolint:gochecknoglobals
+ var routerIDCtr uint64
+
+ return func() string {
+ n := atomic.AddUint64(&routerIDCtr, 1)
+ return fmt.Sprintf("router%d", n)
+ }
+}()
+
+// RouterConfig ...
+type RouterConfig struct {
+ // Name of router. If not specified, a unique name will be assigned.
+ Name string
+ // CIDR notation, like "192.0.2.0/24"
+ CIDR string
+ // StaticIPs is an array of static IP addresses to be assigned for this router.
+ // If no static IP address is given, the router will automatically assign
+ // an IP address.
+ // This will be ignored if this router is the root.
+ StaticIPs []string
+ // StaticIP is deprecated. Use StaticIPs.
+ StaticIP string
+ // Internal queue size
+ QueueSize int
+ // Effective only when this router has a parent router
+ NATType *NATType
+ // Minimum Delay
+ MinDelay time.Duration
+ // Max Jitter
+ MaxJitter time.Duration
+ // Logger factory
+ LoggerFactory logging.LoggerFactory
+}
+
+// NIC is a nework inerface controller that interfaces Router
+type NIC interface {
+ getInterface(ifName string) (*Interface, error)
+ onInboundChunk(c Chunk)
+ getStaticIPs() []net.IP
+ setRouter(r *Router) error
+}
+
+// ChunkFilter is a handler users can add to filter chunks.
+// If the filter returns false, the packet will be dropped.
+type ChunkFilter func(c Chunk) bool
+
+// Router ...
+type Router struct {
+ name string // read-only
+ interfaces []*Interface // read-only
+ ipv4Net *net.IPNet // read-only
+ staticIPs []net.IP // read-only
+ staticLocalIPs map[string]net.IP // read-only,
+ lastID byte // requires mutex [x], used to assign the last digit of IPv4 address
+ queue *chunkQueue // read-only
+ parent *Router // read-only
+ children []*Router // read-only
+ natType *NATType // read-only
+ nat *networkAddressTranslator // read-only
+ nics map[string]NIC // read-only
+ stopFunc func() // requires mutex [x]
+ resolver *resolver // read-only
+ chunkFilters []ChunkFilter // requires mutex [x]
+ minDelay time.Duration // requires mutex [x]
+ maxJitter time.Duration // requires mutex [x]
+ mutex sync.RWMutex // thread-safe
+ pushCh chan struct{} // writer requires mutex
+ loggerFactory logging.LoggerFactory // read-only
+ log logging.LeveledLogger // read-only
+}
+
+// NewRouter ...
+func NewRouter(config *RouterConfig) (*Router, error) {
+ loggerFactory := config.LoggerFactory
+ log := loggerFactory.NewLogger("vnet")
+
+ _, ipv4Net, err := net.ParseCIDR(config.CIDR)
+ if err != nil {
+ return nil, err
+ }
+
+ queueSize := defaultRouterQueueSize
+ if config.QueueSize > 0 {
+ queueSize = config.QueueSize
+ }
+
+ // set up network interface, lo0
+ lo0 := NewInterface(net.Interface{
+ Index: 1,
+ MTU: 16384,
+ Name: lo0String,
+ HardwareAddr: nil,
+ Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast,
+ })
+ lo0.AddAddr(&net.IPAddr{IP: net.ParseIP("127.0.0.1"), Zone: ""})
+
+ // set up network interface, eth0
+ eth0 := NewInterface(net.Interface{
+ Index: 2,
+ MTU: 1500,
+ Name: "eth0",
+ HardwareAddr: newMACAddress(),
+ Flags: net.FlagUp | net.FlagMulticast,
+ })
+
+ // local host name resolver
+ resolver := newResolver(&resolverConfig{
+ LoggerFactory: config.LoggerFactory,
+ })
+
+ name := config.Name
+ if len(name) == 0 {
+ name = assignRouterName()
+ }
+
+ var staticIPs []net.IP
+ staticLocalIPs := map[string]net.IP{}
+ for _, ipStr := range config.StaticIPs {
+ ipPair := strings.Split(ipStr, "/")
+ if ip := net.ParseIP(ipPair[0]); ip != nil {
+ if len(ipPair) > 1 {
+ locIP := net.ParseIP(ipPair[1])
+ if locIP == nil {
+ return nil, errInvalidLocalIPinStaticIPs
+ }
+ if !ipv4Net.Contains(locIP) {
+ return nil, fmt.Errorf("local IP %s %w", locIP.String(), errLocalIPBeyondStaticIPsSubset)
+ }
+ staticLocalIPs[ip.String()] = locIP
+ }
+ staticIPs = append(staticIPs, ip)
+ }
+ }
+ if len(config.StaticIP) > 0 {
+ log.Warn("StaticIP is deprecated. Use StaticIPs instead")
+ if ip := net.ParseIP(config.StaticIP); ip != nil {
+ staticIPs = append(staticIPs, ip)
+ }
+ }
+
+ if nStaticLocal := len(staticLocalIPs); nStaticLocal > 0 {
+ if nStaticLocal != len(staticIPs) {
+ return nil, errLocalIPNoStaticsIPsAssociated
+ }
+ }
+
+ return &Router{
+ name: name,
+ interfaces: []*Interface{lo0, eth0},
+ ipv4Net: ipv4Net,
+ staticIPs: staticIPs,
+ staticLocalIPs: staticLocalIPs,
+ queue: newChunkQueue(queueSize),
+ natType: config.NATType,
+ nics: map[string]NIC{},
+ resolver: resolver,
+ minDelay: config.MinDelay,
+ maxJitter: config.MaxJitter,
+ pushCh: make(chan struct{}, 1),
+ loggerFactory: loggerFactory,
+ log: log,
+ }, nil
+}
+
+// caller must hold the mutex
+func (r *Router) getInterfaces() ([]*Interface, error) {
+ if len(r.interfaces) == 0 {
+ return nil, fmt.Errorf("%w is available", errNoInterface)
+ }
+
+ return r.interfaces, nil
+}
+
+func (r *Router) getInterface(ifName string) (*Interface, error) {
+ r.mutex.RLock()
+ defer r.mutex.RUnlock()
+
+ ifs, err := r.getInterfaces()
+ if err != nil {
+ return nil, err
+ }
+ for _, ifc := range ifs {
+ if ifc.Name == ifName {
+ return ifc, nil
+ }
+ }
+
+ return nil, fmt.Errorf("interface %s %w", ifName, errNotFound)
+}
+
+// Start ...
+func (r *Router) Start() error {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ if r.stopFunc != nil {
+ return errRouterAlreadyStarted
+ }
+
+ cancelCh := make(chan struct{})
+
+ go func() {
+ loop:
+ for {
+ d, err := r.processChunks()
+ if err != nil {
+ r.log.Errorf("[%s] %s", r.name, err.Error())
+ break
+ }
+
+ if d <= 0 {
+ select {
+ case <-r.pushCh:
+ case <-cancelCh:
+ break loop
+ }
+ } else {
+ t := time.NewTimer(d)
+ select {
+ case <-t.C:
+ case <-cancelCh:
+ break loop
+ }
+ }
+ }
+ }()
+
+ r.stopFunc = func() {
+ close(cancelCh)
+ }
+
+ for _, child := range r.children {
+ if err := child.Start(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// Stop ...
+func (r *Router) Stop() error {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ if r.stopFunc == nil {
+ return errRouterAlreadyStopped
+ }
+
+ for _, router := range r.children {
+ r.mutex.Unlock()
+ err := router.Stop()
+ r.mutex.Lock()
+
+ if err != nil {
+ return err
+ }
+ }
+
+ r.stopFunc()
+ r.stopFunc = nil
+ return nil
+}
+
+// caller must hold the mutex
+func (r *Router) addNIC(nic NIC) error {
+ ifc, err := nic.getInterface("eth0")
+ if err != nil {
+ return err
+ }
+
+ var ips []net.IP
+
+ if ips = nic.getStaticIPs(); len(ips) == 0 {
+ // assign an IP address
+ ip, err2 := r.assignIPAddress()
+ if err2 != nil {
+ return err2
+ }
+ ips = append(ips, ip)
+ }
+
+ for _, ip := range ips {
+ if !r.ipv4Net.Contains(ip) {
+ return fmt.Errorf("%w: %s", errStaticIPisBeyondSubnet, r.ipv4Net.String())
+ }
+
+ ifc.AddAddr(&net.IPNet{
+ IP: ip,
+ Mask: r.ipv4Net.Mask,
+ })
+
+ r.nics[ip.String()] = nic
+ }
+
+ if err = nic.setRouter(r); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// AddRouter adds a chile Router.
+func (r *Router) AddRouter(router *Router) error {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ // Router is a NIC. Add it as a NIC so that packets are routed to this child
+ // router.
+ err := r.addNIC(router)
+ if err != nil {
+ return err
+ }
+
+ if err = router.setRouter(r); err != nil {
+ return err
+ }
+
+ r.children = append(r.children, router)
+ return nil
+}
+
+// AddNet ...
+func (r *Router) AddNet(nic NIC) error {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ return r.addNIC(nic)
+}
+
+// AddHost adds a mapping of hostname and an IP address to the local resolver.
+func (r *Router) AddHost(hostName string, ipAddr string) error {
+ return r.resolver.addHost(hostName, ipAddr)
+}
+
+// AddChunkFilter adds a filter for chunks traversing this router.
+// You may add more than one filter. The filters are called in the order of this method call.
+// If a chunk is dropped by a filter, subsequent filter will not receive the chunk.
+func (r *Router) AddChunkFilter(filter ChunkFilter) {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ r.chunkFilters = append(r.chunkFilters, filter)
+}
+
+// caller should hold the mutex
+func (r *Router) assignIPAddress() (net.IP, error) {
+ // See: https://stackoverflow.com/questions/14915188/ip-address-ending-with-zero
+
+ if r.lastID == 0xfe {
+ return nil, errAddressSpaceExhausted
+ }
+
+ ip := make(net.IP, 4)
+ copy(ip, r.ipv4Net.IP[:3])
+ r.lastID++
+ ip[3] = r.lastID
+ return ip, nil
+}
+
+func (r *Router) push(c Chunk) {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ r.log.Debugf("[%s] route %s", r.name, c.String())
+ if r.stopFunc != nil {
+ c.setTimestamp()
+ if r.queue.push(c) {
+ select {
+ case r.pushCh <- struct{}{}:
+ default:
+ }
+ } else {
+ r.log.Warnf("[%s] queue was full. dropped a chunk", r.name)
+ }
+ }
+}
+
+func (r *Router) processChunks() (time.Duration, error) {
+ r.mutex.Lock()
+ defer r.mutex.Unlock()
+
+ // Introduce jitter by delaying the processing of chunks.
+ if r.maxJitter > 0 {
+ jitter := time.Duration(rand.Int63n(int64(r.maxJitter))) //nolint:gosec
+ time.Sleep(jitter)
+ }
+
+ // cutOff
+ // v min delay
+ // |<--->|
+ // +------------:--
+ // |OOOOOOXXXXX : --> time
+ // +------------:--
+ // |<--->| now
+ // due
+
+ enteredAt := time.Now()
+ cutOff := enteredAt.Add(-r.minDelay)
+
+ var d time.Duration // the next sleep duration
+
+ for {
+ d = 0
+
+ c := r.queue.peek()
+ if c == nil {
+ break // no more chunk in the queue
+ }
+
+ // check timestamp to find if the chunk is due
+ if c.getTimestamp().After(cutOff) {
+ // There is one or more chunk in the queue but none of them are due.
+ // Calculate the next sleep duration here.
+ nextExpire := c.getTimestamp().Add(r.minDelay)
+ d = nextExpire.Sub(enteredAt)
+ break
+ }
+
+ var ok bool
+ if c, ok = r.queue.pop(); !ok {
+ break // no more chunk in the queue
+ }
+
+ blocked := false
+ for i := 0; i < len(r.chunkFilters); i++ {
+ filter := r.chunkFilters[i]
+ if !filter(c) {
+ blocked = true
+ break
+ }
+ }
+ if blocked {
+ continue // discard
+ }
+
+ dstIP := c.getDestinationIP()
+
+ // check if the desination is in our subnet
+ if r.ipv4Net.Contains(dstIP) {
+ // search for the destination NIC
+ var nic NIC
+ if nic, ok = r.nics[dstIP.String()]; !ok {
+ // NIC not found. drop it.
+ r.log.Debugf("[%s] %s unreachable", r.name, c.String())
+ continue
+ }
+
+ // found the NIC, forward the chunk to the NIC.
+ // call to NIC must unlock mutex
+ r.mutex.Unlock()
+ nic.onInboundChunk(c)
+ r.mutex.Lock()
+ continue
+ }
+
+ // the destination is outside of this subnet
+ // is this WAN?
+ if r.parent == nil {
+ // this WAN. No route for this chunk
+ r.log.Debugf("[%s] no route found for %s", r.name, c.String())
+ continue
+ }
+
+ // Pass it to the parent via NAT
+ toParent, err := r.nat.translateOutbound(c)
+ if err != nil {
+ return 0, err
+ }
+
+ if toParent == nil {
+ continue
+ }
+
+ //nolint:godox
+ /* FIXME: this implementation would introduce a duplicate packet!
+ if r.nat.natType.Hairpining {
+ hairpinned, err := r.nat.translateInbound(toParent)
+ if err != nil {
+ r.log.Warnf("[%s] %s", r.name, err.Error())
+ } else {
+ go func() {
+ r.push(hairpinned)
+ }()
+ }
+ }
+ */
+
+ // call to parent router mutex unlock mutex
+ r.mutex.Unlock()
+ r.parent.push(toParent)
+ r.mutex.Lock()
+ }
+
+ return d, nil
+}
+
+// caller must hold the mutex
+func (r *Router) setRouter(parent *Router) error {
+ r.parent = parent
+ r.resolver.setParent(parent.resolver)
+
+ // when this method is called, one or more IP address has already been assigned by
+ // the parent router.
+ ifc, err := r.getInterface("eth0")
+ if err != nil {
+ return err
+ }
+
+ if len(ifc.addrs) == 0 {
+ return errNoIPAddrEth0
+ }
+
+ mappedIPs := []net.IP{}
+ localIPs := []net.IP{}
+
+ for _, ifcAddr := range ifc.addrs {
+ var ip net.IP
+ switch addr := ifcAddr.(type) {
+ case *net.IPNet:
+ ip = addr.IP
+ case *net.IPAddr: // Do we really need this case?
+ ip = addr.IP
+ default:
+ }
+
+ if ip == nil {
+ continue
+ }
+
+ mappedIPs = append(mappedIPs, ip)
+
+ if locIP := r.staticLocalIPs[ip.String()]; locIP != nil {
+ localIPs = append(localIPs, locIP)
+ }
+ }
+
+ // Set up NAT here
+ if r.natType == nil {
+ r.natType = &NATType{
+ MappingBehavior: EndpointIndependent,
+ FilteringBehavior: EndpointAddrPortDependent,
+ Hairpining: false,
+ PortPreservation: false,
+ MappingLifeTime: 30 * time.Second,
+ }
+ }
+ r.nat, err = newNAT(&natConfig{
+ name: r.name,
+ natType: *r.natType,
+ mappedIPs: mappedIPs,
+ localIPs: localIPs,
+ loggerFactory: r.loggerFactory,
+ })
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (r *Router) onInboundChunk(c Chunk) {
+ fromParent, err := r.nat.translateInbound(c)
+ if err != nil {
+ r.log.Warnf("[%s] %s", r.name, err.Error())
+ return
+ }
+
+ r.push(fromParent)
+}
+
+func (r *Router) getStaticIPs() []net.IP {
+ return r.staticIPs
+}
diff --git a/vendor/github.com/pion/transport/vnet/udpproxy.go b/vendor/github.com/pion/transport/vnet/udpproxy.go
new file mode 100644
index 0000000..c774955
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/udpproxy.go
@@ -0,0 +1,176 @@
+package vnet
+
+import (
+ "net"
+ "sync"
+ "time"
+)
+
+// UDPProxy is a proxy between real server(net.UDPConn) and vnet.UDPConn.
+//
+// High level design:
+// ..............................................
+// : Virtual Network (vnet) :
+// : :
+// +-------+ * 1 +----+ +--------+ :
+// | :App |------------>|:Net|--o<-----|:Router | .............................
+// +-------+ +----+ | | : UDPProxy :
+// : | | +----+ +---------+ +---------+ +--------+
+// : | |--->o--|:Net|-->o-| vnet. |-->o-| net. |--->-| :Real |
+// : | | +----+ | UDPConn | | UDPConn | | Server |
+// : | | : +---------+ +---------+ +--------+
+// : | | ............................:
+// : +--------+ :
+// ...............................................
+type UDPProxy struct {
+ // The router bind to.
+ router *Router
+
+ // Each vnet source, bind to a real socket to server.
+ // key is real server addr, which is net.Addr
+ // value is *aUDPProxyWorker
+ workers sync.Map
+
+ // For each endpoint, we never know when to start and stop proxy,
+ // so we stop the endpoint when timeout.
+ timeout time.Duration
+
+ // For utest, to mock the target real server.
+ // Optional, use the address of received client packet.
+ mockRealServerAddr *net.UDPAddr
+}
+
+// NewProxy create a proxy, the router for this proxy belongs/bind to. If need to proxy for
+// please create a new proxy for each router. For all addresses we proxy, we will create a
+// vnet.Net in this router and proxy all packets.
+func NewProxy(router *Router) (*UDPProxy, error) {
+ v := &UDPProxy{router: router, timeout: 2 * time.Minute}
+ return v, nil
+}
+
+// Close the proxy, stop all workers.
+func (v *UDPProxy) Close() error {
+ // nolint:godox // TODO: FIXME: Do cleanup.
+ return nil
+}
+
+// Proxy starts a worker for server, ignore if already started.
+func (v *UDPProxy) Proxy(client *Net, server *net.UDPAddr) error {
+ // Note that even if the worker exists, it's also ok to create a same worker,
+ // because the router will use the last one, and the real server will see a address
+ // change event after we switch to the next worker.
+ if _, ok := v.workers.Load(server.String()); ok {
+ // nolint:godox // TODO: Need to restart the stopped worker?
+ return nil
+ }
+
+ // Not exists, create a new one.
+ worker := &aUDPProxyWorker{
+ router: v.router, mockRealServerAddr: v.mockRealServerAddr,
+ }
+ v.workers.Store(server.String(), worker)
+
+ return worker.Proxy(client, server)
+}
+
+// A proxy worker for a specified proxy server.
+type aUDPProxyWorker struct {
+ router *Router
+ mockRealServerAddr *net.UDPAddr
+
+ // Each vnet source, bind to a real socket to server.
+ // key is vnet client addr, which is net.Addr
+ // value is *net.UDPConn
+ endpoints sync.Map
+}
+
+func (v *aUDPProxyWorker) Proxy(client *Net, serverAddr *net.UDPAddr) error { // nolint:gocognit
+ // Create vnet for real server by serverAddr.
+ nw := NewNet(&NetConfig{
+ StaticIP: serverAddr.IP.String(),
+ })
+ if err := v.router.AddNet(nw); err != nil {
+ return err
+ }
+
+ // We must create a "same" vnet.UDPConn as the net.UDPConn,
+ // which has the same ip:port, to copy packets between them.
+ vnetSocket, err := nw.ListenUDP("udp4", serverAddr)
+ if err != nil {
+ return err
+ }
+
+ // Got new vnet client, start a new endpoint.
+ findEndpointBy := func(addr net.Addr) (*net.UDPConn, error) {
+ // Exists binding.
+ if value, ok := v.endpoints.Load(addr.String()); ok {
+ // Exists endpoint, reuse it.
+ return value.(*net.UDPConn), nil
+ }
+
+ // The real server we proxy to, for utest to mock it.
+ realAddr := serverAddr
+ if v.mockRealServerAddr != nil {
+ realAddr = v.mockRealServerAddr
+ }
+
+ // Got new vnet client, create new endpoint.
+ realSocket, err := net.DialUDP("udp4", nil, realAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ // Bind address.
+ v.endpoints.Store(addr.String(), realSocket)
+
+ // Got packet from real serverAddr, we should proxy it to vnet.
+ // nolint:godox // TODO: FIXME: Do cleanup.
+ go func(vnetClientAddr net.Addr) {
+ buf := make([]byte, 1500)
+ for {
+ n, _, err := realSocket.ReadFrom(buf)
+ if err != nil {
+ return
+ }
+
+ if n <= 0 {
+ continue // Drop packet
+ }
+
+ if _, err := vnetSocket.WriteTo(buf[:n], vnetClientAddr); err != nil {
+ return
+ }
+ }
+ }(addr)
+
+ return realSocket, nil
+ }
+
+ // Start a proxy goroutine.
+ // nolint:godox // TODO: FIXME: Do cleanup.
+ go func() {
+ buf := make([]byte, 1500)
+
+ for {
+ n, addr, err := vnetSocket.ReadFrom(buf)
+ if err != nil {
+ return
+ }
+
+ if n <= 0 || addr == nil {
+ continue // Drop packet
+ }
+
+ realSocket, err := findEndpointBy(addr)
+ if err != nil {
+ continue // Drop packet.
+ }
+
+ if _, err := realSocket.Write(buf[:n]); err != nil {
+ return
+ }
+ }
+ }()
+
+ return nil
+}
diff --git a/vendor/github.com/pion/transport/vnet/vnet.go b/vendor/github.com/pion/transport/vnet/vnet.go
new file mode 100644
index 0000000..bfe0f0f
--- /dev/null
+++ b/vendor/github.com/pion/transport/vnet/vnet.go
@@ -0,0 +1,2 @@
+// Package vnet provides a virtual network layer for pion
+package vnet
diff --git a/vendor/github.com/pion/turn/v2/.gitignore b/vendor/github.com/pion/turn/v2/.gitignore
new file mode 100644
index 0000000..98c12d9
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/.gitignore
@@ -0,0 +1,9 @@
+*.sw[poe]
+examples/turn-client/tcp/tcp
+examples/turn-client/udp/udp
+examples/turn-server/add-software-attribute/add-software-attribute
+examples/turn-server/log/log
+examples/turn-server/simple/simple
+examples/turn-server/tcp/tcp
+examples/lt-cred-generator/lt-cred-generator
+examples/turn-server/lt-cred/lt-cred
diff --git a/vendor/github.com/pion/turn/v2/.golangci.yml b/vendor/github.com/pion/turn/v2/.golangci.yml
new file mode 100644
index 0000000..8e4185a
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/.golangci.yml
@@ -0,0 +1,88 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/turn/v2/.goreleaser.yml b/vendor/github.com/pion/turn/v2/.goreleaser.yml
new file mode 100644
index 0000000..c7f2efd
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/.goreleaser.yml
@@ -0,0 +1,109 @@
+before:
+ hooks:
+ - go mod tidy
+
+archives:
+- replacements:
+ darwin: Darwin
+ linux: Linux
+ windows: Windows
+ 386: i386
+ amd64: x86_64
+
+checksum:
+ name_template: 'checksums.txt'
+
+snapshot:
+ name_template: "{{ .Tag }}-next"
+
+changelog:
+ sort: asc
+ filters:
+ exclude:
+ - '^docs:'
+ - '^test:'
+
+builds:
+ - binary: turn-client-tcp
+ id: turn-client-tcp
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./examples/turn-client/tcp
+
+ - binary: turn-client-udp
+ id: turn-client-udp
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./examples/turn-client/udp
+
+ - binary: turn-server-add-software-attribute
+ id: turn-server-add-software-attribute
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./examples/turn-server/add-software-attribute
+
+ - binary: turn-server-log
+ id: turn-server-log
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./examples/turn-server/log
+
+ - binary: turn-server-simple
+ id: turn-server-simple
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./examples/turn-server/simple/
+
+ - binary: turn-server-tcp
+ id: turn-server-tcp
+ goos:
+ - darwin
+ - windows
+ - linux
+ - freebsd
+ goarch:
+ - amd64
+ - 386
+ env:
+ - CGO_ENABLED=0
+ main: ./examples/turn-server/tcp/
diff --git a/vendor/github.com/pion/turn/v2/DESIGN.md b/vendor/github.com/pion/turn/v2/DESIGN.md
new file mode 100644
index 0000000..2c52466
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/DESIGN.md
@@ -0,0 +1,31 @@
+# Why Pion TURN
+TURN servers aren't exactly a hot technology, they are usually an after thought when building something. Most of the time
+beginners build an interesting WebRTC application, but at the very end realize they need a TURN server. It is really frustrating when you
+want to share your cool new project, only to realize you have to run another service.
+
+Then you find yourself building from source, fighting with config files and making changes you don't fully understand. Pion TURN was born
+hoping to solve these frustrations. These are the guiding principals/features that define pion-turn.
+
+## Easy setup
+simple-turn is a statically built TURN server, configured by environment variables. The entire install setup is 5 commands, on any platform!
+The goal is that anyone should be able to run a TURN server on any platform.
+
+## Integration first
+pion-turn makes no assumptions about how you authenticate users, how you log, or even your topology! Instead of running a dedicated TURN server you
+can inherit from github.com/pion/turn and set whatever logger you want.
+
+## Embeddable
+You can add this to an existing service. This means all your config files stay homogeneous instead of having the mismatch that makes it harder to manage your services.
+For small setups it is usually an overkill to deploy dedicated TURN servers, this makes it easier to solve the problems you care about.
+
+## Safe
+Golang provides a great foundation to build safe network services. Especially when running a networked service that is highly concurrent bugs can be devastating.
+
+## Readable
+All network interaction is commented with a link to the spec. This makes learning and debugging easier, the TURN server was written to also serve as a guide for others.
+
+## Tested
+Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on.
+
+## Shared libraries
+Every pion product is built using shared libraries, allowing others to build things using existing tested STUN and TURN tools.
diff --git a/vendor/github.com/pion/turn/v2/LICENSE.md b/vendor/github.com/pion/turn/v2/LICENSE.md
new file mode 100644
index 0000000..5cc9cbd
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/LICENSE.md
@@ -0,0 +1,7 @@
+Copyright 2018 Pion LLC
+
+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.
diff --git a/vendor/github.com/pion/turn/v2/README.md b/vendor/github.com/pion/turn/v2/README.md
new file mode 100644
index 0000000..372b286
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/README.md
@@ -0,0 +1,96 @@
+<h1 align="center">
+ <a href="https://pion.ly"><img src="./.github/gopher-pion.png" alt="Pion TURN" height="250px"></a>
+ <br>
+ Pion TURN
+ <br>
+</h1>
+<h4 align="center">A toolkit for building TURN clients and servers in Go</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-turn-gray.svg?longCache=true&colorB=brightgreen" alt="Pion TURN"></a>
+ <a href="http://gophers.slack.com/messages/pion"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <a href="https://github.com/pion/awesome-pion" alt="Awesome Pion"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/turn"><img src="https://travis-ci.org/pion/turn.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/turn/v2"><img src="https://godoc.org/github.com/pion/turn?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/turn"><img src="https://codecov.io/gh/pion/turn/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/turn"><img src="https://goreportcard.com/badge/github.com/pion/turn" alt="Go Report Card"></a>
+ <a href="https://www.codacy.com/app/pion/turn"><img src="https://api.codacy.com/project/badge/Grade/d53ec6c70576476cb16c140c2964afde" alt="Codacy Badge"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+Pion TURN is a Go toolkit for building TURN servers and clients. We wrote it to solve problems we had when building RTC projects.
+
+* **Deployable** - Use modern tooling of the Go ecosystem. Stop generating config files.
+* **Embeddable** - Include `pion/turn` in your existing applications. No need to manage another service.
+* **Extendable** - TURN as an API so you can easily integrate with your existing monitoring and metrics.
+* **Maintainable** - `pion/turn` is simple and well documented. Designed for learning and easy debugging.
+* **Portable** - Quickly deploy to multiple architectures/platforms just by setting an environment variable.
+* **Safe** - Stability and safety is important for network services. Go provides everything we need.
+* **Scalable** - Create allocations and mutate state at runtime. Designed to make scaling easy.
+
+# Using
+`pion/turn` is an API for building STUN/TURN clients and servers, not a binary you deploy then configure. It may require copying our examples and
+making minor modifications to fit your need, no knowledge of Go is required however. You may be able to download the pre-made binaries of our examples
+if you wish to get started quickly.
+
+The advantage of this is that you don't need to deal with complicated config files, or custom APIs to modify the state of Pion TURN.
+After you instantiate an instance of a Pion TURN server or client you interact with it like any library. The quickest way to get started is to look at the
+[examples](examples) or [GoDoc](https://godoc.org/github.com/pion/turn)
+
+# Examples
+We try to cover most common use cases in [examples](examples). If more examples could be helpful please file an issue, we are always looking
+to expand and improve `pion/turn` to make it easier for developers.
+
+To build any example you just need to run `go build` in the directory of the example you care about.
+It is also very easy to [cross compile](https://dave.cheney.net/2015/08/22/cross-compilation-with-go-1-5) Go programs.
+
+You can also see `pion/turn` usage in [pion/ice](https://github.com/pion/ice)
+
+# [FAQ](https://github.com/pion/webrtc/wiki/FAQ)
+
+### RFCs
+#### Implemented
+* [RFC 5389: Session Traversal Utilities for NAT (STUN)](https://tools.ietf.org/html/rfc5389)
+* [RFC 5766: Traversal Using Relays around NAT (TURN)](https://tools.ietf.org/html/rfc5766)
+
+#### Planned
+* [RFC 6062: Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations](https://tools.ietf.org/html/rfc6062)
+* [RFC 6156: Traversal Using Relays around NAT (TURN) Extension for IPv6](https://tools.ietf.org/html/rfc6156)
+
+### Community
+Pion has an active community on the [Golang Slack](https://pion.ly/slack). Sign up and join the **#pion** channel for discussions and support.
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+### Contributing
+Check out the [CONTRIBUTING.md](CONTRIBUTING.md) to join the group of amazing people making this project possible:
+
+* [Michiel De Backker](https://github.com/backkem) - *Documentation*
+* [Ingmar Wittkau](https://github.com/iwittkau) - *STUN client*
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [jose nazario](https://github.com/paralax) - *Documentation*
+* [Mészáros Mihály](https://github.com/misi) - *Documentation*
+* [Mike Santry](https://github.com/santrym) - *Mascot*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [winds2016](https://github.com/winds2016) - *Windows platform testing*
+* [songjiayang](https://github.com/songjiayang) - *SongJiaYang*
+* [Yutaka Takeda](https://github.com/enobufs) - *vnet*
+* [namreg](https://github.com/namreg) - *Igor German*
+* [Aleksandr Razumov](https://github.com/ernado) - *protocol*
+* [Robert Eperjesi](https://github.com/epes)
+* [Lukas Rezek](https://github.com/lrezek)
+* [Hugo Arregui](https://github.com/hugoArregui)
+* [Aaron France](https://github.com/AeroNotix)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Tom Clift](https://github.com/tclift)
+* [lllf](https://github.com/LittleLightLittleFire)
+* nindolabs (Marouane)
+* [Onwuka Gideon](https://github.com/dongido001)
+* [Herman Banken](https://github.com/hermanbanken)
+* [Jannis Mattheis](https://github.com/jmattheis)
+
+### License
+MIT License - see [LICENSE.md](LICENSE.md) for full text
+
+
diff --git a/vendor/github.com/pion/turn/v2/client.go b/vendor/github.com/pion/turn/v2/client.go
new file mode 100644
index 0000000..b04e7d1
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/client.go
@@ -0,0 +1,569 @@
+package turn
+
+import (
+ b64 "encoding/base64"
+ "fmt"
+ "math"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+ "github.com/pion/transport/vnet"
+ "github.com/pion/turn/v2/internal/client"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+const (
+ defaultRTO = 200 * time.Millisecond
+ maxRtxCount = 7 // total 7 requests (Rc)
+ maxDataBufferSize = math.MaxUint16 // message size limit for Chromium
+)
+
+// interval [msec]
+// 0: 0 ms +500
+// 1: 500 ms +1000
+// 2: 1500 ms +2000
+// 3: 3500 ms +4000
+// 4: 7500 ms +8000
+// 5: 15500 ms +16000
+// 6: 31500 ms +32000
+// -: 63500 ms failed
+
+// ClientConfig is a bag of config parameters for Client.
+type ClientConfig struct {
+ STUNServerAddr string // STUN server address (e.g. "stun.abc.com:3478")
+ TURNServerAddr string // TURN server addrees (e.g. "turn.abc.com:3478")
+ Username string
+ Password string
+ Realm string
+ Software string
+ RTO time.Duration
+ Conn net.PacketConn // Listening socket (net.PacketConn)
+ LoggerFactory logging.LoggerFactory
+ Net *vnet.Net
+}
+
+// Client is a STUN server client
+type Client struct {
+ conn net.PacketConn // read-only
+ stunServ net.Addr // read-only
+ turnServ net.Addr // read-only
+ stunServStr string // read-only, used for dmuxing
+ turnServStr string // read-only, used for dmuxing
+ username stun.Username // read-only
+ password string // read-only
+ realm stun.Realm // read-only
+ integrity stun.MessageIntegrity // read-only
+ software stun.Software // read-only
+ trMap *client.TransactionMap // thread-safe
+ rto time.Duration // read-only
+ relayedConn *client.UDPConn // protected by mutex ***
+ allocTryLock client.TryLock // thread-safe
+ listenTryLock client.TryLock // thread-safe
+ net *vnet.Net // read-only
+ mutex sync.RWMutex // thread-safe
+ mutexTrMap sync.Mutex // thread-safe
+ log logging.LeveledLogger // read-only
+}
+
+// NewClient returns a new Client instance. listeningAddress is the address and port to listen on, default "0.0.0.0:0"
+func NewClient(config *ClientConfig) (*Client, error) {
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ log := loggerFactory.NewLogger("turnc")
+
+ if config.Conn == nil {
+ return nil, errNilConn
+ }
+
+ if config.Net == nil {
+ config.Net = vnet.NewNet(nil) // defaults to native operation
+ } else if config.Net.IsVirtual() {
+ log.Warn("vnet is enabled")
+ }
+
+ var stunServ, turnServ net.Addr
+ var stunServStr, turnServStr string
+ var err error
+ if len(config.STUNServerAddr) > 0 {
+ log.Debugf("resolving %s", config.STUNServerAddr)
+ stunServ, err = config.Net.ResolveUDPAddr("udp4", config.STUNServerAddr)
+ if err != nil {
+ return nil, err
+ }
+ stunServStr = stunServ.String()
+ log.Debugf("stunServ: %s", stunServStr)
+ }
+ if len(config.TURNServerAddr) > 0 {
+ log.Debugf("resolving %s", config.TURNServerAddr)
+ turnServ, err = config.Net.ResolveUDPAddr("udp4", config.TURNServerAddr)
+ if err != nil {
+ return nil, err
+ }
+ turnServStr = turnServ.String()
+ log.Debugf("turnServ: %s", turnServStr)
+ }
+
+ rto := defaultRTO
+ if config.RTO > 0 {
+ rto = config.RTO
+ }
+
+ c := &Client{
+ conn: config.Conn,
+ stunServ: stunServ,
+ turnServ: turnServ,
+ stunServStr: stunServStr,
+ turnServStr: turnServStr,
+ username: stun.NewUsername(config.Username),
+ password: config.Password,
+ realm: stun.NewRealm(config.Realm),
+ software: stun.NewSoftware(config.Software),
+ net: config.Net,
+ trMap: client.NewTransactionMap(),
+ rto: rto,
+ log: log,
+ }
+
+ return c, nil
+}
+
+// TURNServerAddr return the TURN server address
+func (c *Client) TURNServerAddr() net.Addr {
+ return c.turnServ
+}
+
+// STUNServerAddr return the STUN server address
+func (c *Client) STUNServerAddr() net.Addr {
+ return c.stunServ
+}
+
+// Username returns username
+func (c *Client) Username() stun.Username {
+ return c.username
+}
+
+// Realm return realm
+func (c *Client) Realm() stun.Realm {
+ return c.realm
+}
+
+// WriteTo sends data to the specified destination using the base socket.
+func (c *Client) WriteTo(data []byte, to net.Addr) (int, error) {
+ return c.conn.WriteTo(data, to)
+}
+
+// Listen will have this client start listening on the conn provided via the config.
+// This is optional. If not used, you will need to call HandleInbound method
+// to supply incoming data, instead.
+func (c *Client) Listen() error {
+ if err := c.listenTryLock.Lock(); err != nil {
+ return fmt.Errorf("%w: %s", errAlreadyListening, err.Error())
+ }
+
+ go func() {
+ buf := make([]byte, maxDataBufferSize)
+ for {
+ n, from, err := c.conn.ReadFrom(buf)
+ if err != nil {
+ c.log.Debugf("exiting read loop: %s", err.Error())
+ break
+ }
+
+ _, err = c.HandleInbound(buf[:n], from)
+ if err != nil {
+ c.log.Debugf("exiting read loop: %s", err.Error())
+ break
+ }
+ }
+
+ c.listenTryLock.Unlock()
+ }()
+
+ return nil
+}
+
+// Close closes this client
+func (c *Client) Close() {
+ c.mutexTrMap.Lock()
+ defer c.mutexTrMap.Unlock()
+
+ c.trMap.CloseAndDeleteAll()
+}
+
+// TransactionID & Base64: https://play.golang.org/p/EEgmJDI971P
+
+// SendBindingRequestTo sends a new STUN request to the given transport address
+func (c *Client) SendBindingRequestTo(to net.Addr) (net.Addr, error) {
+ attrs := []stun.Setter{stun.TransactionID, stun.BindingRequest}
+ if len(c.software) > 0 {
+ attrs = append(attrs, c.software)
+ }
+
+ msg, err := stun.Build(attrs...)
+ if err != nil {
+ return nil, err
+ }
+ trRes, err := c.PerformTransaction(msg, to, false)
+ if err != nil {
+ return nil, err
+ }
+
+ var reflAddr stun.XORMappedAddress
+ if err := reflAddr.GetFrom(trRes.Msg); err != nil {
+ return nil, err
+ }
+
+ return &net.UDPAddr{
+ IP: reflAddr.IP,
+ Port: reflAddr.Port,
+ }, nil
+}
+
+// SendBindingRequest sends a new STUN request to the STUN server
+func (c *Client) SendBindingRequest() (net.Addr, error) {
+ if c.stunServ == nil {
+ return nil, errSTUNServerAddressNotSet
+ }
+ return c.SendBindingRequestTo(c.stunServ)
+}
+
+// Allocate sends a TURN allocation request to the given transport address
+func (c *Client) Allocate() (net.PacketConn, error) {
+ if err := c.allocTryLock.Lock(); err != nil {
+ return nil, fmt.Errorf("%w: %s", errOneAllocateOnly, err.Error())
+ }
+ defer c.allocTryLock.Unlock()
+
+ relayedConn := c.relayedUDPConn()
+ if relayedConn != nil {
+ return nil, fmt.Errorf("%w: %s", errAlreadyAllocated, relayedConn.LocalAddr().String())
+ }
+
+ msg, err := stun.Build(
+ stun.TransactionID,
+ stun.NewType(stun.MethodAllocate, stun.ClassRequest),
+ proto.RequestedTransport{Protocol: proto.ProtoUDP},
+ stun.Fingerprint,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ trRes, err := c.PerformTransaction(msg, c.turnServ, false)
+ if err != nil {
+ return nil, err
+ }
+
+ res := trRes.Msg
+
+ // Anonymous allocate failed, trying to authenticate.
+ var nonce stun.Nonce
+ if err = nonce.GetFrom(res); err != nil {
+ return nil, err
+ }
+ if err = c.realm.GetFrom(res); err != nil {
+ return nil, err
+ }
+ c.realm = append([]byte(nil), c.realm...)
+ c.integrity = stun.NewLongTermIntegrity(
+ c.username.String(), c.realm.String(), c.password,
+ )
+ // Trying to authorize.
+ msg, err = stun.Build(
+ stun.TransactionID,
+ stun.NewType(stun.MethodAllocate, stun.ClassRequest),
+ proto.RequestedTransport{Protocol: proto.ProtoUDP},
+ &c.username,
+ &c.realm,
+ &nonce,
+ &c.integrity,
+ stun.Fingerprint,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ trRes, err = c.PerformTransaction(msg, c.turnServ, false)
+ if err != nil {
+ return nil, err
+ }
+ res = trRes.Msg
+
+ if res.Type.Class == stun.ClassErrorResponse {
+ var code stun.ErrorCodeAttribute
+ if err = code.GetFrom(res); err == nil {
+ return nil, fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113
+ }
+ return nil, fmt.Errorf("%s", res.Type) //nolint:goerr113
+ }
+
+ // Getting relayed addresses from response.
+ var relayed proto.RelayedAddress
+ if err := relayed.GetFrom(res); err != nil {
+ return nil, err
+ }
+ relayedAddr := &net.UDPAddr{
+ IP: relayed.IP,
+ Port: relayed.Port,
+ }
+
+ // Getting lifetime from response
+ var lifetime proto.Lifetime
+ if err := lifetime.GetFrom(res); err != nil {
+ return nil, err
+ }
+
+ relayedConn = client.NewUDPConn(&client.UDPConnConfig{
+ Observer: c,
+ RelayedAddr: relayedAddr,
+ Integrity: c.integrity,
+ Nonce: nonce,
+ Lifetime: lifetime.Duration,
+ Log: c.log,
+ })
+
+ c.setRelayedUDPConn(relayedConn)
+
+ return relayedConn, nil
+}
+
+// PerformTransaction performs STUN transaction
+func (c *Client) PerformTransaction(msg *stun.Message, to net.Addr, ignoreResult bool) (client.TransactionResult,
+ error) {
+ trKey := b64.StdEncoding.EncodeToString(msg.TransactionID[:])
+
+ raw := make([]byte, len(msg.Raw))
+ copy(raw, msg.Raw)
+
+ tr := client.NewTransaction(&client.TransactionConfig{
+ Key: trKey,
+ Raw: raw,
+ To: to,
+ Interval: c.rto,
+ IgnoreResult: ignoreResult,
+ })
+
+ c.trMap.Insert(trKey, tr)
+
+ c.log.Tracef("start %s transaction %s to %s", msg.Type, trKey, tr.To.String())
+ _, err := c.conn.WriteTo(tr.Raw, to)
+ if err != nil {
+ return client.TransactionResult{}, err
+ }
+
+ tr.StartRtxTimer(c.onRtxTimeout)
+
+ // If dontWait is true, get the transaction going and return immediately
+ if ignoreResult {
+ return client.TransactionResult{}, nil
+ }
+
+ res := tr.WaitForResult()
+ if res.Err != nil {
+ return res, res.Err
+ }
+ return res, nil
+}
+
+// OnDeallocated is called when deallocation of relay address has been complete.
+// (Called by UDPConn)
+func (c *Client) OnDeallocated(relayedAddr net.Addr) {
+ c.setRelayedUDPConn(nil)
+}
+
+// HandleInbound handles data received.
+// This method handles incoming packet demultiplex it by the source address
+// and the types of the message.
+// This return a booleen (handled or not) and if there was an error.
+// Caller should check if the packet was handled by this client or not.
+// If not handled, it is assumed that the packet is application data.
+// If an error is returned, the caller should discard the packet regardless.
+func (c *Client) HandleInbound(data []byte, from net.Addr) (bool, error) {
+ // +-------------------+-------------------------------+
+ // | Return Values | |
+ // +-------------------+ Meaning / Action |
+ // | handled | error | |
+ // |=========+=========+===============================+
+ // | false | nil | Handle the packet as app data |
+ // |---------+---------+-------------------------------+
+ // | true | nil | Nothing to do |
+ // |---------+---------+-------------------------------+
+ // | false | error | (shouldn't happen) |
+ // |---------+---------+-------------------------------+
+ // | true | error | Error occurred while handling |
+ // +---------+---------+-------------------------------+
+ // Possible causes of the error:
+ // - Malformed packet (parse error)
+ // - STUN message was a request
+ // - Non-STUN message from the STUN server
+
+ switch {
+ case stun.IsMessage(data):
+ return true, c.handleSTUNMessage(data, from)
+ case proto.IsChannelData(data):
+ return true, c.handleChannelData(data)
+ case len(c.stunServStr) != 0 && from.String() == c.stunServStr:
+ // received from STUN server but it is not a STUN message
+ return true, errNonSTUNMessage
+ default:
+ // assume, this is an application data
+ c.log.Tracef("non-STUN/TURN packect, unhandled")
+ }
+
+ return false, nil
+}
+
+func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error {
+ raw := make([]byte, len(data))
+ copy(raw, data)
+
+ msg := &stun.Message{Raw: raw}
+ if err := msg.Decode(); err != nil {
+ return fmt.Errorf("%w: %s", errFailedToDecodeSTUN, err.Error())
+ }
+
+ if msg.Type.Class == stun.ClassRequest {
+ return fmt.Errorf("%w : %s", errUnexpectedSTUNRequestMessage, msg.String())
+ }
+
+ if msg.Type.Class == stun.ClassIndication {
+ if msg.Type.Method == stun.MethodData {
+ var peerAddr proto.PeerAddress
+ if err := peerAddr.GetFrom(msg); err != nil {
+ return err
+ }
+ from = &net.UDPAddr{
+ IP: peerAddr.IP,
+ Port: peerAddr.Port,
+ }
+
+ var data proto.Data
+ if err := data.GetFrom(msg); err != nil {
+ return err
+ }
+
+ c.log.Debugf("data indication received from %s", from.String())
+
+ relayedConn := c.relayedUDPConn()
+ if relayedConn == nil {
+ c.log.Debug("no relayed conn allocated")
+ return nil // silently discard
+ }
+
+ relayedConn.HandleInbound(data, from)
+ }
+ return nil
+ }
+
+ // This is a STUN response message (transactional)
+ // The type is either:
+ // - stun.ClassSuccessResponse
+ // - stun.ClassErrorResponse
+
+ trKey := b64.StdEncoding.EncodeToString(msg.TransactionID[:])
+
+ c.mutexTrMap.Lock()
+ tr, ok := c.trMap.Find(trKey)
+ if !ok {
+ c.mutexTrMap.Unlock()
+ // silently discard
+ c.log.Debugf("no transaction for %s", msg.String())
+ return nil
+ }
+
+ // End the transaction
+ tr.StopRtxTimer()
+ c.trMap.Delete(trKey)
+ c.mutexTrMap.Unlock()
+
+ if !tr.WriteResult(client.TransactionResult{
+ Msg: msg,
+ From: from,
+ Retries: tr.Retries(),
+ }) {
+ c.log.Debugf("no listener for %s", msg.String())
+ }
+
+ return nil
+}
+
+func (c *Client) handleChannelData(data []byte) error {
+ chData := &proto.ChannelData{
+ Raw: make([]byte, len(data)),
+ }
+ copy(chData.Raw, data)
+ if err := chData.Decode(); err != nil {
+ return err
+ }
+
+ relayedConn := c.relayedUDPConn()
+ if relayedConn == nil {
+ c.log.Debug("no relayed conn allocated")
+ return nil // silently discard
+ }
+
+ addr, ok := relayedConn.FindAddrByChannelNumber(uint16(chData.Number))
+ if !ok {
+ return fmt.Errorf("%w: %d", errChannelBindNotFound, int(chData.Number))
+ }
+
+ c.log.Tracef("channel data received from %s (ch=%d)", addr.String(), int(chData.Number))
+
+ relayedConn.HandleInbound(chData.Data, addr)
+ return nil
+}
+
+func (c *Client) onRtxTimeout(trKey string, nRtx int) {
+ c.mutexTrMap.Lock()
+ defer c.mutexTrMap.Unlock()
+
+ tr, ok := c.trMap.Find(trKey)
+ if !ok {
+ return // already gone
+ }
+
+ if nRtx == maxRtxCount {
+ // all retransmisstions failed
+ c.trMap.Delete(trKey)
+ if !tr.WriteResult(client.TransactionResult{
+ Err: fmt.Errorf("%w %s", errAllRetransmissionsFailed, trKey),
+ }) {
+ c.log.Debug("no listener for transaction")
+ }
+ return
+ }
+
+ c.log.Tracef("retransmitting transaction %s to %s (nRtx=%d)",
+ trKey, tr.To.String(), nRtx)
+ _, err := c.conn.WriteTo(tr.Raw, tr.To)
+ if err != nil {
+ c.trMap.Delete(trKey)
+ if !tr.WriteResult(client.TransactionResult{
+ Err: fmt.Errorf("%w %s", errFailedToRetransmitTransaction, trKey),
+ }) {
+ c.log.Debug("no listener for transaction")
+ }
+ return
+ }
+ tr.StartRtxTimer(c.onRtxTimeout)
+}
+
+func (c *Client) setRelayedUDPConn(conn *client.UDPConn) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ c.relayedConn = conn
+}
+
+func (c *Client) relayedUDPConn() *client.UDPConn {
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+
+ return c.relayedConn
+}
diff --git a/vendor/github.com/pion/turn/v2/codecov.yml b/vendor/github.com/pion/turn/v2/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/turn/v2/errors.go b/vendor/github.com/pion/turn/v2/errors.go
new file mode 100644
index 0000000..12d5e0e
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/errors.go
@@ -0,0 +1,28 @@
+package turn
+
+import "errors"
+
+var (
+ errRelayAddressInvalid = errors.New("turn: RelayAddress must be valid IP to use RelayAddressGeneratorStatic")
+ errNoAvailableConns = errors.New("turn: PacketConnConfigs and ConnConfigs are empty, unable to proceed")
+ errConnUnset = errors.New("turn: PacketConnConfig must have a non-nil Conn")
+ errListenerUnset = errors.New("turn: ListenerConfig must have a non-nil Listener")
+ errListeningAddressInvalid = errors.New("turn: RelayAddressGenerator has invalid ListeningAddress")
+ errRelayAddressGeneratorUnset = errors.New("turn: RelayAddressGenerator in RelayConfig is unset")
+ errMaxRetriesExceeded = errors.New("turn: max retries exceeded")
+ errMaxPortNotZero = errors.New("turn: MaxPort must be not 0")
+ errMinPortNotZero = errors.New("turn: MaxPort must be not 0")
+ errNilConn = errors.New("turn: conn cannot not be nil")
+ errTODO = errors.New("turn: TODO")
+ errAlreadyListening = errors.New("turn: already listening")
+ errFailedToClose = errors.New("turn: Server failed to close")
+ errFailedToRetransmitTransaction = errors.New("turn: failed to retransmit transaction")
+ errAllRetransmissionsFailed = errors.New("all retransmissions failed for")
+ errChannelBindNotFound = errors.New("no binding found for channel")
+ errSTUNServerAddressNotSet = errors.New("STUN server address is not set for the client")
+ errOneAllocateOnly = errors.New("only one Allocate() caller is allowed")
+ errAlreadyAllocated = errors.New("already allocated")
+ errNonSTUNMessage = errors.New("non-STUN message from STUN server")
+ errFailedToDecodeSTUN = errors.New("failed to decode STUN message")
+ errUnexpectedSTUNRequestMessage = errors.New("unexpected STUN request message")
+)
diff --git a/vendor/github.com/pion/turn/v2/go.mod b/vendor/github.com/pion/turn/v2/go.mod
new file mode 100644
index 0000000..de599c3
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/go.mod
@@ -0,0 +1,11 @@
+module github.com/pion/turn/v2
+
+go 1.13
+
+require (
+ github.com/pion/logging v0.2.2
+ github.com/pion/randutil v0.1.0
+ github.com/pion/stun v0.3.5
+ github.com/pion/transport v0.10.1
+ github.com/stretchr/testify v1.6.1
+)
diff --git a/vendor/github.com/pion/turn/v2/go.sum b/vendor/github.com/pion/turn/v2/go.sum
new file mode 100644
index 0000000..7756c37
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/go.sum
@@ -0,0 +1,28 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
+github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
+github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM=
+github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go b/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go
new file mode 100644
index 0000000..d1cb8c6
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go
@@ -0,0 +1,259 @@
+// Package allocation contains all CRUD operations for allocations
+package allocation
+
+import (
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+ "github.com/pion/turn/v2/internal/ipnet"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+// Allocation is tied to a FiveTuple and relays traffic
+// use CreateAllocation and GetAllocation to operate
+type Allocation struct {
+ RelayAddr net.Addr
+ Protocol Protocol
+ TurnSocket net.PacketConn
+ RelaySocket net.PacketConn
+ fiveTuple *FiveTuple
+ permissionsLock sync.RWMutex
+ permissions map[string]*Permission
+ channelBindingsLock sync.RWMutex
+ channelBindings []*ChannelBind
+ lifetimeTimer *time.Timer
+ closed chan interface{}
+ log logging.LeveledLogger
+}
+
+func addr2IPFingerprint(addr net.Addr) string {
+ switch a := addr.(type) {
+ case *net.UDPAddr:
+ return a.IP.String()
+ case *net.TCPAddr: // Do we really need this case?
+ return a.IP.String()
+ }
+ return "" // shoud never happen
+}
+
+// NewAllocation creates a new instance of NewAllocation.
+func NewAllocation(turnSocket net.PacketConn, fiveTuple *FiveTuple, log logging.LeveledLogger) *Allocation {
+ return &Allocation{
+ TurnSocket: turnSocket,
+ fiveTuple: fiveTuple,
+ permissions: make(map[string]*Permission, 64),
+ closed: make(chan interface{}),
+ log: log,
+ }
+}
+
+// GetPermission gets the Permission from the allocation
+func (a *Allocation) GetPermission(addr net.Addr) *Permission {
+ a.permissionsLock.RLock()
+ defer a.permissionsLock.RUnlock()
+
+ return a.permissions[addr2IPFingerprint(addr)]
+}
+
+// AddPermission adds a new permission to the allocation
+func (a *Allocation) AddPermission(p *Permission) {
+ fingerprint := addr2IPFingerprint(p.Addr)
+
+ a.permissionsLock.RLock()
+ existedPermission, ok := a.permissions[fingerprint]
+ a.permissionsLock.RUnlock()
+
+ if ok {
+ existedPermission.refresh(permissionTimeout)
+ return
+ }
+
+ p.allocation = a
+ a.permissionsLock.Lock()
+ a.permissions[fingerprint] = p
+ a.permissionsLock.Unlock()
+
+ p.start(permissionTimeout)
+}
+
+// RemovePermission removes the net.Addr's fingerprint from the allocation's permissions
+func (a *Allocation) RemovePermission(addr net.Addr) {
+ a.permissionsLock.Lock()
+ defer a.permissionsLock.Unlock()
+ delete(a.permissions, addr2IPFingerprint(addr))
+}
+
+// AddChannelBind adds a new ChannelBind to the allocation, it also updates the
+// permissions needed for this ChannelBind
+func (a *Allocation) AddChannelBind(c *ChannelBind, lifetime time.Duration) error {
+ // Check that this channel id isn't bound to another transport address, and
+ // that this transport address isn't bound to another channel number.
+ channelByNumber := a.GetChannelByNumber(c.Number)
+
+ if channelByNumber != a.GetChannelByAddr(c.Peer) {
+ return errSameChannelDifferentPeer
+ }
+
+ // Add or refresh this channel.
+ if channelByNumber == nil {
+ a.channelBindingsLock.Lock()
+ defer a.channelBindingsLock.Unlock()
+
+ c.allocation = a
+ a.channelBindings = append(a.channelBindings, c)
+ c.start(lifetime)
+
+ // Channel binds also refresh permissions.
+ a.AddPermission(NewPermission(c.Peer, a.log))
+ } else {
+ channelByNumber.refresh(lifetime)
+
+ // Channel binds also refresh permissions.
+ a.AddPermission(NewPermission(channelByNumber.Peer, a.log))
+ }
+
+ return nil
+}
+
+// RemoveChannelBind removes the ChannelBind from this allocation by id
+func (a *Allocation) RemoveChannelBind(number proto.ChannelNumber) bool {
+ a.channelBindingsLock.Lock()
+ defer a.channelBindingsLock.Unlock()
+
+ for i := len(a.channelBindings) - 1; i >= 0; i-- {
+ if a.channelBindings[i].Number == number {
+ a.channelBindings = append(a.channelBindings[:i], a.channelBindings[i+1:]...)
+ return true
+ }
+ }
+
+ return false
+}
+
+// GetChannelByNumber gets the ChannelBind from this allocation by id
+func (a *Allocation) GetChannelByNumber(number proto.ChannelNumber) *ChannelBind {
+ a.channelBindingsLock.RLock()
+ defer a.channelBindingsLock.RUnlock()
+ for _, cb := range a.channelBindings {
+ if cb.Number == number {
+ return cb
+ }
+ }
+ return nil
+}
+
+// GetChannelByAddr gets the ChannelBind from this allocation by net.Addr
+func (a *Allocation) GetChannelByAddr(addr net.Addr) *ChannelBind {
+ a.channelBindingsLock.RLock()
+ defer a.channelBindingsLock.RUnlock()
+ for _, cb := range a.channelBindings {
+ if ipnet.AddrEqual(cb.Peer, addr) {
+ return cb
+ }
+ }
+ return nil
+}
+
+// Refresh updates the allocations lifetime
+func (a *Allocation) Refresh(lifetime time.Duration) {
+ if !a.lifetimeTimer.Reset(lifetime) {
+ a.log.Errorf("Failed to reset allocation timer for %v", a.fiveTuple)
+ }
+}
+
+// Close closes the allocation
+func (a *Allocation) Close() error {
+ select {
+ case <-a.closed:
+ return nil
+ default:
+ }
+ close(a.closed)
+
+ a.lifetimeTimer.Stop()
+
+ a.permissionsLock.RLock()
+ for _, p := range a.permissions {
+ p.lifetimeTimer.Stop()
+ }
+ a.permissionsLock.RUnlock()
+
+ a.channelBindingsLock.RLock()
+ for _, c := range a.channelBindings {
+ c.lifetimeTimer.Stop()
+ }
+ a.channelBindingsLock.RUnlock()
+
+ return a.RelaySocket.Close()
+}
+
+// https://tools.ietf.org/html/rfc5766#section-10.3
+// When the server receives a UDP datagram at a currently allocated
+// relayed transport address, the server looks up the allocation
+// associated with the relayed transport address. The server then
+// checks to see whether the set of permissions for the allocation allow
+// the relaying of the UDP datagram as described in Section 8.
+//
+// If relaying is permitted, then the server checks if there is a
+// channel bound to the peer that sent the UDP datagram (see
+// Section 11). If a channel is bound, then processing proceeds as
+// described in Section 11.7.
+//
+// If relaying is permitted but no channel is bound to the peer, then
+// the server forms and sends a Data indication. The Data indication
+// MUST contain both an XOR-PEER-ADDRESS and a DATA attribute. The DATA
+// attribute is set to the value of the 'data octets' field from the
+// datagram, and the XOR-PEER-ADDRESS attribute is set to the source
+// transport address of the received UDP datagram. The Data indication
+// is then sent on the 5-tuple associated with the allocation.
+
+const rtpMTU = 1500
+
+func (a *Allocation) packetHandler(m *Manager) {
+ buffer := make([]byte, rtpMTU)
+
+ for {
+ n, srcAddr, err := a.RelaySocket.ReadFrom(buffer)
+ if err != nil {
+ m.DeleteAllocation(a.fiveTuple)
+ return
+ }
+
+ a.log.Debugf("relay socket %s received %d bytes from %s",
+ a.RelaySocket.LocalAddr().String(),
+ n,
+ srcAddr.String())
+
+ if channel := a.GetChannelByAddr(srcAddr); channel != nil {
+ channelData := &proto.ChannelData{
+ Data: buffer[:n],
+ Number: channel.Number,
+ }
+ channelData.Encode()
+
+ if _, err = a.TurnSocket.WriteTo(channelData.Raw, a.fiveTuple.SrcAddr); err != nil {
+ a.log.Errorf("Failed to send ChannelData from allocation %v %v", srcAddr, err)
+ }
+ } else if p := a.GetPermission(srcAddr); p != nil {
+ udpAddr := srcAddr.(*net.UDPAddr)
+ peerAddressAttr := proto.PeerAddress{IP: udpAddr.IP, Port: udpAddr.Port}
+ dataAttr := proto.Data(buffer[:n])
+
+ msg, err := stun.Build(stun.TransactionID, stun.NewType(stun.MethodData, stun.ClassIndication), peerAddressAttr, dataAttr)
+ if err != nil {
+ a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err)
+ }
+ a.log.Debugf("relaying message from %s to client at %s",
+ srcAddr.String(),
+ a.fiveTuple.SrcAddr.String())
+ if _, err = a.TurnSocket.WriteTo(msg.Raw, a.fiveTuple.SrcAddr); err != nil {
+ a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err)
+ }
+ } else {
+ a.log.Infof("No Permission or Channel exists for %v on allocation %v", srcAddr, a.RelayAddr.String())
+ }
+ }
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go b/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go
new file mode 100644
index 0000000..9a1271a
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go
@@ -0,0 +1,186 @@
+package allocation
+
+import (
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+// ManagerConfig a bag of config params for Manager.
+type ManagerConfig struct {
+ LeveledLogger logging.LeveledLogger
+ AllocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error)
+ AllocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error)
+}
+
+type reservation struct {
+ token string
+ port int
+}
+
+// Manager is used to hold active allocations
+type Manager struct {
+ lock sync.RWMutex
+ log logging.LeveledLogger
+
+ allocations map[string]*Allocation
+ reservations []*reservation
+
+ allocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error)
+ allocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error)
+}
+
+// NewManager creates a new instance of Manager.
+func NewManager(config ManagerConfig) (*Manager, error) {
+ switch {
+ case config.AllocatePacketConn == nil:
+ return nil, errAllocatePacketConnMustBeSet
+ case config.AllocateConn == nil:
+ return nil, errAllocateConnMustBeSet
+ case config.LeveledLogger == nil:
+ return nil, errLeveledLoggerMustBeSet
+ }
+
+ return &Manager{
+ log: config.LeveledLogger,
+ allocations: make(map[string]*Allocation, 64),
+ allocatePacketConn: config.AllocatePacketConn,
+ allocateConn: config.AllocateConn,
+ }, nil
+}
+
+// GetAllocation fetches the allocation matching the passed FiveTuple
+func (m *Manager) GetAllocation(fiveTuple *FiveTuple) *Allocation {
+ m.lock.RLock()
+ defer m.lock.RUnlock()
+ return m.allocations[fiveTuple.Fingerprint()]
+}
+
+// Close closes the manager and closes all allocations it manages
+func (m *Manager) Close() error {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+
+ for _, a := range m.allocations {
+ if err := a.Close(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// CreateAllocation creates a new allocation and starts relaying
+func (m *Manager) CreateAllocation(fiveTuple *FiveTuple, turnSocket net.PacketConn, requestedPort int, lifetime time.Duration) (*Allocation, error) {
+ switch {
+ case fiveTuple == nil:
+ return nil, errNilFiveTuple
+ case fiveTuple.SrcAddr == nil:
+ return nil, errNilFiveTupleSrcAddr
+ case fiveTuple.DstAddr == nil:
+ return nil, errNilFiveTupleDstAddr
+ case turnSocket == nil:
+ return nil, errNilTurnSocket
+ case lifetime == 0:
+ return nil, errLifetimeZero
+ }
+
+ if a := m.GetAllocation(fiveTuple); a != nil {
+ return nil, fmt.Errorf("%w: %v", errDupeFiveTuple, fiveTuple)
+ }
+ a := NewAllocation(turnSocket, fiveTuple, m.log)
+
+ conn, relayAddr, err := m.allocatePacketConn("udp4", requestedPort)
+ if err != nil {
+ return nil, err
+ }
+
+ a.RelaySocket = conn
+ a.RelayAddr = relayAddr
+
+ m.log.Debugf("listening on relay addr: %s", a.RelayAddr.String())
+
+ a.lifetimeTimer = time.AfterFunc(lifetime, func() {
+ m.DeleteAllocation(a.fiveTuple)
+ })
+
+ m.lock.Lock()
+ m.allocations[fiveTuple.Fingerprint()] = a
+ m.lock.Unlock()
+
+ go a.packetHandler(m)
+ return a, nil
+}
+
+// DeleteAllocation removes an allocation
+func (m *Manager) DeleteAllocation(fiveTuple *FiveTuple) {
+ fingerprint := fiveTuple.Fingerprint()
+
+ m.lock.Lock()
+ allocation := m.allocations[fingerprint]
+ delete(m.allocations, fingerprint)
+ m.lock.Unlock()
+
+ if allocation == nil {
+ return
+ }
+
+ if err := allocation.Close(); err != nil {
+ m.log.Errorf("Failed to close allocation: %v", err)
+ }
+}
+
+// CreateReservation stores the reservation for the token+port
+func (m *Manager) CreateReservation(reservationToken string, port int) {
+ time.AfterFunc(30*time.Second, func() {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ for i := len(m.reservations) - 1; i >= 0; i-- {
+ if m.reservations[i].token == reservationToken {
+ m.reservations = append(m.reservations[:i], m.reservations[i+1:]...)
+ return
+ }
+ }
+ })
+
+ m.lock.Lock()
+ m.reservations = append(m.reservations, &reservation{
+ token: reservationToken,
+ port: port,
+ })
+ m.lock.Unlock()
+}
+
+// GetReservation returns the port for a given reservation if it exists
+func (m *Manager) GetReservation(reservationToken string) (int, bool) {
+ m.lock.RLock()
+ defer m.lock.RUnlock()
+
+ for _, r := range m.reservations {
+ if r.token == reservationToken {
+ return r.port, true
+ }
+ }
+ return 0, false
+}
+
+// GetRandomEvenPort returns a random un-allocated udp4 port
+func (m *Manager) GetRandomEvenPort() (int, error) {
+ conn, addr, err := m.allocatePacketConn("udp4", 0)
+ if err != nil {
+ return 0, err
+ }
+
+ udpAddr, ok := addr.(*net.UDPAddr)
+ if !ok {
+ return 0, errFailedToCastUDPAddr
+ } else if err := conn.Close(); err != nil {
+ return 0, err
+ } else if udpAddr.Port%2 == 1 {
+ return m.GetRandomEvenPort()
+ }
+
+ return udpAddr.Port, nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/channel_bind.go b/vendor/github.com/pion/turn/v2/internal/allocation/channel_bind.go
new file mode 100644
index 0000000..6216369
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/allocation/channel_bind.go
@@ -0,0 +1,43 @@
+package allocation
+
+import (
+ "net"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+// ChannelBind represents a TURN Channel
+// https://tools.ietf.org/html/rfc5766#section-2.5
+type ChannelBind struct {
+ Peer net.Addr
+ Number proto.ChannelNumber
+
+ allocation *Allocation
+ lifetimeTimer *time.Timer
+ log logging.LeveledLogger
+}
+
+// NewChannelBind creates a new ChannelBind
+func NewChannelBind(number proto.ChannelNumber, peer net.Addr, log logging.LeveledLogger) *ChannelBind {
+ return &ChannelBind{
+ Number: number,
+ Peer: peer,
+ log: log,
+ }
+}
+
+func (c *ChannelBind) start(lifetime time.Duration) {
+ c.lifetimeTimer = time.AfterFunc(lifetime, func() {
+ if !c.allocation.RemoveChannelBind(c.Number) {
+ c.log.Errorf("Failed to remove ChannelBind for %v %x %v", c.Number, c.Peer, c.allocation.fiveTuple)
+ }
+ })
+}
+
+func (c *ChannelBind) refresh(lifetime time.Duration) {
+ if !c.lifetimeTimer.Reset(lifetime) {
+ c.log.Errorf("Failed to reset ChannelBind timer for %v %x %v", c.Number, c.Peer, c.allocation.fiveTuple)
+ }
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/errors.go b/vendor/github.com/pion/turn/v2/internal/allocation/errors.go
new file mode 100644
index 0000000..fdd8335
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/allocation/errors.go
@@ -0,0 +1,17 @@
+package allocation
+
+import "errors"
+
+var (
+ errAllocatePacketConnMustBeSet = errors.New("AllocatePacketConn must be set")
+ errAllocateConnMustBeSet = errors.New("AllocateConn must be set")
+ errLeveledLoggerMustBeSet = errors.New("LeveledLogger must be set")
+ errSameChannelDifferentPeer = errors.New("you cannot use the same channel number with different peer")
+ errNilFiveTuple = errors.New("allocations must not be created with nil FivTuple")
+ errNilFiveTupleSrcAddr = errors.New("allocations must not be created with nil FiveTuple.SrcAddr")
+ errNilFiveTupleDstAddr = errors.New("allocations must not be created with nil FiveTuple.DstAddr")
+ errNilTurnSocket = errors.New("allocations must not be created with nil turnSocket")
+ errLifetimeZero = errors.New("allocations must not be created with a lifetime of 0")
+ errDupeFiveTuple = errors.New("allocation attempt created with duplicate FiveTuple")
+ errFailedToCastUDPAddr = errors.New("failed to cast net.Addr to *net.UDPAddr")
+)
diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/five_tuple.go b/vendor/github.com/pion/turn/v2/internal/allocation/five_tuple.go
new file mode 100644
index 0000000..1f2b3b5
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/allocation/five_tuple.go
@@ -0,0 +1,36 @@
+package allocation
+
+import (
+ "fmt"
+ "net"
+)
+
+// Protocol is an enum for relay protocol
+type Protocol uint8
+
+// Network protocols for relay
+const (
+ UDP Protocol = iota
+ TCP
+)
+
+// FiveTuple is the combination (client IP address and port, server IP
+// address and port, and transport protocol (currently one of UDP,
+// TCP, or TLS)) used to communicate between the client and the
+// server. The 5-tuple uniquely identifies this communication
+// stream. The 5-tuple also uniquely identifies the Allocation on
+// the server.
+type FiveTuple struct {
+ Protocol
+ SrcAddr, DstAddr net.Addr
+}
+
+// Equal asserts if two FiveTuples are equal
+func (f *FiveTuple) Equal(b *FiveTuple) bool {
+ return f.Fingerprint() == b.Fingerprint()
+}
+
+// Fingerprint is the identity of a FiveTuple
+func (f *FiveTuple) Fingerprint() string {
+ return fmt.Sprintf("%d_%s_%s", f.Protocol, f.SrcAddr.String(), f.DstAddr.String())
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/permission.go b/vendor/github.com/pion/turn/v2/internal/allocation/permission.go
new file mode 100644
index 0000000..03538eb
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/allocation/permission.go
@@ -0,0 +1,40 @@
+package allocation
+
+import (
+ "net"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+const permissionTimeout = time.Duration(5) * time.Minute
+
+// Permission represents a TURN permission. TURN permissions mimic the address-restricted
+// filtering mechanism of NATs that comply with [RFC4787].
+// https://tools.ietf.org/html/rfc5766#section-2.3
+type Permission struct {
+ Addr net.Addr
+ allocation *Allocation
+ lifetimeTimer *time.Timer
+ log logging.LeveledLogger
+}
+
+// NewPermission create a new Permission
+func NewPermission(addr net.Addr, log logging.LeveledLogger) *Permission {
+ return &Permission{
+ Addr: addr,
+ log: log,
+ }
+}
+
+func (p *Permission) start(lifetime time.Duration) {
+ p.lifetimeTimer = time.AfterFunc(lifetime, func() {
+ p.allocation.RemovePermission(p.Addr)
+ })
+}
+
+func (p *Permission) refresh(lifetime time.Duration) {
+ if !p.lifetimeTimer.Reset(lifetime) {
+ p.log.Errorf("Failed to reset permission timer for %v %v", p.Addr, p.allocation.fiveTuple)
+ }
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/client/binding.go b/vendor/github.com/pion/turn/v2/internal/client/binding.go
new file mode 100644
index 0000000..ee52053
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/client/binding.go
@@ -0,0 +1,151 @@
+package client
+
+import (
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// Chanel number:
+// 0x4000 through 0x7FFF: These values are the allowed channel
+// numbers (16,383 possible values).
+const (
+ minChannelNumber uint16 = 0x4000
+ maxChannelNumber uint16 = 0x7fff
+)
+
+type bindingState int32
+
+const (
+ bindingStateIdle bindingState = iota
+ bindingStateRequest
+ bindingStateReady
+ bindingStateRefresh
+ bindingStateFailed
+)
+
+type binding struct {
+ number uint16 // read-only
+ st bindingState // thread-safe (atomic op)
+ addr net.Addr // read-only
+ mgr *bindingManager // read-only
+ muBind sync.Mutex // thread-safe, for ChannelBind ops
+ _refreshedAt time.Time // protected by mutex
+ mutex sync.RWMutex // thread-safe
+}
+
+func (b *binding) setState(state bindingState) {
+ atomic.StoreInt32((*int32)(&b.st), int32(state))
+}
+
+func (b *binding) state() bindingState {
+ return bindingState(atomic.LoadInt32((*int32)(&b.st)))
+}
+
+func (b *binding) setRefreshedAt(at time.Time) {
+ b.mutex.Lock()
+ defer b.mutex.Unlock()
+
+ b._refreshedAt = at
+}
+
+func (b *binding) refreshedAt() time.Time {
+ b.mutex.RLock()
+ defer b.mutex.RUnlock()
+
+ return b._refreshedAt
+}
+
+// Thread-safe binding map
+type bindingManager struct {
+ chanMap map[uint16]*binding
+ addrMap map[string]*binding
+ next uint16
+ mutex sync.RWMutex
+}
+
+func newBindingManager() *bindingManager {
+ return &bindingManager{
+ chanMap: map[uint16]*binding{},
+ addrMap: map[string]*binding{},
+ next: minChannelNumber,
+ }
+}
+
+func (mgr *bindingManager) assignChannelNumber() uint16 {
+ n := mgr.next
+ if mgr.next == maxChannelNumber {
+ mgr.next = minChannelNumber
+ } else {
+ mgr.next++
+ }
+ return n
+}
+
+func (mgr *bindingManager) create(addr net.Addr) *binding {
+ mgr.mutex.Lock()
+ defer mgr.mutex.Unlock()
+
+ b := &binding{
+ number: mgr.assignChannelNumber(),
+ addr: addr,
+ mgr: mgr,
+ _refreshedAt: time.Now(),
+ }
+
+ mgr.chanMap[b.number] = b
+ mgr.addrMap[b.addr.String()] = b
+ return b
+}
+
+func (mgr *bindingManager) findByAddr(addr net.Addr) (*binding, bool) {
+ mgr.mutex.RLock()
+ defer mgr.mutex.RUnlock()
+
+ b, ok := mgr.addrMap[addr.String()]
+ return b, ok
+}
+
+func (mgr *bindingManager) findByNumber(number uint16) (*binding, bool) {
+ mgr.mutex.RLock()
+ defer mgr.mutex.RUnlock()
+
+ b, ok := mgr.chanMap[number]
+ return b, ok
+}
+
+func (mgr *bindingManager) deleteByAddr(addr net.Addr) bool {
+ mgr.mutex.Lock()
+ defer mgr.mutex.Unlock()
+
+ b, ok := mgr.addrMap[addr.String()]
+ if !ok {
+ return false
+ }
+
+ delete(mgr.addrMap, addr.String())
+ delete(mgr.chanMap, b.number)
+ return true
+}
+
+func (mgr *bindingManager) deleteByNumber(number uint16) bool {
+ mgr.mutex.Lock()
+ defer mgr.mutex.Unlock()
+
+ b, ok := mgr.chanMap[number]
+ if !ok {
+ return false
+ }
+
+ delete(mgr.addrMap, b.addr.String())
+ delete(mgr.chanMap, number)
+ return true
+}
+
+func (mgr *bindingManager) size() int {
+ mgr.mutex.RLock()
+ defer mgr.mutex.RUnlock()
+
+ return len(mgr.chanMap)
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/client/conn.go b/vendor/github.com/pion/turn/v2/internal/client/conn.go
new file mode 100644
index 0000000..8a2b1ae
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/client/conn.go
@@ -0,0 +1,613 @@
+// Package client implements the API for a TURN client
+package client
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+const (
+ maxReadQueueSize = 1024
+ permRefreshInterval = 120 * time.Second
+ maxRetryAttempts = 3
+)
+
+const (
+ timerIDRefreshAlloc int = iota
+ timerIDRefreshPerms
+)
+
+func noDeadline() time.Time {
+ return time.Time{}
+}
+
+type inboundData struct {
+ data []byte
+ from net.Addr
+}
+
+// UDPConnObserver is an interface to UDPConn observer
+type UDPConnObserver interface {
+ TURNServerAddr() net.Addr
+ Username() stun.Username
+ Realm() stun.Realm
+ WriteTo(data []byte, to net.Addr) (int, error)
+ PerformTransaction(msg *stun.Message, to net.Addr, dontWait bool) (TransactionResult, error)
+ OnDeallocated(relayedAddr net.Addr)
+}
+
+// UDPConnConfig is a set of configuration params use by NewUDPConn
+type UDPConnConfig struct {
+ Observer UDPConnObserver
+ RelayedAddr net.Addr
+ Integrity stun.MessageIntegrity
+ Nonce stun.Nonce
+ Lifetime time.Duration
+ Log logging.LeveledLogger
+}
+
+// UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections.
+// comatible with net.PacketConn and net.Conn
+type UDPConn struct {
+ obs UDPConnObserver // read-only
+ relayedAddr net.Addr // read-only
+ permMap *permissionMap // thread-safe
+ bindingMgr *bindingManager // thread-safe
+ integrity stun.MessageIntegrity // read-only
+ _nonce stun.Nonce // needs mutex x
+ _lifetime time.Duration // needs mutex x
+ readCh chan *inboundData // thread-safe
+ closeCh chan struct{} // thread-safe
+ readTimer *time.Timer // thread-safe
+ refreshAllocTimer *PeriodicTimer // thread-safe
+ refreshPermsTimer *PeriodicTimer // thread-safe
+ mutex sync.RWMutex // thread-safe
+ log logging.LeveledLogger // read-only
+}
+
+// NewUDPConn creates a new instance of UDPConn
+func NewUDPConn(config *UDPConnConfig) *UDPConn {
+ c := &UDPConn{
+ obs: config.Observer,
+ relayedAddr: config.RelayedAddr,
+ permMap: newPermissionMap(),
+ bindingMgr: newBindingManager(),
+ integrity: config.Integrity,
+ _nonce: config.Nonce,
+ _lifetime: config.Lifetime,
+ readCh: make(chan *inboundData, maxReadQueueSize),
+ closeCh: make(chan struct{}),
+ readTimer: time.NewTimer(time.Duration(math.MaxInt64)),
+ log: config.Log,
+ }
+
+ c.log.Debugf("initial lifetime: %d seconds", int(c.lifetime().Seconds()))
+
+ c.refreshAllocTimer = NewPeriodicTimer(
+ timerIDRefreshAlloc,
+ c.onRefreshTimers,
+ c.lifetime()/2,
+ )
+
+ c.refreshPermsTimer = NewPeriodicTimer(
+ timerIDRefreshPerms,
+ c.onRefreshTimers,
+ permRefreshInterval,
+ )
+
+ if c.refreshAllocTimer.Start() {
+ c.log.Debugf("refreshAllocTimer started")
+ }
+ if c.refreshPermsTimer.Start() {
+ c.log.Debugf("refreshPermsTimer started")
+ }
+
+ return c
+}
+
+// ReadFrom reads a packet from the connection,
+// copying the payload into p. It returns the number of
+// bytes copied into p and the return address that
+// was on the packet.
+// It returns the number of bytes read (0 <= n <= len(p))
+// and any error encountered. Callers should always process
+// the n > 0 bytes returned before considering the error err.
+// ReadFrom can be made to time out and return
+// an Error with Timeout() == true after a fixed time limit;
+// see SetDeadline and SetReadDeadline.
+func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
+ for {
+ select {
+ case ibData := <-c.readCh:
+ n := copy(p, ibData.data)
+ if n < len(ibData.data) {
+ return 0, nil, io.ErrShortBuffer
+ }
+ return n, ibData.from, nil
+
+ case <-c.readTimer.C:
+ return 0, nil, &net.OpError{
+ Op: "read",
+ Net: c.LocalAddr().Network(),
+ Addr: c.LocalAddr(),
+ Err: newTimeoutError("i/o timeout"),
+ }
+
+ case <-c.closeCh:
+ return 0, nil, &net.OpError{
+ Op: "read",
+ Net: c.LocalAddr().Network(),
+ Addr: c.LocalAddr(),
+ Err: errClosed,
+ }
+ }
+ }
+}
+
+// WriteTo writes a packet with payload p to addr.
+// WriteTo can be made to time out and return
+// an Error with Timeout() == true after a fixed time limit;
+// see SetDeadline and SetWriteDeadline.
+// On packet-oriented connections, write timeouts are rare.
+func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: gocognit
+ var err error
+ _, ok := addr.(*net.UDPAddr)
+ if !ok {
+ return 0, errUDPAddrCast
+ }
+
+ // check if we have a permission for the destination IP addr
+ perm, ok := c.permMap.find(addr)
+ if !ok {
+ perm = &permission{}
+ c.permMap.insert(addr, perm)
+ }
+
+ // This func-block would block, per destination IP (, or perm), until
+ // the perm state becomes "requested". Purpose of this is to guarantee
+ // the order of packets (within the same perm).
+ // Note that CreatePermission transaction may not be complete before
+ // all the data transmission. This is done assuming that the request
+ // will be mostly likely successful and we can tolerate some loss of
+ // UDP packet (or reorder), inorder to minimize the latency in most cases.
+ createPermission := func() error {
+ perm.mutex.Lock()
+ defer perm.mutex.Unlock()
+
+ if perm.state() == permStateIdle {
+ // punch a hole! (this would block a bit..)
+ if err = c.createPermissions(addr); err != nil {
+ c.permMap.delete(addr)
+ return err
+ }
+ perm.setState(permStatePermitted)
+ }
+ return nil
+ }
+
+ for i := 0; i < maxRetryAttempts; i++ {
+ if err = createPermission(); !errors.Is(err, errTryAgain) {
+ break
+ }
+ }
+ if err != nil {
+ return 0, err
+ }
+
+ // bind channel
+ b, ok := c.bindingMgr.findByAddr(addr)
+ if !ok {
+ b = c.bindingMgr.create(addr)
+ }
+
+ bindSt := b.state()
+
+ if bindSt == bindingStateIdle || bindSt == bindingStateRequest || bindSt == bindingStateFailed {
+ func() {
+ // block only callers with the same binding until
+ // the binding transaction has been complete
+ b.muBind.Lock()
+ defer b.muBind.Unlock()
+
+ // binding state may have been changed while waiting. check again.
+ if b.state() == bindingStateIdle {
+ b.setState(bindingStateRequest)
+ go func() {
+ err2 := c.bind(b)
+ if err2 != nil {
+ c.log.Warnf("bind() failed: %s", err2.Error())
+ b.setState(bindingStateFailed)
+ // keep going...
+ } else {
+ b.setState(bindingStateReady)
+ }
+ }()
+ }
+ }()
+
+ // send data using SendIndication
+ peerAddr := addr2PeerAddress(addr)
+ var msg *stun.Message
+ msg, err = stun.Build(
+ stun.TransactionID,
+ stun.NewType(stun.MethodSend, stun.ClassIndication),
+ proto.Data(p),
+ peerAddr,
+ stun.Fingerprint,
+ )
+ if err != nil {
+ return 0, err
+ }
+
+ // indication has no transaction (fire-and-forget)
+
+ return c.obs.WriteTo(msg.Raw, c.obs.TURNServerAddr())
+ }
+
+ // binding is either ready
+
+ // check if the binding needs a refresh
+ func() {
+ b.muBind.Lock()
+ defer b.muBind.Unlock()
+
+ if b.state() == bindingStateReady && time.Since(b.refreshedAt()) > 5*time.Minute {
+ b.setState(bindingStateRefresh)
+ go func() {
+ err = c.bind(b)
+ if err != nil {
+ c.log.Warnf("bind() for refresh failed: %s", err.Error())
+ b.setState(bindingStateFailed)
+ // keep going...
+ } else {
+ b.setRefreshedAt(time.Now())
+ b.setState(bindingStateReady)
+ }
+ }()
+ }
+ }()
+
+ // send via ChannelData
+ return c.sendChannelData(p, b.number)
+}
+
+// Close closes the connection.
+// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
+func (c *UDPConn) Close() error {
+ c.refreshAllocTimer.Stop()
+ c.refreshPermsTimer.Stop()
+
+ select {
+ case <-c.closeCh:
+ return errAlreadyClosed
+ default:
+ close(c.closeCh)
+ }
+
+ c.obs.OnDeallocated(c.relayedAddr)
+ return c.refreshAllocation(0, true /* dontWait=true */)
+}
+
+// LocalAddr returns the local network address.
+func (c *UDPConn) LocalAddr() net.Addr {
+ return c.relayedAddr
+}
+
+// SetDeadline sets the read and write deadlines associated
+// with the connection. It is equivalent to calling both
+// SetReadDeadline and SetWriteDeadline.
+//
+// A deadline is an absolute time after which I/O operations
+// fail with a timeout (see type Error) instead of
+// blocking. The deadline applies to all future and pending
+// I/O, not just the immediately following call to ReadFrom or
+// WriteTo. After a deadline has been exceeded, the connection
+// can be refreshed by setting a deadline in the future.
+//
+// An idle timeout can be implemented by repeatedly extending
+// the deadline after successful ReadFrom or WriteTo calls.
+//
+// A zero value for t means I/O operations will not time out.
+func (c *UDPConn) SetDeadline(t time.Time) error {
+ return c.SetReadDeadline(t)
+}
+
+// SetReadDeadline sets the deadline for future ReadFrom calls
+// and any currently-blocked ReadFrom call.
+// A zero value for t means ReadFrom will not time out.
+func (c *UDPConn) SetReadDeadline(t time.Time) error {
+ var d time.Duration
+ if t == noDeadline() {
+ d = time.Duration(math.MaxInt64)
+ } else {
+ d = time.Until(t)
+ }
+ c.readTimer.Reset(d)
+ return nil
+}
+
+// SetWriteDeadline sets the deadline for future WriteTo calls
+// and any currently-blocked WriteTo call.
+// Even if write times out, it may return n > 0, indicating that
+// some of the data was successfully written.
+// A zero value for t means WriteTo will not time out.
+func (c *UDPConn) SetWriteDeadline(t time.Time) error {
+ // Write never blocks.
+ return nil
+}
+
+func addr2PeerAddress(addr net.Addr) proto.PeerAddress {
+ var peerAddr proto.PeerAddress
+ switch a := addr.(type) {
+ case *net.UDPAddr:
+ peerAddr.IP = a.IP
+ peerAddr.Port = a.Port
+ case *net.TCPAddr:
+ peerAddr.IP = a.IP
+ peerAddr.Port = a.Port
+ }
+
+ return peerAddr
+}
+
+func (c *UDPConn) createPermissions(addrs ...net.Addr) error {
+ setters := []stun.Setter{
+ stun.TransactionID,
+ stun.NewType(stun.MethodCreatePermission, stun.ClassRequest),
+ }
+
+ for _, addr := range addrs {
+ setters = append(setters, addr2PeerAddress(addr))
+ }
+
+ setters = append(setters,
+ c.obs.Username(),
+ c.obs.Realm(),
+ c.nonce(),
+ c.integrity,
+ stun.Fingerprint)
+
+ msg, err := stun.Build(setters...)
+ if err != nil {
+ return err
+ }
+
+ trRes, err := c.obs.PerformTransaction(msg, c.obs.TURNServerAddr(), false)
+ if err != nil {
+ return err
+ }
+
+ res := trRes.Msg
+
+ if res.Type.Class == stun.ClassErrorResponse {
+ var code stun.ErrorCodeAttribute
+ if err = code.GetFrom(res); err == nil {
+ if code.Code == stun.CodeStaleNonce {
+ c.setNonceFromMsg(res)
+ return errTryAgain
+ }
+ return fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113
+ }
+
+ return fmt.Errorf("%s", res.Type) //nolint:goerr113
+ }
+
+ return nil
+}
+
+// HandleInbound passes inbound data in UDPConn
+func (c *UDPConn) HandleInbound(data []byte, from net.Addr) {
+ // copy data
+ copied := make([]byte, len(data))
+ copy(copied, data)
+
+ select {
+ case c.readCh <- &inboundData{data: copied, from: from}:
+ default:
+ c.log.Warnf("receive buffer full")
+ }
+}
+
+// FindAddrByChannelNumber returns a peer address associated with the
+// channel number on this UDPConn
+func (c *UDPConn) FindAddrByChannelNumber(chNum uint16) (net.Addr, bool) {
+ b, ok := c.bindingMgr.findByNumber(chNum)
+ if !ok {
+ return nil, false
+ }
+ return b.addr, true
+}
+
+func (c *UDPConn) setNonceFromMsg(msg *stun.Message) {
+ // Update nonce
+ var nonce stun.Nonce
+ if err := nonce.GetFrom(msg); err == nil {
+ c.setNonce(nonce)
+ c.log.Debug("refresh allocation: 438, got new nonce.")
+ } else {
+ c.log.Warn("refresh allocation: 438 but no nonce.")
+ }
+}
+
+func (c *UDPConn) refreshAllocation(lifetime time.Duration, dontWait bool) error {
+ msg, err := stun.Build(
+ stun.TransactionID,
+ stun.NewType(stun.MethodRefresh, stun.ClassRequest),
+ proto.Lifetime{Duration: lifetime},
+ c.obs.Username(),
+ c.obs.Realm(),
+ c.nonce(),
+ c.integrity,
+ stun.Fingerprint,
+ )
+ if err != nil {
+ return fmt.Errorf("%w: %s", errFailedToBuildRefreshRequest, err.Error())
+ }
+
+ c.log.Debugf("send refresh request (dontWait=%v)", dontWait)
+ trRes, err := c.obs.PerformTransaction(msg, c.obs.TURNServerAddr(), dontWait)
+ if err != nil {
+ return fmt.Errorf("%w: %s", errFailedToRefreshAllocation, err.Error())
+ }
+
+ if dontWait {
+ c.log.Debug("refresh request sent")
+ return nil
+ }
+
+ c.log.Debug("refresh request sent, and waiting response")
+
+ res := trRes.Msg
+ if res.Type.Class == stun.ClassErrorResponse {
+ var code stun.ErrorCodeAttribute
+ if err = code.GetFrom(res); err == nil {
+ if code.Code == stun.CodeStaleNonce {
+ c.setNonceFromMsg(res)
+ return errTryAgain
+ }
+ return err
+ }
+ return fmt.Errorf("%s", res.Type) //nolint:goerr113
+ }
+
+ // Getting lifetime from response
+ var updatedLifetime proto.Lifetime
+ if err := updatedLifetime.GetFrom(res); err != nil {
+ return fmt.Errorf("%w: %s", errFailedToGetLifetime, err.Error())
+ }
+
+ c.setLifetime(updatedLifetime.Duration)
+ c.log.Debugf("updated lifetime: %d seconds", int(c.lifetime().Seconds()))
+ return nil
+}
+
+func (c *UDPConn) refreshPermissions() error {
+ addrs := c.permMap.addrs()
+ if len(addrs) == 0 {
+ c.log.Debug("no permission to refresh")
+ return nil
+ }
+ if err := c.createPermissions(addrs...); err != nil {
+ if errors.Is(err, errTryAgain) {
+ return errTryAgain
+ }
+ c.log.Errorf("fail to refresh permissions: %s", err.Error())
+ return err
+ }
+ c.log.Debug("refresh permissions successful")
+ return nil
+}
+
+func (c *UDPConn) bind(b *binding) error {
+ setters := []stun.Setter{
+ stun.TransactionID,
+ stun.NewType(stun.MethodChannelBind, stun.ClassRequest),
+ addr2PeerAddress(b.addr),
+ proto.ChannelNumber(b.number),
+ c.obs.Username(),
+ c.obs.Realm(),
+ c.nonce(),
+ c.integrity,
+ stun.Fingerprint,
+ }
+
+ msg, err := stun.Build(setters...)
+ if err != nil {
+ return err
+ }
+
+ trRes, err := c.obs.PerformTransaction(msg, c.obs.TURNServerAddr(), false)
+ if err != nil {
+ c.bindingMgr.deleteByAddr(b.addr)
+ return err
+ }
+
+ res := trRes.Msg
+
+ if res.Type != stun.NewType(stun.MethodChannelBind, stun.ClassSuccessResponse) {
+ return fmt.Errorf("unexpected response type %s", res.Type) //nolint:goerr113
+ }
+
+ c.log.Debugf("channel binding successful: %s %d", b.addr.String(), b.number)
+
+ // Success.
+ return nil
+}
+
+func (c *UDPConn) sendChannelData(data []byte, chNum uint16) (int, error) {
+ chData := &proto.ChannelData{
+ Data: data,
+ Number: proto.ChannelNumber(chNum),
+ }
+ chData.Encode()
+ return c.obs.WriteTo(chData.Raw, c.obs.TURNServerAddr())
+}
+
+func (c *UDPConn) onRefreshTimers(id int) {
+ c.log.Debugf("refresh timer %d expired", id)
+ switch id {
+ case timerIDRefreshAlloc:
+ var err error
+ lifetime := c.lifetime()
+ // limit the max retries on errTryAgain to 3
+ // when stale nonce returns, sencond retry should succeed
+ for i := 0; i < maxRetryAttempts; i++ {
+ err = c.refreshAllocation(lifetime, false)
+ if !errors.Is(err, errTryAgain) {
+ break
+ }
+ }
+ if err != nil {
+ c.log.Warnf("refresh allocation failed")
+ }
+ case timerIDRefreshPerms:
+ var err error
+ for i := 0; i < maxRetryAttempts; i++ {
+ err = c.refreshPermissions()
+ if !errors.Is(err, errTryAgain) {
+ break
+ }
+ }
+ if err != nil {
+ c.log.Warnf("refresh permissions failed")
+ }
+ }
+}
+
+func (c *UDPConn) nonce() stun.Nonce {
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+
+ return c._nonce
+}
+
+func (c *UDPConn) setNonce(nonce stun.Nonce) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ c.log.Debugf("set new nonce with %d bytes", len(nonce))
+ c._nonce = nonce
+}
+
+func (c *UDPConn) lifetime() time.Duration {
+ c.mutex.RLock()
+ defer c.mutex.RUnlock()
+
+ return c._lifetime
+}
+
+func (c *UDPConn) setLifetime(lifetime time.Duration) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ c._lifetime = lifetime
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/client/errors.go b/vendor/github.com/pion/turn/v2/internal/client/errors.go
new file mode 100644
index 0000000..2d4bd30
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/client/errors.go
@@ -0,0 +1,37 @@
+package client
+
+import (
+ "errors"
+)
+
+var (
+ errFakeErr = errors.New("fake error")
+ errTryAgain = errors.New("try again")
+ errClosed = errors.New("use of closed network connection")
+ errUDPAddrCast = errors.New("addr is not a net.UDPAddr")
+ errAlreadyClosed = errors.New("already closed")
+ errDoubleLock = errors.New("try-lock is already locked")
+ errTransactionClosed = errors.New("transaction closed")
+ errWaitForResultOnNonResultTransaction = errors.New("WaitForResult called on non-result transaction")
+ errFailedToBuildRefreshRequest = errors.New("failed to build refresh request")
+ errFailedToRefreshAllocation = errors.New("failed to refresh allocation")
+ errFailedToGetLifetime = errors.New("failed to get lifetime from refresh response")
+)
+
+type timeoutError struct {
+ msg string
+}
+
+func newTimeoutError(msg string) error {
+ return &timeoutError{
+ msg: msg,
+ }
+}
+
+func (e *timeoutError) Error() string {
+ return e.msg
+}
+
+func (e *timeoutError) Timeout() bool {
+ return true
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/client/periodic_timer.go b/vendor/github.com/pion/turn/v2/internal/client/periodic_timer.go
new file mode 100644
index 0000000..fcd5678
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/client/periodic_timer.go
@@ -0,0 +1,82 @@
+package client
+
+import (
+ "sync"
+ "time"
+)
+
+// PeriodicTimerTimeoutHandler is a handler called on timeout
+type PeriodicTimerTimeoutHandler func(timerID int)
+
+// PeriodicTimer is a periodic timer
+type PeriodicTimer struct {
+ id int
+ interval time.Duration
+ timeoutHandler PeriodicTimerTimeoutHandler
+ stopFunc func()
+ mutex sync.RWMutex
+}
+
+// NewPeriodicTimer create a new timer
+func NewPeriodicTimer(id int, timeoutHandler PeriodicTimerTimeoutHandler, interval time.Duration) *PeriodicTimer {
+ return &PeriodicTimer{
+ id: id,
+ interval: interval,
+ timeoutHandler: timeoutHandler,
+ }
+}
+
+// Start starts the timer.
+func (t *PeriodicTimer) Start() bool {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ // this is a noop if the timer is always running
+ if t.stopFunc != nil {
+ return false
+ }
+
+ cancelCh := make(chan struct{})
+
+ go func() {
+ canceling := false
+
+ for !canceling {
+ timer := time.NewTimer(t.interval)
+
+ select {
+ case <-timer.C:
+ t.timeoutHandler(t.id)
+ case <-cancelCh:
+ canceling = true
+ timer.Stop()
+ }
+ }
+ }()
+
+ t.stopFunc = func() {
+ close(cancelCh)
+ }
+
+ return true
+}
+
+// Stop stops the timer.
+func (t *PeriodicTimer) Stop() {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ if t.stopFunc != nil {
+ t.stopFunc()
+ t.stopFunc = nil
+ }
+}
+
+// IsRunning tests if the timer is running.
+// Debug purpose only
+func (t *PeriodicTimer) IsRunning() bool {
+ t.mutex.RLock()
+ defer t.mutex.RUnlock()
+
+ return (t.stopFunc != nil)
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/client/permission.go b/vendor/github.com/pion/turn/v2/internal/client/permission.go
new file mode 100644
index 0000000..5546a22
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/client/permission.go
@@ -0,0 +1,90 @@
+package client
+
+import (
+ "net"
+ "sync"
+ "sync/atomic"
+)
+
+type permState int32
+
+const (
+ permStateIdle permState = iota
+ permStatePermitted
+)
+
+type permission struct {
+ st permState // thread-safe (atomic op)
+ mutex sync.RWMutex // thread-safe
+}
+
+func (p *permission) setState(state permState) {
+ atomic.StoreInt32((*int32)(&p.st), int32(state))
+}
+
+func (p *permission) state() permState {
+ return permState(atomic.LoadInt32((*int32)(&p.st)))
+}
+
+// Thread-safe permission map
+type permissionMap struct {
+ permMap map[string]*permission
+ mutex sync.RWMutex
+}
+
+func (m *permissionMap) insert(addr net.Addr, p *permission) bool {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ udpAddr, ok := addr.(*net.UDPAddr)
+ if !ok {
+ return false
+ }
+
+ m.permMap[udpAddr.IP.String()] = p
+ return true
+}
+
+func (m *permissionMap) find(addr net.Addr) (*permission, bool) {
+ m.mutex.RLock()
+ defer m.mutex.RUnlock()
+
+ udpAddr, ok := addr.(*net.UDPAddr)
+ if !ok {
+ return nil, false
+ }
+
+ p, ok := m.permMap[udpAddr.IP.String()]
+ return p, ok
+}
+
+func (m *permissionMap) delete(addr net.Addr) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ udpAddr, ok := addr.(*net.UDPAddr)
+ if !ok {
+ return
+ }
+
+ delete(m.permMap, udpAddr.IP.String())
+}
+
+func (m *permissionMap) addrs() []net.Addr {
+ m.mutex.RLock()
+ defer m.mutex.RUnlock()
+
+ addrs := []net.Addr{}
+ for k := range m.permMap {
+ addrs = append(addrs, &net.UDPAddr{
+ IP: net.ParseIP(k),
+ })
+ }
+ return addrs
+}
+
+func newPermissionMap() *permissionMap {
+ return &permissionMap{
+ permMap: map[string]*permission{},
+ }
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/client/transaction.go b/vendor/github.com/pion/turn/v2/internal/client/transaction.go
new file mode 100644
index 0000000..610a4d4
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/client/transaction.go
@@ -0,0 +1,185 @@
+package client
+
+import (
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/stun"
+)
+
+const (
+ maxRtxInterval time.Duration = 1600 * time.Millisecond
+)
+
+// TransactionResult is a bag of result values of a transaction
+type TransactionResult struct {
+ Msg *stun.Message
+ From net.Addr
+ Retries int
+ Err error
+}
+
+// TransactionConfig is a set of config params used by NewTransaction
+type TransactionConfig struct {
+ Key string
+ Raw []byte
+ To net.Addr
+ Interval time.Duration
+ IgnoreResult bool // true to throw away the result of this transaction (it will not be readable using WaitForResult)
+}
+
+// Transaction represents a transaction
+type Transaction struct {
+ Key string // read-only
+ Raw []byte // read-only
+ To net.Addr // read-only
+ nRtx int // modified only by the timer thread
+ interval time.Duration // modified only by the timer thread
+ timer *time.Timer // thread-safe, set only by the creator, and stopper
+ resultCh chan TransactionResult // thread-safe
+ mutex sync.RWMutex
+}
+
+// NewTransaction creates a new instance of Transaction
+func NewTransaction(config *TransactionConfig) *Transaction {
+ var resultCh chan TransactionResult
+ if !config.IgnoreResult {
+ resultCh = make(chan TransactionResult)
+ }
+
+ return &Transaction{
+ Key: config.Key, // read-only
+ Raw: config.Raw, // read-only
+ To: config.To, // read-only
+ interval: config.Interval, // modified only by the timer thread
+ resultCh: resultCh, // thread-safe
+ }
+}
+
+// StartRtxTimer starts the transaction timer
+func (t *Transaction) StartRtxTimer(onTimeout func(trKey string, nRtx int)) {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ t.timer = time.AfterFunc(t.interval, func() {
+ t.mutex.Lock()
+ t.nRtx++
+ nRtx := t.nRtx
+ t.interval *= 2
+ if t.interval > maxRtxInterval {
+ t.interval = maxRtxInterval
+ }
+ t.mutex.Unlock()
+ onTimeout(t.Key, nRtx)
+ })
+}
+
+// StopRtxTimer stop the transaction timer
+func (t *Transaction) StopRtxTimer() {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
+ if t.timer != nil {
+ t.timer.Stop()
+ }
+}
+
+// WriteResult writes the result to the result channel
+func (t *Transaction) WriteResult(res TransactionResult) bool {
+ if t.resultCh == nil {
+ return false
+ }
+
+ t.resultCh <- res
+
+ return true
+}
+
+// WaitForResult waits for the transaction result
+func (t *Transaction) WaitForResult() TransactionResult {
+ if t.resultCh == nil {
+ return TransactionResult{
+ Err: errWaitForResultOnNonResultTransaction,
+ }
+ }
+
+ result, ok := <-t.resultCh
+ if !ok {
+ result.Err = errTransactionClosed
+ }
+ return result
+}
+
+// Close closes the transaction
+func (t *Transaction) Close() {
+ if t.resultCh != nil {
+ close(t.resultCh)
+ }
+}
+
+// Retries returns the number of retransmission it has made
+func (t *Transaction) Retries() int {
+ t.mutex.RLock()
+ defer t.mutex.RUnlock()
+
+ return t.nRtx
+}
+
+// TransactionMap is a thread-safe transaction map
+type TransactionMap struct {
+ trMap map[string]*Transaction
+ mutex sync.RWMutex
+}
+
+// NewTransactionMap create a new instance of the transaction map
+func NewTransactionMap() *TransactionMap {
+ return &TransactionMap{
+ trMap: map[string]*Transaction{},
+ }
+}
+
+// Insert inserts a trasaction to the map
+func (m *TransactionMap) Insert(key string, tr *Transaction) bool {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ m.trMap[key] = tr
+ return true
+}
+
+// Find looks up a transaction by its key
+func (m *TransactionMap) Find(key string) (*Transaction, bool) {
+ m.mutex.RLock()
+ defer m.mutex.RUnlock()
+
+ tr, ok := m.trMap[key]
+ return tr, ok
+}
+
+// Delete deletes a transaction by its key
+func (m *TransactionMap) Delete(key string) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ delete(m.trMap, key)
+}
+
+// CloseAndDeleteAll closes and deletes all transactions
+func (m *TransactionMap) CloseAndDeleteAll() {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ for trKey, tr := range m.trMap {
+ tr.Close()
+ delete(m.trMap, trKey)
+ }
+}
+
+// Size returns the length of the transaction map
+func (m *TransactionMap) Size() int {
+ m.mutex.RLock()
+ defer m.mutex.RUnlock()
+
+ return len(m.trMap)
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/client/trylock.go b/vendor/github.com/pion/turn/v2/internal/client/trylock.go
new file mode 100644
index 0000000..48e25a0
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/client/trylock.go
@@ -0,0 +1,24 @@
+package client
+
+import (
+ "sync/atomic"
+)
+
+// TryLock implement the classic "try-lock" operation.
+type TryLock struct {
+ n int32
+}
+
+// Lock tries to lock the try-lock. If successful, it returns true.
+// Otherwise, it returns false immedidately.
+func (c *TryLock) Lock() error {
+ if !atomic.CompareAndSwapInt32(&c.n, 0, 1) {
+ return errDoubleLock
+ }
+ return nil
+}
+
+// Unlock unlocks the try-lock.
+func (c *TryLock) Unlock() {
+ atomic.StoreInt32(&c.n, 0)
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/ipnet/util.go b/vendor/github.com/pion/turn/v2/internal/ipnet/util.go
new file mode 100644
index 0000000..9df7f56
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/ipnet/util.go
@@ -0,0 +1,40 @@
+// Package ipnet contains helper functions around net and IP
+package ipnet
+
+import (
+ "errors"
+ "net"
+)
+
+var errFailedToCastAddr = errors.New("failed to cast net.Addr to *net.UDPAddr or *net.TCPAddr")
+
+// AddrIPPort extracts the IP and Port from a net.Addr
+func AddrIPPort(a net.Addr) (net.IP, int, error) {
+ aUDP, ok := a.(*net.UDPAddr)
+ if ok {
+ return aUDP.IP, aUDP.Port, nil
+ }
+
+ aTCP, ok := a.(*net.TCPAddr)
+ if ok {
+ return aTCP.IP, aTCP.Port, nil
+ }
+
+ return nil, 0, errFailedToCastAddr
+}
+
+// AddrEqual asserts that two net.Addrs are equal
+// Currently only supprots UDP but will be extended in the future to support others
+func AddrEqual(a, b net.Addr) bool {
+ aUDP, ok := a.(*net.UDPAddr)
+ if !ok {
+ return false
+ }
+
+ bUDP, ok := b.(*net.UDPAddr)
+ if !ok {
+ return false
+ }
+
+ return aUDP.IP.Equal(bUDP.IP) && aUDP.Port == bUDP.Port
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/addr.go b/vendor/github.com/pion/turn/v2/internal/proto/addr.go
new file mode 100644
index 0000000..b1d654d
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/addr.go
@@ -0,0 +1,65 @@
+package proto
+
+import (
+ "fmt"
+ "net"
+)
+
+// Addr is ip:port.
+type Addr struct {
+ IP net.IP
+ Port int
+}
+
+// Network implements net.Addr.
+func (Addr) Network() string { return "turn" }
+
+// FromUDPAddr sets addr to UDPAddr.
+func (a *Addr) FromUDPAddr(n *net.UDPAddr) {
+ a.IP = n.IP
+ a.Port = n.Port
+}
+
+// Equal returns true if b == a.
+func (a Addr) Equal(b Addr) bool {
+ if a.Port != b.Port {
+ return false
+ }
+ return a.IP.Equal(b.IP)
+}
+
+// EqualIP returns true if a and b have equal IP addresses.
+func (a Addr) EqualIP(b Addr) bool {
+ return a.IP.Equal(b.IP)
+}
+
+func (a Addr) String() string {
+ return fmt.Sprintf("%s:%d", a.IP, a.Port)
+}
+
+// FiveTuple represents 5-TUPLE value.
+type FiveTuple struct {
+ Client Addr
+ Server Addr
+ Proto Protocol
+}
+
+func (t FiveTuple) String() string {
+ return fmt.Sprintf("%s->%s (%s)",
+ t.Client, t.Server, t.Proto,
+ )
+}
+
+// Equal returns true if b == t.
+func (t FiveTuple) Equal(b FiveTuple) bool {
+ if t.Proto != b.Proto {
+ return false
+ }
+ if !t.Client.Equal(b.Client) {
+ return false
+ }
+ if !t.Server.Equal(b.Server) {
+ return false
+ }
+ return true
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/chandata.go b/vendor/github.com/pion/turn/v2/internal/proto/chandata.go
new file mode 100644
index 0000000..fb1295b
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/chandata.go
@@ -0,0 +1,140 @@
+package proto
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "io"
+)
+
+// ChannelData represents The ChannelData Message.
+//
+// See RFC 5766 Section 11.4
+type ChannelData struct {
+ Data []byte // can be subslice of Raw
+ Length int // ignored while encoding, len(Data) is used
+ Number ChannelNumber
+ Raw []byte
+}
+
+// Equal returns true if b == c.
+func (c *ChannelData) Equal(b *ChannelData) bool {
+ if c == nil && b == nil {
+ return true
+ }
+ if c == nil || b == nil {
+ return false
+ }
+ if c.Number != b.Number {
+ return false
+ }
+ if len(c.Data) != len(b.Data) {
+ return false
+ }
+ return bytes.Equal(c.Data, b.Data)
+}
+
+// grow ensures that internal buffer will fit v more bytes and
+// increases it capacity if necessary.
+//
+// Similar to stun.Message.grow method.
+func (c *ChannelData) grow(v int) {
+ n := len(c.Raw) + v
+ for cap(c.Raw) < n {
+ c.Raw = append(c.Raw, 0)
+ }
+ c.Raw = c.Raw[:n]
+}
+
+// Reset resets Length, Data and Raw length.
+func (c *ChannelData) Reset() {
+ c.Raw = c.Raw[:0]
+ c.Length = 0
+ c.Data = c.Data[:0]
+}
+
+// Encode encodes ChannelData Message to Raw.
+func (c *ChannelData) Encode() {
+ c.Raw = c.Raw[:0]
+ c.WriteHeader()
+ c.Raw = append(c.Raw, c.Data...)
+ padded := nearestPaddedValueLength(len(c.Raw))
+ if bytesToAdd := padded - len(c.Raw); bytesToAdd > 0 {
+ for i := 0; i < bytesToAdd; i++ {
+ c.Raw = append(c.Raw, 0)
+ }
+ }
+}
+
+const padding = 4
+
+func nearestPaddedValueLength(l int) int {
+ n := padding * (l / padding)
+ if n < l {
+ n += padding
+ }
+ return n
+}
+
+// WriteHeader writes channel number and length.
+func (c *ChannelData) WriteHeader() {
+ if len(c.Raw) < channelDataHeaderSize {
+ // Making WriteHeader call valid even when c.Raw
+ // is nil or len(c.Raw) is less than needed for header.
+ c.grow(channelDataHeaderSize)
+ }
+ // Early bounds check to guarantee safety of writes below.
+ _ = c.Raw[:channelDataHeaderSize]
+ binary.BigEndian.PutUint16(c.Raw[:channelDataNumberSize], uint16(c.Number))
+ binary.BigEndian.PutUint16(c.Raw[channelDataNumberSize:channelDataHeaderSize],
+ uint16(len(c.Data)),
+ )
+}
+
+// ErrBadChannelDataLength means that channel data length is not equal
+// to actual data length.
+var ErrBadChannelDataLength = errors.New("channelData length != len(Data)")
+
+// Decode decodes The ChannelData Message from Raw.
+func (c *ChannelData) Decode() error {
+ buf := c.Raw
+ if len(buf) < channelDataHeaderSize {
+ return io.ErrUnexpectedEOF
+ }
+ num := binary.BigEndian.Uint16(buf[:channelDataNumberSize])
+ c.Number = ChannelNumber(num)
+ l := binary.BigEndian.Uint16(buf[channelDataNumberSize:channelDataHeaderSize])
+ c.Data = buf[channelDataHeaderSize:]
+ c.Length = int(l)
+ if !c.Number.Valid() {
+ return ErrInvalidChannelNumber
+ }
+ if int(l) < len(c.Data) {
+ c.Data = c.Data[:int(l)]
+ }
+ if int(l) > len(buf[channelDataHeaderSize:]) {
+ return ErrBadChannelDataLength
+ }
+ return nil
+}
+
+const (
+ channelDataLengthSize = 2
+ channelDataNumberSize = channelDataLengthSize
+ channelDataHeaderSize = channelDataLengthSize + channelDataNumberSize
+)
+
+// IsChannelData returns true if buf looks like the ChannelData Message.
+func IsChannelData(buf []byte) bool {
+ if len(buf) < channelDataHeaderSize {
+ return false
+ }
+
+ if int(binary.BigEndian.Uint16(buf[channelDataNumberSize:channelDataHeaderSize])) > len(buf[channelDataHeaderSize:]) {
+ return false
+ }
+
+ // Quick check for channel number.
+ num := binary.BigEndian.Uint16(buf[0:channelNumberSize])
+ return isChannelNumberValid(num)
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/chann.go b/vendor/github.com/pion/turn/v2/internal/proto/chann.go
new file mode 100644
index 0000000..d64ef73
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/chann.go
@@ -0,0 +1,67 @@
+package proto
+
+import (
+ "encoding/binary"
+ "errors"
+ "strconv"
+
+ "github.com/pion/stun"
+)
+
+// ChannelNumber represents CHANNEL-NUMBER attribute.
+//
+// The CHANNEL-NUMBER attribute contains the number of the channel.
+//
+// RFC 5766 Section 14.1
+type ChannelNumber uint16 // encoded as uint16
+
+func (n ChannelNumber) String() string { return strconv.Itoa(int(n)) }
+
+// 16 bits of uint + 16 bits of RFFU = 0.
+const channelNumberSize = 4
+
+// AddTo adds CHANNEL-NUMBER to message.
+func (n ChannelNumber) AddTo(m *stun.Message) error {
+ v := make([]byte, channelNumberSize)
+ binary.BigEndian.PutUint16(v[:2], uint16(n))
+ // v[2:4] are zeroes (RFFU = 0)
+ m.Add(stun.AttrChannelNumber, v)
+ return nil
+}
+
+// GetFrom decodes CHANNEL-NUMBER from message.
+func (n *ChannelNumber) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrChannelNumber)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(stun.AttrChannelNumber, len(v), channelNumberSize); err != nil {
+ return err
+ }
+ _ = v[channelNumberSize-1] // asserting length
+ *n = ChannelNumber(binary.BigEndian.Uint16(v[:2]))
+ // v[2:4] is RFFU and equals to 0.
+ return nil
+}
+
+// See https://tools.ietf.org/html/rfc5766#section-11:
+//
+// 0x4000 through 0x7FFF: These values are the allowed channel
+// numbers (16,383 possible values).
+const (
+ MinChannelNumber = 0x4000
+ MaxChannelNumber = 0x7FFF
+)
+
+// ErrInvalidChannelNumber means that channel number is not valid as by RFC 5766 Section 11.
+var ErrInvalidChannelNumber = errors.New("channel number not in [0x4000, 0x7FFF]")
+
+// isChannelNumberValid returns true if c in [0x4000, 0x7FFF].
+func isChannelNumberValid(c uint16) bool {
+ return c >= MinChannelNumber && c <= MaxChannelNumber
+}
+
+// Valid returns true if channel number has correct value that complies RFC 5766 Section 11 range.
+func (n ChannelNumber) Valid() bool {
+ return isChannelNumberValid(uint16(n))
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/data.go b/vendor/github.com/pion/turn/v2/internal/proto/data.go
new file mode 100644
index 0000000..64243e0
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/data.go
@@ -0,0 +1,30 @@
+package proto
+
+import "github.com/pion/stun"
+
+// Data represents DATA attribute.
+//
+// The DATA attribute is present in all Send and Data indications. The
+// value portion of this attribute is variable length and consists of
+// the application data (that is, the data that would immediately follow
+// the UDP header if the data was been sent directly between the client
+// and the peer).
+//
+// RFC 5766 Section 14.4
+type Data []byte
+
+// AddTo adds DATA to message.
+func (d Data) AddTo(m *stun.Message) error {
+ m.Add(stun.AttrData, d)
+ return nil
+}
+
+// GetFrom decodes DATA from message.
+func (d *Data) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrData)
+ if err != nil {
+ return err
+ }
+ *d = v
+ return nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go b/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go
new file mode 100644
index 0000000..eb4d8ca
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go
@@ -0,0 +1,18 @@
+package proto
+
+import "github.com/pion/stun"
+
+// DontFragmentAttr represents DONT-FRAGMENT attribute.
+type DontFragmentAttr struct{}
+
+// AddTo adds DONT-FRAGMENT attribute to message.
+func (DontFragmentAttr) AddTo(m *stun.Message) error {
+ m.Add(stun.AttrDontFragment, nil)
+ return nil
+}
+
+// IsSet returns true if DONT-FRAGMENT attribute is set.
+func (DontFragmentAttr) IsSet(m *stun.Message) bool {
+ _, err := m.Get(stun.AttrDontFragment)
+ return err == nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/evenport.go b/vendor/github.com/pion/turn/v2/internal/proto/evenport.go
new file mode 100644
index 0000000..a5a3882
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/evenport.go
@@ -0,0 +1,55 @@
+package proto
+
+import "github.com/pion/stun"
+
+// EvenPort represents EVEN-PORT attribute.
+//
+// This attribute allows the client to request that the port in the
+// relayed transport address be even, and (optionally) that the server
+// reserve the next-higher port number.
+//
+// RFC 5766 Section 14.6
+type EvenPort struct {
+ // ReservePort means that the server is requested to reserve
+ // the next-higher port number (on the same IP address)
+ // for a subsequent allocation.
+ ReservePort bool
+}
+
+func (p EvenPort) String() string {
+ if p.ReservePort {
+ return "reserve: true"
+ }
+ return "reserve: false"
+}
+
+const (
+ evenPortSize = 1
+ firstBitSet = (1 << 8) - 1 // 0b100000000
+)
+
+// AddTo adds EVEN-PORT to message.
+func (p EvenPort) AddTo(m *stun.Message) error {
+ v := make([]byte, evenPortSize)
+ if p.ReservePort {
+ // Set first bit to 1.
+ v[0] = firstBitSet
+ }
+ m.Add(stun.AttrEvenPort, v)
+ return nil
+}
+
+// GetFrom decodes EVEN-PORT from message.
+func (p *EvenPort) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrEvenPort)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(stun.AttrEvenPort, len(v), evenPortSize); err != nil {
+ return err
+ }
+ if v[0]&firstBitSet > 0 {
+ p.ReservePort = true
+ }
+ return nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/fuzz.go b/vendor/github.com/pion/turn/v2/internal/proto/fuzz.go
new file mode 100644
index 0000000..1a171fb
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/fuzz.go
@@ -0,0 +1,111 @@
+// +build gofuzz
+
+package proto
+
+import (
+ "fmt"
+
+ "github.com/pion/stun"
+)
+
+type attr interface {
+ stun.Getter
+ stun.Setter
+}
+
+type attrs []struct {
+ g attr
+ t stun.AttrType
+}
+
+func (a attrs) pick(v byte) struct {
+ g attr
+ t stun.AttrType
+} {
+ idx := int(v) % len(a)
+ return a[idx]
+}
+
+func FuzzSetters(data []byte) int {
+ var (
+ m1 = &stun.Message{
+ Raw: make([]byte, 0, 2048),
+ }
+ m2 = &stun.Message{
+ Raw: make([]byte, 0, 2048),
+ }
+ m3 = &stun.Message{
+ Raw: make([]byte, 0, 2048),
+ }
+ )
+ attributes := attrs{
+ {new(RequestedTransport), stun.AttrRequestedTransport},
+ {new(RelayedAddress), stun.AttrXORRelayedAddress},
+ {new(ChannelNumber), stun.AttrChannelNumber},
+ {new(Data), stun.AttrData},
+ {new(EvenPort), stun.AttrEvenPort},
+ {new(Lifetime), stun.AttrLifetime},
+ {new(ReservationToken), stun.AttrReservationToken},
+ }
+ var firstByte = byte(0)
+ if len(data) > 0 {
+ firstByte = data[0]
+ }
+ a := attributes.pick(firstByte)
+ value := data
+ if len(data) > 1 {
+ value = value[1:]
+ }
+ m1.WriteHeader()
+ m1.Add(a.t, value)
+ err := a.g.GetFrom(m1)
+ if err == stun.ErrAttributeNotFound {
+ fmt.Println("unexpected 404") // nolint
+ panic(err) // nolint
+ }
+ if err != nil {
+ return 1
+ }
+ m2.WriteHeader()
+ if err := a.g.AddTo(m2); err != nil {
+ fmt.Println("failed to add attribute to m2") // nolint
+ panic(err) // nolint
+ }
+ m3.WriteHeader()
+ v, err := m2.Get(a.t)
+ if err != nil {
+ panic(err) // nolint
+ }
+ m3.Add(a.t, v)
+
+ if !m2.Equal(m3) {
+ fmt.Println(m2, "not equal", m3) // nolint
+ panic("not equal") // nolint
+ }
+ return 1
+}
+
+var d = &ChannelData{}
+
+func FuzzChannelData(data []byte) int {
+ d.Reset()
+ if b := bin.Uint16(data[0:4]); b > 20000 {
+ bin.PutUint16(data[0:4], MinChannelNumber-1)
+ } else if b > 40000 {
+ bin.PutUint16(data[0:4], MinChannelNumber+(MaxChannelNumber-MinChannelNumber)%b)
+ }
+ d.Raw = append(d.Raw, data...)
+ if d.Decode() != nil {
+ return 0
+ }
+ d2 := &ChannelData{}
+ d.Encode()
+ if !d.Number.Valid() {
+ return 1
+ }
+ d2.Raw = d.Raw
+ if err := d2.Decode(); err != nil {
+ panic(err) //nolint
+ }
+ return 1
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/lifetime.go b/vendor/github.com/pion/turn/v2/internal/proto/lifetime.go
new file mode 100644
index 0000000..b781696
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/lifetime.go
@@ -0,0 +1,52 @@
+package proto
+
+import (
+ "encoding/binary"
+ "time"
+
+ "github.com/pion/stun"
+)
+
+// DefaultLifetime in RFC 5766 is 10 minutes.
+//
+// RFC 5766 Section 2.2
+const DefaultLifetime = time.Minute * 10
+
+// Lifetime represents LIFETIME attribute.
+//
+// The LIFETIME attribute represents the duration for which the server
+// will maintain an allocation in the absence of a refresh. The value
+// portion of this attribute is 4-bytes long and consists of a 32-bit
+// unsigned integral value representing the number of seconds remaining
+// until expiration.
+//
+// RFC 5766 Section 14.2
+type Lifetime struct {
+ time.Duration
+}
+
+// uint32 seconds
+const lifetimeSize = 4 // 4 bytes, 32 bits
+
+// AddTo adds LIFETIME to message.
+func (l Lifetime) AddTo(m *stun.Message) error {
+ v := make([]byte, lifetimeSize)
+ binary.BigEndian.PutUint32(v, uint32(l.Seconds()))
+ m.Add(stun.AttrLifetime, v)
+ return nil
+}
+
+// GetFrom decodes LIFETIME from message.
+func (l *Lifetime) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrLifetime)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(stun.AttrLifetime, len(v), lifetimeSize); err != nil {
+ return err
+ }
+ _ = v[lifetimeSize-1] // asserting length
+ seconds := binary.BigEndian.Uint32(v)
+ l.Duration = time.Second * time.Duration(seconds)
+ return nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/peeraddr.go b/vendor/github.com/pion/turn/v2/internal/proto/peeraddr.go
new file mode 100644
index 0000000..b357b82
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/peeraddr.go
@@ -0,0 +1,42 @@
+package proto
+
+import (
+ "net"
+
+ "github.com/pion/stun"
+)
+
+// PeerAddress implements XOR-PEER-ADDRESS attribute.
+//
+// The XOR-PEER-ADDRESS specifies the address and port of the peer as
+// seen from the TURN server. (For example, the peer's server-reflexive
+// transport address if the peer is behind a NAT.)
+//
+// RFC 5766 Section 14.3
+type PeerAddress struct {
+ IP net.IP
+ Port int
+}
+
+func (a PeerAddress) String() string {
+ return stun.XORMappedAddress(a).String()
+}
+
+// AddTo adds XOR-PEER-ADDRESS to message.
+func (a PeerAddress) AddTo(m *stun.Message) error {
+ return stun.XORMappedAddress(a).AddToAs(m, stun.AttrXORPeerAddress)
+}
+
+// GetFrom decodes XOR-PEER-ADDRESS from message.
+func (a *PeerAddress) GetFrom(m *stun.Message) error {
+ return (*stun.XORMappedAddress)(a).GetFromAs(m, stun.AttrXORPeerAddress)
+}
+
+// XORPeerAddress implements XOR-PEER-ADDRESS attribute.
+//
+// The XOR-PEER-ADDRESS specifies the address and port of the peer as
+// seen from the TURN server. (For example, the peer's server-reflexive
+// transport address if the peer is behind a NAT.)
+//
+// RFC 5766 Section 14.3
+type XORPeerAddress = PeerAddress
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/proto.go b/vendor/github.com/pion/turn/v2/internal/proto/proto.go
new file mode 100644
index 0000000..4b08c76
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/proto.go
@@ -0,0 +1,30 @@
+// Package proto implements RFC 5766 Traversal Using Relays around NAT.
+//
+// Merged from gortc/turn v0.80.
+package proto
+
+import (
+ "github.com/pion/stun"
+)
+
+// Default ports for TURN from RFC 5766 Section 4.
+const (
+ // DefaultPort for TURN is same as STUN.
+ DefaultPort = stun.DefaultPort
+ // DefaultTLSPort is for TURN over TLS and is same as STUN.
+ DefaultTLSPort = stun.DefaultTLSPort
+)
+
+// CreatePermissionRequest is shorthand for create permission request type.
+func CreatePermissionRequest() stun.MessageType {
+ return stun.NewType(stun.MethodCreatePermission, stun.ClassRequest)
+}
+
+// AllocateRequest is shorthand for allocation request message type.
+func AllocateRequest() stun.MessageType { return stun.NewType(stun.MethodAllocate, stun.ClassRequest) }
+
+// SendIndication is shorthand for send indication message type.
+func SendIndication() stun.MessageType { return stun.NewType(stun.MethodSend, stun.ClassIndication) }
+
+// RefreshRequest is shorthand for refresh request message type.
+func RefreshRequest() stun.MessageType { return stun.NewType(stun.MethodRefresh, stun.ClassRequest) }
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/relayedaddr.go b/vendor/github.com/pion/turn/v2/internal/proto/relayedaddr.go
new file mode 100644
index 0000000..2169cb7
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/relayedaddr.go
@@ -0,0 +1,40 @@
+package proto
+
+import (
+ "net"
+
+ "github.com/pion/stun"
+)
+
+// RelayedAddress implements XOR-RELAYED-ADDRESS attribute.
+//
+// It specifies the address and port that the server allocated to the
+// client. It is encoded in the same way as XOR-MAPPED-ADDRESS.
+//
+// RFC 5766 Section 14.5
+type RelayedAddress struct {
+ IP net.IP
+ Port int
+}
+
+func (a RelayedAddress) String() string {
+ return stun.XORMappedAddress(a).String()
+}
+
+// AddTo adds XOR-PEER-ADDRESS to message.
+func (a RelayedAddress) AddTo(m *stun.Message) error {
+ return stun.XORMappedAddress(a).AddToAs(m, stun.AttrXORRelayedAddress)
+}
+
+// GetFrom decodes XOR-PEER-ADDRESS from message.
+func (a *RelayedAddress) GetFrom(m *stun.Message) error {
+ return (*stun.XORMappedAddress)(a).GetFromAs(m, stun.AttrXORRelayedAddress)
+}
+
+// XORRelayedAddress implements XOR-RELAYED-ADDRESS attribute.
+//
+// It specifies the address and port that the server allocated to the
+// client. It is encoded in the same way as XOR-MAPPED-ADDRESS.
+//
+// RFC 5766 Section 14.5
+type XORRelayedAddress = RelayedAddress
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/reqfamily.go b/vendor/github.com/pion/turn/v2/internal/proto/reqfamily.go
new file mode 100644
index 0000000..e83d6bb
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/reqfamily.go
@@ -0,0 +1,61 @@
+package proto
+
+import (
+ "errors"
+
+ "github.com/pion/stun"
+)
+
+// RequestedAddressFamily represents the REQUESTED-ADDRESS-FAMILY Attribute as
+// defined in RFC 6156 Section 4.1.1.
+type RequestedAddressFamily byte
+
+const requestedFamilySize = 4
+
+var errInvalidRequestedFamilyValue = errors.New("invalid value for requested family attribute")
+
+// GetFrom decodes REQUESTED-ADDRESS-FAMILY from message.
+func (f *RequestedAddressFamily) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrRequestedAddressFamily)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(stun.AttrRequestedAddressFamily, len(v), requestedFamilySize); err != nil {
+ return err
+ }
+ switch v[0] {
+ case byte(RequestedFamilyIPv4), byte(RequestedFamilyIPv6):
+ *f = RequestedAddressFamily(v[0])
+ default:
+ return errInvalidRequestedFamilyValue
+ }
+ return nil
+}
+
+func (f RequestedAddressFamily) String() string {
+ switch f {
+ case RequestedFamilyIPv4:
+ return "IPv4"
+ case RequestedFamilyIPv6:
+ return "IPv6"
+ default:
+ return "unknown"
+ }
+}
+
+// AddTo adds REQUESTED-ADDRESS-FAMILY to message.
+func (f RequestedAddressFamily) AddTo(m *stun.Message) error {
+ v := make([]byte, requestedFamilySize)
+ v[0] = byte(f)
+ // b[1:4] is RFFU = 0.
+ // The RFFU field MUST be set to zero on transmission and MUST be
+ // ignored on reception. It is reserved for future uses.
+ m.Add(stun.AttrRequestedAddressFamily, v)
+ return nil
+}
+
+// Values for RequestedAddressFamily as defined in RFC 6156 Section 4.1.1.
+const (
+ RequestedFamilyIPv4 RequestedAddressFamily = 0x01
+ RequestedFamilyIPv6 RequestedAddressFamily = 0x02
+)
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go b/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go
new file mode 100644
index 0000000..a4e4863
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go
@@ -0,0 +1,65 @@
+package proto
+
+import (
+ "strconv"
+
+ "github.com/pion/stun"
+)
+
+// Protocol is IANA assigned protocol number.
+type Protocol byte
+
+const (
+ // ProtoUDP is IANA assigned protocol number for UDP.
+ ProtoUDP Protocol = 17
+)
+
+func (p Protocol) String() string {
+ switch p {
+ case ProtoUDP:
+ return "UDP"
+ default:
+ return strconv.Itoa(int(p))
+ }
+}
+
+// RequestedTransport represents REQUESTED-TRANSPORT attribute.
+//
+// This attribute is used by the client to request a specific transport
+// protocol for the allocated transport address. RFC 5766 only allows the use of
+// codepoint 17 (User Datagram Protocol).
+//
+// RFC 5766 Section 14.7
+type RequestedTransport struct {
+ Protocol Protocol
+}
+
+func (t RequestedTransport) String() string {
+ return "protocol: " + t.Protocol.String()
+}
+
+const requestedTransportSize = 4
+
+// AddTo adds REQUESTED-TRANSPORT to message.
+func (t RequestedTransport) AddTo(m *stun.Message) error {
+ v := make([]byte, requestedTransportSize)
+ v[0] = byte(t.Protocol)
+ // b[1:4] is RFFU = 0.
+ // The RFFU field MUST be set to zero on transmission and MUST be
+ // ignored on reception. It is reserved for future uses.
+ m.Add(stun.AttrRequestedTransport, v)
+ return nil
+}
+
+// GetFrom decodes REQUESTED-TRANSPORT from message.
+func (t *RequestedTransport) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrRequestedTransport)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(stun.AttrRequestedTransport, len(v), requestedTransportSize); err != nil {
+ return err
+ }
+ t.Protocol = Protocol(v[0])
+ return nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/proto/rsrvtoken.go b/vendor/github.com/pion/turn/v2/internal/proto/rsrvtoken.go
new file mode 100644
index 0000000..6e2ed4d
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/proto/rsrvtoken.go
@@ -0,0 +1,39 @@
+package proto
+
+import "github.com/pion/stun"
+
+// ReservationToken represents RESERVATION-TOKEN attribute.
+//
+// The RESERVATION-TOKEN attribute contains a token that uniquely
+// identifies a relayed transport address being held in reserve by the
+// server. The server includes this attribute in a success response to
+// tell the client about the token, and the client includes this
+// attribute in a subsequent Allocate request to request the server use
+// that relayed transport address for the allocation.
+//
+// RFC 5766 Section 14.9
+type ReservationToken []byte
+
+const reservationTokenSize = 8 // 8 bytes
+
+// AddTo adds RESERVATION-TOKEN to message.
+func (t ReservationToken) AddTo(m *stun.Message) error {
+ if err := stun.CheckSize(stun.AttrReservationToken, len(t), reservationTokenSize); err != nil {
+ return err
+ }
+ m.Add(stun.AttrReservationToken, t)
+ return nil
+}
+
+// GetFrom decodes RESERVATION-TOKEN from message.
+func (t *ReservationToken) GetFrom(m *stun.Message) error {
+ v, err := m.Get(stun.AttrReservationToken)
+ if err != nil {
+ return err
+ }
+ if err = stun.CheckSize(stun.AttrReservationToken, len(v), reservationTokenSize); err != nil {
+ return err
+ }
+ *t = v
+ return nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/server/errors.go b/vendor/github.com/pion/turn/v2/internal/server/errors.go
new file mode 100644
index 0000000..13f8ee1
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/server/errors.go
@@ -0,0 +1,26 @@
+package server
+
+import "errors"
+
+var (
+ errFailedToGenerateNonce = errors.New("failed to generate nonce")
+ errFailedToSendError = errors.New("failed to send error message")
+ errDuplicatedNonce = errors.New("duplicated Nonce generated, discarding request")
+ errNoSuchUser = errors.New("no such user exists")
+ errUnexpectedClass = errors.New("unexpected class")
+ errUnexpectedMethod = errors.New("unexpected method")
+ errFailedToHandle = errors.New("failed to handle")
+ errUnhandledSTUNPacket = errors.New("unhandled STUN packet")
+ errUnableToHandleChannelData = errors.New("unable to handle ChannelData")
+ errFailedToCreateSTUNPacket = errors.New("failed to create stun message from packet")
+ errFailedToCreateChannelData = errors.New("failed to create channel data from packet")
+ errRelayAlreadyAllocatedForFiveTuple = errors.New("relay already allocated for 5-TUPLE")
+ errRequestedTransportMustBeUDP = errors.New("RequestedTransport must be UDP")
+ errNoDontFragmentSupport = errors.New("no support for DONT-FRAGMENT")
+ errRequestWithReservationTokenAndEvenPort = errors.New("Request must not contain RESERVATION-TOKEN and EVEN-PORT")
+ errNoAllocationFound = errors.New("no allocation found")
+ errNoPermission = errors.New("unable to handle send-indication, no permission added")
+ errShortWrite = errors.New("packet write smaller than packet")
+ errNoSuchChannelBind = errors.New("no such channel bind")
+ errFailedWriteSocket = errors.New("failed writing to socket")
+)
diff --git a/vendor/github.com/pion/turn/v2/internal/server/server.go b/vendor/github.com/pion/turn/v2/internal/server/server.go
new file mode 100644
index 0000000..27f3758
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/server/server.go
@@ -0,0 +1,109 @@
+// Package server implements the private API to implement a TURN server
+package server
+
+import (
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/stun"
+ "github.com/pion/turn/v2/internal/allocation"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+// Request contains all the state needed to process a single incoming datagram
+type Request struct {
+ // Current Request State
+ Conn net.PacketConn
+ SrcAddr net.Addr
+ Buff []byte
+
+ // Server State
+ AllocationManager *allocation.Manager
+ Nonces *sync.Map
+
+ // User Configuration
+ AuthHandler func(username string, realm string, srcAddr net.Addr) (key []byte, ok bool)
+ Log logging.LeveledLogger
+ Realm string
+ ChannelBindTimeout time.Duration
+}
+
+// HandleRequest processes the give Request
+func HandleRequest(r Request) error {
+ r.Log.Debugf("received %d bytes of udp from %s on %s", len(r.Buff), r.SrcAddr.String(), r.Conn.LocalAddr().String())
+
+ if proto.IsChannelData(r.Buff) {
+ return handleDataPacket(r)
+ }
+
+ return handleTURNPacket(r)
+}
+
+func handleDataPacket(r Request) error {
+ r.Log.Debugf("received DataPacket from %s", r.SrcAddr.String())
+ c := proto.ChannelData{Raw: r.Buff}
+ if err := c.Decode(); err != nil {
+ return fmt.Errorf("%w: %v", errFailedToCreateChannelData, err)
+ }
+
+ err := handleChannelData(r, &c)
+ if err != nil {
+ err = fmt.Errorf("%w from %v: %v", errUnableToHandleChannelData, r.SrcAddr, err)
+ }
+
+ return err
+}
+
+func handleTURNPacket(r Request) error {
+ r.Log.Debug("handleTURNPacket")
+ m := &stun.Message{Raw: append([]byte{}, r.Buff...)}
+ if err := m.Decode(); err != nil {
+ return fmt.Errorf("%w: %v", errFailedToCreateSTUNPacket, err)
+ }
+
+ h, err := getMessageHandler(m.Type.Class, m.Type.Method)
+ if err != nil {
+ return fmt.Errorf("%w %v-%v from %v: %v", errUnhandledSTUNPacket, m.Type.Method, m.Type.Class, r.SrcAddr, err)
+ }
+
+ err = h(r, m)
+ if err != nil {
+ return fmt.Errorf("%w %v-%v from %v: %v", errFailedToHandle, m.Type.Method, m.Type.Class, r.SrcAddr, err)
+ }
+
+ return nil
+}
+
+func getMessageHandler(class stun.MessageClass, method stun.Method) (func(r Request, m *stun.Message) error, error) {
+ switch class {
+ case stun.ClassIndication:
+ switch method {
+ case stun.MethodSend:
+ return handleSendIndication, nil
+ default:
+ return nil, fmt.Errorf("%w: %s", errUnexpectedMethod, method)
+ }
+
+ case stun.ClassRequest:
+ switch method {
+ case stun.MethodAllocate:
+ return handleAllocateRequest, nil
+ case stun.MethodRefresh:
+ return handleRefreshRequest, nil
+ case stun.MethodCreatePermission:
+ return handleCreatePermissionRequest, nil
+ case stun.MethodChannelBind:
+ return handleChannelBindRequest, nil
+ case stun.MethodBinding:
+ return handleBindingRequest, nil
+ default:
+ return nil, fmt.Errorf("%w: %s", errUnexpectedMethod, method)
+ }
+
+ default:
+ return nil, fmt.Errorf("%w: %s", errUnexpectedClass, class)
+ }
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/server/stun.go b/vendor/github.com/pion/turn/v2/internal/server/stun.go
new file mode 100644
index 0000000..673e99f
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/server/stun.go
@@ -0,0 +1,22 @@
+package server
+
+import (
+ "github.com/pion/stun"
+ "github.com/pion/turn/v2/internal/ipnet"
+)
+
+func handleBindingRequest(r Request, m *stun.Message) error {
+ r.Log.Debugf("received BindingRequest from %s", r.SrcAddr.String())
+
+ ip, port, err := ipnet.AddrIPPort(r.SrcAddr)
+ if err != nil {
+ return err
+ }
+
+ attrs := buildMsg(m.TransactionID, stun.BindingSuccess, &stun.XORMappedAddress{
+ IP: ip,
+ Port: port,
+ }, stun.Fingerprint)
+
+ return buildAndSend(r.Conn, r.SrcAddr, attrs...)
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/server/turn.go b/vendor/github.com/pion/turn/v2/internal/server/turn.go
new file mode 100644
index 0000000..cbac09c
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/server/turn.go
@@ -0,0 +1,352 @@
+package server
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/pion/stun"
+ "github.com/pion/turn/v2/internal/allocation"
+ "github.com/pion/turn/v2/internal/ipnet"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+// // https://tools.ietf.org/html/rfc5766#section-6.2
+func handleAllocateRequest(r Request, m *stun.Message) error {
+ r.Log.Debugf("received AllocateRequest from %s", r.SrcAddr.String())
+
+ // 1. The server MUST require that the request be authenticated. This
+ // authentication MUST be done using the long-term credential
+ // mechanism of [https://tools.ietf.org/html/rfc5389#section-10.2.2]
+ // unless the client and server agree to use another mechanism through
+ // some procedure outside the scope of this document.
+ messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodAllocate)
+ if !hasAuth {
+ return err
+ }
+
+ fiveTuple := &allocation.FiveTuple{
+ SrcAddr: r.SrcAddr,
+ DstAddr: r.Conn.LocalAddr(),
+ Protocol: allocation.UDP,
+ }
+ requestedPort := 0
+ reservationToken := ""
+
+ badRequestMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest})
+ insufficentCapacityMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeInsufficientCapacity})
+
+ // 2. The server checks if the 5-tuple is currently in use by an
+ // existing allocation. If yes, the server rejects the request with
+ // a 437 (Allocation Mismatch) error.
+ if alloc := r.AllocationManager.GetAllocation(fiveTuple); alloc != nil {
+ msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch})
+ return buildAndSendErr(r.Conn, r.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...)
+ }
+
+ // 3. The server checks if the request contains a REQUESTED-TRANSPORT
+ // attribute. If the REQUESTED-TRANSPORT attribute is not included
+ // or is malformed, the server rejects the request with a 400 (Bad
+ // Request) error. Otherwise, if the attribute is included but
+ // specifies a protocol other that UDP, the server rejects the
+ // request with a 442 (Unsupported Transport Protocol) error.
+ var requestedTransport proto.RequestedTransport
+ if err = requestedTransport.GetFrom(m); err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ } else if requestedTransport.Protocol != proto.ProtoUDP {
+ msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeUnsupportedTransProto})
+ return buildAndSendErr(r.Conn, r.SrcAddr, errRequestedTransportMustBeUDP, msg...)
+ }
+
+ // 4. The request may contain a DONT-FRAGMENT attribute. If it does,
+ // but the server does not support sending UDP datagrams with the DF
+ // bit set to 1 (see Section 12), then the server treats the DONT-
+ // FRAGMENT attribute in the Allocate request as an unknown
+ // comprehension-required attribute.
+ if m.Contains(stun.AttrDontFragment) {
+ msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeUnknownAttribute}, &stun.UnknownAttributes{stun.AttrDontFragment})
+ return buildAndSendErr(r.Conn, r.SrcAddr, errNoDontFragmentSupport, msg...)
+ }
+
+ // 5. The server checks if the request contains a RESERVATION-TOKEN
+ // attribute. If yes, and the request also contains an EVEN-PORT
+ // attribute, then the server rejects the request with a 400 (Bad
+ // Request) error. Otherwise, it checks to see if the token is
+ // valid (i.e., the token is in range and has not expired and the
+ // corresponding relayed transport address is still available). If
+ // the token is not valid for some reason, the server rejects the
+ // request with a 508 (Insufficient Capacity) error.
+ var reservationTokenAttr proto.ReservationToken
+ if err = reservationTokenAttr.GetFrom(m); err == nil {
+ var evenPort proto.EvenPort
+ if err = evenPort.GetFrom(m); err == nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, errRequestWithReservationTokenAndEvenPort, badRequestMsg...)
+ }
+ }
+
+ // 6. The server checks if the request contains an EVEN-PORT attribute.
+ // If yes, then the server checks that it can satisfy the request
+ // (i.e., can allocate a relayed transport address as described
+ // below). If the server cannot satisfy the request, then the
+ // server rejects the request with a 508 (Insufficient Capacity)
+ // error.
+ var evenPort proto.EvenPort
+ if err = evenPort.GetFrom(m); err == nil {
+ randomPort := 0
+ randomPort, err = r.AllocationManager.GetRandomEvenPort()
+ if err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficentCapacityMsg...)
+ }
+ requestedPort = randomPort
+ reservationToken = randSeq(8)
+ }
+
+ // 7. At any point, the server MAY choose to reject the request with a
+ // 486 (Allocation Quota Reached) error if it feels the client is
+ // trying to exceed some locally defined allocation quota. The
+ // server is free to define this allocation quota any way it wishes,
+ // but SHOULD define it based on the username used to authenticate
+ // the request, and not on the client's transport address.
+
+ // 8. Also at any point, the server MAY choose to reject the request
+ // with a 300 (Try Alternate) error if it wishes to redirect the
+ // client to a different server. The use of this error code and
+ // attribute follow the specification in [RFC5389].
+ lifetimeDuration := allocationLifeTime(m)
+ a, err := r.AllocationManager.CreateAllocation(
+ fiveTuple,
+ r.Conn,
+ requestedPort,
+ lifetimeDuration)
+ if err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficentCapacityMsg...)
+ }
+
+ // Once the allocation is created, the server replies with a success
+ // response. The success response contains:
+ // * An XOR-RELAYED-ADDRESS attribute containing the relayed transport
+ // address.
+ // * A LIFETIME attribute containing the current value of the time-to-
+ // expiry timer.
+ // * A RESERVATION-TOKEN attribute (if a second relayed transport
+ // address was reserved).
+ // * An XOR-MAPPED-ADDRESS attribute containing the client's IP address
+ // and port (from the 5-tuple).
+
+ srcIP, srcPort, err := ipnet.AddrIPPort(r.SrcAddr)
+ if err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ relayIP, relayPort, err := ipnet.AddrIPPort(a.RelayAddr)
+ if err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ responseAttrs := []stun.Setter{
+ &proto.RelayedAddress{
+ IP: relayIP,
+ Port: relayPort,
+ },
+ &proto.Lifetime{
+ Duration: lifetimeDuration,
+ },
+ &stun.XORMappedAddress{
+ IP: srcIP,
+ Port: srcPort,
+ },
+ }
+
+ if reservationToken != "" {
+ r.AllocationManager.CreateReservation(reservationToken, relayPort)
+ responseAttrs = append(responseAttrs, proto.ReservationToken([]byte(reservationToken)))
+ }
+
+ msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), append(responseAttrs, messageIntegrity)...)
+ return buildAndSend(r.Conn, r.SrcAddr, msg...)
+}
+
+func handleRefreshRequest(r Request, m *stun.Message) error {
+ r.Log.Debugf("received RefreshRequest from %s", r.SrcAddr.String())
+
+ messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodRefresh)
+ if !hasAuth {
+ return err
+ }
+
+ lifetimeDuration := allocationLifeTime(m)
+ fiveTuple := &allocation.FiveTuple{
+ SrcAddr: r.SrcAddr,
+ DstAddr: r.Conn.LocalAddr(),
+ Protocol: allocation.UDP,
+ }
+
+ if lifetimeDuration != 0 {
+ a := r.AllocationManager.GetAllocation(fiveTuple)
+
+ if a == nil {
+ return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
+ }
+ a.Refresh(lifetimeDuration)
+ } else {
+ r.AllocationManager.DeleteAllocation(fiveTuple)
+ }
+
+ return buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, stun.NewType(stun.MethodRefresh, stun.ClassSuccessResponse), []stun.Setter{
+ &proto.Lifetime{
+ Duration: lifetimeDuration,
+ },
+ messageIntegrity,
+ }...)...)
+}
+
+func handleCreatePermissionRequest(r Request, m *stun.Message) error {
+ r.Log.Debugf("received CreatePermission from %s", r.SrcAddr.String())
+
+ a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{
+ SrcAddr: r.SrcAddr,
+ DstAddr: r.Conn.LocalAddr(),
+ Protocol: allocation.UDP,
+ })
+ if a == nil {
+ return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
+ }
+
+ messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodCreatePermission)
+ if !hasAuth {
+ return err
+ }
+
+ addCount := 0
+
+ if err := m.ForEach(stun.AttrXORPeerAddress, func(m *stun.Message) error {
+ var peerAddress proto.PeerAddress
+ if err := peerAddress.GetFrom(m); err != nil {
+ return err
+ }
+
+ r.Log.Debugf("adding permission for %s", fmt.Sprintf("%s:%d",
+ peerAddress.IP.String(), peerAddress.Port))
+ a.AddPermission(allocation.NewPermission(
+ &net.UDPAddr{
+ IP: peerAddress.IP,
+ Port: peerAddress.Port,
+ },
+ r.Log,
+ ))
+ addCount++
+ return nil
+ }); err != nil {
+ addCount = 0
+ }
+
+ respClass := stun.ClassSuccessResponse
+ if addCount == 0 {
+ respClass = stun.ClassErrorResponse
+ }
+
+ return buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, stun.NewType(stun.MethodCreatePermission, respClass), []stun.Setter{messageIntegrity}...)...)
+}
+
+func handleSendIndication(r Request, m *stun.Message) error {
+ r.Log.Debugf("received SendIndication from %s", r.SrcAddr.String())
+ a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{
+ SrcAddr: r.SrcAddr,
+ DstAddr: r.Conn.LocalAddr(),
+ Protocol: allocation.UDP,
+ })
+ if a == nil {
+ return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
+ }
+
+ dataAttr := proto.Data{}
+ if err := dataAttr.GetFrom(m); err != nil {
+ return err
+ }
+
+ peerAddress := proto.PeerAddress{}
+ if err := peerAddress.GetFrom(m); err != nil {
+ return err
+ }
+
+ msgDst := &net.UDPAddr{IP: peerAddress.IP, Port: peerAddress.Port}
+ if perm := a.GetPermission(msgDst); perm == nil {
+ return fmt.Errorf("%w: %v", errNoPermission, msgDst)
+ }
+
+ l, err := a.RelaySocket.WriteTo(dataAttr, msgDst)
+ if l != len(dataAttr) {
+ return fmt.Errorf("%w %d != %d (expected) err: %v", errShortWrite, l, len(dataAttr), err)
+ }
+ return err
+}
+
+func handleChannelBindRequest(r Request, m *stun.Message) error {
+ r.Log.Debugf("received ChannelBindRequest from %s", r.SrcAddr.String())
+
+ a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{
+ SrcAddr: r.SrcAddr,
+ DstAddr: r.Conn.LocalAddr(),
+ Protocol: allocation.UDP,
+ })
+ if a == nil {
+ return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
+ }
+
+ badRequestMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest})
+
+ messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodChannelBind)
+ if !hasAuth {
+ return err
+ }
+
+ var channel proto.ChannelNumber
+ if err = channel.GetFrom(m); err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ peerAddr := proto.PeerAddress{}
+ if err = peerAddr.GetFrom(m); err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ r.Log.Debugf("binding channel %d to %s",
+ channel,
+ fmt.Sprintf("%s:%d", peerAddr.IP.String(), peerAddr.Port))
+ err = a.AddChannelBind(allocation.NewChannelBind(
+ channel,
+ &net.UDPAddr{IP: peerAddr.IP, Port: peerAddr.Port},
+ r.Log,
+ ), r.ChannelBindTimeout)
+ if err != nil {
+ return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ return buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassSuccessResponse), []stun.Setter{messageIntegrity}...)...)
+}
+
+func handleChannelData(r Request, c *proto.ChannelData) error {
+ r.Log.Debugf("received ChannelData from %s", r.SrcAddr.String())
+
+ a := r.AllocationManager.GetAllocation(&allocation.FiveTuple{
+ SrcAddr: r.SrcAddr,
+ DstAddr: r.Conn.LocalAddr(),
+ Protocol: allocation.UDP,
+ })
+ if a == nil {
+ return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
+ }
+
+ channel := a.GetChannelByNumber(c.Number)
+ if channel == nil {
+ return fmt.Errorf("%w %x", errNoSuchChannelBind, uint16(c.Number))
+ }
+
+ l, err := a.RelaySocket.WriteTo(c.Data, channel.Peer)
+ if err != nil {
+ return fmt.Errorf("%w: %s", errFailedWriteSocket, err.Error())
+ } else if l != len(c.Data) {
+ return fmt.Errorf("%w %d != %d (expected)", errShortWrite, l, len(c.Data))
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/turn/v2/internal/server/util.go b/vendor/github.com/pion/turn/v2/internal/server/util.go
new file mode 100644
index 0000000..c6baffb
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/internal/server/util.go
@@ -0,0 +1,133 @@
+package server
+
+import (
+ "crypto/md5" //nolint:gosec,gci
+ "fmt"
+ "io"
+ "math/rand"
+ "net"
+ "strconv"
+ "time"
+
+ "github.com/pion/stun"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+const (
+ maximumAllocationLifetime = time.Hour // https://tools.ietf.org/html/rfc5766#section-6.2 defines 3600 seconds recommendation
+ nonceLifetime = time.Hour // https://tools.ietf.org/html/rfc5766#section-4
+
+)
+
+func randSeq(n int) string {
+ letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ b := make([]rune, n)
+ for i := range b {
+ b[i] = letters[rand.Intn(len(letters))] //nolint:gosec
+ }
+ return string(b)
+}
+
+func buildNonce() (string, error) {
+ /* #nosec */
+ h := md5.New()
+ if _, err := io.WriteString(h, strconv.FormatInt(time.Now().Unix(), 10)); err != nil {
+ return "", fmt.Errorf("%w: %v", errFailedToGenerateNonce, err)
+ }
+ if _, err := io.WriteString(h, strconv.FormatInt(rand.Int63(), 10)); err != nil { //nolint:gosec
+ return "", fmt.Errorf("%w: %v", errFailedToGenerateNonce, err)
+ }
+ return fmt.Sprintf("%x", h.Sum(nil)), nil
+}
+
+func buildAndSend(conn net.PacketConn, dst net.Addr, attrs ...stun.Setter) error {
+ msg, err := stun.Build(attrs...)
+ if err != nil {
+ return err
+ }
+ _, err = conn.WriteTo(msg.Raw, dst)
+ return err
+}
+
+// Send a STUN packet and return the original error to the caller
+func buildAndSendErr(conn net.PacketConn, dst net.Addr, err error, attrs ...stun.Setter) error {
+ if sendErr := buildAndSend(conn, dst, attrs...); sendErr != nil {
+ err = fmt.Errorf("%w %v %v", errFailedToSendError, sendErr, err)
+ }
+ return err
+}
+
+func buildMsg(transactionID [stun.TransactionIDSize]byte, msgType stun.MessageType, additional ...stun.Setter) []stun.Setter {
+ return append([]stun.Setter{&stun.Message{TransactionID: transactionID}, msgType}, additional...)
+}
+
+func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method) (stun.MessageIntegrity, bool, error) {
+ respondWithNonce := func(responseCode stun.ErrorCode) (stun.MessageIntegrity, bool, error) {
+ nonce, err := buildNonce()
+ if err != nil {
+ return nil, false, err
+ }
+
+ // Nonce has already been taken
+ if _, keyCollision := r.Nonces.LoadOrStore(nonce, time.Now()); keyCollision {
+ return nil, false, errDuplicatedNonce
+ }
+
+ return nil, false, buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID,
+ stun.NewType(callingMethod, stun.ClassErrorResponse),
+ &stun.ErrorCodeAttribute{Code: responseCode},
+ stun.NewNonce(nonce),
+ stun.NewRealm(r.Realm),
+ )...)
+ }
+
+ if !m.Contains(stun.AttrMessageIntegrity) {
+ return respondWithNonce(stun.CodeUnauthorized)
+ }
+
+ nonceAttr := &stun.Nonce{}
+ usernameAttr := &stun.Username{}
+ realmAttr := &stun.Realm{}
+ badRequestMsg := buildMsg(m.TransactionID, stun.NewType(callingMethod, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest})
+
+ if err := nonceAttr.GetFrom(m); err != nil {
+ return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ // Assert Nonce exists and is not expired
+ nonceCreationTime, ok := r.Nonces.Load(string(*nonceAttr))
+ if !ok || time.Since(nonceCreationTime.(time.Time)) >= nonceLifetime {
+ r.Nonces.Delete(nonceAttr)
+ return respondWithNonce(stun.CodeStaleNonce)
+ }
+
+ if err := realmAttr.GetFrom(m); err != nil {
+ return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ } else if err := usernameAttr.GetFrom(m); err != nil {
+ return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ ourKey, ok := r.AuthHandler(usernameAttr.String(), realmAttr.String(), r.SrcAddr)
+ if !ok {
+ return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("%w %s", errNoSuchUser, usernameAttr.String()), badRequestMsg...)
+ }
+
+ if err := stun.MessageIntegrity(ourKey).Check(m); err != nil {
+ return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
+ }
+
+ return stun.MessageIntegrity(ourKey), true, nil
+}
+
+func allocationLifeTime(m *stun.Message) time.Duration {
+ lifetimeDuration := proto.DefaultLifetime
+
+ var lifetime proto.Lifetime
+ if err := lifetime.GetFrom(m); err == nil {
+ if lifetime.Duration < maximumAllocationLifetime {
+ lifetimeDuration = lifetime.Duration
+ }
+ }
+
+ return lifetimeDuration
+}
diff --git a/vendor/github.com/pion/turn/v2/lt_cred.go b/vendor/github.com/pion/turn/v2/lt_cred.go
new file mode 100644
index 0000000..07520b1
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/lt_cred.go
@@ -0,0 +1,56 @@
+package turn
+
+import ( //nolint:gci
+ "crypto/hmac"
+ "crypto/sha1" //nolint:gosec,gci
+ "encoding/base64"
+ "net"
+ "strconv"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+// GenerateCredentials can be used to create credentials valid for [duration] time
+func GenerateLongTermCredentials(sharedSecret string, duration time.Duration) (string, string, error) {
+ t := time.Now().Add(duration).Unix()
+ username := strconv.FormatInt(t, 10)
+ password, err := longTermCredentials(username, sharedSecret)
+ return username, password, err
+}
+
+func longTermCredentials(username string, sharedSecret string) (string, error) {
+ mac := hmac.New(sha1.New, []byte(sharedSecret))
+ _, err := mac.Write([]byte(username))
+ if err != nil {
+ return "", err // Not sure if this will ever happen
+ }
+ password := mac.Sum(nil)
+ return base64.StdEncoding.EncodeToString(password), nil
+}
+
+// NewAuthHandler returns a turn.AuthAuthHandler used with Long Term (or Time Windowed) Credentials.
+// https://tools.ietf.org/search/rfc5389#section-10.2
+func NewLongTermAuthHandler(sharedSecret string, l logging.LeveledLogger) AuthHandler {
+ if l == nil {
+ l = logging.NewDefaultLoggerFactory().NewLogger("turn")
+ }
+ return func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) {
+ l.Tracef("Authentication username=%q realm=%q srcAddr=%v\n", username, realm, srcAddr)
+ t, err := strconv.Atoi(username)
+ if err != nil {
+ l.Errorf("Invalid time-windowed username %q", username)
+ return nil, false
+ }
+ if int64(t) < time.Now().Unix() {
+ l.Errorf("Expired time-windowed username %q", username)
+ return nil, false
+ }
+ password, err := longTermCredentials(username, sharedSecret)
+ if err != nil {
+ l.Error(err.Error())
+ return nil, false
+ }
+ return GenerateAuthKey(username, realm, password), true
+ }
+}
diff --git a/vendor/github.com/pion/turn/v2/relay_address_generator_none.go b/vendor/github.com/pion/turn/v2/relay_address_generator_none.go
new file mode 100644
index 0000000..6fab061
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/relay_address_generator_none.go
@@ -0,0 +1,45 @@
+package turn
+
+import (
+ "net"
+ "strconv"
+
+ "github.com/pion/transport/vnet"
+)
+
+// RelayAddressGeneratorNone returns the listener with no modifications
+type RelayAddressGeneratorNone struct {
+ // Address is passed to Listen/ListenPacket when creating the Relay
+ Address string
+
+ Net *vnet.Net
+}
+
+// Validate is caled on server startup and confirms the RelayAddressGenerator is properly configured
+func (r *RelayAddressGeneratorNone) Validate() error {
+ if r.Net == nil {
+ r.Net = vnet.NewNet(nil)
+ }
+
+ switch {
+ case r.Address == "":
+ return errListeningAddressInvalid
+ default:
+ return nil
+ }
+}
+
+// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port to populate the allocation response with
+func (r *RelayAddressGeneratorNone) AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) {
+ conn, err := r.Net.ListenPacket(network, r.Address+":"+strconv.Itoa(requestedPort))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return conn, conn.LocalAddr(), nil
+}
+
+// AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with
+func (r *RelayAddressGeneratorNone) AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error) {
+ return nil, nil, errTODO
+}
diff --git a/vendor/github.com/pion/turn/v2/relay_address_generator_range.go b/vendor/github.com/pion/turn/v2/relay_address_generator_range.go
new file mode 100644
index 0000000..9f95429
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/relay_address_generator_range.go
@@ -0,0 +1,92 @@
+package turn
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/pion/randutil"
+ "github.com/pion/transport/vnet"
+)
+
+// RelayAddressGeneratorPortRange can be used to only allocate connections inside a defined port range.
+// Similar to the RelayAddressGeneratorStatic a static ip address can be set.
+type RelayAddressGeneratorPortRange struct {
+ // RelayAddress is the IP returned to the user when the relay is created
+ RelayAddress net.IP
+
+ // MinPort the minimum port to allocate
+ MinPort uint16
+ // MaxPort the maximum (inclusive) port to allocate
+ MaxPort uint16
+
+ // MaxRetries the amount of tries to allocate a random port in the defined range
+ MaxRetries int
+
+ // Rand the random source of numbers
+ Rand randutil.MathRandomGenerator
+
+ // Address is passed to Listen/ListenPacket when creating the Relay
+ Address string
+
+ Net *vnet.Net
+}
+
+// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured
+func (r *RelayAddressGeneratorPortRange) Validate() error {
+ if r.Net == nil {
+ r.Net = vnet.NewNet(nil)
+ }
+
+ if r.Rand == nil {
+ r.Rand = randutil.NewMathRandomGenerator()
+ }
+
+ if r.MaxRetries == 0 {
+ r.MaxRetries = 10
+ }
+
+ switch {
+ case r.MinPort == 0:
+ return errMinPortNotZero
+ case r.MaxPort == 0:
+ return errMaxPortNotZero
+ case r.RelayAddress == nil:
+ return errRelayAddressInvalid
+ case r.Address == "":
+ return errListeningAddressInvalid
+ default:
+ return nil
+ }
+}
+
+// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port to populate the allocation response with
+func (r *RelayAddressGeneratorPortRange) AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) {
+ if requestedPort != 0 {
+ conn, err := r.Net.ListenPacket(network, fmt.Sprintf("%s:%d", r.Address, requestedPort))
+ if err != nil {
+ return nil, nil, err
+ }
+ relayAddr := conn.LocalAddr().(*net.UDPAddr)
+ relayAddr.IP = r.RelayAddress
+ return conn, relayAddr, nil
+ }
+
+ for try := 0; try < r.MaxRetries; try++ {
+ port := r.MinPort + uint16(r.Rand.Intn(int((r.MaxPort+1)-r.MinPort)))
+ conn, err := r.Net.ListenPacket(network, fmt.Sprintf("%s:%d", r.Address, port))
+ if err != nil {
+ continue
+ }
+
+ relayAddr := conn.LocalAddr().(*net.UDPAddr)
+ relayAddr.IP = r.RelayAddress
+ return conn, relayAddr, nil
+ }
+
+ return nil, nil, errMaxRetriesExceeded
+}
+
+// AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with
+func (r *RelayAddressGeneratorPortRange) AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error) {
+ return nil, nil, errTODO
+}
diff --git a/vendor/github.com/pion/turn/v2/relay_address_generator_static.go b/vendor/github.com/pion/turn/v2/relay_address_generator_static.go
new file mode 100644
index 0000000..ae921e7
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/relay_address_generator_static.go
@@ -0,0 +1,55 @@
+package turn
+
+import (
+ "net"
+ "strconv"
+
+ "github.com/pion/transport/vnet"
+)
+
+// RelayAddressGeneratorStatic can be used to return static IP address each time a relay is created.
+// This can be used when you have a single static IP address that you want to use
+type RelayAddressGeneratorStatic struct {
+ // RelayAddress is the IP returned to the user when the relay is created
+ RelayAddress net.IP
+
+ // Address is passed to Listen/ListenPacket when creating the Relay
+ Address string
+
+ Net *vnet.Net
+}
+
+// Validate is caled on server startup and confirms the RelayAddressGenerator is properly configured
+func (r *RelayAddressGeneratorStatic) Validate() error {
+ if r.Net == nil {
+ r.Net = vnet.NewNet(nil)
+ }
+
+ switch {
+ case r.RelayAddress == nil:
+ return errRelayAddressInvalid
+ case r.Address == "":
+ return errListeningAddressInvalid
+ default:
+ return nil
+ }
+}
+
+// AllocatePacketConn generates a new PacketConn to receive traffic on and the IP/Port to populate the allocation response with
+func (r *RelayAddressGeneratorStatic) AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error) {
+ conn, err := r.Net.ListenPacket(network, r.Address+":"+strconv.Itoa(requestedPort))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Replace actual listening IP with the user requested one of RelayAddressGeneratorStatic
+ relayAddr := conn.LocalAddr().(*net.UDPAddr)
+ relayAddr.IP = r.RelayAddress
+
+ return conn, relayAddr, nil
+}
+
+// AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with
+func (r *RelayAddressGeneratorStatic) AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error) {
+ return nil, nil, errTODO
+}
diff --git a/vendor/github.com/pion/turn/v2/renovate.json b/vendor/github.com/pion/turn/v2/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/turn/v2/server.go b/vendor/github.com/pion/turn/v2/server.go
new file mode 100644
index 0000000..c8deba8
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/server.go
@@ -0,0 +1,161 @@
+// Package turn contains the public API for pion/turn, a toolkit for building TURN clients and servers
+package turn
+
+import (
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/turn/v2/internal/allocation"
+ "github.com/pion/turn/v2/internal/proto"
+ "github.com/pion/turn/v2/internal/server"
+)
+
+const (
+ inboundMTU = 1500
+)
+
+// Server is an instance of the Pion TURN Server
+type Server struct {
+ log logging.LeveledLogger
+ authHandler AuthHandler
+ realm string
+ channelBindTimeout time.Duration
+ nonces *sync.Map
+
+ packetConnConfigs []PacketConnConfig
+ listenerConfigs []ListenerConfig
+}
+
+// NewServer creates the Pion TURN server
+func NewServer(config ServerConfig) (*Server, error) {
+ if err := config.validate(); err != nil {
+ return nil, err
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ s := &Server{
+ log: loggerFactory.NewLogger("turn"),
+ authHandler: config.AuthHandler,
+ realm: config.Realm,
+ channelBindTimeout: config.ChannelBindTimeout,
+ packetConnConfigs: config.PacketConnConfigs,
+ listenerConfigs: config.ListenerConfigs,
+ nonces: &sync.Map{},
+ }
+
+ if s.channelBindTimeout == 0 {
+ s.channelBindTimeout = proto.DefaultLifetime
+ }
+
+ for i := range s.packetConnConfigs {
+ go func(p PacketConnConfig) {
+ allocationManager, err := allocation.NewManager(allocation.ManagerConfig{
+ AllocatePacketConn: p.RelayAddressGenerator.AllocatePacketConn,
+ AllocateConn: p.RelayAddressGenerator.AllocateConn,
+ LeveledLogger: s.log,
+ })
+ if err != nil {
+ s.log.Errorf("exit read loop on error: %s", err.Error())
+ return
+ }
+ defer func() {
+ if err := allocationManager.Close(); err != nil {
+ s.log.Errorf("Failed to close AllocationManager: %s", err.Error())
+ }
+ }()
+
+ s.readLoop(p.PacketConn, allocationManager)
+ }(s.packetConnConfigs[i])
+ }
+
+ for _, listener := range s.listenerConfigs {
+ go func(l ListenerConfig) {
+ allocationManager, err := allocation.NewManager(allocation.ManagerConfig{
+ AllocatePacketConn: l.RelayAddressGenerator.AllocatePacketConn,
+ AllocateConn: l.RelayAddressGenerator.AllocateConn,
+ LeveledLogger: s.log,
+ })
+ if err != nil {
+ s.log.Errorf("exit read loop on error: %s", err.Error())
+ return
+ }
+ defer func() {
+ if err := allocationManager.Close(); err != nil {
+ s.log.Errorf("Failed to close AllocationManager: %s", err.Error())
+ }
+ }()
+
+ for {
+ conn, err := l.Listener.Accept()
+ if err != nil {
+ s.log.Debugf("exit accept loop on error: %s", err.Error())
+ return
+ }
+
+ go s.readLoop(NewSTUNConn(conn), allocationManager)
+ }
+ }(listener)
+ }
+
+ return s, nil
+}
+
+// Close stops the TURN Server. It cleans up any associated state and closes all connections it is managing
+func (s *Server) Close() error {
+ var errors []error
+
+ for _, p := range s.packetConnConfigs {
+ if err := p.PacketConn.Close(); err != nil {
+ errors = append(errors, err)
+ }
+ }
+
+ for _, l := range s.listenerConfigs {
+ if err := l.Listener.Close(); err != nil {
+ errors = append(errors, err)
+ }
+ }
+
+ if len(errors) == 0 {
+ return nil
+ }
+
+ err := errFailedToClose
+ for _, e := range errors {
+ err = fmt.Errorf("%s; Close error (%v) ", err.Error(), e) //nolint:goerr113
+ }
+
+ return err
+}
+
+func (s *Server) readLoop(p net.PacketConn, allocationManager *allocation.Manager) {
+ buf := make([]byte, inboundMTU)
+ for {
+ n, addr, err := p.ReadFrom(buf)
+ if err != nil {
+ s.log.Debugf("exit read loop on error: %s", err.Error())
+ return
+ }
+
+ if err := server.HandleRequest(server.Request{
+ Conn: p,
+ SrcAddr: addr,
+ Buff: buf[:n],
+ Log: s.log,
+ AuthHandler: s.authHandler,
+ Realm: s.realm,
+ AllocationManager: allocationManager,
+ ChannelBindTimeout: s.channelBindTimeout,
+ Nonces: s.nonces,
+ }); err != nil {
+ s.log.Errorf("error when handling datagram: %v", err)
+ }
+ }
+}
diff --git a/vendor/github.com/pion/turn/v2/server_config.go b/vendor/github.com/pion/turn/v2/server_config.go
new file mode 100644
index 0000000..9ff3872
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/server_config.go
@@ -0,0 +1,116 @@
+package turn
+
+import (
+ "crypto/md5" //nolint:gosec,gci
+ "fmt"
+ "net"
+ "strings"
+ "time"
+
+ "github.com/pion/logging"
+)
+
+// RelayAddressGenerator is used to generate a RelayAddress when creating an allocation.
+// You can use one of the provided ones or provide your own.
+type RelayAddressGenerator interface {
+ // Validate confirms that the RelayAddressGenerator is properly initialized
+ Validate() error
+
+ // Allocate a PacketConn (UDP) RelayAddress
+ AllocatePacketConn(network string, requestedPort int) (net.PacketConn, net.Addr, error)
+
+ // Allocate a Conn (TCP) RelayAddress
+ AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error)
+}
+
+// PacketConnConfig is a single net.PacketConn to listen/write on. This will be used for UDP listeners
+type PacketConnConfig struct {
+ PacketConn net.PacketConn
+
+ // When an allocation is generated the RelayAddressGenerator
+ // creates the net.PacketConn and returns the IP/Port it is available at
+ RelayAddressGenerator RelayAddressGenerator
+}
+
+func (c *PacketConnConfig) validate() error {
+ if c.PacketConn == nil {
+ return errConnUnset
+ }
+ if c.RelayAddressGenerator == nil {
+ return errRelayAddressGeneratorUnset
+ }
+
+ return c.RelayAddressGenerator.Validate()
+}
+
+// ListenerConfig is a single net.Listener to accept connections on. This will be used for TCP, TLS and DTLS listeners
+type ListenerConfig struct {
+ Listener net.Listener
+
+ // When an allocation is generated the RelayAddressGenerator
+ // creates the net.PacketConn and returns the IP/Port it is available at
+ RelayAddressGenerator RelayAddressGenerator
+}
+
+func (c *ListenerConfig) validate() error {
+ if c.Listener == nil {
+ return errListenerUnset
+ }
+
+ if c.RelayAddressGenerator == nil {
+ return errRelayAddressGeneratorUnset
+ }
+
+ return c.RelayAddressGenerator.Validate()
+}
+
+// AuthHandler is a callback used to handle incoming auth requests, allowing users to customize Pion TURN with custom behavior
+type AuthHandler func(username, realm string, srcAddr net.Addr) (key []byte, ok bool)
+
+// GenerateAuthKey is a convince function to easily generate keys in the format used by AuthHandler
+func GenerateAuthKey(username, realm, password string) []byte {
+ // #nosec
+ h := md5.New()
+ fmt.Fprint(h, strings.Join([]string{username, realm, password}, ":"))
+ return h.Sum(nil)
+}
+
+// ServerConfig configures the Pion TURN Server
+type ServerConfig struct {
+ // PacketConnConfigs and ListenerConfigs are a list of all the turn listeners
+ // Each listener can have custom behavior around the creation of Relays
+ PacketConnConfigs []PacketConnConfig
+ ListenerConfigs []ListenerConfig
+
+ // LoggerFactory must be set for logging from this server.
+ LoggerFactory logging.LoggerFactory
+
+ // Realm sets the realm for this server
+ Realm string
+
+ // AuthHandler is a callback used to handle incoming auth requests, allowing users to customize Pion TURN with custom behavior
+ AuthHandler AuthHandler
+
+ // ChannelBindTimeout sets the lifetime of channel binding. Defaults to 10 minutes.
+ ChannelBindTimeout time.Duration
+}
+
+func (s *ServerConfig) validate() error {
+ if len(s.PacketConnConfigs) == 0 && len(s.ListenerConfigs) == 0 {
+ return errNoAvailableConns
+ }
+
+ for _, s := range s.PacketConnConfigs {
+ if err := s.validate(); err != nil {
+ return err
+ }
+ }
+
+ for _, s := range s.ListenerConfigs {
+ if err := s.validate(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/turn/v2/stun_conn.go b/vendor/github.com/pion/turn/v2/stun_conn.go
new file mode 100644
index 0000000..e1446bd
--- /dev/null
+++ b/vendor/github.com/pion/turn/v2/stun_conn.go
@@ -0,0 +1,121 @@
+package turn
+
+import (
+ "encoding/binary"
+ "errors"
+ "net"
+ "time"
+
+ "github.com/pion/stun"
+ "github.com/pion/turn/v2/internal/proto"
+)
+
+var (
+ errInvalidTURNFrame = errors.New("data is not a valid TURN frame, no STUN or ChannelData found")
+ errIncompleteTURNFrame = errors.New("data contains incomplete STUN or TURN frame")
+)
+
+// STUNConn wraps a net.Conn and implements
+// net.PacketConn by being STUN aware and
+// packetizing the stream
+type STUNConn struct {
+ nextConn net.Conn
+ buff []byte
+}
+
+const (
+ stunHeaderSize = 20
+
+ channelDataLengthSize = 2
+ channelDataNumberSize = channelDataLengthSize
+ channelDataHeaderSize = channelDataLengthSize + channelDataNumberSize
+ channelDataPadding = 4
+)
+
+// Given a buffer give the last offset of the TURN frame
+// If the buffer isn't a valid STUN or ChannelData packet
+// or the length doesn't match return false
+func consumeSingleTURNFrame(p []byte) (int, error) {
+ // Too short to determine if ChannelData or STUN
+ if len(p) < 9 {
+ return 0, errIncompleteTURNFrame
+ }
+
+ var datagramSize uint16
+ if stun.IsMessage(p) {
+ datagramSize = binary.BigEndian.Uint16(p[2:4]) + stunHeaderSize
+ } else if num := binary.BigEndian.Uint16(p[0:4]); proto.ChannelNumber(num).Valid() {
+ datagramSize = binary.BigEndian.Uint16(p[channelDataNumberSize:channelDataHeaderSize])
+ if paddingOverflow := (datagramSize + channelDataPadding) % channelDataPadding; paddingOverflow != 0 {
+ datagramSize = (datagramSize + channelDataPadding) - paddingOverflow
+ }
+
+ datagramSize += channelDataHeaderSize
+ } else {
+ return 0, errInvalidTURNFrame
+ }
+
+ if len(p) < int(datagramSize) {
+ return 0, errIncompleteTURNFrame
+ }
+
+ return int(datagramSize), nil
+}
+
+// ReadFrom implements ReadFrom from net.PacketConn
+func (s *STUNConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
+ // First pass any buffered data from previous reads
+ n, err = consumeSingleTURNFrame(s.buff)
+ if errors.Is(err, errInvalidTURNFrame) {
+ return 0, nil, err
+ } else if err == nil {
+ copy(p, s.buff[:n])
+ s.buff = s.buff[n:]
+
+ return n, s.nextConn.RemoteAddr(), nil
+ }
+
+ // Then read from the nextConn, appending to our buff
+ n, err = s.nextConn.Read(p)
+ if err != nil {
+ return 0, nil, err
+ }
+
+ s.buff = append(s.buff, append([]byte{}, p[:n]...)...)
+ return s.ReadFrom(p)
+}
+
+// WriteTo implements WriteTo from net.PacketConn
+func (s *STUNConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
+ return s.nextConn.Write(p)
+}
+
+// Close implements Close from net.PacketConn
+func (s *STUNConn) Close() error {
+ return s.nextConn.Close()
+}
+
+// LocalAddr implements LocalAddr from net.PacketConn
+func (s *STUNConn) LocalAddr() net.Addr {
+ return s.nextConn.LocalAddr()
+}
+
+// SetDeadline implements SetDeadline from net.PacketConn
+func (s *STUNConn) SetDeadline(t time.Time) error {
+ return s.nextConn.SetDeadline(t)
+}
+
+// SetReadDeadline implements SetReadDeadline from net.PacketConn
+func (s *STUNConn) SetReadDeadline(t time.Time) error {
+ return s.nextConn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline implements SetWriteDeadline from net.PacketConn
+func (s *STUNConn) SetWriteDeadline(t time.Time) error {
+ return s.nextConn.SetWriteDeadline(t)
+}
+
+// NewSTUNConn creates a STUNConn
+func NewSTUNConn(nextConn net.Conn) *STUNConn {
+ return &STUNConn{nextConn: nextConn}
+}
diff --git a/vendor/github.com/pion/udp/.gitignore b/vendor/github.com/pion/udp/.gitignore
new file mode 100644
index 0000000..f7b3492
--- /dev/null
+++ b/vendor/github.com/pion/udp/.gitignore
@@ -0,0 +1,15 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/ \ No newline at end of file
diff --git a/vendor/github.com/pion/udp/.golangci.yml b/vendor/github.com/pion/udp/.golangci.yml
new file mode 100644
index 0000000..1387b55
--- /dev/null
+++ b/vendor/github.com/pion/udp/.golangci.yml
@@ -0,0 +1,12 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+
+linters:
+ enable-all: true
+ disable:
+
+issues:
+ exclude-use-default: false
diff --git a/vendor/github.com/pion/udp/LICENSE b/vendor/github.com/pion/udp/LICENSE
new file mode 100644
index 0000000..81f990d
--- /dev/null
+++ b/vendor/github.com/pion/udp/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020
+
+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.
diff --git a/vendor/github.com/pion/udp/README.md b/vendor/github.com/pion/udp/README.md
new file mode 100644
index 0000000..f96659f
--- /dev/null
+++ b/vendor/github.com/pion/udp/README.md
@@ -0,0 +1,39 @@
+<h1 align="center">
+ <br>
+ Pion UDP
+ <br>
+</h1>
+<h4 align="center">A connection-oriented listener over a UDP PacketConn</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-udp-gray.svg?longCache=true&colorB=brightgreen" alt="Pion UDP"></a>
+ <!--<a href="https://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/badge.svg" alt="Sourcegraph Widget"></a>-->
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/udp"><img src="https://travis-ci.org/pion/udp.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/udp"><img src="https://godoc.org/github.com/pion/udp?status.svg" alt="GoDoc"></a>
+ <a href="https://codecov.io/gh/pion/udp"><img src="https://codecov.io/gh/pion/udp/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/udp"><img src="https://goreportcard.com/badge/github.com/pion/udp" alt="Go Report Card"></a>
+ <!--<a href="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>-->
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+### Roadmap
+This package is used in the [DTLS](https://github.com/pion/dtls) and [SCTP](https://github.com/pion/sctp) transport to provide a connection-oriented listener over a UDP.
+
+### Community
+Pion has an active community on the [Golang Slack](https://pion.ly/slack/). You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion).
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *Original Author*
+* [Atsushi Watanabe](https://github.com/at-wat) - *Original Author*
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/udp/conn.go b/vendor/github.com/pion/udp/conn.go
new file mode 100644
index 0000000..9f680e8
--- /dev/null
+++ b/vendor/github.com/pion/udp/conn.go
@@ -0,0 +1,296 @@
+// Package udp provides a connection-oriented listener over a UDP PacketConn
+package udp
+
+import (
+ "context"
+ "errors"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/transport/deadline"
+ "github.com/pion/transport/packetio"
+)
+
+const receiveMTU = 8192
+const defaultListenBacklog = 128 // same as Linux default
+
+var errClosedListener = errors.New("udp: listener closed")
+var errListenQueueExceeded = errors.New("udp: listen queue exceeded")
+
+// listener augments a connection-oriented Listener over a UDP PacketConn
+type listener struct {
+ pConn *net.UDPConn
+
+ accepting atomic.Value // bool
+ acceptCh chan *Conn
+ doneCh chan struct{}
+ doneOnce sync.Once
+ acceptFilter func([]byte) bool
+
+ connLock sync.Mutex
+ conns map[string]*Conn
+ connWG sync.WaitGroup
+
+ readWG sync.WaitGroup
+ errClose atomic.Value // error
+}
+
+// Accept waits for and returns the next connection to the listener.
+func (l *listener) Accept() (net.Conn, error) {
+ select {
+ case c := <-l.acceptCh:
+ l.connWG.Add(1)
+ return c, nil
+
+ case <-l.doneCh:
+ return nil, errClosedListener
+ }
+}
+
+// Close closes the listener.
+// Any blocked Accept operations will be unblocked and return errors.
+func (l *listener) Close() error {
+ var err error
+ l.doneOnce.Do(func() {
+ l.accepting.Store(false)
+ close(l.doneCh)
+
+ l.connLock.Lock()
+ // Close unaccepted connections
+ L_CLOSE:
+ for {
+ select {
+ case c := <-l.acceptCh:
+ close(c.doneCh)
+ delete(l.conns, c.rAddr.String())
+
+ default:
+ break L_CLOSE
+ }
+ }
+ nConns := len(l.conns)
+ l.connLock.Unlock()
+
+ l.connWG.Done()
+
+ if nConns == 0 {
+ // Wait if this is the final connection
+ l.readWG.Wait()
+ if errClose, ok := l.errClose.Load().(error); ok {
+ err = errClose
+ }
+ } else {
+ err = nil
+ }
+ })
+
+ return err
+}
+
+// Addr returns the listener's network address.
+func (l *listener) Addr() net.Addr {
+ return l.pConn.LocalAddr()
+}
+
+// ListenConfig stores options for listening to an address.
+type ListenConfig struct {
+ // Backlog defines the maximum length of the queue of pending
+ // connections. It is equivalent of the backlog argument of
+ // POSIX listen function.
+ // If a connection request arrives when the queue is full,
+ // the request will be silently discarded, unlike TCP.
+ // Set zero to use default value 128 which is same as Linux default.
+ Backlog int
+
+ // AcceptFilter determines whether the new conn should be made for
+ // the incoming packet. If not set, any packet creates new conn.
+ AcceptFilter func([]byte) bool
+}
+
+// Listen creates a new listener based on the ListenConfig.
+func (lc *ListenConfig) Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
+ if lc.Backlog == 0 {
+ lc.Backlog = defaultListenBacklog
+ }
+
+ conn, err := net.ListenUDP(network, laddr)
+ if err != nil {
+ return nil, err
+ }
+
+ l := &listener{
+ pConn: conn,
+ acceptCh: make(chan *Conn, lc.Backlog),
+ conns: make(map[string]*Conn),
+ doneCh: make(chan struct{}),
+ acceptFilter: lc.AcceptFilter,
+ }
+ l.accepting.Store(true)
+ l.connWG.Add(1)
+ l.readWG.Add(2) // wait readLoop and Close execution routine
+
+ go l.readLoop()
+ go func() {
+ l.connWG.Wait()
+ if err := l.pConn.Close(); err != nil {
+ l.errClose.Store(err)
+ }
+ l.readWG.Done()
+ }()
+
+ return l, nil
+}
+
+// Listen creates a new listener using default ListenConfig.
+func Listen(network string, laddr *net.UDPAddr) (net.Listener, error) {
+ return (&ListenConfig{}).Listen(network, laddr)
+}
+
+var readBufferPool = &sync.Pool{
+ New: func() interface{} {
+ buf := make([]byte, receiveMTU)
+ return &buf
+ },
+}
+
+// readLoop has to tasks:
+// 1. Dispatching incoming packets to the correct Conn.
+// It can therefore not be ended until all Conns are closed.
+// 2. Creating a new Conn when receiving from a new remote.
+func (l *listener) readLoop() {
+ defer l.readWG.Done()
+
+ for {
+ buf := *(readBufferPool.Get().(*[]byte))
+ n, raddr, err := l.pConn.ReadFrom(buf)
+ if err != nil {
+ return
+ }
+ conn, ok, err := l.getConn(raddr, buf[:n])
+ if err != nil {
+ continue
+ }
+ if ok {
+ _, _ = conn.buffer.Write(buf[:n])
+ }
+ }
+}
+
+func (l *listener) getConn(raddr net.Addr, buf []byte) (*Conn, bool, error) {
+ l.connLock.Lock()
+ defer l.connLock.Unlock()
+ conn, ok := l.conns[raddr.String()]
+ if !ok {
+ if !l.accepting.Load().(bool) {
+ return nil, false, errClosedListener
+ }
+ if l.acceptFilter != nil {
+ if !l.acceptFilter(buf) {
+ return nil, false, nil
+ }
+ }
+ conn = l.newConn(raddr)
+ select {
+ case l.acceptCh <- conn:
+ l.conns[raddr.String()] = conn
+ default:
+ return nil, false, errListenQueueExceeded
+ }
+ }
+ return conn, true, nil
+}
+
+// Conn augments a connection-oriented connection over a UDP PacketConn
+type Conn struct {
+ listener *listener
+
+ rAddr net.Addr
+
+ buffer *packetio.Buffer
+
+ doneCh chan struct{}
+ doneOnce sync.Once
+
+ writeDeadline *deadline.Deadline
+}
+
+func (l *listener) newConn(rAddr net.Addr) *Conn {
+ return &Conn{
+ listener: l,
+ rAddr: rAddr,
+ buffer: packetio.NewBuffer(),
+ doneCh: make(chan struct{}),
+ writeDeadline: deadline.New(),
+ }
+}
+
+// Read
+func (c *Conn) Read(p []byte) (int, error) {
+ return c.buffer.Read(p)
+}
+
+// Write writes len(p) bytes from p to the DTLS connection
+func (c *Conn) Write(p []byte) (n int, err error) {
+ select {
+ case <-c.writeDeadline.Done():
+ return 0, context.DeadlineExceeded
+ default:
+ }
+ return c.listener.pConn.WriteTo(p, c.rAddr)
+}
+
+// Close closes the conn and releases any Read calls
+func (c *Conn) Close() error {
+ var err error
+ c.doneOnce.Do(func() {
+ c.listener.connWG.Done()
+ close(c.doneCh)
+ c.listener.connLock.Lock()
+ delete(c.listener.conns, c.rAddr.String())
+ nConns := len(c.listener.conns)
+ c.listener.connLock.Unlock()
+
+ if nConns == 0 && !c.listener.accepting.Load().(bool) {
+ // Wait if this is the final connection
+ c.listener.readWG.Wait()
+ if errClose, ok := c.listener.errClose.Load().(error); ok {
+ err = errClose
+ }
+ } else {
+ err = nil
+ }
+ })
+
+ return err
+}
+
+// LocalAddr implements net.Conn.LocalAddr
+func (c *Conn) LocalAddr() net.Addr {
+ return c.listener.pConn.LocalAddr()
+}
+
+// RemoteAddr implements net.Conn.RemoteAddr
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.rAddr
+}
+
+// SetDeadline implements net.Conn.SetDeadline
+func (c *Conn) SetDeadline(t time.Time) error {
+ c.writeDeadline.Set(t)
+ return c.SetReadDeadline(t)
+}
+
+// SetReadDeadline implements net.Conn.SetDeadline
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ return c.buffer.SetReadDeadline(t)
+}
+
+// SetWriteDeadline implements net.Conn.SetDeadline
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ c.writeDeadline.Set(t)
+ // Write deadline of underlying connection should not be changed
+ // since the connection can be shared.
+ return nil
+}
diff --git a/vendor/github.com/pion/udp/go.mod b/vendor/github.com/pion/udp/go.mod
new file mode 100644
index 0000000..59289b6
--- /dev/null
+++ b/vendor/github.com/pion/udp/go.mod
@@ -0,0 +1,5 @@
+module github.com/pion/udp
+
+go 1.14
+
+require github.com/pion/transport v0.10.0
diff --git a/vendor/github.com/pion/udp/go.sum b/vendor/github.com/pion/udp/go.sum
new file mode 100644
index 0000000..cd0abb2
--- /dev/null
+++ b/vendor/github.com/pion/udp/go.sum
@@ -0,0 +1,19 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80=
+github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/pion/webrtc/v3/.codacy.yaml b/vendor/github.com/pion/webrtc/v3/.codacy.yaml
new file mode 100644
index 0000000..a8c225b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/.codacy.yaml
@@ -0,0 +1,3 @@
+---
+exclude_paths:
+ - examples/examples.json
diff --git a/vendor/github.com/pion/webrtc/v3/.eslintrc.json b/vendor/github.com/pion/webrtc/v3/.eslintrc.json
new file mode 100644
index 0000000..a755cdb
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": ["standard"]
+}
diff --git a/vendor/github.com/pion/webrtc/v3/.gitignore b/vendor/github.com/pion/webrtc/v3/.gitignore
new file mode 100644
index 0000000..83db74b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/.gitignore
@@ -0,0 +1,24 @@
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
diff --git a/vendor/github.com/pion/webrtc/v3/.golangci.yml b/vendor/github.com/pion/webrtc/v3/.golangci.yml
new file mode 100644
index 0000000..d6162c9
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/.golangci.yml
@@ -0,0 +1,89 @@
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - deadcode # Finds unused code
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - noctx # noctx finds sending http request without context.Context
+ - scopelint # Scopelint checks for unpinned variables in go programs
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - structcheck # Finds unused struct fields
+ - stylecheck # Stylecheck is a replacement for golint
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - varcheck # Finds unused global variables and constants
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - lll # Reports long lines
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+run:
+ skip-dirs-use-default: false
diff --git a/vendor/github.com/pion/webrtc/v3/DESIGN.md b/vendor/github.com/pion/webrtc/v3/DESIGN.md
new file mode 100644
index 0000000..e22b08e
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/DESIGN.md
@@ -0,0 +1,43 @@
+<h1 align="center">
+ Design
+</h1>
+WebRTC is a powerful, but complicated technology you can build amazing things with, it comes with a steep learning curve though.
+Using WebRTC in the browser is easy, but outside the browser is more of a challenge. There are multiple libraries, and they all have
+varying levels of quality. Most are also difficult to build, and depend on libraries that aren't available in repos or portable.
+
+Pion WebRTC aims to solve all that! Built in native Go you should be able to send and receive media and text from anywhere with minimal headache.
+These are the design principals that drive Pion WebRTC and hopefully convince you it is worth a try.
+
+### Portable
+Pion WebRTC is written in Go and extremely portable. Anywhere Golang runs, Pion WebRTC should work as well! Instead of dealing with complicated
+cross-compiling of multiple libraries, you now can run anywhere with one `go build`
+
+### Flexible
+When possible we leave all decisions to the user. When choice is possible (like what logging library is used) we defer to the developer.
+
+### Simple API
+If you know how to use WebRTC in your browser, you know how to use Pion WebRTC.
+We try our best just to duplicate the Javascript API, so your code can look the same everywhere.
+
+If this is your first time using WebRTC, don't worry! We have multiple [examples](https://github.com/pion/webrtc/tree/master/examples) and [GoDoc](https://pkg.go.dev/github.com/pion/webrtc/v3)
+
+### Bring your own media
+Pion WebRTC doesn't make any assumptions about where your audio, video or text come from. You can use FFmpeg, GStreamer, MLT or just serve a video file.
+This library only serves to transport, not create media.
+
+### Safe
+Golang provides a great foundation to build safe network services.
+Especially when running a networked service that is highly concurrent bugs can be devastating.
+
+### Readable
+If code comes from an RFC we try to make sure everything is commented with a link to the spec.
+This makes learning and debugging easier, this WebRTC library was written to also serve as a guide for others.
+
+### Tested
+Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on.
+
+### Shared libraries
+Every Pion project is built using shared libraries, allowing others to review and reuse our libraries.
+
+### Community
+The most important part of Pion is the community. This projects only exist because of individual contributions. We aim to be radically open and do everything we can to support those that make Pion possible.
diff --git a/vendor/github.com/pion/webrtc/v3/LICENSE b/vendor/github.com/pion/webrtc/v3/LICENSE
new file mode 100644
index 0000000..ab60297
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018
+
+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.
diff --git a/vendor/github.com/pion/webrtc/v3/README.md b/vendor/github.com/pion/webrtc/v3/README.md
new file mode 100644
index 0000000..6fc98d5
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/README.md
@@ -0,0 +1,271 @@
+<h1 align="center">
+ <a href="https://pion.ly"><img src="./.github/pion-gopher-webrtc.png" alt="Pion WebRTC" height="250px"></a>
+ <br>
+ Pion WebRTC
+ <br>
+</h1>
+<h4 align="center">A pure Go implementation of the WebRTC API</h4>
+<p align="center">
+ <a href="https://pion.ly"><img src="https://img.shields.io/badge/pion-webrtc-gray.svg?longCache=true&colorB=brightgreen" alt="Pion webrtc"></a>
+ <a href="https://sourcegraph.com/github.com/pion/webrtc?badge"><img src="https://sourcegraph.com/github.com/pion/webrtc/-/badge.svg" alt="Sourcegraph Widget"></a>
+ <a href="https://pion.ly/slack"><img src="https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=brightgreen" alt="Slack Widget"></a>
+ <a href="https://twitter.com/_pion?ref_src=twsrc%5Etfw"><img src="https://img.shields.io/twitter/url.svg?label=Follow%20%40_pion&style=social&url=https%3A%2F%2Ftwitter.com%2F_pion" alt="Twitter Widget"></a>
+ <a href="https://github.com/pion/awesome-pion" alt="Awesome Pion"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg"></a>
+ <br>
+ <a href="https://travis-ci.org/pion/webrtc"><img src="https://travis-ci.org/pion/webrtc.svg?branch=master" alt="Build Status"></a>
+ <a href="https://pkg.go.dev/github.com/pion/webrtc/v3"><img src="https://pkg.go.dev/badge/github.com/pion/webrtc/v3" alt="PkgGoDev"></a>
+ <a href="https://codecov.io/gh/pion/webrtc"><img src="https://codecov.io/gh/pion/webrtc/branch/master/graph/badge.svg" alt="Coverage Status"></a>
+ <a href="https://goreportcard.com/report/github.com/pion/webrtc"><img src="https://goreportcard.com/badge/github.com/pion/webrtc" alt="Go Report Card"></a>
+ <a href="https://www.codacy.com/app/Sean-Der/webrtc"><img src="https://api.codacy.com/project/badge/Grade/18f4aec384894e6aac0b94effe51961d" alt="Codacy Badge"></a>
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
+</p>
+<br>
+
+### New Release
+
+Pion WebRTC v3.0.0 has been released! See the [release notes](https://github.com/pion/webrtc/wiki/Release-WebRTC@v3.0.0) to learn about new features and breaking changes.
+
+If you aren't able to upgrade yet check the [tags](https://github.com/pion/webrtc/tags) for the latest `v2` release.
+
+We would love your feedback! Please create GitHub issues or join [the Slack channel](https://pion.ly/slack) to follow development and speak with the maintainers.
+
+----
+
+### Usage
+[Go Modules](https://blog.golang.org/using-go-modules) are mandatory for using Pion WebRTC. So make sure you set `export GO111MODULE=on`, and explicitly specify `/v2` or `/v3` when importing.
+
+
+**[example applications](examples/README.md)** contains code samples of common things people build with Pion WebRTC.
+
+**[example-webrtc-applications](https://github.com/pion/example-webrtc-applications)** contains more full featured examples that use 3rd party libraries.
+
+**[awesome-pion](https://github.com/pion/awesome-pion)** contains projects that have used Pion, and serve as real world examples of usage.
+
+**[GoDoc](https://pkg.go.dev/github.com/pion/webrtc/v3)** is an auto generated API reference. All our Public APIs are commented.
+
+**[FAQ](https://github.com/pion/webrtc/wiki/FAQ)** has answers to common questions. If you have a question not covered please ask in [Slack](https://pion.ly/slack) we are always looking to expand it.
+
+Now go build something awesome! Here are some **ideas** to get your creative juices flowing:
+* Send a video file to multiple browser in real time for perfectly synchronized movie watching.
+* Send a webcam on an embedded device to your browser with no additional server required!
+* Securely send data between two servers, without using pub/sub.
+* Record your webcam and do special effects server side.
+* Build a conferencing application that processes audio/video and make decisions off of it.
+* Remotely control a robots and stream its cameras in realtime.
+
+### Want to learn more about WebRTC?
+Check out [WebRTC for the Curious](https://webrtcforthecurious.com). A book about WebRTC in depth, not just about the APIs.
+Learn the full details of ICE, SCTP, DTLS, SRTP, and how they work together to make up the WebRTC stack.
+
+This is also a great resource if you are trying to debug. Learn the tools of the trade and how to approach WebRTC issues.
+
+This book is vendor agnostic and will not have any Pion specific information.
+
+### Features
+#### PeerConnection API
+* Go implementation of [webrtc-pc](https://w3c.github.io/webrtc-pc/) and [webrtc-stats](https://www.w3.org/TR/webrtc-stats/)
+* DataChannels
+* Send/Receive audio and video
+* Renegotiation
+* Plan-B and Unified Plan
+* [SettingEngine](https://pkg.go.dev/github.com/pion/webrtc/v3#SettingEngine) for Pion specific extensions
+
+
+#### Connectivity
+* Full ICE Agent
+* ICE Restart
+* Trickle ICE
+* STUN
+* TURN (UDP, TCP, DTLS and TLS)
+* mDNS candidates
+
+#### DataChannels
+* Ordered/Unordered
+* Lossy/Lossless
+
+#### Media
+* API with direct RTP/RTCP access
+* Opus, PCM, H264, VP8 and VP9 packetizer
+* API also allows developer to pass their own packetizer
+* IVF, Ogg, H264 and Matroska provided for easy sending and saving
+* [getUserMedia](https://github.com/pion/mediadevices) implementation (Requires Cgo)
+* Easy integration with x264, libvpx, GStreamer and ffmpeg.
+* [Simulcast](https://github.com/pion/webrtc/tree/master/examples/simulcast)
+* [SVC](https://github.com/pion/rtp/blob/master/codecs/vp9_packet.go#L138)
+* [NACK](https://github.com/pion/interceptor/pull/4)
+* Full loss recovery and congestion control is not complete, see [pion/interceptor](https://github.com/pion/interceptor) for progress
+ * See [ion](https://github.com/pion/ion-sfu/tree/master/pkg/buffer) for how an implementor can do it today
+
+#### Security
+* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 and TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA for DTLS v1.2
+* SRTP_AEAD_AES_256_GCM and SRTP_AES128_CM_HMAC_SHA1_80 for SRTP
+* Hardware acceleration available for GCM suites
+
+#### Pure Go
+* No Cgo usage
+* Wide platform support
+ * Windows, macOS, Linux, FreeBSD
+ * iOS, Android
+ * [WASM](https://github.com/pion/webrtc/wiki/WebAssembly-Development-and-Testing) see [examples](examples/README.md#webassembly)
+ * 386, amd64, arm, mips, ppc64
+* Easy to build *Numbers generated on Intel(R) Core(TM) i5-2520M CPU @ 2.50GHz*
+ * **Time to build examples/play-from-disk** - 0.66s user 0.20s system 306% cpu 0.279 total
+ * **Time to run entire test suite** - 25.60s user 9.40s system 45% cpu 1:16.69 total
+* Tools to measure performance [provided](https://github.com/pion/rtsp-bench)
+
+
+### Roadmap
+The library is in active development, please refer to the [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+We also maintain a list of [Big Ideas](https://github.com/pion/webrtc/wiki/Big-Ideas) these are things we want to build but don't have a clear plan or the resources yet.
+If you are looking to get involved this is a great place to get started! We would also love to hear your ideas! Even if you can't implement it yourself, it could inspire others.
+
+### Community
+Pion has an active community on the [Slack](https://pion.ly/slack).
+
+Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible:
+
+* [John Bradley](https://github.com/kc5nra) - *Original Author*
+* [Michael Melvin Santry](https://github.com/santrym) - *Mascot*
+* [Raphael Randschau](https://github.com/nicolai86) - *STUN*
+* [Sean DuBois](https://github.com/Sean-Der) - *Original Author*
+* [Michiel De Backker](https://github.com/backkem) - *SDP, Public API, Project Management*
+* [Brendan Rius](https://github.com/brendanrius) - *Cleanup*
+* [Konstantin Itskov](https://github.com/trivigy) - *SDP Parsing*
+* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter*
+* [Ronan J](https://github.com/ronanj) - *Fix STCP PPID*
+* [wattanakorn495](https://github.com/wattanakorn495)
+* [Max Hawkins](https://github.com/maxhawkins) - *RTCP*
+* [Justin Okamoto](https://github.com/justinokamoto) - *Fix Docs*
+* [leeoxiang](https://github.com/notedit) - *Implement Janus examples*
+* [Denis](https://github.com/Hixon10) - *Adding docker-compose to pion-to-pion example*
+* [earle](https://github.com/aguilEA) - *Generate DTLS fingerprint in Go*
+* [Jake B](https://github.com/silbinarywolf) - *Fix Windows installation instructions*
+* [Michael MacDonald](https://github.com/mjmac) - *Plan B compatibility, Remote TURN/Trickle-ICE, Logging framework*
+* [Oleg Kovalov](https://github.com/cristaloleg) *Use wildcards instead of hardcoding travis-ci config*
+* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes*
+* [Tobias Fridén](https://github.com/tobiasfriden) *SRTP authentication verification*
+* [Yutaka Takeda](https://github.com/enobufs) *Fix ICE connection timeout*
+* [Hugo Arregui](https://github.com/hugoArregui) *Fix connection timeout*
+* [Rob Deutsch](https://github.com/rob-deutsch) *RTPReceiver graceful shutdown*
+* [Jin Lei](https://github.com/jinleileiking) - *SFU example use http*
+* [Will Watson](https://github.com/wwatson) - *Enable gocritic*
+* [Luke Curley](https://github.com/kixelated)
+* [Antoine Baché](https://github.com/Antonito) - *OGG Opus export*
+* [frank](https://github.com/feixiao) - *Building examples on OSX*
+* [mxmCherry](https://github.com/mxmCherry)
+* [Alex Browne](https://github.com/albrow) - *JavaScript/Wasm bindings*
+* [adwpc](https://github.com/adwpc) - *SFU example with websocket*
+* [imalic3](https://github.com/imalic3) - *SFU websocket example with datachannel broadcast*
+* [Žiga Željko](https://github.com/zigazeljko)
+* [Simonacca Fotokite](https://github.com/simonacca-fotokite)
+* [Marouane](https://github.com/nindolabs) *Fix Offer bundle generation*
+* [Christopher Fry](https://github.com/christopherfry)
+* [Adam Kiss](https://github.com/masterada)
+* [xsbchen](https://github.com/xsbchen)
+* [Alex Harford](https://github.com/alexjh)
+* [Aleksandr Razumov](https://github.com/ernado)
+* [mchlrhw](https://github.com/mchlrhw)
+* [AlexWoo(武杰)](https://github.com/AlexWoo) *Fix RemoteDescription parsing for certificate fingerprint*
+* [Cecylia Bocovich](https://github.com/cohosh)
+* [Slugalisk](https://github.com/slugalisk)
+* [Agugua Kenechukwu](https://github.com/spaceCh1mp)
+* [Ato Araki](https://github.com/atotto)
+* [Rafael Viscarra](https://github.com/rviscarra)
+* [Mike Coleman](https://github.com/fivebats)
+* [Suhas Gaddam](https://github.com/suhasgaddam)
+* [Atsushi Watanabe](https://github.com/at-wat)
+* [Robert Eperjesi](https://github.com/epes)
+* [Aaron France](https://github.com/AeroNotix)
+* [Gareth Hayes](https://github.com/gazhayes)
+* [Sebastian Waisbrot](https://github.com/seppo0010)
+* [Masataka Hisasue](https://github.com/sylba2050) - *Fix Docs*
+* [Hongchao Ma(马洪超)](https://github.com/hcm007)
+* [Aaron France](https://github.com/AeroNotix)
+* [Chris Hiszpanski](https://github.com/thinkski) - *Fix Answer bundle generation*
+* [Vicken Simonian](https://github.com/vsimon)
+* [Guilherme Souza](https://github.com/gqgs)
+* [Andrew N. Shalaev](https://github.com/isqad)
+* [David Hamilton](https://github.com/dihamilton)
+* [Ilya Mayorov](https://github.com/faroyam)
+* [Patrick Lange](https://github.com/langep)
+* [cyannuk](https://github.com/cyannuk)
+* [Lukas Herman](https://github.com/lherman-cs)
+* [Konstantin Chugalinskiy](https://github.com/kchugalinskiy)
+* [Bao Nguyen](https://github.com/sysbot)
+* [Luke S](https://github.com/encounter)
+* [Hendrik Hofstadt](https://github.com/hendrikhofstadt)
+* [Clayton McCray](https://github.com/ClaytonMcCray)
+* [lawl](https://github.com/lawl)
+* [Jorropo](https://github.com/Jorropo)
+* [Akil](https://github.com/akilude)
+* [Quentin Renard](https://github.com/asticode)
+* [opennota](https://github.com/opennota)
+* [Simon Eisenmann](https://github.com/longsleep)
+* [Ben Weitzman](https://github.com/benweitzman)
+* [Masahiro Nakamura](https://github.com/tsuu32)
+* [Tarrence van As](https://github.com/tarrencev)
+* [Yuki Igarashi](https://github.com/bonprosoft)
+* [Egon Elbre](https://github.com/egonelbre)
+* [Jerko Steiner](https://github.com/jeremija)
+* [Roman Romanenko](https://github.com/r-novel)
+* [YongXin SHI](https://github.com/a-wing)
+* [Magnus Wahlstrand](https://github.com/kyeett)
+* [Chad Retz](https://github.com/cretz)
+* [Simone Gotti](https://github.com/sgotti)
+* [Cedric Fung](https://github.com/cedricfung)
+* [Norman Rasmussen](https://github.com/normanr) - *Fix Empty DataChannel messages*
+* [salmān aljammāz](https://github.com/saljam)
+* [cnderrauber](https://github.com/cnderrauber)
+* [Juliusz Chroboczek](https://github.com/jech)
+* [John Berthels](https://github.com/jbert)
+* [Somers Matthews](https://github.com/somersbmatthews)
+* [Vitaliy F](https://github.com/funvit)
+* [Ivan Egorov](https://github.com/vany-egorov)
+* [Nick Mykins](https://github.com/nmyk)
+* [Jason Brady](https://github.com/jbrady42)
+* [krishna chiatanya](https://github.com/kittuov)
+* [JacobZwang](https://github.com/JacobZwang)
+* [박종훈](https://github.com/JonghunBok)
+* [Sam Lancia](https://github.com/nerd2)
+* [Henry](https://github.com/cryptix)
+* [Jeff Tchang](https://github.com/tachang)
+* [JooYoung Lim](https://github.com/DevRockstarZ)
+* [Sidney San Martín](https://github.com/s4y)
+* [soolaugust](https://github.com/soolaugust)
+* [Kuzmin Vladimir](https://github.com/tekig)
+* [Alessandro Ros](https://github.com/aler9)
+* [Thomas Miller](https://github.com/tmiv)
+* [yoko(q191201771)](https://github.com/q191201771)
+* [Joshua Obasaju](https://github.com/obasajujoshua31)
+* [Mission Liao](https://github.com/mission-liao)
+* [Hanjun Kim](https://github.com/hallazzang)
+* [ZHENK](https://github.com/scorpionknifes)
+* [Rahul Nakre](https://github.com/rahulnakre)
+* [OrlandoCo](https://github.com/OrlandoCo)
+* [Assad Obaid](https://github.com/assadobaid)
+* [Jamie Good](https://github.com/jamiegood) - *Bug fix in jsfiddle example*
+* [Artur Shellunts](https://github.com/ashellunts)
+* [Sean Knight](https://github.com/SeanKnight)
+* [o0olele](https://github.com/o0olele)
+* [Bo Shi](https://github.com/bshimc)
+* [Suzuki Takeo](https://github.com/BambooTuna)
+* [baiyufei](https://github.com/baiyufei)
+* [pascal-ace](https://github.com/pascal-ace)
+* [Threadnaught](https://github.com/Threadnaught)
+* [Dean Eigenmann](https://github.com/decanus)
+* [Cameron Elliott](https://github.com/cameronelliott)
+* [Pascal Benoit](https://github.com/pascal-ace)
+* [Mats](https://github.com/Mindgamesnl)
+* [donotanswer](https://github.com/f-viktor)
+* [Reese](https://github.com/figadore)
+* [David Zhao](https://github.com/davidzhao)
+* [Nam V. Do](https://github.com/namvdo)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/vendor/github.com/pion/webrtc/v3/api.go b/vendor/github.com/pion/webrtc/v3/api.go
new file mode 100644
index 0000000..60ac728
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/api.go
@@ -0,0 +1,73 @@
+// +build !js
+
+package webrtc
+
+import (
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+)
+
+// API bundles the global functions of the WebRTC and ORTC API.
+// Some of these functions are also exported globally using the
+// defaultAPI object. Note that the global version of the API
+// may be phased out in the future.
+type API struct {
+ settingEngine *SettingEngine
+ mediaEngine *MediaEngine
+ interceptor interceptor.Interceptor
+}
+
+// NewAPI Creates a new API object for keeping semi-global settings to WebRTC objects
+func NewAPI(options ...func(*API)) *API {
+ a := &API{}
+
+ for _, o := range options {
+ o(a)
+ }
+
+ if a.settingEngine == nil {
+ a.settingEngine = &SettingEngine{}
+ }
+
+ if a.settingEngine.LoggerFactory == nil {
+ a.settingEngine.LoggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ if a.mediaEngine == nil {
+ a.mediaEngine = &MediaEngine{}
+ }
+
+ if a.interceptor == nil {
+ a.interceptor = &interceptor.NoOp{}
+ }
+
+ return a
+}
+
+// WithMediaEngine allows providing a MediaEngine to the API.
+// Settings can be changed after passing the engine to an API.
+func WithMediaEngine(m *MediaEngine) func(a *API) {
+ return func(a *API) {
+ if m != nil {
+ a.mediaEngine = m
+ } else {
+ a.mediaEngine = &MediaEngine{}
+ }
+ }
+}
+
+// WithSettingEngine allows providing a SettingEngine to the API.
+// Settings should not be changed after passing the engine to an API.
+func WithSettingEngine(s SettingEngine) func(a *API) {
+ return func(a *API) {
+ a.settingEngine = &s
+ }
+}
+
+// WithInterceptorRegistry allows providing Interceptors to the API.
+// Settings should not be changed after passing the registry to an API.
+func WithInterceptorRegistry(interceptorRegistry *interceptor.Registry) func(a *API) {
+ return func(a *API) {
+ a.interceptor = interceptorRegistry.Build()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/api_js.go b/vendor/github.com/pion/webrtc/v3/api_js.go
new file mode 100644
index 0000000..964b7b0
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/api_js.go
@@ -0,0 +1,31 @@
+// +build js,wasm
+
+package webrtc
+
+// API bundles the global funcions of the WebRTC and ORTC API.
+type API struct {
+ settingEngine *SettingEngine
+}
+
+// NewAPI Creates a new API object for keeping semi-global settings to WebRTC objects
+func NewAPI(options ...func(*API)) *API {
+ a := &API{}
+
+ for _, o := range options {
+ o(a)
+ }
+
+ if a.settingEngine == nil {
+ a.settingEngine = &SettingEngine{}
+ }
+
+ return a
+}
+
+// WithSettingEngine allows providing a SettingEngine to the API.
+// Settings should not be changed after passing the engine to an API.
+func WithSettingEngine(s SettingEngine) func(a *API) {
+ return func(a *API) {
+ a.settingEngine = &s
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/atomicbool.go b/vendor/github.com/pion/webrtc/v3/atomicbool.go
new file mode 100644
index 0000000..c5ace62
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/atomicbool.go
@@ -0,0 +1,20 @@
+package webrtc
+
+import "sync/atomic"
+
+type atomicBool struct {
+ val int32
+}
+
+func (b *atomicBool) set(value bool) { // nolint: unparam
+ var i int32
+ if value {
+ i = 1
+ }
+
+ atomic.StoreInt32(&(b.val), i)
+}
+
+func (b *atomicBool) get() bool {
+ return atomic.LoadInt32(&(b.val)) != 0
+}
diff --git a/vendor/github.com/pion/webrtc/v3/bundlepolicy.go b/vendor/github.com/pion/webrtc/v3/bundlepolicy.go
new file mode 100644
index 0000000..6d39a27
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/bundlepolicy.go
@@ -0,0 +1,78 @@
+package webrtc
+
+import (
+ "encoding/json"
+)
+
+// BundlePolicy affects which media tracks are negotiated if the remote
+// endpoint is not bundle-aware, and what ICE candidates are gathered. If the
+// remote endpoint is bundle-aware, all media tracks and data channels are
+// bundled onto the same transport.
+type BundlePolicy int
+
+const (
+ // BundlePolicyBalanced indicates to gather ICE candidates for each
+ // media type in use (audio, video, and data). If the remote endpoint is
+ // not bundle-aware, negotiate only one audio and video track on separate
+ // transports.
+ BundlePolicyBalanced BundlePolicy = iota + 1
+
+ // BundlePolicyMaxCompat indicates to gather ICE candidates for each
+ // track. If the remote endpoint is not bundle-aware, negotiate all media
+ // tracks on separate transports.
+ BundlePolicyMaxCompat
+
+ // BundlePolicyMaxBundle indicates to gather ICE candidates for only
+ // one track. If the remote endpoint is not bundle-aware, negotiate only
+ // one media track.
+ BundlePolicyMaxBundle
+)
+
+// This is done this way because of a linter.
+const (
+ bundlePolicyBalancedStr = "balanced"
+ bundlePolicyMaxCompatStr = "max-compat"
+ bundlePolicyMaxBundleStr = "max-bundle"
+)
+
+func newBundlePolicy(raw string) BundlePolicy {
+ switch raw {
+ case bundlePolicyBalancedStr:
+ return BundlePolicyBalanced
+ case bundlePolicyMaxCompatStr:
+ return BundlePolicyMaxCompat
+ case bundlePolicyMaxBundleStr:
+ return BundlePolicyMaxBundle
+ default:
+ return BundlePolicy(Unknown)
+ }
+}
+
+func (t BundlePolicy) String() string {
+ switch t {
+ case BundlePolicyBalanced:
+ return bundlePolicyBalancedStr
+ case BundlePolicyMaxCompat:
+ return bundlePolicyMaxCompatStr
+ case BundlePolicyMaxBundle:
+ return bundlePolicyMaxBundleStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// UnmarshalJSON parses the JSON-encoded data and stores the result
+func (t *BundlePolicy) UnmarshalJSON(b []byte) error {
+ var val string
+ if err := json.Unmarshal(b, &val); err != nil {
+ return err
+ }
+
+ *t = newBundlePolicy(val)
+ return nil
+}
+
+// MarshalJSON returns the JSON encoding
+func (t BundlePolicy) MarshalJSON() ([]byte, error) {
+ return json.Marshal(t.String())
+}
diff --git a/vendor/github.com/pion/webrtc/v3/certificate.go b/vendor/github.com/pion/webrtc/v3/certificate.go
new file mode 100644
index 0000000..bf106d6
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/certificate.go
@@ -0,0 +1,185 @@
+// +build !js
+
+package webrtc
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/base64"
+ "encoding/hex"
+ "fmt"
+ "math/big"
+ "time"
+
+ "github.com/pion/dtls/v2/pkg/crypto/fingerprint"
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+// Certificate represents a x509Cert used to authenticate WebRTC communications.
+type Certificate struct {
+ privateKey crypto.PrivateKey
+ x509Cert *x509.Certificate
+ statsID string
+}
+
+// NewCertificate generates a new x509 compliant Certificate to be used
+// by DTLS for encrypting data sent over the wire. This method differs from
+// GenerateCertificate by allowing to specify a template x509.Certificate to
+// be used in order to define certificate parameters.
+func NewCertificate(key crypto.PrivateKey, tpl x509.Certificate) (*Certificate, error) {
+ var err error
+ var certDER []byte
+ switch sk := key.(type) {
+ case *rsa.PrivateKey:
+ pk := sk.Public()
+ tpl.SignatureAlgorithm = x509.SHA256WithRSA
+ certDER, err = x509.CreateCertificate(rand.Reader, &tpl, &tpl, pk, sk)
+ if err != nil {
+ return nil, &rtcerr.UnknownError{Err: err}
+ }
+ case *ecdsa.PrivateKey:
+ pk := sk.Public()
+ tpl.SignatureAlgorithm = x509.ECDSAWithSHA256
+ certDER, err = x509.CreateCertificate(rand.Reader, &tpl, &tpl, pk, sk)
+ if err != nil {
+ return nil, &rtcerr.UnknownError{Err: err}
+ }
+ default:
+ return nil, &rtcerr.NotSupportedError{Err: ErrPrivateKeyType}
+ }
+
+ cert, err := x509.ParseCertificate(certDER)
+ if err != nil {
+ return nil, &rtcerr.UnknownError{Err: err}
+ }
+
+ return &Certificate{privateKey: key, x509Cert: cert, statsID: fmt.Sprintf("certificate-%d", time.Now().UnixNano())}, nil
+}
+
+// Equals determines if two certificates are identical by comparing both the
+// secretKeys and x509Certificates.
+func (c Certificate) Equals(o Certificate) bool {
+ switch cSK := c.privateKey.(type) {
+ case *rsa.PrivateKey:
+ if oSK, ok := o.privateKey.(*rsa.PrivateKey); ok {
+ if cSK.N.Cmp(oSK.N) != 0 {
+ return false
+ }
+ return c.x509Cert.Equal(o.x509Cert)
+ }
+ return false
+ case *ecdsa.PrivateKey:
+ if oSK, ok := o.privateKey.(*ecdsa.PrivateKey); ok {
+ if cSK.X.Cmp(oSK.X) != 0 || cSK.Y.Cmp(oSK.Y) != 0 {
+ return false
+ }
+ return c.x509Cert.Equal(o.x509Cert)
+ }
+ return false
+ default:
+ return false
+ }
+}
+
+// Expires returns the timestamp after which this certificate is no longer valid.
+func (c Certificate) Expires() time.Time {
+ if c.x509Cert == nil {
+ return time.Time{}
+ }
+ return c.x509Cert.NotAfter
+}
+
+// GetFingerprints returns the list of certificate fingerprints, one of which
+// is computed with the digest algorithm used in the certificate signature.
+func (c Certificate) GetFingerprints() ([]DTLSFingerprint, error) {
+ fingerprintAlgorithms := []crypto.Hash{crypto.SHA256}
+ res := make([]DTLSFingerprint, len(fingerprintAlgorithms))
+
+ i := 0
+ for _, algo := range fingerprintAlgorithms {
+ name, err := fingerprint.StringFromHash(algo)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err)
+ }
+ value, err := fingerprint.Fingerprint(c.x509Cert, algo)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err)
+ }
+ res[i] = DTLSFingerprint{
+ Algorithm: name,
+ Value: value,
+ }
+ }
+
+ return res[:i+1], nil
+}
+
+// GenerateCertificate causes the creation of an X.509 certificate and
+// corresponding private key.
+func GenerateCertificate(secretKey crypto.PrivateKey) (*Certificate, error) {
+ origin := make([]byte, 16)
+ /* #nosec */
+ if _, err := rand.Read(origin); err != nil {
+ return nil, &rtcerr.UnknownError{Err: err}
+ }
+
+ // Max random value, a 130-bits integer, i.e 2^130 - 1
+ maxBigInt := new(big.Int)
+ /* #nosec */
+ maxBigInt.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(maxBigInt, big.NewInt(1))
+ /* #nosec */
+ serialNumber, err := rand.Int(rand.Reader, maxBigInt)
+ if err != nil {
+ return nil, &rtcerr.UnknownError{Err: err}
+ }
+
+ return NewCertificate(secretKey, x509.Certificate{
+ ExtKeyUsage: []x509.ExtKeyUsage{
+ x509.ExtKeyUsageClientAuth,
+ x509.ExtKeyUsageServerAuth,
+ },
+ BasicConstraintsValid: true,
+ NotBefore: time.Now(),
+ KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+ NotAfter: time.Now().AddDate(0, 1, 0),
+ SerialNumber: serialNumber,
+ Version: 2,
+ Subject: pkix.Name{CommonName: hex.EncodeToString(origin)},
+ IsCA: true,
+ })
+}
+
+// CertificateFromX509 creates a new WebRTC Certificate from a given PrivateKey and Certificate
+//
+// This can be used if you want to share a certificate across multiple PeerConnections
+func CertificateFromX509(privateKey crypto.PrivateKey, certificate *x509.Certificate) Certificate {
+ return Certificate{privateKey, certificate, fmt.Sprintf("certificate-%d", time.Now().UnixNano())}
+}
+
+func (c Certificate) collectStats(report *statsReportCollector) error {
+ report.Collecting()
+
+ fingerPrintAlgo, err := c.GetFingerprints()
+ if err != nil {
+ return err
+ }
+
+ base64Certificate := base64.RawURLEncoding.EncodeToString(c.x509Cert.Raw)
+
+ stats := CertificateStats{
+ Timestamp: statsTimestampFrom(time.Now()),
+ Type: StatsTypeCertificate,
+ ID: c.statsID,
+ Fingerprint: fingerPrintAlgo[0].Value,
+ FingerprintAlgorithm: fingerPrintAlgo[0].Algorithm,
+ Base64Certificate: base64Certificate,
+ IssuerCertificateID: c.x509Cert.Issuer.String(),
+ }
+
+ report.Collect(stats.ID, stats)
+ return nil
+}
diff --git a/vendor/github.com/pion/webrtc/v3/codecov.yml b/vendor/github.com/pion/webrtc/v3/codecov.yml
new file mode 100644
index 0000000..085200a
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/codecov.yml
@@ -0,0 +1,20 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/vendor/github.com/pion/webrtc/v3/configuration.go b/vendor/github.com/pion/webrtc/v3/configuration.go
new file mode 100644
index 0000000..712bab9
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/configuration.go
@@ -0,0 +1,51 @@
+// +build !js
+
+package webrtc
+
+// A Configuration defines how peer-to-peer communication via PeerConnection
+// is established or re-established.
+// Configurations may be set up once and reused across multiple connections.
+// Configurations are treated as readonly. As long as they are unmodified,
+// they are safe for concurrent use.
+type Configuration struct {
+ // ICEServers defines a slice describing servers available to be used by
+ // ICE, such as STUN and TURN servers.
+ ICEServers []ICEServer `json:"iceServers,omitempty"`
+
+ // ICETransportPolicy indicates which candidates the ICEAgent is allowed
+ // to use.
+ ICETransportPolicy ICETransportPolicy `json:"iceTransportPolicy,omitempty"`
+
+ // BundlePolicy indicates which media-bundling policy to use when gathering
+ // ICE candidates.
+ BundlePolicy BundlePolicy `json:"bundlePolicy,omitempty"`
+
+ // RTCPMuxPolicy indicates which rtcp-mux policy to use when gathering ICE
+ // candidates.
+ RTCPMuxPolicy RTCPMuxPolicy `json:"rtcpMuxPolicy,omitempty"`
+
+ // PeerIdentity sets the target peer identity for the PeerConnection.
+ // The PeerConnection will not establish a connection to a remote peer
+ // unless it can be successfully authenticated with the provided name.
+ PeerIdentity string `json:"peerIdentity,omitempty"`
+
+ // Certificates describes a set of certificates that the PeerConnection
+ // uses to authenticate. Valid values for this parameter are created
+ // through calls to the GenerateCertificate function. Although any given
+ // DTLS connection will use only one certificate, this attribute allows the
+ // caller to provide multiple certificates that support different
+ // algorithms. The final certificate will be selected based on the DTLS
+ // handshake, which establishes which certificates are allowed. The
+ // PeerConnection implementation selects which of the certificates is
+ // used for a given connection; how certificates are selected is outside
+ // the scope of this specification. If this value is absent, then a default
+ // set of certificates is generated for each PeerConnection instance.
+ Certificates []Certificate `json:"certificates,omitempty"`
+
+ // ICECandidatePoolSize describes the size of the prefetched ICE pool.
+ ICECandidatePoolSize uint8 `json:"iceCandidatePoolSize,omitempty"`
+
+ // SDPSemantics controls the type of SDP offers accepted by and
+ // SDP answers generated by the PeerConnection.
+ SDPSemantics SDPSemantics `json:"sdpSemantics,omitempty"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/configuration_common.go b/vendor/github.com/pion/webrtc/v3/configuration_common.go
new file mode 100644
index 0000000..92fc228
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/configuration_common.go
@@ -0,0 +1,24 @@
+package webrtc
+
+import "strings"
+
+// getICEServers side-steps the strict parsing mode of the ice package
+// (as defined in https://tools.ietf.org/html/rfc7064) by copying and then
+// stripping any erroneous queries from "stun(s):" URLs before parsing.
+func (c Configuration) getICEServers() []ICEServer {
+ iceServers := append([]ICEServer{}, c.ICEServers...)
+
+ for iceServersIndex := range iceServers {
+ iceServers[iceServersIndex].URLs = append([]string{}, iceServers[iceServersIndex].URLs...)
+
+ for urlsIndex, rawURL := range iceServers[iceServersIndex].URLs {
+ if strings.HasPrefix(rawURL, "stun") {
+ // strip the query from "stun(s):" if present
+ parts := strings.Split(rawURL, "?")
+ rawURL = parts[0]
+ }
+ iceServers[iceServersIndex].URLs[urlsIndex] = rawURL
+ }
+ }
+ return iceServers
+}
diff --git a/vendor/github.com/pion/webrtc/v3/configuration_js.go b/vendor/github.com/pion/webrtc/v3/configuration_js.go
new file mode 100644
index 0000000..44ab9f3
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/configuration_js.go
@@ -0,0 +1,35 @@
+// +build js,wasm
+
+package webrtc
+
+// Configuration defines a set of parameters to configure how the
+// peer-to-peer communication via PeerConnection is established or
+// re-established.
+type Configuration struct {
+ // ICEServers defines a slice describing servers available to be used by
+ // ICE, such as STUN and TURN servers.
+ ICEServers []ICEServer
+
+ // ICETransportPolicy indicates which candidates the ICEAgent is allowed
+ // to use.
+ ICETransportPolicy ICETransportPolicy
+
+ // BundlePolicy indicates which media-bundling policy to use when gathering
+ // ICE candidates.
+ BundlePolicy BundlePolicy
+
+ // RTCPMuxPolicy indicates which rtcp-mux policy to use when gathering ICE
+ // candidates.
+ RTCPMuxPolicy RTCPMuxPolicy
+
+ // PeerIdentity sets the target peer identity for the PeerConnection.
+ // The PeerConnection will not establish a connection to a remote peer
+ // unless it can be successfully authenticated with the provided name.
+ PeerIdentity string
+
+ // Certificates are not supported in the JavaScript/Wasm bindings.
+ // Certificates []Certificate
+
+ // ICECandidatePoolSize describes the size of the prefetched ICE pool.
+ ICECandidatePoolSize uint8
+}
diff --git a/vendor/github.com/pion/webrtc/v3/constants.go b/vendor/github.com/pion/webrtc/v3/constants.go
new file mode 100644
index 0000000..79d759a
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/constants.go
@@ -0,0 +1,25 @@
+package webrtc
+
+const (
+ // Unknown defines default public constant to use for "enum" like struct
+ // comparisons when no value was defined.
+ Unknown = iota
+ unknownStr = "unknown"
+ ssrcStr = "ssrc"
+
+ // Equal to UDP MTU
+ receiveMTU = 1460
+
+ // simulcastProbeCount is the amount of RTP Packets
+ // that handleUndeclaredSSRC will read and try to dispatch from
+ // mid and rid values
+ simulcastProbeCount = 10
+
+ // simulcastMaxProbeRoutines is how many active routines can be used to probe
+ // If the total amount of incoming SSRCes exceeds this new requests will be ignored
+ simulcastMaxProbeRoutines = 25
+
+ mediaSectionApplication = "application"
+
+ rtpOutboundMTU = 1200
+)
diff --git a/vendor/github.com/pion/webrtc/v3/datachannel.go b/vendor/github.com/pion/webrtc/v3/datachannel.go
new file mode 100644
index 0000000..f391e7f
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/datachannel.go
@@ -0,0 +1,597 @@
+// +build !js
+
+package webrtc
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/datachannel"
+ "github.com/pion/logging"
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+const dataChannelBufferSize = math.MaxUint16 // message size limit for Chromium
+var errSCTPNotEstablished = errors.New("SCTP not established")
+
+// DataChannel represents a WebRTC DataChannel
+// The DataChannel interface represents a network channel
+// which can be used for bidirectional peer-to-peer transfers of arbitrary data
+type DataChannel struct {
+ mu sync.RWMutex
+
+ statsID string
+ label string
+ ordered bool
+ maxPacketLifeTime *uint16
+ maxRetransmits *uint16
+ protocol string
+ negotiated bool
+ id *uint16
+ readyState atomic.Value // DataChannelState
+ bufferedAmountLowThreshold uint64
+ detachCalled bool
+
+ // The binaryType represents attribute MUST, on getting, return the value to
+ // which it was last set. On setting, if the new value is either the string
+ // "blob" or the string "arraybuffer", then set the IDL attribute to this
+ // new value. Otherwise, throw a SyntaxError. When an DataChannel object
+ // is created, the binaryType attribute MUST be initialized to the string
+ // "blob". This attribute controls how binary data is exposed to scripts.
+ // binaryType string
+
+ onMessageHandler func(DataChannelMessage)
+ openHandlerOnce sync.Once
+ onOpenHandler func()
+ onCloseHandler func()
+ onBufferedAmountLow func()
+ onErrorHandler func(error)
+
+ sctpTransport *SCTPTransport
+ dataChannel *datachannel.DataChannel
+
+ // A reference to the associated api object used by this datachannel
+ api *API
+ log logging.LeveledLogger
+}
+
+// NewDataChannel creates a new DataChannel.
+// This constructor is part of the ORTC API. It is not
+// meant to be used together with the basic WebRTC API.
+func (api *API) NewDataChannel(transport *SCTPTransport, params *DataChannelParameters) (*DataChannel, error) {
+ d, err := api.newDataChannel(params, api.settingEngine.LoggerFactory.NewLogger("ortc"))
+ if err != nil {
+ return nil, err
+ }
+
+ err = d.open(transport)
+ if err != nil {
+ return nil, err
+ }
+
+ return d, nil
+}
+
+// newDataChannel is an internal constructor for the data channel used to
+// create the DataChannel object before the networking is set up.
+func (api *API) newDataChannel(params *DataChannelParameters, log logging.LeveledLogger) (*DataChannel, error) {
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #5)
+ if len(params.Label) > 65535 {
+ return nil, &rtcerr.TypeError{Err: ErrStringSizeLimit}
+ }
+
+ d := &DataChannel{
+ statsID: fmt.Sprintf("DataChannel-%d", time.Now().UnixNano()),
+ label: params.Label,
+ protocol: params.Protocol,
+ negotiated: params.Negotiated,
+ id: params.ID,
+ ordered: params.Ordered,
+ maxPacketLifeTime: params.MaxPacketLifeTime,
+ maxRetransmits: params.MaxRetransmits,
+ api: api,
+ log: log,
+ }
+
+ d.setReadyState(DataChannelStateConnecting)
+ return d, nil
+}
+
+// open opens the datachannel over the sctp transport
+func (d *DataChannel) open(sctpTransport *SCTPTransport) error {
+ d.mu.Lock()
+ if d.sctpTransport != nil {
+ // already open
+ d.mu.Unlock()
+ return nil
+ }
+ d.sctpTransport = sctpTransport
+
+ if err := d.ensureSCTP(); err != nil {
+ d.mu.Unlock()
+ return err
+ }
+
+ var channelType datachannel.ChannelType
+ var reliabilityParameter uint32
+
+ switch {
+ case d.maxPacketLifeTime == nil && d.maxRetransmits == nil:
+ if d.ordered {
+ channelType = datachannel.ChannelTypeReliable
+ } else {
+ channelType = datachannel.ChannelTypeReliableUnordered
+ }
+
+ case d.maxRetransmits != nil:
+ reliabilityParameter = uint32(*d.maxRetransmits)
+ if d.ordered {
+ channelType = datachannel.ChannelTypePartialReliableRexmit
+ } else {
+ channelType = datachannel.ChannelTypePartialReliableRexmitUnordered
+ }
+ default:
+ reliabilityParameter = uint32(*d.maxPacketLifeTime)
+ if d.ordered {
+ channelType = datachannel.ChannelTypePartialReliableTimed
+ } else {
+ channelType = datachannel.ChannelTypePartialReliableTimedUnordered
+ }
+ }
+
+ cfg := &datachannel.Config{
+ ChannelType: channelType,
+ Priority: datachannel.ChannelPriorityNormal,
+ ReliabilityParameter: reliabilityParameter,
+ Label: d.label,
+ Protocol: d.protocol,
+ Negotiated: d.negotiated,
+ LoggerFactory: d.api.settingEngine.LoggerFactory,
+ }
+
+ if d.id == nil {
+ err := d.sctpTransport.generateAndSetDataChannelID(d.sctpTransport.dtlsTransport.role(), &d.id)
+ if err != nil {
+ return err
+ }
+ }
+
+ dc, err := datachannel.Dial(d.sctpTransport.association, *d.id, cfg)
+ if err != nil {
+ d.mu.Unlock()
+ return err
+ }
+
+ // bufferedAmountLowThreshold and onBufferedAmountLow might be set earlier
+ dc.SetBufferedAmountLowThreshold(d.bufferedAmountLowThreshold)
+ dc.OnBufferedAmountLow(d.onBufferedAmountLow)
+ d.mu.Unlock()
+
+ d.handleOpen(dc)
+ return nil
+}
+
+func (d *DataChannel) ensureSCTP() error {
+ if d.sctpTransport == nil {
+ return errSCTPNotEstablished
+ }
+
+ d.sctpTransport.lock.RLock()
+ defer d.sctpTransport.lock.RUnlock()
+ if d.sctpTransport.association == nil {
+ return errSCTPNotEstablished
+ }
+ return nil
+}
+
+// Transport returns the SCTPTransport instance the DataChannel is sending over.
+func (d *DataChannel) Transport() *SCTPTransport {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.sctpTransport
+}
+
+// After onOpen is complete check that the user called detach
+// and provide an error message if the call was missed
+func (d *DataChannel) checkDetachAfterOpen() {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ if d.api.settingEngine.detach.DataChannels && !d.detachCalled {
+ d.log.Warn("webrtc.DetachDataChannels() enabled but didn't Detach, call Detach from OnOpen")
+ }
+}
+
+// OnOpen sets an event handler which is invoked when
+// the underlying data transport has been established (or re-established).
+func (d *DataChannel) OnOpen(f func()) {
+ d.mu.Lock()
+ d.openHandlerOnce = sync.Once{}
+ d.onOpenHandler = f
+ d.mu.Unlock()
+
+ if d.ReadyState() == DataChannelStateOpen {
+ // If the data channel is already open, call the handler immediately.
+ go d.openHandlerOnce.Do(func() {
+ f()
+ d.checkDetachAfterOpen()
+ })
+ }
+}
+
+func (d *DataChannel) onOpen() {
+ d.mu.RLock()
+ handler := d.onOpenHandler
+ d.mu.RUnlock()
+
+ if handler != nil {
+ go d.openHandlerOnce.Do(func() {
+ handler()
+ d.checkDetachAfterOpen()
+ })
+ }
+}
+
+// OnClose sets an event handler which is invoked when
+// the underlying data transport has been closed.
+func (d *DataChannel) OnClose(f func()) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.onCloseHandler = f
+}
+
+func (d *DataChannel) onClose() {
+ d.mu.RLock()
+ handler := d.onCloseHandler
+ d.mu.RUnlock()
+
+ if handler != nil {
+ go handler()
+ }
+}
+
+// OnMessage sets an event handler which is invoked on a binary
+// message arrival over the sctp transport from a remote peer.
+// OnMessage can currently receive messages up to 16384 bytes
+// in size. Check out the detach API if you want to use larger
+// message sizes. Note that browser support for larger messages
+// is also limited.
+func (d *DataChannel) OnMessage(f func(msg DataChannelMessage)) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.onMessageHandler = f
+}
+
+func (d *DataChannel) onMessage(msg DataChannelMessage) {
+ d.mu.RLock()
+ handler := d.onMessageHandler
+ d.mu.RUnlock()
+
+ if handler == nil {
+ return
+ }
+ handler(msg)
+}
+
+func (d *DataChannel) handleOpen(dc *datachannel.DataChannel) {
+ d.mu.Lock()
+ d.dataChannel = dc
+ d.mu.Unlock()
+ d.setReadyState(DataChannelStateOpen)
+
+ d.onOpen()
+
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ if !d.api.settingEngine.detach.DataChannels {
+ go d.readLoop()
+ }
+}
+
+// OnError sets an event handler which is invoked when
+// the underlying data transport cannot be read.
+func (d *DataChannel) OnError(f func(err error)) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.onErrorHandler = f
+}
+
+func (d *DataChannel) onError(err error) {
+ d.mu.RLock()
+ handler := d.onErrorHandler
+ d.mu.RUnlock()
+
+ if handler != nil {
+ go handler(err)
+ }
+}
+
+// See https://github.com/pion/webrtc/issues/1516
+// nolint:gochecknoglobals
+var rlBufPool = sync.Pool{New: func() interface{} {
+ return make([]byte, dataChannelBufferSize)
+}}
+
+func (d *DataChannel) readLoop() {
+ for {
+ buffer := rlBufPool.Get().([]byte)
+ n, isString, err := d.dataChannel.ReadDataChannel(buffer)
+ if err != nil {
+ rlBufPool.Put(buffer) // nolint:staticcheck
+ d.setReadyState(DataChannelStateClosed)
+ if err != io.EOF {
+ d.onError(err)
+ }
+ d.onClose()
+ return
+ }
+
+ m := DataChannelMessage{Data: make([]byte, n), IsString: isString}
+ copy(m.Data, buffer[:n])
+ // The 'staticcheck' pragma is a false positive on the part of the CI linter.
+ rlBufPool.Put(buffer) // nolint:staticcheck
+
+ // NB: Why was DataChannelMessage not passed as a pointer value?
+ d.onMessage(m) // nolint:staticcheck
+ }
+}
+
+// Send sends the binary message to the DataChannel peer
+func (d *DataChannel) Send(data []byte) error {
+ err := d.ensureOpen()
+ if err != nil {
+ return err
+ }
+
+ _, err = d.dataChannel.WriteDataChannel(data, false)
+ return err
+}
+
+// SendText sends the text message to the DataChannel peer
+func (d *DataChannel) SendText(s string) error {
+ err := d.ensureOpen()
+ if err != nil {
+ return err
+ }
+
+ _, err = d.dataChannel.WriteDataChannel([]byte(s), true)
+ return err
+}
+
+func (d *DataChannel) ensureOpen() error {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ if d.ReadyState() != DataChannelStateOpen {
+ return io.ErrClosedPipe
+ }
+ return nil
+}
+
+// Detach allows you to detach the underlying datachannel. This provides
+// an idiomatic API to work with, however it disables the OnMessage callback.
+// Before calling Detach you have to enable this behavior by calling
+// webrtc.DetachDataChannels(). Combining detached and normal data channels
+// is not supported.
+// Please refer to the data-channels-detach example and the
+// pion/datachannel documentation for the correct way to handle the
+// resulting DataChannel object.
+func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ if !d.api.settingEngine.detach.DataChannels {
+ return nil, errDetachNotEnabled
+ }
+
+ if d.dataChannel == nil {
+ return nil, errDetachBeforeOpened
+ }
+
+ d.detachCalled = true
+
+ return d.dataChannel, nil
+}
+
+// Close Closes the DataChannel. It may be called regardless of whether
+// the DataChannel object was created by this peer or the remote peer.
+func (d *DataChannel) Close() error {
+ d.mu.Lock()
+ haveSctpTransport := d.dataChannel != nil
+ d.mu.Unlock()
+
+ if d.ReadyState() == DataChannelStateClosed {
+ return nil
+ }
+
+ d.setReadyState(DataChannelStateClosing)
+ if !haveSctpTransport {
+ return nil
+ }
+
+ return d.dataChannel.Close()
+}
+
+// Label represents a label that can be used to distinguish this
+// DataChannel object from other DataChannel objects. Scripts are
+// allowed to create multiple DataChannel objects with the same label.
+func (d *DataChannel) Label() string {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.label
+}
+
+// Ordered represents if the DataChannel is ordered, and false if
+// out-of-order delivery is allowed.
+func (d *DataChannel) Ordered() bool {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.ordered
+}
+
+// MaxPacketLifeTime represents the length of the time window (msec) during
+// which transmissions and retransmissions may occur in unreliable mode.
+func (d *DataChannel) MaxPacketLifeTime() *uint16 {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.maxPacketLifeTime
+}
+
+// MaxRetransmits represents the maximum number of retransmissions that are
+// attempted in unreliable mode.
+func (d *DataChannel) MaxRetransmits() *uint16 {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.maxRetransmits
+}
+
+// Protocol represents the name of the sub-protocol used with this
+// DataChannel.
+func (d *DataChannel) Protocol() string {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.protocol
+}
+
+// Negotiated represents whether this DataChannel was negotiated by the
+// application (true), or not (false).
+func (d *DataChannel) Negotiated() bool {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.negotiated
+}
+
+// ID represents the ID for this DataChannel. The value is initially
+// null, which is what will be returned if the ID was not provided at
+// channel creation time, and the DTLS role of the SCTP transport has not
+// yet been negotiated. Otherwise, it will return the ID that was either
+// selected by the script or generated. After the ID is set to a non-null
+// value, it will not change.
+func (d *DataChannel) ID() *uint16 {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ return d.id
+}
+
+// ReadyState represents the state of the DataChannel object.
+func (d *DataChannel) ReadyState() DataChannelState {
+ if v := d.readyState.Load(); v != nil {
+ return v.(DataChannelState)
+ }
+ return DataChannelState(0)
+}
+
+// BufferedAmount represents the number of bytes of application data
+// (UTF-8 text and binary data) that have been queued using send(). Even
+// though the data transmission can occur in parallel, the returned value
+// MUST NOT be decreased before the current task yielded back to the event
+// loop to prevent race conditions. The value does not include framing
+// overhead incurred by the protocol, or buffering done by the operating
+// system or network hardware. The value of BufferedAmount slot will only
+// increase with each call to the send() method as long as the ReadyState is
+// open; however, BufferedAmount does not reset to zero once the channel
+// closes.
+func (d *DataChannel) BufferedAmount() uint64 {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ if d.dataChannel == nil {
+ return 0
+ }
+ return d.dataChannel.BufferedAmount()
+}
+
+// BufferedAmountLowThreshold represents the threshold at which the
+// bufferedAmount is considered to be low. When the bufferedAmount decreases
+// from above this threshold to equal or below it, the bufferedamountlow
+// event fires. BufferedAmountLowThreshold is initially zero on each new
+// DataChannel, but the application may change its value at any time.
+// The threshold is set to 0 by default.
+func (d *DataChannel) BufferedAmountLowThreshold() uint64 {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+
+ if d.dataChannel == nil {
+ return d.bufferedAmountLowThreshold
+ }
+ return d.dataChannel.BufferedAmountLowThreshold()
+}
+
+// SetBufferedAmountLowThreshold is used to update the threshold.
+// See BufferedAmountLowThreshold().
+func (d *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ d.bufferedAmountLowThreshold = th
+
+ if d.dataChannel != nil {
+ d.dataChannel.SetBufferedAmountLowThreshold(th)
+ }
+}
+
+// OnBufferedAmountLow sets an event handler which is invoked when
+// the number of bytes of outgoing data becomes lower than the
+// BufferedAmountLowThreshold.
+func (d *DataChannel) OnBufferedAmountLow(f func()) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ d.onBufferedAmountLow = f
+ if d.dataChannel != nil {
+ d.dataChannel.OnBufferedAmountLow(f)
+ }
+}
+
+func (d *DataChannel) getStatsID() string {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ return d.statsID
+}
+
+func (d *DataChannel) collectStats(collector *statsReportCollector) {
+ collector.Collecting()
+
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ stats := DataChannelStats{
+ Timestamp: statsTimestampNow(),
+ Type: StatsTypeDataChannel,
+ ID: d.statsID,
+ Label: d.label,
+ Protocol: d.protocol,
+ // TransportID string `json:"transportId"`
+ State: d.ReadyState(),
+ }
+
+ if d.id != nil {
+ stats.DataChannelIdentifier = int32(*d.id)
+ }
+
+ if d.dataChannel != nil {
+ stats.MessagesSent = d.dataChannel.MessagesSent()
+ stats.BytesSent = d.dataChannel.BytesSent()
+ stats.MessagesReceived = d.dataChannel.MessagesReceived()
+ stats.BytesReceived = d.dataChannel.BytesReceived()
+ }
+
+ collector.Collect(stats.ID, stats)
+}
+
+func (d *DataChannel) setReadyState(r DataChannelState) {
+ d.readyState.Store(r)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/datachannel_js.go b/vendor/github.com/pion/webrtc/v3/datachannel_js.go
new file mode 100644
index 0000000..7aa6d99
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/datachannel_js.go
@@ -0,0 +1,319 @@
+// +build js,wasm
+
+package webrtc
+
+import (
+ "fmt"
+ "syscall/js"
+
+ "github.com/pion/datachannel"
+)
+
+const dataChannelBufferSize = 16384 // Lowest common denominator among browsers
+
+// DataChannel represents a WebRTC DataChannel
+// The DataChannel interface represents a network channel
+// which can be used for bidirectional peer-to-peer transfers of arbitrary data
+type DataChannel struct {
+ // Pointer to the underlying JavaScript RTCPeerConnection object.
+ underlying js.Value
+
+ // Keep track of handlers/callbacks so we can call Release as required by the
+ // syscall/js API. Initially nil.
+ onOpenHandler *js.Func
+ onCloseHandler *js.Func
+ onMessageHandler *js.Func
+ onBufferedAmountLow *js.Func
+
+ // A reference to the associated api object used by this datachannel
+ api *API
+}
+
+// OnOpen sets an event handler which is invoked when
+// the underlying data transport has been established (or re-established).
+func (d *DataChannel) OnOpen(f func()) {
+ if d.onOpenHandler != nil {
+ oldHandler := d.onOpenHandler
+ defer oldHandler.Release()
+ }
+ onOpenHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go f()
+ return js.Undefined()
+ })
+ d.onOpenHandler = &onOpenHandler
+ d.underlying.Set("onopen", onOpenHandler)
+}
+
+// OnClose sets an event handler which is invoked when
+// the underlying data transport has been closed.
+func (d *DataChannel) OnClose(f func()) {
+ if d.onCloseHandler != nil {
+ oldHandler := d.onCloseHandler
+ defer oldHandler.Release()
+ }
+ onCloseHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go f()
+ return js.Undefined()
+ })
+ d.onCloseHandler = &onCloseHandler
+ d.underlying.Set("onclose", onCloseHandler)
+}
+
+// OnMessage sets an event handler which is invoked on a binary message arrival
+// from a remote peer. Note that browsers may place limitations on message size.
+func (d *DataChannel) OnMessage(f func(msg DataChannelMessage)) {
+ if d.onMessageHandler != nil {
+ oldHandler := d.onMessageHandler
+ defer oldHandler.Release()
+ }
+ onMessageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ // pion/webrtc/projects/15
+ data := args[0].Get("data")
+ go func() {
+ // valueToDataChannelMessage may block when handling 'Blob' data
+ // so we need to call it from a new routine. See:
+ // https://pkg.go.dev/syscall/js#FuncOf
+ msg := valueToDataChannelMessage(data)
+ f(msg)
+ }()
+ return js.Undefined()
+ })
+ d.onMessageHandler = &onMessageHandler
+ d.underlying.Set("onmessage", onMessageHandler)
+}
+
+// Send sends the binary message to the DataChannel peer
+func (d *DataChannel) Send(data []byte) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ array := js.Global().Get("Uint8Array").New(len(data))
+ js.CopyBytesToJS(array, data)
+ d.underlying.Call("send", array)
+ return nil
+}
+
+// SendText sends the text message to the DataChannel peer
+func (d *DataChannel) SendText(s string) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ d.underlying.Call("send", s)
+ return nil
+}
+
+// Detach allows you to detach the underlying datachannel. This provides
+// an idiomatic API to work with, however it disables the OnMessage callback.
+// Before calling Detach you have to enable this behavior by calling
+// webrtc.DetachDataChannels(). Combining detached and normal data channels
+// is not supported.
+// Please reffer to the data-channels-detach example and the
+// pion/datachannel documentation for the correct way to handle the
+// resulting DataChannel object.
+func (d *DataChannel) Detach() (datachannel.ReadWriteCloser, error) {
+ if !d.api.settingEngine.detach.DataChannels {
+ return nil, fmt.Errorf("enable detaching by calling webrtc.DetachDataChannels()")
+ }
+
+ detached := newDetachedDataChannel(d)
+ return detached, nil
+}
+
+// Close Closes the DataChannel. It may be called regardless of whether
+// the DataChannel object was created by this peer or the remote peer.
+func (d *DataChannel) Close() (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+
+ d.underlying.Call("close")
+
+ // Release any handlers as required by the syscall/js API.
+ if d.onOpenHandler != nil {
+ d.onOpenHandler.Release()
+ }
+ if d.onCloseHandler != nil {
+ d.onCloseHandler.Release()
+ }
+ if d.onMessageHandler != nil {
+ d.onMessageHandler.Release()
+ }
+ if d.onBufferedAmountLow != nil {
+ d.onBufferedAmountLow.Release()
+ }
+
+ return nil
+}
+
+// Label represents a label that can be used to distinguish this
+// DataChannel object from other DataChannel objects. Scripts are
+// allowed to create multiple DataChannel objects with the same label.
+func (d *DataChannel) Label() string {
+ return d.underlying.Get("label").String()
+}
+
+// Ordered represents if the DataChannel is ordered, and false if
+// out-of-order delivery is allowed.
+func (d *DataChannel) Ordered() bool {
+ ordered := d.underlying.Get("ordered")
+ if jsValueIsUndefined(ordered) {
+ return true // default is true
+ }
+ return ordered.Bool()
+}
+
+// MaxPacketLifeTime represents the length of the time window (msec) during
+// which transmissions and retransmissions may occur in unreliable mode.
+func (d *DataChannel) MaxPacketLifeTime() *uint16 {
+ if !jsValueIsUndefined(d.underlying.Get("maxPacketLifeTime")) {
+ return valueToUint16Pointer(d.underlying.Get("maxPacketLifeTime"))
+ } else {
+ // See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
+ // Chrome calls this "maxRetransmitTime"
+ return valueToUint16Pointer(d.underlying.Get("maxRetransmitTime"))
+ }
+}
+
+// MaxRetransmits represents the maximum number of retransmissions that are
+// attempted in unreliable mode.
+func (d *DataChannel) MaxRetransmits() *uint16 {
+ return valueToUint16Pointer(d.underlying.Get("maxRetransmits"))
+}
+
+// Protocol represents the name of the sub-protocol used with this
+// DataChannel.
+func (d *DataChannel) Protocol() string {
+ return d.underlying.Get("protocol").String()
+}
+
+// Negotiated represents whether this DataChannel was negotiated by the
+// application (true), or not (false).
+func (d *DataChannel) Negotiated() bool {
+ return d.underlying.Get("negotiated").Bool()
+}
+
+// ID represents the ID for this DataChannel. The value is initially
+// null, which is what will be returned if the ID was not provided at
+// channel creation time. Otherwise, it will return the ID that was either
+// selected by the script or generated. After the ID is set to a non-null
+// value, it will not change.
+func (d *DataChannel) ID() *uint16 {
+ return valueToUint16Pointer(d.underlying.Get("id"))
+}
+
+// ReadyState represents the state of the DataChannel object.
+func (d *DataChannel) ReadyState() DataChannelState {
+ return newDataChannelState(d.underlying.Get("readyState").String())
+}
+
+// BufferedAmount represents the number of bytes of application data
+// (UTF-8 text and binary data) that have been queued using send(). Even
+// though the data transmission can occur in parallel, the returned value
+// MUST NOT be decreased before the current task yielded back to the event
+// loop to prevent race conditions. The value does not include framing
+// overhead incurred by the protocol, or buffering done by the operating
+// system or network hardware. The value of BufferedAmount slot will only
+// increase with each call to the send() method as long as the ReadyState is
+// open; however, BufferedAmount does not reset to zero once the channel
+// closes.
+func (d *DataChannel) BufferedAmount() uint64 {
+ return uint64(d.underlying.Get("bufferedAmount").Int())
+}
+
+// BufferedAmountLowThreshold represents the threshold at which the
+// bufferedAmount is considered to be low. When the bufferedAmount decreases
+// from above this threshold to equal or below it, the bufferedamountlow
+// event fires. BufferedAmountLowThreshold is initially zero on each new
+// DataChannel, but the application may change its value at any time.
+func (d *DataChannel) BufferedAmountLowThreshold() uint64 {
+ return uint64(d.underlying.Get("bufferedAmountLowThreshold").Int())
+}
+
+// SetBufferedAmountLowThreshold is used to update the threshold.
+// See BufferedAmountLowThreshold().
+func (d *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
+ d.underlying.Set("bufferedAmountLowThreshold", th)
+}
+
+// OnBufferedAmountLow sets an event handler which is invoked when
+// the number of bytes of outgoing data becomes lower than the
+// BufferedAmountLowThreshold.
+func (d *DataChannel) OnBufferedAmountLow(f func()) {
+ if d.onBufferedAmountLow != nil {
+ oldHandler := d.onBufferedAmountLow
+ defer oldHandler.Release()
+ }
+ onBufferedAmountLow := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go f()
+ return js.Undefined()
+ })
+ d.onBufferedAmountLow = &onBufferedAmountLow
+ d.underlying.Set("onbufferedamountlow", onBufferedAmountLow)
+}
+
+// valueToDataChannelMessage converts the given value to a DataChannelMessage.
+// val should be obtained from MessageEvent.data where MessageEvent is received
+// via the RTCDataChannel.onmessage callback.
+func valueToDataChannelMessage(val js.Value) DataChannelMessage {
+ // If val is of type string, the conversion is straightforward.
+ if val.Type() == js.TypeString {
+ return DataChannelMessage{
+ IsString: true,
+ Data: []byte(val.String()),
+ }
+ }
+
+ // For other types, we need to first determine val.constructor.name.
+ constructorName := val.Get("constructor").Get("name").String()
+ var data []byte
+ switch constructorName {
+ case "Uint8Array":
+ // We can easily convert Uint8Array to []byte
+ data = uint8ArrayValueToBytes(val)
+ case "Blob":
+ // Convert the Blob to an ArrayBuffer and then convert the ArrayBuffer
+ // to a Uint8Array.
+ // See: https://developer.mozilla.org/en-US/docs/Web/API/Blob
+
+ // The JavaScript API for reading from the Blob is asynchronous. We use a
+ // channel to signal when reading is done.
+ reader := js.Global().Get("FileReader").New()
+ doneChan := make(chan struct{})
+ reader.Call("addEventListener", "loadend", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go func() {
+ // Signal that the FileReader is done reading/loading by sending through
+ // the doneChan.
+ doneChan <- struct{}{}
+ }()
+ return js.Undefined()
+ }))
+
+ reader.Call("readAsArrayBuffer", val)
+
+ // Wait for the FileReader to finish reading/loading.
+ <-doneChan
+
+ // At this point buffer.result is a typed array, which we know how to
+ // handle.
+ buffer := reader.Get("result")
+ uint8Array := js.Global().Get("Uint8Array").New(buffer)
+ data = uint8ArrayValueToBytes(uint8Array)
+ default:
+ // Assume we have an ArrayBufferView type which we can convert to a
+ // Uint8Array in JavaScript.
+ // See: https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView
+ uint8Array := js.Global().Get("Uint8Array").New(val)
+ data = uint8ArrayValueToBytes(uint8Array)
+ }
+
+ return DataChannelMessage{
+ IsString: false,
+ Data: data,
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go b/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go
new file mode 100644
index 0000000..dd0069e
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go
@@ -0,0 +1,71 @@
+// +build js,wasm
+
+package webrtc
+
+import (
+ "errors"
+)
+
+type detachedDataChannel struct {
+ dc *DataChannel
+
+ read chan DataChannelMessage
+ done chan struct{}
+}
+
+func newDetachedDataChannel(dc *DataChannel) *detachedDataChannel {
+ read := make(chan DataChannelMessage)
+ done := make(chan struct{})
+
+ // Wire up callbacks
+ dc.OnMessage(func(msg DataChannelMessage) {
+ read <- msg // pion/webrtc/projects/15
+ })
+
+ // pion/webrtc/projects/15
+
+ return &detachedDataChannel{
+ dc: dc,
+ read: read,
+ done: done,
+ }
+}
+
+func (c *detachedDataChannel) Read(p []byte) (int, error) {
+ n, _, err := c.ReadDataChannel(p)
+ return n, err
+}
+
+func (c *detachedDataChannel) ReadDataChannel(p []byte) (int, bool, error) {
+ select {
+ case <-c.done:
+ return 0, false, errors.New("Reader closed")
+ case msg := <-c.read:
+ n := copy(p, msg.Data)
+ if n < len(msg.Data) {
+ return n, msg.IsString, errors.New("Read buffer to small")
+ }
+ return n, msg.IsString, nil
+ }
+}
+
+func (c *detachedDataChannel) Write(p []byte) (n int, err error) {
+ return c.WriteDataChannel(p, false)
+}
+
+func (c *detachedDataChannel) WriteDataChannel(p []byte, isString bool) (n int, err error) {
+ if isString {
+ err = c.dc.SendText(string(p))
+ return len(p), err
+ }
+
+ err = c.dc.Send(p)
+
+ return len(p), err
+}
+
+func (c *detachedDataChannel) Close() error {
+ close(c.done)
+
+ return c.dc.Close()
+}
diff --git a/vendor/github.com/pion/webrtc/v3/datachannelinit.go b/vendor/github.com/pion/webrtc/v3/datachannelinit.go
new file mode 100644
index 0000000..a4320e4
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/datachannelinit.go
@@ -0,0 +1,33 @@
+package webrtc
+
+// DataChannelInit can be used to configure properties of the underlying
+// channel such as data reliability.
+type DataChannelInit struct {
+ // Ordered indicates if data is allowed to be delivered out of order. The
+ // default value of true, guarantees that data will be delivered in order.
+ Ordered *bool
+
+ // MaxPacketLifeTime limits the time (in milliseconds) during which the
+ // channel will transmit or retransmit data if not acknowledged. This value
+ // may be clamped if it exceeds the maximum value supported.
+ MaxPacketLifeTime *uint16
+
+ // MaxRetransmits limits the number of times a channel will retransmit data
+ // if not successfully delivered. This value may be clamped if it exceeds
+ // the maximum value supported.
+ MaxRetransmits *uint16
+
+ // Protocol describes the subprotocol name used for this channel.
+ Protocol *string
+
+ // Negotiated describes if the data channel is created by the local peer or
+ // the remote peer. The default value of false tells the user agent to
+ // announce the channel in-band and instruct the other peer to dispatch a
+ // corresponding DataChannel. If set to true, it is up to the application
+ // to negotiate the channel and create an DataChannel with the same id
+ // at the other peer.
+ Negotiated *bool
+
+ // ID overrides the default selection of ID for this channel.
+ ID *uint16
+}
diff --git a/vendor/github.com/pion/webrtc/v3/datachannelmessage.go b/vendor/github.com/pion/webrtc/v3/datachannelmessage.go
new file mode 100644
index 0000000..1e3c63b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/datachannelmessage.go
@@ -0,0 +1,10 @@
+package webrtc
+
+// DataChannelMessage represents a message received from the
+// data channel. IsString will be set to true if the incoming
+// message is of the string type. Otherwise the message is of
+// a binary type.
+type DataChannelMessage struct {
+ IsString bool
+ Data []byte
+}
diff --git a/vendor/github.com/pion/webrtc/v3/datachannelparameters.go b/vendor/github.com/pion/webrtc/v3/datachannelparameters.go
new file mode 100644
index 0000000..d67a63b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/datachannelparameters.go
@@ -0,0 +1,12 @@
+package webrtc
+
+// DataChannelParameters describes the configuration of the DataChannel.
+type DataChannelParameters struct {
+ Label string `json:"label"`
+ Protocol string `json:"protocol"`
+ ID *uint16 `json:"id"`
+ Ordered bool `json:"ordered"`
+ MaxPacketLifeTime *uint16 `json:"maxPacketLifeTime"`
+ MaxRetransmits *uint16 `json:"maxRetransmits"`
+ Negotiated bool `json:"negotiated"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/datachannelstate.go b/vendor/github.com/pion/webrtc/v3/datachannelstate.go
new file mode 100644
index 0000000..a2c7b95
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/datachannelstate.go
@@ -0,0 +1,61 @@
+package webrtc
+
+// DataChannelState indicates the state of a data channel.
+type DataChannelState int
+
+const (
+ // DataChannelStateConnecting indicates that the data channel is being
+ // established. This is the initial state of DataChannel, whether created
+ // with CreateDataChannel, or dispatched as a part of an DataChannelEvent.
+ DataChannelStateConnecting DataChannelState = iota + 1
+
+ // DataChannelStateOpen indicates that the underlying data transport is
+ // established and communication is possible.
+ DataChannelStateOpen
+
+ // DataChannelStateClosing indicates that the procedure to close down the
+ // underlying data transport has started.
+ DataChannelStateClosing
+
+ // DataChannelStateClosed indicates that the underlying data transport
+ // has been closed or could not be established.
+ DataChannelStateClosed
+)
+
+// This is done this way because of a linter.
+const (
+ dataChannelStateConnectingStr = "connecting"
+ dataChannelStateOpenStr = "open"
+ dataChannelStateClosingStr = "closing"
+ dataChannelStateClosedStr = "closed"
+)
+
+func newDataChannelState(raw string) DataChannelState {
+ switch raw {
+ case dataChannelStateConnectingStr:
+ return DataChannelStateConnecting
+ case dataChannelStateOpenStr:
+ return DataChannelStateOpen
+ case dataChannelStateClosingStr:
+ return DataChannelStateClosing
+ case dataChannelStateClosedStr:
+ return DataChannelStateClosed
+ default:
+ return DataChannelState(Unknown)
+ }
+}
+
+func (t DataChannelState) String() string {
+ switch t {
+ case DataChannelStateConnecting:
+ return dataChannelStateConnectingStr
+ case DataChannelStateOpen:
+ return dataChannelStateOpenStr
+ case DataChannelStateClosing:
+ return dataChannelStateClosingStr
+ case DataChannelStateClosed:
+ return dataChannelStateClosedStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go b/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go
new file mode 100644
index 0000000..db13d3e
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go
@@ -0,0 +1,14 @@
+package webrtc
+
+// DTLSFingerprint specifies the hash function algorithm and certificate
+// fingerprint as described in https://tools.ietf.org/html/rfc4572.
+type DTLSFingerprint struct {
+ // Algorithm specifies one of the the hash function algorithms defined in
+ // the 'Hash function Textual Names' registry.
+ Algorithm string `json:"algorithm"`
+
+ // Value specifies the value of the certificate fingerprint in lowercase
+ // hex string as expressed utilizing the syntax of 'fingerprint' in
+ // https://tools.ietf.org/html/rfc4572#section-5.
+ Value string `json:"value"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/dtlsparameters.go b/vendor/github.com/pion/webrtc/v3/dtlsparameters.go
new file mode 100644
index 0000000..4b4b568
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/dtlsparameters.go
@@ -0,0 +1,7 @@
+package webrtc
+
+// DTLSParameters holds information relating to DTLS configuration.
+type DTLSParameters struct {
+ Role DTLSRole `json:"role"`
+ Fingerprints []DTLSFingerprint `json:"fingerprints"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/dtlsrole.go b/vendor/github.com/pion/webrtc/v3/dtlsrole.go
new file mode 100644
index 0000000..6e67f60
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/dtlsrole.go
@@ -0,0 +1,92 @@
+package webrtc
+
+import (
+ "github.com/pion/sdp/v3"
+)
+
+// DTLSRole indicates the role of the DTLS transport.
+type DTLSRole byte
+
+const (
+ // DTLSRoleAuto defines the DTLS role is determined based on
+ // the resolved ICE role: the ICE controlled role acts as the DTLS
+ // client and the ICE controlling role acts as the DTLS server.
+ DTLSRoleAuto DTLSRole = iota + 1
+
+ // DTLSRoleClient defines the DTLS client role.
+ DTLSRoleClient
+
+ // DTLSRoleServer defines the DTLS server role.
+ DTLSRoleServer
+)
+
+const (
+ // https://tools.ietf.org/html/rfc5763
+ /*
+ The answerer MUST use either a
+ setup attribute value of setup:active or setup:passive. Note that
+ if the answerer uses setup:passive, then the DTLS handshake will
+ not begin until the answerer is received, which adds additional
+ latency. setup:active allows the answer and the DTLS handshake to
+ occur in parallel. Thus, setup:active is RECOMMENDED.
+ */
+ defaultDtlsRoleAnswer = DTLSRoleClient
+ /*
+ The endpoint that is the offerer MUST use the setup attribute
+ value of setup:actpass and be prepared to receive a client_hello
+ before it receives the answer.
+ */
+ defaultDtlsRoleOffer = DTLSRoleAuto
+)
+
+func (r DTLSRole) String() string {
+ switch r {
+ case DTLSRoleAuto:
+ return "auto"
+ case DTLSRoleClient:
+ return "client"
+ case DTLSRoleServer:
+ return "server"
+ default:
+ return unknownStr
+ }
+}
+
+// Iterate a SessionDescription from a remote to determine if an explicit
+// role can been determined from it. The decision is made from the first role we we parse.
+// If no role can be found we return DTLSRoleAuto
+func dtlsRoleFromRemoteSDP(sessionDescription *sdp.SessionDescription) DTLSRole {
+ if sessionDescription == nil {
+ return DTLSRoleAuto
+ }
+
+ for _, mediaSection := range sessionDescription.MediaDescriptions {
+ for _, attribute := range mediaSection.Attributes {
+ if attribute.Key == "setup" {
+ switch attribute.Value {
+ case sdp.ConnectionRoleActive.String():
+ return DTLSRoleClient
+ case sdp.ConnectionRolePassive.String():
+ return DTLSRoleServer
+ default:
+ return DTLSRoleAuto
+ }
+ }
+ }
+ }
+
+ return DTLSRoleAuto
+}
+
+func connectionRoleFromDtlsRole(d DTLSRole) sdp.ConnectionRole {
+ switch d {
+ case DTLSRoleClient:
+ return sdp.ConnectionRoleActive
+ case DTLSRoleServer:
+ return sdp.ConnectionRolePassive
+ case DTLSRoleAuto:
+ return sdp.ConnectionRoleActpass
+ default:
+ return sdp.ConnectionRole(0)
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/dtlstransport.go b/vendor/github.com/pion/webrtc/v3/dtlstransport.go
new file mode 100644
index 0000000..cc25889
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/dtlstransport.go
@@ -0,0 +1,430 @@
+// +build !js
+
+package webrtc
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/dtls/v2"
+ "github.com/pion/dtls/v2/pkg/crypto/fingerprint"
+ "github.com/pion/logging"
+ "github.com/pion/srtp/v2"
+ "github.com/pion/webrtc/v3/internal/mux"
+ "github.com/pion/webrtc/v3/internal/util"
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+// DTLSTransport allows an application access to information about the DTLS
+// transport over which RTP and RTCP packets are sent and received by
+// RTPSender and RTPReceiver, as well other data such as SCTP packets sent
+// and received by data channels.
+type DTLSTransport struct {
+ lock sync.RWMutex
+
+ iceTransport *ICETransport
+ certificates []Certificate
+ remoteParameters DTLSParameters
+ remoteCertificate []byte
+ state DTLSTransportState
+ srtpProtectionProfile srtp.ProtectionProfile
+
+ onStateChangeHandler func(DTLSTransportState)
+
+ conn *dtls.Conn
+
+ srtpSession, srtcpSession atomic.Value
+ srtpEndpoint, srtcpEndpoint *mux.Endpoint
+ simulcastStreams []*srtp.ReadStreamSRTP
+ srtpReady chan struct{}
+
+ dtlsMatcher mux.MatchFunc
+
+ api *API
+ log logging.LeveledLogger
+}
+
+// NewDTLSTransport creates a new DTLSTransport.
+// This constructor is part of the ORTC API. It is not
+// meant to be used together with the basic WebRTC API.
+func (api *API) NewDTLSTransport(transport *ICETransport, certificates []Certificate) (*DTLSTransport, error) {
+ t := &DTLSTransport{
+ iceTransport: transport,
+ api: api,
+ state: DTLSTransportStateNew,
+ dtlsMatcher: mux.MatchDTLS,
+ srtpReady: make(chan struct{}),
+ log: api.settingEngine.LoggerFactory.NewLogger("DTLSTransport"),
+ }
+
+ if len(certificates) > 0 {
+ now := time.Now()
+ for _, x509Cert := range certificates {
+ if !x509Cert.Expires().IsZero() && now.After(x509Cert.Expires()) {
+ return nil, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}
+ }
+ t.certificates = append(t.certificates, x509Cert)
+ }
+ } else {
+ sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ return nil, &rtcerr.UnknownError{Err: err}
+ }
+ certificate, err := GenerateCertificate(sk)
+ if err != nil {
+ return nil, err
+ }
+ t.certificates = []Certificate{*certificate}
+ }
+
+ return t, nil
+}
+
+// ICETransport returns the currently-configured *ICETransport or nil
+// if one has not been configured
+func (t *DTLSTransport) ICETransport() *ICETransport {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+ return t.iceTransport
+}
+
+// onStateChange requires the caller holds the lock
+func (t *DTLSTransport) onStateChange(state DTLSTransportState) {
+ t.state = state
+ handler := t.onStateChangeHandler
+ if handler != nil {
+ handler(state)
+ }
+}
+
+// OnStateChange sets a handler that is fired when the DTLS
+// connection state changes.
+func (t *DTLSTransport) OnStateChange(f func(DTLSTransportState)) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+ t.onStateChangeHandler = f
+}
+
+// State returns the current dtls transport state.
+func (t *DTLSTransport) State() DTLSTransportState {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+ return t.state
+}
+
+// GetLocalParameters returns the DTLS parameters of the local DTLSTransport upon construction.
+func (t *DTLSTransport) GetLocalParameters() (DTLSParameters, error) {
+ fingerprints := []DTLSFingerprint{}
+
+ for _, c := range t.certificates {
+ prints, err := c.GetFingerprints()
+ if err != nil {
+ return DTLSParameters{}, err
+ }
+
+ fingerprints = append(fingerprints, prints...)
+ }
+
+ return DTLSParameters{
+ Role: DTLSRoleAuto, // always returns the default role
+ Fingerprints: fingerprints,
+ }, nil
+}
+
+// GetRemoteCertificate returns the certificate chain in use by the remote side
+// returns an empty list prior to selection of the remote certificate
+func (t *DTLSTransport) GetRemoteCertificate() []byte {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+ return t.remoteCertificate
+}
+
+func (t *DTLSTransport) startSRTP() error {
+ srtpConfig := &srtp.Config{
+ Profile: t.srtpProtectionProfile,
+ BufferFactory: t.api.settingEngine.BufferFactory,
+ LoggerFactory: t.api.settingEngine.LoggerFactory,
+ }
+ if t.api.settingEngine.replayProtection.SRTP != nil {
+ srtpConfig.RemoteOptions = append(
+ srtpConfig.RemoteOptions,
+ srtp.SRTPReplayProtection(*t.api.settingEngine.replayProtection.SRTP),
+ )
+ }
+
+ if t.api.settingEngine.disableSRTPReplayProtection {
+ srtpConfig.RemoteOptions = append(
+ srtpConfig.RemoteOptions,
+ srtp.SRTPNoReplayProtection(),
+ )
+ }
+
+ if t.api.settingEngine.replayProtection.SRTCP != nil {
+ srtpConfig.RemoteOptions = append(
+ srtpConfig.RemoteOptions,
+ srtp.SRTCPReplayProtection(*t.api.settingEngine.replayProtection.SRTCP),
+ )
+ }
+
+ if t.api.settingEngine.disableSRTCPReplayProtection {
+ srtpConfig.RemoteOptions = append(
+ srtpConfig.RemoteOptions,
+ srtp.SRTCPNoReplayProtection(),
+ )
+ }
+
+ connState := t.conn.ConnectionState()
+ err := srtpConfig.ExtractSessionKeysFromDTLS(&connState, t.role() == DTLSRoleClient)
+ if err != nil {
+ return fmt.Errorf("%w: %v", errDtlsKeyExtractionFailed, err)
+ }
+
+ srtpSession, err := srtp.NewSessionSRTP(t.srtpEndpoint, srtpConfig)
+ if err != nil {
+ return fmt.Errorf("%w: %v", errFailedToStartSRTP, err)
+ }
+
+ srtcpSession, err := srtp.NewSessionSRTCP(t.srtcpEndpoint, srtpConfig)
+ if err != nil {
+ return fmt.Errorf("%w: %v", errFailedToStartSRTCP, err)
+ }
+
+ t.srtpSession.Store(srtpSession)
+ t.srtcpSession.Store(srtcpSession)
+ close(t.srtpReady)
+ return nil
+}
+
+func (t *DTLSTransport) getSRTPSession() (*srtp.SessionSRTP, error) {
+ if value := t.srtpSession.Load(); value != nil {
+ return value.(*srtp.SessionSRTP), nil
+ }
+
+ return nil, errDtlsTransportNotStarted
+}
+
+func (t *DTLSTransport) getSRTCPSession() (*srtp.SessionSRTCP, error) {
+ if value := t.srtcpSession.Load(); value != nil {
+ return value.(*srtp.SessionSRTCP), nil
+ }
+
+ return nil, errDtlsTransportNotStarted
+}
+
+func (t *DTLSTransport) role() DTLSRole {
+ // If remote has an explicit role use the inverse
+ switch t.remoteParameters.Role {
+ case DTLSRoleClient:
+ return DTLSRoleServer
+ case DTLSRoleServer:
+ return DTLSRoleClient
+ default:
+ }
+
+ // If SettingEngine has an explicit role
+ switch t.api.settingEngine.answeringDTLSRole {
+ case DTLSRoleServer:
+ return DTLSRoleServer
+ case DTLSRoleClient:
+ return DTLSRoleClient
+ default:
+ }
+
+ // Remote was auto and no explicit role was configured via SettingEngine
+ if t.iceTransport.Role() == ICERoleControlling {
+ return DTLSRoleServer
+ }
+ return defaultDtlsRoleAnswer
+}
+
+// Start DTLS transport negotiation with the parameters of the remote DTLS transport
+func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error {
+ // Take lock and prepare connection, we must not hold the lock
+ // when connecting
+ prepareTransport := func() (DTLSRole, *dtls.Config, error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if err := t.ensureICEConn(); err != nil {
+ return DTLSRole(0), nil, err
+ }
+
+ if t.state != DTLSTransportStateNew {
+ return DTLSRole(0), nil, &rtcerr.InvalidStateError{Err: fmt.Errorf("%w: %s", errInvalidDTLSStart, t.state)}
+ }
+
+ t.srtpEndpoint = t.iceTransport.NewEndpoint(mux.MatchSRTP)
+ t.srtcpEndpoint = t.iceTransport.NewEndpoint(mux.MatchSRTCP)
+ t.remoteParameters = remoteParameters
+
+ cert := t.certificates[0]
+ t.onStateChange(DTLSTransportStateConnecting)
+
+ return t.role(), &dtls.Config{
+ Certificates: []tls.Certificate{
+ {
+ Certificate: [][]byte{cert.x509Cert.Raw},
+ PrivateKey: cert.privateKey,
+ },
+ },
+ SRTPProtectionProfiles: []dtls.SRTPProtectionProfile{dtls.SRTP_AEAD_AES_128_GCM, dtls.SRTP_AES128_CM_HMAC_SHA1_80},
+ ClientAuth: dtls.RequireAnyClientCert,
+ LoggerFactory: t.api.settingEngine.LoggerFactory,
+ InsecureSkipVerify: true,
+ }, nil
+ }
+
+ var dtlsConn *dtls.Conn
+ dtlsEndpoint := t.iceTransport.NewEndpoint(mux.MatchDTLS)
+ role, dtlsConfig, err := prepareTransport()
+ if err != nil {
+ return err
+ }
+
+ if t.api.settingEngine.replayProtection.DTLS != nil {
+ dtlsConfig.ReplayProtectionWindow = int(*t.api.settingEngine.replayProtection.DTLS)
+ }
+
+ // Connect as DTLS Client/Server, function is blocking and we
+ // must not hold the DTLSTransport lock
+ if role == DTLSRoleClient {
+ dtlsConn, err = dtls.Client(dtlsEndpoint, dtlsConfig)
+ } else {
+ dtlsConn, err = dtls.Server(dtlsEndpoint, dtlsConfig)
+ }
+
+ // Re-take the lock, nothing beyond here is blocking
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if err != nil {
+ t.onStateChange(DTLSTransportStateFailed)
+ return err
+ }
+
+ srtpProfile, ok := dtlsConn.SelectedSRTPProtectionProfile()
+ if !ok {
+ t.onStateChange(DTLSTransportStateFailed)
+ return ErrNoSRTPProtectionProfile
+ }
+
+ switch srtpProfile {
+ case dtls.SRTP_AEAD_AES_128_GCM:
+ t.srtpProtectionProfile = srtp.ProtectionProfileAeadAes128Gcm
+ case dtls.SRTP_AES128_CM_HMAC_SHA1_80:
+ t.srtpProtectionProfile = srtp.ProtectionProfileAes128CmHmacSha1_80
+ default:
+ t.onStateChange(DTLSTransportStateFailed)
+ return ErrNoSRTPProtectionProfile
+ }
+
+ if t.api.settingEngine.disableCertificateFingerprintVerification {
+ return nil
+ }
+
+ // Check the fingerprint if a certificate was exchanged
+ remoteCerts := dtlsConn.ConnectionState().PeerCertificates
+ if len(remoteCerts) == 0 {
+ t.onStateChange(DTLSTransportStateFailed)
+ return errNoRemoteCertificate
+ }
+ t.remoteCertificate = remoteCerts[0]
+
+ parsedRemoteCert, err := x509.ParseCertificate(t.remoteCertificate)
+ if err != nil {
+ if closeErr := dtlsConn.Close(); closeErr != nil {
+ t.log.Error(err.Error())
+ }
+
+ t.onStateChange(DTLSTransportStateFailed)
+ return err
+ }
+
+ if err = t.validateFingerPrint(parsedRemoteCert); err != nil {
+ if closeErr := dtlsConn.Close(); closeErr != nil {
+ t.log.Error(err.Error())
+ }
+
+ t.onStateChange(DTLSTransportStateFailed)
+ return err
+ }
+
+ t.conn = dtlsConn
+ t.onStateChange(DTLSTransportStateConnected)
+
+ return t.startSRTP()
+}
+
+// Stop stops and closes the DTLSTransport object.
+func (t *DTLSTransport) Stop() error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ // Try closing everything and collect the errors
+ var closeErrs []error
+
+ if srtpSessionValue := t.srtpSession.Load(); srtpSessionValue != nil {
+ closeErrs = append(closeErrs, srtpSessionValue.(*srtp.SessionSRTP).Close())
+ }
+
+ if srtcpSessionValue := t.srtcpSession.Load(); srtcpSessionValue != nil {
+ closeErrs = append(closeErrs, srtcpSessionValue.(*srtp.SessionSRTCP).Close())
+ }
+
+ for i := range t.simulcastStreams {
+ closeErrs = append(closeErrs, t.simulcastStreams[i].Close())
+ }
+
+ if t.conn != nil {
+ // dtls connection may be closed on sctp close.
+ if err := t.conn.Close(); err != nil && !errors.Is(err, dtls.ErrConnClosed) {
+ closeErrs = append(closeErrs, err)
+ }
+ }
+ t.onStateChange(DTLSTransportStateClosed)
+ return util.FlattenErrs(closeErrs)
+}
+
+func (t *DTLSTransport) validateFingerPrint(remoteCert *x509.Certificate) error {
+ for _, fp := range t.remoteParameters.Fingerprints {
+ hashAlgo, err := fingerprint.HashFromString(fp.Algorithm)
+ if err != nil {
+ return err
+ }
+
+ remoteValue, err := fingerprint.Fingerprint(remoteCert, hashAlgo)
+ if err != nil {
+ return err
+ }
+
+ if strings.EqualFold(remoteValue, fp.Value) {
+ return nil
+ }
+ }
+
+ return errNoMatchingCertificateFingerprint
+}
+
+func (t *DTLSTransport) ensureICEConn() error {
+ if t.iceTransport == nil || t.iceTransport.State() == ICETransportStateNew {
+ return errICEConnectionNotStarted
+ }
+
+ return nil
+}
+
+func (t *DTLSTransport) storeSimulcastStream(s *srtp.ReadStreamSRTP) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.simulcastStreams = append(t.simulcastStreams, s)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go b/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go
new file mode 100644
index 0000000..900b50b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go
@@ -0,0 +1,71 @@
+package webrtc
+
+// DTLSTransportState indicates the DTLS transport establishment state.
+type DTLSTransportState int
+
+const (
+ // DTLSTransportStateNew indicates that DTLS has not started negotiating
+ // yet.
+ DTLSTransportStateNew DTLSTransportState = iota + 1
+
+ // DTLSTransportStateConnecting indicates that DTLS is in the process of
+ // negotiating a secure connection and verifying the remote fingerprint.
+ DTLSTransportStateConnecting
+
+ // DTLSTransportStateConnected indicates that DTLS has completed
+ // negotiation of a secure connection and verified the remote fingerprint.
+ DTLSTransportStateConnected
+
+ // DTLSTransportStateClosed indicates that the transport has been closed
+ // intentionally as the result of receipt of a close_notify alert, or
+ // calling close().
+ DTLSTransportStateClosed
+
+ // DTLSTransportStateFailed indicates that the transport has failed as
+ // the result of an error (such as receipt of an error alert or failure to
+ // validate the remote fingerprint).
+ DTLSTransportStateFailed
+)
+
+// This is done this way because of a linter.
+const (
+ dtlsTransportStateNewStr = "new"
+ dtlsTransportStateConnectingStr = "connecting"
+ dtlsTransportStateConnectedStr = "connected"
+ dtlsTransportStateClosedStr = "closed"
+ dtlsTransportStateFailedStr = "failed"
+)
+
+func newDTLSTransportState(raw string) DTLSTransportState {
+ switch raw {
+ case dtlsTransportStateNewStr:
+ return DTLSTransportStateNew
+ case dtlsTransportStateConnectingStr:
+ return DTLSTransportStateConnecting
+ case dtlsTransportStateConnectedStr:
+ return DTLSTransportStateConnected
+ case dtlsTransportStateClosedStr:
+ return DTLSTransportStateClosed
+ case dtlsTransportStateFailedStr:
+ return DTLSTransportStateFailed
+ default:
+ return DTLSTransportState(Unknown)
+ }
+}
+
+func (t DTLSTransportState) String() string {
+ switch t {
+ case DTLSTransportStateNew:
+ return dtlsTransportStateNewStr
+ case DTLSTransportStateConnecting:
+ return dtlsTransportStateConnectingStr
+ case DTLSTransportStateConnected:
+ return dtlsTransportStateConnectedStr
+ case DTLSTransportStateClosed:
+ return dtlsTransportStateClosedStr
+ case DTLSTransportStateFailed:
+ return dtlsTransportStateFailedStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/errors.go b/vendor/github.com/pion/webrtc/v3/errors.go
new file mode 100644
index 0000000..a9daf16
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/errors.go
@@ -0,0 +1,220 @@
+package webrtc
+
+import (
+ "errors"
+)
+
+var (
+ // ErrUnknownType indicates an error with Unknown info.
+ ErrUnknownType = errors.New("unknown")
+
+ // ErrConnectionClosed indicates an operation executed after connection
+ // has already been closed.
+ ErrConnectionClosed = errors.New("connection closed")
+
+ // ErrDataChannelNotOpen indicates an operation executed when the data
+ // channel is not (yet) open.
+ ErrDataChannelNotOpen = errors.New("data channel not open")
+
+ // ErrCertificateExpired indicates that an x509 certificate has expired.
+ ErrCertificateExpired = errors.New("x509Cert expired")
+
+ // ErrNoTurnCredentials indicates that a TURN server URL was provided
+ // without required credentials.
+ ErrNoTurnCredentials = errors.New("turn server credentials required")
+
+ // ErrTurnCredentials indicates that provided TURN credentials are partial
+ // or malformed.
+ ErrTurnCredentials = errors.New("invalid turn server credentials")
+
+ // ErrExistingTrack indicates that a track already exists.
+ ErrExistingTrack = errors.New("track already exists")
+
+ // ErrPrivateKeyType indicates that a particular private key encryption
+ // chosen to generate a certificate is not supported.
+ ErrPrivateKeyType = errors.New("private key type not supported")
+
+ // ErrModifyingPeerIdentity indicates that an attempt to modify
+ // PeerIdentity was made after PeerConnection has been initialized.
+ ErrModifyingPeerIdentity = errors.New("peerIdentity cannot be modified")
+
+ // ErrModifyingCertificates indicates that an attempt to modify
+ // Certificates was made after PeerConnection has been initialized.
+ ErrModifyingCertificates = errors.New("certificates cannot be modified")
+
+ // ErrModifyingBundlePolicy indicates that an attempt to modify
+ // BundlePolicy was made after PeerConnection has been initialized.
+ ErrModifyingBundlePolicy = errors.New("bundle policy cannot be modified")
+
+ // ErrModifyingRTCPMuxPolicy indicates that an attempt to modify
+ // RTCPMuxPolicy was made after PeerConnection has been initialized.
+ ErrModifyingRTCPMuxPolicy = errors.New("rtcp mux policy cannot be modified")
+
+ // ErrModifyingICECandidatePoolSize indicates that an attempt to modify
+ // ICECandidatePoolSize was made after PeerConnection has been initialized.
+ ErrModifyingICECandidatePoolSize = errors.New("ice candidate pool size cannot be modified")
+
+ // ErrStringSizeLimit indicates that the character size limit of string is
+ // exceeded. The limit is hardcoded to 65535 according to specifications.
+ ErrStringSizeLimit = errors.New("data channel label exceeds size limit")
+
+ // ErrMaxDataChannelID indicates that the maximum number ID that could be
+ // specified for a data channel has been exceeded.
+ ErrMaxDataChannelID = errors.New("maximum number ID for datachannel specified")
+
+ // ErrNegotiatedWithoutID indicates that an attempt to create a data channel
+ // was made while setting the negotiated option to true without providing
+ // the negotiated channel ID.
+ ErrNegotiatedWithoutID = errors.New("negotiated set without channel id")
+
+ // ErrRetransmitsOrPacketLifeTime indicates that an attempt to create a data
+ // channel was made with both options MaxPacketLifeTime and MaxRetransmits
+ // set together. Such configuration is not supported by the specification
+ // and is mutually exclusive.
+ ErrRetransmitsOrPacketLifeTime = errors.New("both MaxPacketLifeTime and MaxRetransmits was set")
+
+ // ErrCodecNotFound is returned when a codec search to the Media Engine fails
+ ErrCodecNotFound = errors.New("codec not found")
+
+ // ErrNoRemoteDescription indicates that an operation was rejected because
+ // the remote description is not set
+ ErrNoRemoteDescription = errors.New("remote description is not set")
+
+ // ErrIncorrectSDPSemantics indicates that the PeerConnection was configured to
+ // generate SDP Answers with different SDP Semantics than the received Offer
+ ErrIncorrectSDPSemantics = errors.New("offer SDP semantics does not match configuration")
+
+ // ErrIncorrectSignalingState indicates that the signaling state of PeerConnection is not correct
+ ErrIncorrectSignalingState = errors.New("operation can not be run in current signaling state")
+
+ // ErrProtocolTooLarge indicates that value given for a DataChannelInit protocol is
+ // longer then 65535 bytes
+ ErrProtocolTooLarge = errors.New("protocol is larger then 65535 bytes")
+
+ // ErrSenderNotCreatedByConnection indicates RemoveTrack was called with a RtpSender not created
+ // by this PeerConnection
+ ErrSenderNotCreatedByConnection = errors.New("RtpSender not created by this PeerConnection")
+
+ // ErrSessionDescriptionNoFingerprint indicates SetRemoteDescription was called with a SessionDescription that has no
+ // fingerprint
+ ErrSessionDescriptionNoFingerprint = errors.New("SetRemoteDescription called with no fingerprint")
+
+ // ErrSessionDescriptionInvalidFingerprint indicates SetRemoteDescription was called with a SessionDescription that
+ // has an invalid fingerprint
+ ErrSessionDescriptionInvalidFingerprint = errors.New("SetRemoteDescription called with an invalid fingerprint")
+
+ // ErrSessionDescriptionConflictingFingerprints indicates SetRemoteDescription was called with a SessionDescription that
+ // has an conflicting fingerprints
+ ErrSessionDescriptionConflictingFingerprints = errors.New("SetRemoteDescription called with multiple conflicting fingerprint")
+
+ // ErrSessionDescriptionMissingIceUfrag indicates SetRemoteDescription was called with a SessionDescription that
+ // is missing an ice-ufrag value
+ ErrSessionDescriptionMissingIceUfrag = errors.New("SetRemoteDescription called with no ice-ufrag")
+
+ // ErrSessionDescriptionMissingIcePwd indicates SetRemoteDescription was called with a SessionDescription that
+ // is missing an ice-pwd value
+ ErrSessionDescriptionMissingIcePwd = errors.New("SetRemoteDescription called with no ice-pwd")
+
+ // ErrSessionDescriptionConflictingIceUfrag indicates SetRemoteDescription was called with a SessionDescription that
+ // contains multiple conflicting ice-ufrag values
+ ErrSessionDescriptionConflictingIceUfrag = errors.New("SetRemoteDescription called with multiple conflicting ice-ufrag values")
+
+ // ErrSessionDescriptionConflictingIcePwd indicates SetRemoteDescription was called with a SessionDescription that
+ // contains multiple conflicting ice-pwd values
+ ErrSessionDescriptionConflictingIcePwd = errors.New("SetRemoteDescription called with multiple conflicting ice-pwd values")
+
+ // ErrNoSRTPProtectionProfile indicates that the DTLS handshake completed and no SRTP Protection Profile was chosen
+ ErrNoSRTPProtectionProfile = errors.New("DTLS Handshake completed and no SRTP Protection Profile was chosen")
+
+ // ErrFailedToGenerateCertificateFingerprint indicates that we failed to generate the fingerprint used for comparing certificates
+ ErrFailedToGenerateCertificateFingerprint = errors.New("failed to generate certificate fingerprint")
+
+ // ErrNoCodecsAvailable indicates that operation isn't possible because the MediaEngine has no codecs available
+ ErrNoCodecsAvailable = errors.New("operation failed no codecs are available")
+
+ // ErrUnsupportedCodec indicates the remote peer doesn't support the requested codec
+ ErrUnsupportedCodec = errors.New("unable to start track, codec is not supported by remote")
+
+ // ErrUnbindFailed indicates that a TrackLocal was not able to be unbind
+ ErrUnbindFailed = errors.New("failed to unbind TrackLocal from PeerConnection")
+
+ // ErrNoPayloaderForCodec indicates that the requested codec does not have a payloader
+ ErrNoPayloaderForCodec = errors.New("the requested codec does not have a payloader")
+
+ // ErrRegisterHeaderExtensionInvalidDirection indicates that a extension was registered with a direction besides `sendonly` or `recvonly`
+ ErrRegisterHeaderExtensionInvalidDirection = errors.New("a header extension must be registered as 'recvonly', 'sendonly' or both")
+
+ // ErrSimulcastProbeOverflow indicates that too many Simulcast probe streams are in flight and the requested SSRC was ignored
+ ErrSimulcastProbeOverflow = errors.New("simulcast probe limit has been reached, new SSRC has been discarded")
+
+ errDetachNotEnabled = errors.New("enable detaching by calling webrtc.DetachDataChannels()")
+ errDetachBeforeOpened = errors.New("datachannel not opened yet, try calling Detach from OnOpen")
+ errDtlsTransportNotStarted = errors.New("the DTLS transport has not started yet")
+ errDtlsKeyExtractionFailed = errors.New("failed extracting keys from DTLS for SRTP")
+ errFailedToStartSRTP = errors.New("failed to start SRTP")
+ errFailedToStartSRTCP = errors.New("failed to start SRTCP")
+ errInvalidDTLSStart = errors.New("attempted to start DTLSTransport that is not in new state")
+ errNoRemoteCertificate = errors.New("peer didn't provide certificate via DTLS")
+ errIdentityProviderNotImplemented = errors.New("identity provider is not implemented")
+ errNoMatchingCertificateFingerprint = errors.New("remote certificate does not match any fingerprint")
+
+ errICEConnectionNotStarted = errors.New("ICE connection not started")
+ errICECandidateTypeUnknown = errors.New("unknown candidate type")
+ errICEInvalidConvertCandidateType = errors.New("cannot convert ice.CandidateType into webrtc.ICECandidateType, invalid type")
+ errICEAgentNotExist = errors.New("ICEAgent does not exist")
+ errICECandiatesCoversionFailed = errors.New("unable to convert ICE candidates to ICECandidates")
+ errICERoleUnknown = errors.New("unknown ICE Role")
+ errICEProtocolUnknown = errors.New("unknown protocol")
+ errICEGathererNotStarted = errors.New("gatherer not started")
+
+ errNetworkTypeUnknown = errors.New("unknown network type")
+
+ errSDPDoesNotMatchOffer = errors.New("new sdp does not match previous offer")
+ errSDPDoesNotMatchAnswer = errors.New("new sdp does not match previous answer")
+ errPeerConnSDPTypeInvalidValue = errors.New("provided value is not a valid enum value of type SDPType")
+ errPeerConnStateChangeInvalid = errors.New("invalid state change op")
+ errPeerConnStateChangeUnhandled = errors.New("unhandled state change op")
+ errPeerConnSDPTypeInvalidValueSetLocalDescription = errors.New("invalid SDP type supplied to SetLocalDescription()")
+ errPeerConnRemoteDescriptionWithoutMidValue = errors.New("remoteDescription contained media section without mid value")
+ errPeerConnRemoteDescriptionNil = errors.New("remoteDescription has not been set yet")
+ errPeerConnSingleMediaSectionHasExplicitSSRC = errors.New("single media section has an explicit SSRC")
+ errPeerConnRemoteSSRCAddTransceiver = errors.New("could not add transceiver for remote SSRC")
+ errPeerConnSimulcastMidRTPExtensionRequired = errors.New("mid RTP Extensions required for Simulcast")
+ errPeerConnSimulcastStreamIDRTPExtensionRequired = errors.New("stream id RTP Extensions required for Simulcast")
+ errPeerConnSimulcastIncomingSSRCFailed = errors.New("incoming SSRC failed Simulcast probing")
+ errPeerConnAddTransceiverFromKindOnlyAcceptsOne = errors.New("AddTransceiverFromKind only accepts one RtpTransceiverInit")
+ errPeerConnAddTransceiverFromTrackOnlyAcceptsOne = errors.New("AddTransceiverFromTrack only accepts one RtpTransceiverInit")
+ errPeerConnAddTransceiverFromKindSupport = errors.New("AddTransceiverFromKind currently only supports recvonly")
+ errPeerConnAddTransceiverFromTrackSupport = errors.New("AddTransceiverFromTrack currently only supports sendonly and sendrecv")
+ errPeerConnSetIdentityProviderNotImplemented = errors.New("TODO SetIdentityProvider")
+ errPeerConnWriteRTCPOpenWriteStream = errors.New("WriteRTCP failed to open WriteStream")
+ errPeerConnTranscieverMidNil = errors.New("cannot find transceiver with mid")
+
+ errRTPReceiverDTLSTransportNil = errors.New("DTLSTransport must not be nil")
+ errRTPReceiverReceiveAlreadyCalled = errors.New("Receive has already been called")
+ errRTPReceiverWithSSRCTrackStreamNotFound = errors.New("unable to find stream for Track with SSRC")
+ errRTPReceiverForSSRCTrackStreamNotFound = errors.New("no trackStreams found for SSRC")
+ errRTPReceiverForRIDTrackStreamNotFound = errors.New("no trackStreams found for RID")
+
+ errRTPSenderTrackNil = errors.New("Track must not be nil")
+ errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil")
+ errRTPSenderSendAlreadyCalled = errors.New("Send has already been called")
+
+ errRTPTransceiverCannotChangeMid = errors.New("errRTPSenderTrackNil")
+ errRTPTransceiverSetSendingInvalidState = errors.New("invalid state change in RTPTransceiver.setSending")
+
+ errSCTPTransportDTLS = errors.New("DTLS not established")
+
+ errSDPZeroTransceivers = errors.New("addTransceiverSDP() called with 0 transceivers")
+ errSDPMediaSectionMediaDataChanInvalid = errors.New("invalid Media Section. Media + DataChannel both enabled")
+ errSDPMediaSectionMultipleTrackInvalid = errors.New("invalid Media Section. Can not have multiple tracks in one MediaSection in UnifiedPlan")
+
+ errSettingEngineSetAnsweringDTLSRole = errors.New("SetAnsweringDTLSRole must DTLSRoleClient or DTLSRoleServer")
+
+ errSignalingStateCannotRollback = errors.New("can't rollback from stable state")
+ errSignalingStateProposedTransitionInvalid = errors.New("invalid proposed signaling state transition")
+
+ errStatsICECandidateStateInvalid = errors.New("cannot convert to StatsICECandidatePairStateSucceeded invalid ice candidate state")
+
+ errICETransportNotInNew = errors.New("ICETransport can only be called in ICETransportStateNew")
+)
diff --git a/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go b/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go
new file mode 100644
index 0000000..a4d52f9
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go
@@ -0,0 +1,24 @@
+package webrtc
+
+import (
+ "context"
+)
+
+// GatheringCompletePromise is a Pion specific helper function that returns a channel that is closed when gathering is complete.
+// This function may be helpful in cases where you are unable to trickle your ICE Candidates.
+//
+// It is better to not use this function, and instead trickle candidates. If you use this function you will see longer connection startup times.
+// When the call is connected you will see no impact however.
+func GatheringCompletePromise(pc *PeerConnection) (gatherComplete <-chan struct{}) {
+ gatheringComplete, done := context.WithCancel(context.Background())
+
+ // It's possible to miss the GatherComplete event since setGatherCompleteHandler is an atomic operation and the
+ // promise might have been created after the gathering is finished. Therefore, we need to check if the ICE gathering
+ // state has changed to complete so that we don't block the caller forever.
+ pc.setGatherCompleteHandler(func() { done() })
+ if pc.ICEGatheringState() == ICEGatheringStateComplete {
+ done()
+ }
+
+ return gatheringComplete.Done()
+}
diff --git a/vendor/github.com/pion/webrtc/v3/go.mod b/vendor/github.com/pion/webrtc/v3/go.mod
new file mode 100644
index 0000000..19d5a7e
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/go.mod
@@ -0,0 +1,23 @@
+module github.com/pion/webrtc/v3
+
+go 1.12
+
+require (
+ github.com/onsi/ginkgo v1.14.2 // indirect
+ github.com/onsi/gomega v1.10.3 // indirect
+ github.com/pion/datachannel v1.4.21
+ github.com/pion/dtls/v2 v2.0.8
+ github.com/pion/ice/v2 v2.0.15
+ github.com/pion/interceptor v0.0.10
+ github.com/pion/logging v0.2.2
+ github.com/pion/randutil v0.1.0
+ github.com/pion/rtcp v1.2.6
+ github.com/pion/rtp v1.6.2
+ github.com/pion/sctp v1.7.11
+ github.com/pion/sdp/v3 v3.0.4
+ github.com/pion/srtp/v2 v2.0.2
+ github.com/pion/transport v0.12.2
+ github.com/sclevine/agouti v3.0.0+incompatible
+ github.com/stretchr/testify v1.7.0
+ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
+)
diff --git a/vendor/github.com/pion/webrtc/v3/go.sum b/vendor/github.com/pion/webrtc/v3/go.sum
new file mode 100644
index 0000000..397bbfd
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/go.sum
@@ -0,0 +1,151 @@
+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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+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 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+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 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
+github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
+github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
+github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
+github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
+github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
+github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
+github.com/pion/dtls/v2 v2.0.8 h1:reGe8rNIMfO/UAeFLqO61tl64t154Qfkr4U3Gzu1tsg=
+github.com/pion/dtls/v2 v2.0.8/go.mod h1:QuDII+8FVvk9Dp5t5vYIMTo7hh7uBkra+8QIm7QGm10=
+github.com/pion/ice/v2 v2.0.15 h1:KZrwa2ciL9od8+TUVJiYTNsCW9J5lktBjGwW1MacEnQ=
+github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI=
+github.com/pion/interceptor v0.0.10 h1:dXFyFWRJFwmzQqyn0U8dUAbOJu+JJnMVAqxmvTu30B4=
+github.com/pion/interceptor v0.0.10/go.mod h1:qzeuWuD/ZXvPqOnxNcnhWfkCZ2e1kwwslicyyPnhoK4=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
+github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
+github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
+github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
+github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
+github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
+github.com/pion/sctp v1.7.11 h1:UCnj7MsobLKLuP/Hh+JMiI/6W5Bs/VF45lWKgHFjSIE=
+github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
+github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8=
+github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
+github.com/pion/srtp/v2 v2.0.2 h1:664iGzVmaY7KYS5M0gleY0DscRo9ReDfTxQrq4UgGoU=
+github.com/pion/srtp/v2 v2.0.2/go.mod h1:VEyLv4CuxrwGY8cxM+Ng3bmVy8ckz/1t6A0q/msKOw0=
+github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
+github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
+github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
+github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
+github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
+github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
+github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys=
+github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
+github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
+github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
+github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
+github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
+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/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4=
+github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
+golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8=
+golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+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 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/pion/webrtc/v3/ice_go.go b/vendor/github.com/pion/webrtc/v3/ice_go.go
new file mode 100644
index 0000000..897ed0d
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/ice_go.go
@@ -0,0 +1,10 @@
+// +build !js
+
+package webrtc
+
+// NewICETransport creates a new NewICETransport.
+// This constructor is part of the ORTC API. It is not
+// meant to be used together with the basic WebRTC API.
+func (api *API) NewICETransport(gatherer *ICEGatherer) *ICETransport {
+ return NewICETransport(gatherer, api.settingEngine.LoggerFactory)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icecandidate.go b/vendor/github.com/pion/webrtc/v3/icecandidate.go
new file mode 100644
index 0000000..1b0fbfc
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icecandidate.go
@@ -0,0 +1,169 @@
+package webrtc
+
+import (
+ "fmt"
+
+ "github.com/pion/ice/v2"
+)
+
+// ICECandidate represents a ice candidate
+type ICECandidate struct {
+ statsID string
+ Foundation string `json:"foundation"`
+ Priority uint32 `json:"priority"`
+ Address string `json:"address"`
+ Protocol ICEProtocol `json:"protocol"`
+ Port uint16 `json:"port"`
+ Typ ICECandidateType `json:"type"`
+ Component uint16 `json:"component"`
+ RelatedAddress string `json:"relatedAddress"`
+ RelatedPort uint16 `json:"relatedPort"`
+ TCPType string `json:"tcpType"`
+}
+
+// Conversion for package ice
+
+func newICECandidatesFromICE(iceCandidates []ice.Candidate) ([]ICECandidate, error) {
+ candidates := []ICECandidate{}
+
+ for _, i := range iceCandidates {
+ c, err := newICECandidateFromICE(i)
+ if err != nil {
+ return nil, err
+ }
+ candidates = append(candidates, c)
+ }
+
+ return candidates, nil
+}
+
+func newICECandidateFromICE(i ice.Candidate) (ICECandidate, error) {
+ typ, err := convertTypeFromICE(i.Type())
+ if err != nil {
+ return ICECandidate{}, err
+ }
+ protocol, err := NewICEProtocol(i.NetworkType().NetworkShort())
+ if err != nil {
+ return ICECandidate{}, err
+ }
+
+ c := ICECandidate{
+ statsID: i.ID(),
+ Foundation: i.Foundation(),
+ Priority: i.Priority(),
+ Address: i.Address(),
+ Protocol: protocol,
+ Port: uint16(i.Port()),
+ Component: i.Component(),
+ Typ: typ,
+ TCPType: i.TCPType().String(),
+ }
+
+ if i.RelatedAddress() != nil {
+ c.RelatedAddress = i.RelatedAddress().Address
+ c.RelatedPort = uint16(i.RelatedAddress().Port)
+ }
+
+ return c, nil
+}
+
+func (c ICECandidate) toICE() (ice.Candidate, error) {
+ candidateID := c.statsID
+ switch c.Typ {
+ case ICECandidateTypeHost:
+ config := ice.CandidateHostConfig{
+ CandidateID: candidateID,
+ Network: c.Protocol.String(),
+ Address: c.Address,
+ Port: int(c.Port),
+ Component: c.Component,
+ TCPType: ice.NewTCPType(c.TCPType),
+ Foundation: c.Foundation,
+ Priority: c.Priority,
+ }
+ return ice.NewCandidateHost(&config)
+ case ICECandidateTypeSrflx:
+ config := ice.CandidateServerReflexiveConfig{
+ CandidateID: candidateID,
+ Network: c.Protocol.String(),
+ Address: c.Address,
+ Port: int(c.Port),
+ Component: c.Component,
+ Foundation: c.Foundation,
+ Priority: c.Priority,
+ RelAddr: c.RelatedAddress,
+ RelPort: int(c.RelatedPort),
+ }
+ return ice.NewCandidateServerReflexive(&config)
+ case ICECandidateTypePrflx:
+ config := ice.CandidatePeerReflexiveConfig{
+ CandidateID: candidateID,
+ Network: c.Protocol.String(),
+ Address: c.Address,
+ Port: int(c.Port),
+ Component: c.Component,
+ Foundation: c.Foundation,
+ Priority: c.Priority,
+ RelAddr: c.RelatedAddress,
+ RelPort: int(c.RelatedPort),
+ }
+ return ice.NewCandidatePeerReflexive(&config)
+ case ICECandidateTypeRelay:
+ config := ice.CandidateRelayConfig{
+ CandidateID: candidateID,
+ Network: c.Protocol.String(),
+ Address: c.Address,
+ Port: int(c.Port),
+ Component: c.Component,
+ Foundation: c.Foundation,
+ Priority: c.Priority,
+ RelAddr: c.RelatedAddress,
+ RelPort: int(c.RelatedPort),
+ }
+ return ice.NewCandidateRelay(&config)
+ default:
+ return nil, fmt.Errorf("%w: %s", errICECandidateTypeUnknown, c.Typ)
+ }
+}
+
+func convertTypeFromICE(t ice.CandidateType) (ICECandidateType, error) {
+ switch t {
+ case ice.CandidateTypeHost:
+ return ICECandidateTypeHost, nil
+ case ice.CandidateTypeServerReflexive:
+ return ICECandidateTypeSrflx, nil
+ case ice.CandidateTypePeerReflexive:
+ return ICECandidateTypePrflx, nil
+ case ice.CandidateTypeRelay:
+ return ICECandidateTypeRelay, nil
+ default:
+ return ICECandidateType(t), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, t)
+ }
+}
+
+func (c ICECandidate) String() string {
+ ic, err := c.toICE()
+ if err != nil {
+ return fmt.Sprintf("%#v failed to convert to ICE: %s", c, err)
+ }
+ return ic.String()
+}
+
+// ToJSON returns an ICECandidateInit
+// as indicated by the spec https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-tojson
+func (c ICECandidate) ToJSON() ICECandidateInit {
+ zeroVal := uint16(0)
+ emptyStr := ""
+ candidateStr := ""
+
+ candidate, err := c.toICE()
+ if err == nil {
+ candidateStr = candidate.Marshal()
+ }
+
+ return ICECandidateInit{
+ Candidate: fmt.Sprintf("candidate:%s", candidateStr),
+ SDPMid: &emptyStr,
+ SDPMLineIndex: &zeroVal,
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icecandidateinit.go b/vendor/github.com/pion/webrtc/v3/icecandidateinit.go
new file mode 100644
index 0000000..31ebb4b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icecandidateinit.go
@@ -0,0 +1,9 @@
+package webrtc
+
+// ICECandidateInit is used to serialize ice candidates
+type ICECandidateInit struct {
+ Candidate string `json:"candidate"`
+ SDPMid *string `json:"sdpMid"`
+ SDPMLineIndex *uint16 `json:"sdpMLineIndex"`
+ UsernameFragment *string `json:"usernameFragment"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icecandidatepair.go b/vendor/github.com/pion/webrtc/v3/icecandidatepair.go
new file mode 100644
index 0000000..7350fbe
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icecandidatepair.go
@@ -0,0 +1,29 @@
+package webrtc
+
+import "fmt"
+
+// ICECandidatePair represents an ICE Candidate pair
+type ICECandidatePair struct {
+ statsID string
+ Local *ICECandidate
+ Remote *ICECandidate
+}
+
+func newICECandidatePairStatsID(localID, remoteID string) string {
+ return fmt.Sprintf("%s-%s", localID, remoteID)
+}
+
+func (p *ICECandidatePair) String() string {
+ return fmt.Sprintf("(local) %s <-> (remote) %s", p.Local, p.Remote)
+}
+
+// NewICECandidatePair returns an initialized *ICECandidatePair
+// for the given pair of ICECandidate instances
+func NewICECandidatePair(local, remote *ICECandidate) *ICECandidatePair {
+ statsID := newICECandidatePairStatsID(local.statsID, remote.statsID)
+ return &ICECandidatePair{
+ statsID: statsID,
+ Local: local,
+ Remote: remote,
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icecandidatetype.go b/vendor/github.com/pion/webrtc/v3/icecandidatetype.go
new file mode 100644
index 0000000..e57bf14
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icecandidatetype.go
@@ -0,0 +1,94 @@
+package webrtc
+
+import (
+ "fmt"
+
+ "github.com/pion/ice/v2"
+)
+
+// ICECandidateType represents the type of the ICE candidate used.
+type ICECandidateType int
+
+const (
+ // ICECandidateTypeHost indicates that the candidate is of Host type as
+ // described in https://tools.ietf.org/html/rfc8445#section-5.1.1.1. A
+ // candidate obtained by binding to a specific port from an IP address on
+ // the host. This includes IP addresses on physical interfaces and logical
+ // ones, such as ones obtained through VPNs.
+ ICECandidateTypeHost ICECandidateType = iota + 1
+
+ // ICECandidateTypeSrflx indicates the the candidate is of Server
+ // Reflexive type as described
+ // https://tools.ietf.org/html/rfc8445#section-5.1.1.2. A candidate type
+ // whose IP address and port are a binding allocated by a NAT for an ICE
+ // agent after it sends a packet through the NAT to a server, such as a
+ // STUN server.
+ ICECandidateTypeSrflx
+
+ // ICECandidateTypePrflx indicates that the candidate is of Peer
+ // Reflexive type. A candidate type whose IP address and port are a binding
+ // allocated by a NAT for an ICE agent after it sends a packet through the
+ // NAT to its peer.
+ ICECandidateTypePrflx
+
+ // ICECandidateTypeRelay indicates the the candidate is of Relay type as
+ // described in https://tools.ietf.org/html/rfc8445#section-5.1.1.2. A
+ // candidate type obtained from a relay server, such as a TURN server.
+ ICECandidateTypeRelay
+)
+
+// This is done this way because of a linter.
+const (
+ iceCandidateTypeHostStr = "host"
+ iceCandidateTypeSrflxStr = "srflx"
+ iceCandidateTypePrflxStr = "prflx"
+ iceCandidateTypeRelayStr = "relay"
+)
+
+// NewICECandidateType takes a string and converts it into ICECandidateType
+func NewICECandidateType(raw string) (ICECandidateType, error) {
+ switch raw {
+ case iceCandidateTypeHostStr:
+ return ICECandidateTypeHost, nil
+ case iceCandidateTypeSrflxStr:
+ return ICECandidateTypeSrflx, nil
+ case iceCandidateTypePrflxStr:
+ return ICECandidateTypePrflx, nil
+ case iceCandidateTypeRelayStr:
+ return ICECandidateTypeRelay, nil
+ default:
+ return ICECandidateType(Unknown), fmt.Errorf("%w: %s", errICECandidateTypeUnknown, raw)
+ }
+}
+
+func (t ICECandidateType) String() string {
+ switch t {
+ case ICECandidateTypeHost:
+ return iceCandidateTypeHostStr
+ case ICECandidateTypeSrflx:
+ return iceCandidateTypeSrflxStr
+ case ICECandidateTypePrflx:
+ return iceCandidateTypePrflxStr
+ case ICECandidateTypeRelay:
+ return iceCandidateTypeRelayStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+func getCandidateType(candidateType ice.CandidateType) (ICECandidateType, error) {
+ switch candidateType {
+ case ice.CandidateTypeHost:
+ return ICECandidateTypeHost, nil
+ case ice.CandidateTypeServerReflexive:
+ return ICECandidateTypeSrflx, nil
+ case ice.CandidateTypePeerReflexive:
+ return ICECandidateTypePrflx, nil
+ case ice.CandidateTypeRelay:
+ return ICECandidateTypeRelay, nil
+ default:
+ // NOTE: this should never happen[tm]
+ err := fmt.Errorf("%w: %s", errICEInvalidConvertCandidateType, candidateType.String())
+ return ICECandidateType(Unknown), err
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icecomponent.go b/vendor/github.com/pion/webrtc/v3/icecomponent.go
new file mode 100644
index 0000000..1f03ec5
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icecomponent.go
@@ -0,0 +1,47 @@
+package webrtc
+
+// ICEComponent describes if the ice transport is used for RTP
+// (or RTCP multiplexing).
+type ICEComponent int
+
+const (
+ // ICEComponentRTP indicates that the ICE Transport is used for RTP (or
+ // RTCP multiplexing), as defined in
+ // https://tools.ietf.org/html/rfc5245#section-4.1.1.1. Protocols
+ // multiplexed with RTP (e.g. data channel) share its component ID. This
+ // represents the component-id value 1 when encoded in candidate-attribute.
+ ICEComponentRTP ICEComponent = iota + 1
+
+ // ICEComponentRTCP indicates that the ICE Transport is used for RTCP as
+ // defined by https://tools.ietf.org/html/rfc5245#section-4.1.1.1. This
+ // represents the component-id value 2 when encoded in candidate-attribute.
+ ICEComponentRTCP
+)
+
+// This is done this way because of a linter.
+const (
+ iceComponentRTPStr = "rtp"
+ iceComponentRTCPStr = "rtcp"
+)
+
+func newICEComponent(raw string) ICEComponent {
+ switch raw {
+ case iceComponentRTPStr:
+ return ICEComponentRTP
+ case iceComponentRTCPStr:
+ return ICEComponentRTCP
+ default:
+ return ICEComponent(Unknown)
+ }
+}
+
+func (t ICEComponent) String() string {
+ switch t {
+ case ICEComponentRTP:
+ return iceComponentRTPStr
+ case ICEComponentRTCP:
+ return iceComponentRTCPStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go b/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go
new file mode 100644
index 0000000..22fd269
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go
@@ -0,0 +1,94 @@
+package webrtc
+
+// ICEConnectionState indicates signaling state of the ICE Connection.
+type ICEConnectionState int
+
+const (
+ // ICEConnectionStateNew indicates that any of the ICETransports are
+ // in the "new" state and none of them are in the "checking", "disconnected"
+ // or "failed" state, or all ICETransports are in the "closed" state, or
+ // there are no transports.
+ ICEConnectionStateNew ICEConnectionState = iota + 1
+
+ // ICEConnectionStateChecking indicates that any of the ICETransports
+ // are in the "checking" state and none of them are in the "disconnected"
+ // or "failed" state.
+ ICEConnectionStateChecking
+
+ // ICEConnectionStateConnected indicates that all ICETransports are
+ // in the "connected", "completed" or "closed" state and at least one of
+ // them is in the "connected" state.
+ ICEConnectionStateConnected
+
+ // ICEConnectionStateCompleted indicates that all ICETransports are
+ // in the "completed" or "closed" state and at least one of them is in the
+ // "completed" state.
+ ICEConnectionStateCompleted
+
+ // ICEConnectionStateDisconnected indicates that any of the
+ // ICETransports are in the "disconnected" state and none of them are
+ // in the "failed" state.
+ ICEConnectionStateDisconnected
+
+ // ICEConnectionStateFailed indicates that any of the ICETransports
+ // are in the "failed" state.
+ ICEConnectionStateFailed
+
+ // ICEConnectionStateClosed indicates that the PeerConnection's
+ // isClosed is true.
+ ICEConnectionStateClosed
+)
+
+// This is done this way because of a linter.
+const (
+ iceConnectionStateNewStr = "new"
+ iceConnectionStateCheckingStr = "checking"
+ iceConnectionStateConnectedStr = "connected"
+ iceConnectionStateCompletedStr = "completed"
+ iceConnectionStateDisconnectedStr = "disconnected"
+ iceConnectionStateFailedStr = "failed"
+ iceConnectionStateClosedStr = "closed"
+)
+
+// NewICEConnectionState takes a string and converts it to ICEConnectionState
+func NewICEConnectionState(raw string) ICEConnectionState {
+ switch raw {
+ case iceConnectionStateNewStr:
+ return ICEConnectionStateNew
+ case iceConnectionStateCheckingStr:
+ return ICEConnectionStateChecking
+ case iceConnectionStateConnectedStr:
+ return ICEConnectionStateConnected
+ case iceConnectionStateCompletedStr:
+ return ICEConnectionStateCompleted
+ case iceConnectionStateDisconnectedStr:
+ return ICEConnectionStateDisconnected
+ case iceConnectionStateFailedStr:
+ return ICEConnectionStateFailed
+ case iceConnectionStateClosedStr:
+ return ICEConnectionStateClosed
+ default:
+ return ICEConnectionState(Unknown)
+ }
+}
+
+func (c ICEConnectionState) String() string {
+ switch c {
+ case ICEConnectionStateNew:
+ return iceConnectionStateNewStr
+ case ICEConnectionStateChecking:
+ return iceConnectionStateCheckingStr
+ case ICEConnectionStateConnected:
+ return iceConnectionStateConnectedStr
+ case ICEConnectionStateCompleted:
+ return iceConnectionStateCompletedStr
+ case ICEConnectionStateDisconnected:
+ return iceConnectionStateDisconnectedStr
+ case ICEConnectionStateFailed:
+ return iceConnectionStateFailedStr
+ case ICEConnectionStateClosed:
+ return iceConnectionStateClosedStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icecredentialtype.go b/vendor/github.com/pion/webrtc/v3/icecredentialtype.go
new file mode 100644
index 0000000..3967c16
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icecredentialtype.go
@@ -0,0 +1,43 @@
+package webrtc
+
+// ICECredentialType indicates the type of credentials used to connect to
+// an ICE server.
+type ICECredentialType int
+
+const (
+ // ICECredentialTypePassword describes username and password based
+ // credentials as described in https://tools.ietf.org/html/rfc5389.
+ ICECredentialTypePassword ICECredentialType = iota
+
+ // ICECredentialTypeOauth describes token based credential as described
+ // in https://tools.ietf.org/html/rfc7635.
+ ICECredentialTypeOauth
+)
+
+// This is done this way because of a linter.
+const (
+ iceCredentialTypePasswordStr = "password"
+ iceCredentialTypeOauthStr = "oauth"
+)
+
+func newICECredentialType(raw string) ICECredentialType {
+ switch raw {
+ case iceCredentialTypePasswordStr:
+ return ICECredentialTypePassword
+ case iceCredentialTypeOauthStr:
+ return ICECredentialTypeOauth
+ default:
+ return ICECredentialType(Unknown)
+ }
+}
+
+func (t ICECredentialType) String() string {
+ switch t {
+ case ICECredentialTypePassword:
+ return iceCredentialTypePasswordStr
+ case ICECredentialTypeOauth:
+ return iceCredentialTypeOauthStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icegatherer.go b/vendor/github.com/pion/webrtc/v3/icegatherer.go
new file mode 100644
index 0000000..31c64ee
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icegatherer.go
@@ -0,0 +1,367 @@
+// +build !js
+
+package webrtc
+
+import (
+ "sync"
+ "sync/atomic"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/logging"
+)
+
+// ICEGatherer gathers local host, server reflexive and relay
+// candidates, as well as enabling the retrieval of local Interactive
+// Connectivity Establishment (ICE) parameters which can be
+// exchanged in signaling.
+type ICEGatherer struct {
+ lock sync.RWMutex
+ log logging.LeveledLogger
+ state ICEGathererState
+
+ validatedServers []*ice.URL
+ gatherPolicy ICETransportPolicy
+
+ agent *ice.Agent
+
+ onLocalCandidateHandler atomic.Value // func(candidate *ICECandidate)
+ onStateChangeHandler atomic.Value // func(state ICEGathererState)
+
+ // Used for GatheringCompletePromise
+ onGatheringCompleteHandler atomic.Value // func()
+
+ api *API
+}
+
+// NewICEGatherer creates a new NewICEGatherer.
+// This constructor is part of the ORTC API. It is not
+// meant to be used together with the basic WebRTC API.
+func (api *API) NewICEGatherer(opts ICEGatherOptions) (*ICEGatherer, error) {
+ var validatedServers []*ice.URL
+ if len(opts.ICEServers) > 0 {
+ for _, server := range opts.ICEServers {
+ url, err := server.urls()
+ if err != nil {
+ return nil, err
+ }
+ validatedServers = append(validatedServers, url...)
+ }
+ }
+
+ return &ICEGatherer{
+ state: ICEGathererStateNew,
+ gatherPolicy: opts.ICEGatherPolicy,
+ validatedServers: validatedServers,
+ api: api,
+ log: api.settingEngine.LoggerFactory.NewLogger("ice"),
+ }, nil
+}
+
+func (g *ICEGatherer) createAgent() error {
+ g.lock.Lock()
+ defer g.lock.Unlock()
+
+ if g.agent != nil || g.State() != ICEGathererStateNew {
+ return nil
+ }
+
+ candidateTypes := []ice.CandidateType{}
+ if g.api.settingEngine.candidates.ICELite {
+ candidateTypes = append(candidateTypes, ice.CandidateTypeHost)
+ } else if g.gatherPolicy == ICETransportPolicyRelay {
+ candidateTypes = append(candidateTypes, ice.CandidateTypeRelay)
+ }
+
+ var nat1To1CandiTyp ice.CandidateType
+ switch g.api.settingEngine.candidates.NAT1To1IPCandidateType {
+ case ICECandidateTypeHost:
+ nat1To1CandiTyp = ice.CandidateTypeHost
+ case ICECandidateTypeSrflx:
+ nat1To1CandiTyp = ice.CandidateTypeServerReflexive
+ default:
+ nat1To1CandiTyp = ice.CandidateTypeUnspecified
+ }
+
+ mDNSMode := g.api.settingEngine.candidates.MulticastDNSMode
+ if mDNSMode != ice.MulticastDNSModeDisabled && mDNSMode != ice.MulticastDNSModeQueryAndGather {
+ // If enum is in state we don't recognized default to MulticastDNSModeQueryOnly
+ mDNSMode = ice.MulticastDNSModeQueryOnly
+ }
+
+ config := &ice.AgentConfig{
+ Lite: g.api.settingEngine.candidates.ICELite,
+ Urls: g.validatedServers,
+ PortMin: g.api.settingEngine.ephemeralUDP.PortMin,
+ PortMax: g.api.settingEngine.ephemeralUDP.PortMax,
+ DisconnectedTimeout: g.api.settingEngine.timeout.ICEDisconnectedTimeout,
+ FailedTimeout: g.api.settingEngine.timeout.ICEFailedTimeout,
+ KeepaliveInterval: g.api.settingEngine.timeout.ICEKeepaliveInterval,
+ LoggerFactory: g.api.settingEngine.LoggerFactory,
+ CandidateTypes: candidateTypes,
+ HostAcceptanceMinWait: g.api.settingEngine.timeout.ICEHostAcceptanceMinWait,
+ SrflxAcceptanceMinWait: g.api.settingEngine.timeout.ICESrflxAcceptanceMinWait,
+ PrflxAcceptanceMinWait: g.api.settingEngine.timeout.ICEPrflxAcceptanceMinWait,
+ RelayAcceptanceMinWait: g.api.settingEngine.timeout.ICERelayAcceptanceMinWait,
+ InterfaceFilter: g.api.settingEngine.candidates.InterfaceFilter,
+ NAT1To1IPs: g.api.settingEngine.candidates.NAT1To1IPs,
+ NAT1To1IPCandidateType: nat1To1CandiTyp,
+ Net: g.api.settingEngine.vnet,
+ MulticastDNSMode: mDNSMode,
+ MulticastDNSHostName: g.api.settingEngine.candidates.MulticastDNSHostName,
+ LocalUfrag: g.api.settingEngine.candidates.UsernameFragment,
+ LocalPwd: g.api.settingEngine.candidates.Password,
+ TCPMux: g.api.settingEngine.iceTCPMux,
+ ProxyDialer: g.api.settingEngine.iceProxyDialer,
+ }
+
+ requestedNetworkTypes := g.api.settingEngine.candidates.ICENetworkTypes
+ if len(requestedNetworkTypes) == 0 {
+ requestedNetworkTypes = supportedNetworkTypes()
+ }
+
+ for _, typ := range requestedNetworkTypes {
+ config.NetworkTypes = append(config.NetworkTypes, ice.NetworkType(typ))
+ }
+
+ agent, err := ice.NewAgent(config)
+ if err != nil {
+ return err
+ }
+
+ g.agent = agent
+ return nil
+}
+
+// Gather ICE candidates.
+func (g *ICEGatherer) Gather() error {
+ if err := g.createAgent(); err != nil {
+ return err
+ }
+
+ g.lock.Lock()
+ agent := g.agent
+ g.lock.Unlock()
+
+ g.setState(ICEGathererStateGathering)
+ if err := agent.OnCandidate(func(candidate ice.Candidate) {
+ onLocalCandidateHandler := func(*ICECandidate) {}
+ if handler, ok := g.onLocalCandidateHandler.Load().(func(candidate *ICECandidate)); ok && handler != nil {
+ onLocalCandidateHandler = handler
+ }
+
+ onGatheringCompleteHandler := func() {}
+ if handler, ok := g.onGatheringCompleteHandler.Load().(func()); ok && handler != nil {
+ onGatheringCompleteHandler = handler
+ }
+
+ if candidate != nil {
+ c, err := newICECandidateFromICE(candidate)
+ if err != nil {
+ g.log.Warnf("Failed to convert ice.Candidate: %s", err)
+ return
+ }
+ onLocalCandidateHandler(&c)
+ } else {
+ g.setState(ICEGathererStateComplete)
+
+ onGatheringCompleteHandler()
+ onLocalCandidateHandler(nil)
+ }
+ }); err != nil {
+ return err
+ }
+ return agent.GatherCandidates()
+}
+
+// Close prunes all local candidates, and closes the ports.
+func (g *ICEGatherer) Close() error {
+ g.lock.Lock()
+ defer g.lock.Unlock()
+
+ if g.agent == nil {
+ return nil
+ } else if err := g.agent.Close(); err != nil {
+ return err
+ }
+
+ g.agent = nil
+ g.setState(ICEGathererStateClosed)
+
+ return nil
+}
+
+// GetLocalParameters returns the ICE parameters of the ICEGatherer.
+func (g *ICEGatherer) GetLocalParameters() (ICEParameters, error) {
+ if err := g.createAgent(); err != nil {
+ return ICEParameters{}, err
+ }
+
+ frag, pwd, err := g.agent.GetLocalUserCredentials()
+ if err != nil {
+ return ICEParameters{}, err
+ }
+
+ return ICEParameters{
+ UsernameFragment: frag,
+ Password: pwd,
+ ICELite: false,
+ }, nil
+}
+
+// GetLocalCandidates returns the sequence of valid local candidates associated with the ICEGatherer.
+func (g *ICEGatherer) GetLocalCandidates() ([]ICECandidate, error) {
+ if err := g.createAgent(); err != nil {
+ return nil, err
+ }
+ iceCandidates, err := g.agent.GetLocalCandidates()
+ if err != nil {
+ return nil, err
+ }
+
+ return newICECandidatesFromICE(iceCandidates)
+}
+
+// OnLocalCandidate sets an event handler which fires when a new local ICE candidate is available
+// Take note that the handler is gonna be called with a nil pointer when gathering is finished.
+func (g *ICEGatherer) OnLocalCandidate(f func(*ICECandidate)) {
+ g.onLocalCandidateHandler.Store(f)
+}
+
+// OnStateChange fires any time the ICEGatherer changes
+func (g *ICEGatherer) OnStateChange(f func(ICEGathererState)) {
+ g.onStateChangeHandler.Store(f)
+}
+
+// State indicates the current state of the ICE gatherer.
+func (g *ICEGatherer) State() ICEGathererState {
+ return atomicLoadICEGathererState(&g.state)
+}
+
+func (g *ICEGatherer) setState(s ICEGathererState) {
+ atomicStoreICEGathererState(&g.state, s)
+
+ if handler, ok := g.onStateChangeHandler.Load().(func(state ICEGathererState)); ok && handler != nil {
+ handler(s)
+ }
+}
+
+func (g *ICEGatherer) getAgent() *ice.Agent {
+ g.lock.RLock()
+ defer g.lock.RUnlock()
+ return g.agent
+}
+
+func (g *ICEGatherer) collectStats(collector *statsReportCollector) {
+ agent := g.getAgent()
+ if agent == nil {
+ return
+ }
+
+ collector.Collecting()
+ go func(collector *statsReportCollector, agent *ice.Agent) {
+ for _, candidatePairStats := range agent.GetCandidatePairsStats() {
+ collector.Collecting()
+
+ state, err := toStatsICECandidatePairState(candidatePairStats.State)
+ if err != nil {
+ g.log.Error(err.Error())
+ }
+
+ pairID := newICECandidatePairStatsID(candidatePairStats.LocalCandidateID,
+ candidatePairStats.RemoteCandidateID)
+
+ stats := ICECandidatePairStats{
+ Timestamp: statsTimestampFrom(candidatePairStats.Timestamp),
+ Type: StatsTypeCandidatePair,
+ ID: pairID,
+ // TransportID:
+ LocalCandidateID: candidatePairStats.LocalCandidateID,
+ RemoteCandidateID: candidatePairStats.RemoteCandidateID,
+ State: state,
+ Nominated: candidatePairStats.Nominated,
+ PacketsSent: candidatePairStats.PacketsSent,
+ PacketsReceived: candidatePairStats.PacketsReceived,
+ BytesSent: candidatePairStats.BytesSent,
+ BytesReceived: candidatePairStats.BytesReceived,
+ LastPacketSentTimestamp: statsTimestampFrom(candidatePairStats.LastPacketSentTimestamp),
+ LastPacketReceivedTimestamp: statsTimestampFrom(candidatePairStats.LastPacketReceivedTimestamp),
+ FirstRequestTimestamp: statsTimestampFrom(candidatePairStats.FirstRequestTimestamp),
+ LastRequestTimestamp: statsTimestampFrom(candidatePairStats.LastRequestTimestamp),
+ LastResponseTimestamp: statsTimestampFrom(candidatePairStats.LastResponseTimestamp),
+ TotalRoundTripTime: candidatePairStats.TotalRoundTripTime,
+ CurrentRoundTripTime: candidatePairStats.CurrentRoundTripTime,
+ AvailableOutgoingBitrate: candidatePairStats.AvailableOutgoingBitrate,
+ AvailableIncomingBitrate: candidatePairStats.AvailableIncomingBitrate,
+ CircuitBreakerTriggerCount: candidatePairStats.CircuitBreakerTriggerCount,
+ RequestsReceived: candidatePairStats.RequestsReceived,
+ RequestsSent: candidatePairStats.RequestsSent,
+ ResponsesReceived: candidatePairStats.ResponsesReceived,
+ ResponsesSent: candidatePairStats.ResponsesSent,
+ RetransmissionsReceived: candidatePairStats.RetransmissionsReceived,
+ RetransmissionsSent: candidatePairStats.RetransmissionsSent,
+ ConsentRequestsSent: candidatePairStats.ConsentRequestsSent,
+ ConsentExpiredTimestamp: statsTimestampFrom(candidatePairStats.ConsentExpiredTimestamp),
+ }
+ collector.Collect(stats.ID, stats)
+ }
+
+ for _, candidateStats := range agent.GetLocalCandidatesStats() {
+ collector.Collecting()
+
+ networkType, err := getNetworkType(candidateStats.NetworkType)
+ if err != nil {
+ g.log.Error(err.Error())
+ }
+
+ candidateType, err := getCandidateType(candidateStats.CandidateType)
+ if err != nil {
+ g.log.Error(err.Error())
+ }
+
+ stats := ICECandidateStats{
+ Timestamp: statsTimestampFrom(candidateStats.Timestamp),
+ ID: candidateStats.ID,
+ Type: StatsTypeLocalCandidate,
+ NetworkType: networkType,
+ IP: candidateStats.IP,
+ Port: int32(candidateStats.Port),
+ Protocol: networkType.Protocol(),
+ CandidateType: candidateType,
+ Priority: int32(candidateStats.Priority),
+ URL: candidateStats.URL,
+ RelayProtocol: candidateStats.RelayProtocol,
+ Deleted: candidateStats.Deleted,
+ }
+ collector.Collect(stats.ID, stats)
+ }
+
+ for _, candidateStats := range agent.GetRemoteCandidatesStats() {
+ collector.Collecting()
+ networkType, err := getNetworkType(candidateStats.NetworkType)
+ if err != nil {
+ g.log.Error(err.Error())
+ }
+
+ candidateType, err := getCandidateType(candidateStats.CandidateType)
+ if err != nil {
+ g.log.Error(err.Error())
+ }
+
+ stats := ICECandidateStats{
+ Timestamp: statsTimestampFrom(candidateStats.Timestamp),
+ ID: candidateStats.ID,
+ Type: StatsTypeRemoteCandidate,
+ NetworkType: networkType,
+ IP: candidateStats.IP,
+ Port: int32(candidateStats.Port),
+ Protocol: networkType.Protocol(),
+ CandidateType: candidateType,
+ Priority: int32(candidateStats.Priority),
+ URL: candidateStats.URL,
+ RelayProtocol: candidateStats.RelayProtocol,
+ }
+ collector.Collect(stats.ID, stats)
+ }
+ collector.Done()
+ }(collector, agent)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icegathererstate.go b/vendor/github.com/pion/webrtc/v3/icegathererstate.go
new file mode 100644
index 0000000..80dc77a
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icegathererstate.go
@@ -0,0 +1,48 @@
+package webrtc
+
+import (
+ "sync/atomic"
+)
+
+// ICEGathererState represents the current state of the ICE gatherer.
+type ICEGathererState uint32
+
+const (
+ // ICEGathererStateNew indicates object has been created but
+ // gather() has not been called.
+ ICEGathererStateNew ICEGathererState = iota + 1
+
+ // ICEGathererStateGathering indicates gather() has been called,
+ // and the ICEGatherer is in the process of gathering candidates.
+ ICEGathererStateGathering
+
+ // ICEGathererStateComplete indicates the ICEGatherer has completed gathering.
+ ICEGathererStateComplete
+
+ // ICEGathererStateClosed indicates the closed state can only be entered
+ // when the ICEGatherer has been closed intentionally by calling close().
+ ICEGathererStateClosed
+)
+
+func (s ICEGathererState) String() string {
+ switch s {
+ case ICEGathererStateNew:
+ return "new"
+ case ICEGathererStateGathering:
+ return "gathering"
+ case ICEGathererStateComplete:
+ return "complete"
+ case ICEGathererStateClosed:
+ return "closed"
+ default:
+ return unknownStr
+ }
+}
+
+func atomicStoreICEGathererState(state *ICEGathererState, newState ICEGathererState) {
+ atomic.StoreUint32((*uint32)(state), uint32(newState))
+}
+
+func atomicLoadICEGathererState(state *ICEGathererState) ICEGathererState {
+ return ICEGathererState(atomic.LoadUint32((*uint32)(state)))
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icegatheringstate.go b/vendor/github.com/pion/webrtc/v3/icegatheringstate.go
new file mode 100644
index 0000000..21361f9
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icegatheringstate.go
@@ -0,0 +1,53 @@
+package webrtc
+
+// ICEGatheringState describes the state of the candidate gathering process.
+type ICEGatheringState int
+
+const (
+ // ICEGatheringStateNew indicates that any of the ICETransports are
+ // in the "new" gathering state and none of the transports are in the
+ // "gathering" state, or there are no transports.
+ ICEGatheringStateNew ICEGatheringState = iota + 1
+
+ // ICEGatheringStateGathering indicates that any of the ICETransports
+ // are in the "gathering" state.
+ ICEGatheringStateGathering
+
+ // ICEGatheringStateComplete indicates that at least one ICETransport
+ // exists, and all ICETransports are in the "completed" gathering state.
+ ICEGatheringStateComplete
+)
+
+// This is done this way because of a linter.
+const (
+ iceGatheringStateNewStr = "new"
+ iceGatheringStateGatheringStr = "gathering"
+ iceGatheringStateCompleteStr = "complete"
+)
+
+// NewICEGatheringState takes a string and converts it to ICEGatheringState
+func NewICEGatheringState(raw string) ICEGatheringState {
+ switch raw {
+ case iceGatheringStateNewStr:
+ return ICEGatheringStateNew
+ case iceGatheringStateGatheringStr:
+ return ICEGatheringStateGathering
+ case iceGatheringStateCompleteStr:
+ return ICEGatheringStateComplete
+ default:
+ return ICEGatheringState(Unknown)
+ }
+}
+
+func (t ICEGatheringState) String() string {
+ switch t {
+ case ICEGatheringStateNew:
+ return iceGatheringStateNewStr
+ case ICEGatheringStateGathering:
+ return iceGatheringStateGatheringStr
+ case ICEGatheringStateComplete:
+ return iceGatheringStateCompleteStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icegatheroptions.go b/vendor/github.com/pion/webrtc/v3/icegatheroptions.go
new file mode 100644
index 0000000..88421c7
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icegatheroptions.go
@@ -0,0 +1,7 @@
+package webrtc
+
+// ICEGatherOptions provides options relating to the gathering of ICE candidates.
+type ICEGatherOptions struct {
+ ICEServers []ICEServer
+ ICEGatherPolicy ICETransportPolicy
+}
diff --git a/vendor/github.com/pion/webrtc/v3/iceparameters.go b/vendor/github.com/pion/webrtc/v3/iceparameters.go
new file mode 100644
index 0000000..0c03a88
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/iceparameters.go
@@ -0,0 +1,9 @@
+package webrtc
+
+// ICEParameters includes the ICE username fragment
+// and password and other ICE-related parameters.
+type ICEParameters struct {
+ UsernameFragment string `json:"usernameFragment"`
+ Password string `json:"password"`
+ ICELite bool `json:"iceLite"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/iceprotocol.go b/vendor/github.com/pion/webrtc/v3/iceprotocol.go
new file mode 100644
index 0000000..f9eb0cf
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/iceprotocol.go
@@ -0,0 +1,47 @@
+package webrtc
+
+import (
+ "fmt"
+ "strings"
+)
+
+// ICEProtocol indicates the transport protocol type that is used in the
+// ice.URL structure.
+type ICEProtocol int
+
+const (
+ // ICEProtocolUDP indicates the URL uses a UDP transport.
+ ICEProtocolUDP ICEProtocol = iota + 1
+
+ // ICEProtocolTCP indicates the URL uses a TCP transport.
+ ICEProtocolTCP
+)
+
+// This is done this way because of a linter.
+const (
+ iceProtocolUDPStr = "udp"
+ iceProtocolTCPStr = "tcp"
+)
+
+// NewICEProtocol takes a string and converts it to ICEProtocol
+func NewICEProtocol(raw string) (ICEProtocol, error) {
+ switch {
+ case strings.EqualFold(iceProtocolUDPStr, raw):
+ return ICEProtocolUDP, nil
+ case strings.EqualFold(iceProtocolTCPStr, raw):
+ return ICEProtocolTCP, nil
+ default:
+ return ICEProtocol(Unknown), fmt.Errorf("%w: %s", errICEProtocolUnknown, raw)
+ }
+}
+
+func (t ICEProtocol) String() string {
+ switch t {
+ case ICEProtocolUDP:
+ return iceProtocolUDPStr
+ case ICEProtocolTCP:
+ return iceProtocolTCPStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icerole.go b/vendor/github.com/pion/webrtc/v3/icerole.go
new file mode 100644
index 0000000..1118786
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icerole.go
@@ -0,0 +1,45 @@
+package webrtc
+
+// ICERole describes the role ice.Agent is playing in selecting the
+// preferred the candidate pair.
+type ICERole int
+
+const (
+ // ICERoleControlling indicates that the ICE agent that is responsible
+ // for selecting the final choice of candidate pairs and signaling them
+ // through STUN and an updated offer, if needed. In any session, one agent
+ // is always controlling. The other is the controlled agent.
+ ICERoleControlling ICERole = iota + 1
+
+ // ICERoleControlled indicates that an ICE agent that waits for the
+ // controlling agent to select the final choice of candidate pairs.
+ ICERoleControlled
+)
+
+// This is done this way because of a linter.
+const (
+ iceRoleControllingStr = "controlling"
+ iceRoleControlledStr = "controlled"
+)
+
+func newICERole(raw string) ICERole {
+ switch raw {
+ case iceRoleControllingStr:
+ return ICERoleControlling
+ case iceRoleControlledStr:
+ return ICERoleControlled
+ default:
+ return ICERole(Unknown)
+ }
+}
+
+func (t ICERole) String() string {
+ switch t {
+ case ICERoleControlling:
+ return iceRoleControllingStr
+ case ICERoleControlled:
+ return iceRoleControlledStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/iceserver.go b/vendor/github.com/pion/webrtc/v3/iceserver.go
new file mode 100644
index 0000000..76146ec
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/iceserver.go
@@ -0,0 +1,68 @@
+// +build !js
+
+package webrtc
+
+import (
+ "github.com/pion/ice/v2"
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+// ICEServer describes a single STUN and TURN server that can be used by
+// the ICEAgent to establish a connection with a peer.
+type ICEServer struct {
+ URLs []string `json:"urls"`
+ Username string `json:"username,omitempty"`
+ Credential interface{} `json:"credential,omitempty"`
+ CredentialType ICECredentialType `json:"credentialType,omitempty"`
+}
+
+func (s ICEServer) parseURL(i int) (*ice.URL, error) {
+ return ice.ParseURL(s.URLs[i])
+}
+
+func (s ICEServer) validate() error {
+ _, err := s.urls()
+ return err
+}
+
+func (s ICEServer) urls() ([]*ice.URL, error) {
+ urls := []*ice.URL{}
+
+ for i := range s.URLs {
+ url, err := s.parseURL(i)
+ if err != nil {
+ return nil, &rtcerr.InvalidAccessError{Err: err}
+ }
+
+ if url.Scheme == ice.SchemeTypeTURN || url.Scheme == ice.SchemeTypeTURNS {
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.2)
+ if s.Username == "" || s.Credential == nil {
+ return nil, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials}
+ }
+ url.Username = s.Username
+
+ switch s.CredentialType {
+ case ICECredentialTypePassword:
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.3)
+ password, ok := s.Credential.(string)
+ if !ok {
+ return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
+ }
+ url.Password = password
+
+ case ICECredentialTypeOauth:
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.4)
+ if _, ok := s.Credential.(OAuthCredential); !ok {
+ return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
+ }
+
+ default:
+ return nil, &rtcerr.InvalidAccessError{Err: ErrTurnCredentials}
+ }
+ }
+
+ urls = append(urls, url)
+ }
+
+ return urls, nil
+}
diff --git a/vendor/github.com/pion/webrtc/v3/iceserver_js.go b/vendor/github.com/pion/webrtc/v3/iceserver_js.go
new file mode 100644
index 0000000..07b2dcb
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/iceserver_js.go
@@ -0,0 +1,42 @@
+// +build js,wasm
+
+package webrtc
+
+import (
+ "errors"
+
+ "github.com/pion/ice/v2"
+)
+
+// ICEServer describes a single STUN and TURN server that can be used by
+// the ICEAgent to establish a connection with a peer.
+type ICEServer struct {
+ URLs []string
+ Username string
+ // Note: TURN is not supported in the WASM bindings yet
+ Credential interface{}
+ CredentialType ICECredentialType
+}
+
+func (s ICEServer) parseURL(i int) (*ice.URL, error) {
+ return ice.ParseURL(s.URLs[i])
+}
+
+func (s ICEServer) validate() ([]*ice.URL, error) {
+ urls := []*ice.URL{}
+
+ for i := range s.URLs {
+ url, err := s.parseURL(i)
+ if err != nil {
+ return nil, err
+ }
+
+ if url.Scheme == ice.SchemeTypeTURN || url.Scheme == ice.SchemeTypeTURNS {
+ return nil, errors.New("TURN is not currently supported in the JavaScript/Wasm bindings")
+ }
+
+ urls = append(urls, url)
+ }
+
+ return urls, nil
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icetcp.go b/vendor/github.com/pion/webrtc/v3/icetcp.go
new file mode 100644
index 0000000..a5f1d09
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icetcp.go
@@ -0,0 +1,18 @@
+package webrtc
+
+import (
+ "net"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/logging"
+)
+
+// NewICETCPMux creates a new instance of ice.TCPMuxDefault. It enables use of
+// passive ICE TCP candidates.
+func NewICETCPMux(logger logging.LeveledLogger, listener net.Listener, readBufferSize int) ice.TCPMux {
+ return ice.NewTCPMuxDefault(ice.TCPMuxParams{
+ Listener: listener,
+ Logger: logger,
+ ReadBufferSize: readBufferSize,
+ })
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icetransport.go b/vendor/github.com/pion/webrtc/v3/icetransport.go
new file mode 100644
index 0000000..ba20ec1
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icetransport.go
@@ -0,0 +1,368 @@
+// +build !js
+
+package webrtc
+
+import (
+ "context"
+ "fmt"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/logging"
+ "github.com/pion/webrtc/v3/internal/mux"
+)
+
+// ICETransport allows an application access to information about the ICE
+// transport over which packets are sent and received.
+type ICETransport struct {
+ lock sync.RWMutex
+
+ role ICERole
+ // Component ICEComponent
+ // State ICETransportState
+ // gatheringState ICEGathererState
+
+ onConnectionStateChangeHandler atomic.Value // func(ICETransportState)
+ onSelectedCandidatePairChangeHandler atomic.Value // func(*ICECandidatePair)
+
+ state atomic.Value // ICETransportState
+
+ gatherer *ICEGatherer
+ conn *ice.Conn
+ mux *mux.Mux
+
+ ctx context.Context
+ ctxCancel func()
+
+ loggerFactory logging.LoggerFactory
+
+ log logging.LeveledLogger
+}
+
+// func (t *ICETransport) GetLocalCandidates() []ICECandidate {
+//
+// }
+//
+// func (t *ICETransport) GetRemoteCandidates() []ICECandidate {
+//
+// }
+//
+// func (t *ICETransport) GetSelectedCandidatePair() ICECandidatePair {
+//
+// }
+//
+// func (t *ICETransport) GetLocalParameters() ICEParameters {
+//
+// }
+//
+// func (t *ICETransport) GetRemoteParameters() ICEParameters {
+//
+// }
+
+// NewICETransport creates a new NewICETransport.
+func NewICETransport(gatherer *ICEGatherer, loggerFactory logging.LoggerFactory) *ICETransport {
+ iceTransport := &ICETransport{
+ gatherer: gatherer,
+ loggerFactory: loggerFactory,
+ log: loggerFactory.NewLogger("ortc"),
+ }
+ iceTransport.setState(ICETransportStateNew)
+ return iceTransport
+}
+
+// Start incoming connectivity checks based on its configured role.
+func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *ICERole) error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if t.State() != ICETransportStateNew {
+ return errICETransportNotInNew
+ }
+
+ if gatherer != nil {
+ t.gatherer = gatherer
+ }
+
+ if err := t.ensureGatherer(); err != nil {
+ return err
+ }
+
+ agent := t.gatherer.getAgent()
+ if agent == nil {
+ return fmt.Errorf("%w: unable to start ICETransport", errICEAgentNotExist)
+ }
+
+ if err := agent.OnConnectionStateChange(func(iceState ice.ConnectionState) {
+ state := newICETransportStateFromICE(iceState)
+
+ t.setState(state)
+ t.onConnectionStateChange(state)
+ }); err != nil {
+ return err
+ }
+ if err := agent.OnSelectedCandidatePairChange(func(local, remote ice.Candidate) {
+ candidates, err := newICECandidatesFromICE([]ice.Candidate{local, remote})
+ if err != nil {
+ t.log.Warnf("%w: %s", errICECandiatesCoversionFailed, err)
+ return
+ }
+ t.onSelectedCandidatePairChange(NewICECandidatePair(&candidates[0], &candidates[1]))
+ }); err != nil {
+ return err
+ }
+
+ if role == nil {
+ controlled := ICERoleControlled
+ role = &controlled
+ }
+ t.role = *role
+
+ t.ctx, t.ctxCancel = context.WithCancel(context.Background())
+
+ // Drop the lock here to allow ICE candidates to be
+ // added so that the agent can complete a connection
+ t.lock.Unlock()
+
+ var iceConn *ice.Conn
+ var err error
+ switch *role {
+ case ICERoleControlling:
+ iceConn, err = agent.Dial(t.ctx,
+ params.UsernameFragment,
+ params.Password)
+
+ case ICERoleControlled:
+ iceConn, err = agent.Accept(t.ctx,
+ params.UsernameFragment,
+ params.Password)
+
+ default:
+ err = errICERoleUnknown
+ }
+
+ // Reacquire the lock to set the connection/mux
+ t.lock.Lock()
+ if err != nil {
+ return err
+ }
+
+ t.conn = iceConn
+
+ config := mux.Config{
+ Conn: t.conn,
+ BufferSize: receiveMTU,
+ LoggerFactory: t.loggerFactory,
+ }
+ t.mux = mux.NewMux(config)
+
+ return nil
+}
+
+// restart is not exposed currently because ORTC has users create a whole new ICETransport
+// so for now lets keep it private so we don't cause ORTC users to depend on non-standard APIs
+func (t *ICETransport) restart() error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ agent := t.gatherer.getAgent()
+ if agent == nil {
+ return fmt.Errorf("%w: unable to restart ICETransport", errICEAgentNotExist)
+ }
+
+ if err := agent.Restart(t.gatherer.api.settingEngine.candidates.UsernameFragment, t.gatherer.api.settingEngine.candidates.Password); err != nil {
+ return err
+ }
+ return t.gatherer.Gather()
+}
+
+// Stop irreversibly stops the ICETransport.
+func (t *ICETransport) Stop() error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.setState(ICETransportStateClosed)
+
+ if t.ctxCancel != nil {
+ t.ctxCancel()
+ }
+
+ if t.mux != nil {
+ return t.mux.Close()
+ } else if t.gatherer != nil {
+ return t.gatherer.Close()
+ }
+ return nil
+}
+
+// OnSelectedCandidatePairChange sets a handler that is invoked when a new
+// ICE candidate pair is selected
+func (t *ICETransport) OnSelectedCandidatePairChange(f func(*ICECandidatePair)) {
+ t.onSelectedCandidatePairChangeHandler.Store(f)
+}
+
+func (t *ICETransport) onSelectedCandidatePairChange(pair *ICECandidatePair) {
+ handler := t.onSelectedCandidatePairChangeHandler.Load()
+ if handler != nil {
+ handler.(func(*ICECandidatePair))(pair)
+ }
+}
+
+// OnConnectionStateChange sets a handler that is fired when the ICE
+// connection state changes.
+func (t *ICETransport) OnConnectionStateChange(f func(ICETransportState)) {
+ t.onConnectionStateChangeHandler.Store(f)
+}
+
+func (t *ICETransport) onConnectionStateChange(state ICETransportState) {
+ handler := t.onConnectionStateChangeHandler.Load()
+ if handler != nil {
+ handler.(func(ICETransportState))(state)
+ }
+}
+
+// Role indicates the current role of the ICE transport.
+func (t *ICETransport) Role() ICERole {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ return t.role
+}
+
+// SetRemoteCandidates sets the sequence of candidates associated with the remote ICETransport.
+func (t *ICETransport) SetRemoteCandidates(remoteCandidates []ICECandidate) error {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ if err := t.ensureGatherer(); err != nil {
+ return err
+ }
+
+ agent := t.gatherer.getAgent()
+ if agent == nil {
+ return fmt.Errorf("%w: unable to set remote candidates", errICEAgentNotExist)
+ }
+
+ for _, c := range remoteCandidates {
+ i, err := c.toICE()
+ if err != nil {
+ return err
+ }
+
+ if err = agent.AddRemoteCandidate(i); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// AddRemoteCandidate adds a candidate associated with the remote ICETransport.
+func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidate) error {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ var (
+ c ice.Candidate
+ err error
+ )
+
+ if err = t.ensureGatherer(); err != nil {
+ return err
+ }
+
+ if remoteCandidate != nil {
+ if c, err = remoteCandidate.toICE(); err != nil {
+ return err
+ }
+ }
+
+ agent := t.gatherer.getAgent()
+ if agent == nil {
+ return fmt.Errorf("%w: unable to add remote candidates", errICEAgentNotExist)
+ }
+
+ return agent.AddRemoteCandidate(c)
+}
+
+// State returns the current ice transport state.
+func (t *ICETransport) State() ICETransportState {
+ if v := t.state.Load(); v != nil {
+ return v.(ICETransportState)
+ }
+ return ICETransportState(0)
+}
+
+func (t *ICETransport) setState(i ICETransportState) {
+ t.state.Store(i)
+}
+
+// NewEndpoint registers a new endpoint on the underlying mux.
+func (t *ICETransport) NewEndpoint(f mux.MatchFunc) *mux.Endpoint {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+ return t.mux.NewEndpoint(f)
+}
+
+func (t *ICETransport) ensureGatherer() error {
+ if t.gatherer == nil {
+ return errICEGathererNotStarted
+ } else if t.gatherer.getAgent() == nil {
+ if err := t.gatherer.createAgent(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (t *ICETransport) collectStats(collector *statsReportCollector) {
+ t.lock.Lock()
+ conn := t.conn
+ t.lock.Unlock()
+
+ collector.Collecting()
+
+ stats := TransportStats{
+ Timestamp: statsTimestampFrom(time.Now()),
+ Type: StatsTypeTransport,
+ ID: "iceTransport",
+ }
+
+ if conn != nil {
+ stats.BytesSent = conn.BytesSent()
+ stats.BytesReceived = conn.BytesReceived()
+ }
+
+ collector.Collect(stats.ID, stats)
+}
+
+func (t *ICETransport) haveRemoteCredentialsChange(newUfrag, newPwd string) bool {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ agent := t.gatherer.getAgent()
+ if agent == nil {
+ return false
+ }
+
+ uFrag, uPwd, err := agent.GetRemoteUserCredentials()
+ if err != nil {
+ return false
+ }
+
+ return uFrag != newUfrag || uPwd != newPwd
+}
+
+func (t *ICETransport) setRemoteCredentials(newUfrag, newPwd string) error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ agent := t.gatherer.getAgent()
+ if agent == nil {
+ return fmt.Errorf("%w: unable to SetRemoteCredentials", errICEAgentNotExist)
+ }
+
+ return agent.SetRemoteCredentials(newUfrag, newPwd)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go b/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go
new file mode 100644
index 0000000..16273f5
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go
@@ -0,0 +1,65 @@
+package webrtc
+
+import (
+ "encoding/json"
+)
+
+// ICETransportPolicy defines the ICE candidate policy surface the
+// permitted candidates. Only these candidates are used for connectivity checks.
+type ICETransportPolicy int
+
+// ICEGatherPolicy is the ORTC equivalent of ICETransportPolicy
+type ICEGatherPolicy = ICETransportPolicy
+
+const (
+ // ICETransportPolicyAll indicates any type of candidate is used.
+ ICETransportPolicyAll ICETransportPolicy = iota
+
+ // ICETransportPolicyRelay indicates only media relay candidates such
+ // as candidates passing through a TURN server are used.
+ ICETransportPolicyRelay
+)
+
+// This is done this way because of a linter.
+const (
+ iceTransportPolicyRelayStr = "relay"
+ iceTransportPolicyAllStr = "all"
+)
+
+// NewICETransportPolicy takes a string and converts it to ICETransportPolicy
+func NewICETransportPolicy(raw string) ICETransportPolicy {
+ switch raw {
+ case iceTransportPolicyRelayStr:
+ return ICETransportPolicyRelay
+ case iceTransportPolicyAllStr:
+ return ICETransportPolicyAll
+ default:
+ return ICETransportPolicy(Unknown)
+ }
+}
+
+func (t ICETransportPolicy) String() string {
+ switch t {
+ case ICETransportPolicyRelay:
+ return iceTransportPolicyRelayStr
+ case ICETransportPolicyAll:
+ return iceTransportPolicyAllStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// UnmarshalJSON parses the JSON-encoded data and stores the result
+func (t *ICETransportPolicy) UnmarshalJSON(b []byte) error {
+ var val string
+ if err := json.Unmarshal(b, &val); err != nil {
+ return err
+ }
+ *t = NewICETransportPolicy(val)
+ return nil
+}
+
+// MarshalJSON returns the JSON encoding
+func (t ICETransportPolicy) MarshalJSON() ([]byte, error) {
+ return json.Marshal(t.String())
+}
diff --git a/vendor/github.com/pion/webrtc/v3/icetransportstate.go b/vendor/github.com/pion/webrtc/v3/icetransportstate.go
new file mode 100644
index 0000000..da93e44
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/icetransportstate.go
@@ -0,0 +1,107 @@
+package webrtc
+
+import "github.com/pion/ice/v2"
+
+// ICETransportState represents the current state of the ICE transport.
+type ICETransportState int
+
+const (
+ // ICETransportStateNew indicates the ICETransport is waiting
+ // for remote candidates to be supplied.
+ ICETransportStateNew = iota + 1
+
+ // ICETransportStateChecking indicates the ICETransport has
+ // received at least one remote candidate, and a local and remote
+ // ICECandidateComplete dictionary was not added as the last candidate.
+ ICETransportStateChecking
+
+ // ICETransportStateConnected indicates the ICETransport has
+ // received a response to an outgoing connectivity check, or has
+ // received incoming DTLS/media after a successful response to an
+ // incoming connectivity check, but is still checking other candidate
+ // pairs to see if there is a better connection.
+ ICETransportStateConnected
+
+ // ICETransportStateCompleted indicates the ICETransport tested
+ // all appropriate candidate pairs and at least one functioning
+ // candidate pair has been found.
+ ICETransportStateCompleted
+
+ // ICETransportStateFailed indicates the ICETransport the last
+ // candidate was added and all appropriate candidate pairs have either
+ // failed connectivity checks or have lost consent.
+ ICETransportStateFailed
+
+ // ICETransportStateDisconnected indicates the ICETransport has received
+ // at least one local and remote candidate, but the final candidate was
+ // received yet and all appropriate candidate pairs thus far have been
+ // tested and failed.
+ ICETransportStateDisconnected
+
+ // ICETransportStateClosed indicates the ICETransport has shut down
+ // and is no longer responding to STUN requests.
+ ICETransportStateClosed
+)
+
+func (c ICETransportState) String() string {
+ switch c {
+ case ICETransportStateNew:
+ return "new"
+ case ICETransportStateChecking:
+ return "checking"
+ case ICETransportStateConnected:
+ return "connected"
+ case ICETransportStateCompleted:
+ return "completed"
+ case ICETransportStateFailed:
+ return "failed"
+ case ICETransportStateDisconnected:
+ return "disconnected"
+ case ICETransportStateClosed:
+ return "closed"
+ default:
+ return unknownStr
+ }
+}
+
+func newICETransportStateFromICE(i ice.ConnectionState) ICETransportState {
+ switch i {
+ case ice.ConnectionStateNew:
+ return ICETransportStateNew
+ case ice.ConnectionStateChecking:
+ return ICETransportStateChecking
+ case ice.ConnectionStateConnected:
+ return ICETransportStateConnected
+ case ice.ConnectionStateCompleted:
+ return ICETransportStateCompleted
+ case ice.ConnectionStateFailed:
+ return ICETransportStateFailed
+ case ice.ConnectionStateDisconnected:
+ return ICETransportStateDisconnected
+ case ice.ConnectionStateClosed:
+ return ICETransportStateClosed
+ default:
+ return ICETransportState(Unknown)
+ }
+}
+
+func (c ICETransportState) toICE() ice.ConnectionState {
+ switch c {
+ case ICETransportStateNew:
+ return ice.ConnectionStateNew
+ case ICETransportStateChecking:
+ return ice.ConnectionStateChecking
+ case ICETransportStateConnected:
+ return ice.ConnectionStateConnected
+ case ICETransportStateCompleted:
+ return ice.ConnectionStateCompleted
+ case ICETransportStateFailed:
+ return ice.ConnectionStateFailed
+ case ICETransportStateDisconnected:
+ return ice.ConnectionStateDisconnected
+ case ICETransportStateClosed:
+ return ice.ConnectionStateClosed
+ default:
+ return ice.ConnectionState(Unknown)
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/interceptor.go b/vendor/github.com/pion/webrtc/v3/interceptor.go
new file mode 100644
index 0000000..eff9496
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/interceptor.go
@@ -0,0 +1,107 @@
+// +build !js
+
+package webrtc
+
+import (
+ "sync/atomic"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/interceptor/pkg/nack"
+ "github.com/pion/interceptor/pkg/report"
+ "github.com/pion/rtp"
+)
+
+// RegisterDefaultInterceptors will register some useful interceptors.
+// If you want to customize which interceptors are loaded, you should copy the
+// code from this method and remove unwanted interceptors.
+func RegisterDefaultInterceptors(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error {
+ if err := ConfigureNack(mediaEngine, interceptorRegistry); err != nil {
+ return err
+ }
+
+ if err := ConfigureRTCPReports(interceptorRegistry); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ConfigureRTCPReports will setup everything necessary for generating Sender and Receiver Reports
+func ConfigureRTCPReports(interceptorRegistry *interceptor.Registry) error {
+ reciver, err := report.NewReceiverInterceptor()
+ if err != nil {
+ return err
+ }
+
+ sender, err := report.NewSenderInterceptor()
+ if err != nil {
+ return err
+ }
+
+ interceptorRegistry.Add(reciver)
+ interceptorRegistry.Add(sender)
+ return nil
+}
+
+// ConfigureNack will setup everything necessary for handling generating/responding to nack messages.
+func ConfigureNack(mediaEngine *MediaEngine, interceptorRegistry *interceptor.Registry) error {
+ generator, err := nack.NewGeneratorInterceptor()
+ if err != nil {
+ return err
+ }
+
+ responder, err := nack.NewResponderInterceptor()
+ if err != nil {
+ return err
+ }
+
+ mediaEngine.RegisterFeedback(RTCPFeedback{Type: "nack"}, RTPCodecTypeVideo)
+ mediaEngine.RegisterFeedback(RTCPFeedback{Type: "nack", Parameter: "pli"}, RTPCodecTypeVideo)
+ interceptorRegistry.Add(responder)
+ interceptorRegistry.Add(generator)
+ return nil
+}
+
+type interceptorToTrackLocalWriter struct{ interceptor atomic.Value } // interceptor.RTPWriter }
+
+func (i *interceptorToTrackLocalWriter) WriteRTP(header *rtp.Header, payload []byte) (int, error) {
+ if writer, ok := i.interceptor.Load().(interceptor.RTPWriter); ok && writer != nil {
+ return writer.Write(header, payload, interceptor.Attributes{})
+ }
+
+ return 0, nil
+}
+
+func (i *interceptorToTrackLocalWriter) Write(b []byte) (int, error) {
+ packet := &rtp.Packet{}
+ if err := packet.Unmarshal(b); err != nil {
+ return 0, err
+ }
+
+ return i.WriteRTP(&packet.Header, packet.Payload)
+}
+
+func createStreamInfo(id string, ssrc SSRC, payloadType PayloadType, codec RTPCodecCapability, webrtcHeaderExtensions []RTPHeaderExtensionParameter) interceptor.StreamInfo {
+ headerExtensions := make([]interceptor.RTPHeaderExtension, 0, len(webrtcHeaderExtensions))
+ for _, h := range webrtcHeaderExtensions {
+ headerExtensions = append(headerExtensions, interceptor.RTPHeaderExtension{ID: h.ID, URI: h.URI})
+ }
+
+ feedbacks := make([]interceptor.RTCPFeedback, 0, len(codec.RTCPFeedback))
+ for _, f := range codec.RTCPFeedback {
+ feedbacks = append(feedbacks, interceptor.RTCPFeedback{Type: f.Type, Parameter: f.Parameter})
+ }
+
+ return interceptor.StreamInfo{
+ ID: id,
+ Attributes: interceptor.Attributes{},
+ SSRC: uint32(ssrc),
+ PayloadType: uint8(payloadType),
+ RTPHeaderExtensions: headerExtensions,
+ MimeType: codec.MimeType,
+ ClockRate: codec.ClockRate,
+ Channels: codec.Channels,
+ SDPFmtpLine: codec.SDPFmtpLine,
+ RTCPFeedback: feedbacks,
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go b/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go
new file mode 100644
index 0000000..afccc07
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go
@@ -0,0 +1,75 @@
+package mux
+
+import (
+ "errors"
+ "io"
+ "net"
+ "time"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/transport/packetio"
+)
+
+// Endpoint implements net.Conn. It is used to read muxed packets.
+type Endpoint struct {
+ mux *Mux
+ buffer *packetio.Buffer
+}
+
+// Close unregisters the endpoint from the Mux
+func (e *Endpoint) Close() (err error) {
+ err = e.close()
+ if err != nil {
+ return err
+ }
+
+ e.mux.RemoveEndpoint(e)
+ return nil
+}
+
+func (e *Endpoint) close() error {
+ return e.buffer.Close()
+}
+
+// Read reads a packet of len(p) bytes from the underlying conn
+// that are matched by the associated MuxFunc
+func (e *Endpoint) Read(p []byte) (int, error) {
+ return e.buffer.Read(p)
+}
+
+// Write writes len(p) bytes to the underlying conn
+func (e *Endpoint) Write(p []byte) (int, error) {
+ n, err := e.mux.nextConn.Write(p)
+ if errors.Is(err, ice.ErrNoCandidatePairs) {
+ return 0, nil
+ } else if errors.Is(err, ice.ErrClosed) {
+ return 0, io.ErrClosedPipe
+ }
+
+ return n, err
+}
+
+// LocalAddr is a stub
+func (e *Endpoint) LocalAddr() net.Addr {
+ return e.mux.nextConn.LocalAddr()
+}
+
+// RemoteAddr is a stub
+func (e *Endpoint) RemoteAddr() net.Addr {
+ return e.mux.nextConn.RemoteAddr()
+}
+
+// SetDeadline is a stub
+func (e *Endpoint) SetDeadline(t time.Time) error {
+ return nil
+}
+
+// SetReadDeadline is a stub
+func (e *Endpoint) SetReadDeadline(t time.Time) error {
+ return nil
+}
+
+// SetWriteDeadline is a stub
+func (e *Endpoint) SetWriteDeadline(t time.Time) error {
+ return nil
+}
diff --git a/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go b/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go
new file mode 100644
index 0000000..83aaa26
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go
@@ -0,0 +1,145 @@
+// Package mux multiplexes packets on a single socket (RFC7983)
+package mux
+
+import (
+ "net"
+ "sync"
+
+ "github.com/pion/logging"
+ "github.com/pion/transport/packetio"
+)
+
+// The maximum amount of data that can be buffered before returning errors.
+const maxBufferSize = 1000 * 1000 // 1MB
+
+// Config collects the arguments to mux.Mux construction into
+// a single structure
+type Config struct {
+ Conn net.Conn
+ BufferSize int
+ LoggerFactory logging.LoggerFactory
+}
+
+// Mux allows multiplexing
+type Mux struct {
+ lock sync.RWMutex
+ nextConn net.Conn
+ endpoints map[*Endpoint]MatchFunc
+ bufferSize int
+ closedCh chan struct{}
+
+ log logging.LeveledLogger
+}
+
+// NewMux creates a new Mux
+func NewMux(config Config) *Mux {
+ m := &Mux{
+ nextConn: config.Conn,
+ endpoints: make(map[*Endpoint]MatchFunc),
+ bufferSize: config.BufferSize,
+ closedCh: make(chan struct{}),
+ log: config.LoggerFactory.NewLogger("mux"),
+ }
+
+ go m.readLoop()
+
+ return m
+}
+
+// NewEndpoint creates a new Endpoint
+func (m *Mux) NewEndpoint(f MatchFunc) *Endpoint {
+ e := &Endpoint{
+ mux: m,
+ buffer: packetio.NewBuffer(),
+ }
+
+ // Set a maximum size of the buffer in bytes.
+ // NOTE: We actually won't get anywhere close to this limit.
+ // SRTP will constantly read from the endpoint and drop packets if it's full.
+ e.buffer.SetLimitSize(maxBufferSize)
+
+ m.lock.Lock()
+ m.endpoints[e] = f
+ m.lock.Unlock()
+
+ return e
+}
+
+// RemoveEndpoint removes an endpoint from the Mux
+func (m *Mux) RemoveEndpoint(e *Endpoint) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ delete(m.endpoints, e)
+}
+
+// Close closes the Mux and all associated Endpoints.
+func (m *Mux) Close() error {
+ m.lock.Lock()
+ for e := range m.endpoints {
+ err := e.close()
+ if err != nil {
+ return err
+ }
+
+ delete(m.endpoints, e)
+ }
+ m.lock.Unlock()
+
+ err := m.nextConn.Close()
+ if err != nil {
+ return err
+ }
+
+ // Wait for readLoop to end
+ <-m.closedCh
+
+ return nil
+}
+
+func (m *Mux) readLoop() {
+ defer func() {
+ close(m.closedCh)
+ }()
+
+ buf := make([]byte, m.bufferSize)
+ for {
+ n, err := m.nextConn.Read(buf)
+ if err != nil {
+ return
+ }
+
+ err = m.dispatch(buf[:n])
+ if err != nil {
+ return
+ }
+ }
+}
+
+func (m *Mux) dispatch(buf []byte) error {
+ var endpoint *Endpoint
+
+ m.lock.Lock()
+ for e, f := range m.endpoints {
+ if f(buf) {
+ endpoint = e
+ break
+ }
+ }
+ m.lock.Unlock()
+
+ if endpoint == nil {
+ if len(buf) > 0 {
+ m.log.Warnf("Warning: mux: no endpoint for packet starting with %d\n", buf[0])
+ } else {
+ m.log.Warnf("Warning: mux: no endpoint for zero length packet")
+ }
+ return nil
+ }
+
+ _, err := endpoint.buffer.Write(buf)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go b/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go
new file mode 100644
index 0000000..2ab585b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go
@@ -0,0 +1,101 @@
+package mux
+
+import (
+ "bytes"
+ "encoding/binary"
+)
+
+// MatchFunc allows custom logic for mapping packets to an Endpoint
+type MatchFunc func([]byte) bool
+
+// MatchAll always returns true
+func MatchAll(b []byte) bool {
+ return true
+}
+
+// MatchNone always returns false
+func MatchNone(b []byte) bool {
+ return false
+}
+
+// MatchRange is a MatchFunc that accepts packets with the first byte in [lower..upper]
+func MatchRange(lower, upper byte) MatchFunc {
+ return func(buf []byte) bool {
+ if len(buf) < 1 {
+ return false
+ }
+ b := buf[0]
+ return b >= lower && b <= upper
+ }
+}
+
+// MatchFuncs as described in RFC7983
+// https://tools.ietf.org/html/rfc7983
+// +----------------+
+// | [0..3] -+--> forward to STUN
+// | |
+// | [16..19] -+--> forward to ZRTP
+// | |
+// packet --> | [20..63] -+--> forward to DTLS
+// | |
+// | [64..79] -+--> forward to TURN Channel
+// | |
+// | [128..191] -+--> forward to RTP/RTCP
+// +----------------+
+
+// MatchSTUN is a MatchFunc that accepts packets with the first byte in [0..3]
+// as defied in RFC7983
+func MatchSTUN(b []byte) bool {
+ return MatchRange(0, 3)(b)
+}
+
+// MatchZRTP is a MatchFunc that accepts packets with the first byte in [16..19]
+// as defied in RFC7983
+func MatchZRTP(b []byte) bool {
+ return MatchRange(16, 19)(b)
+}
+
+// MatchDTLS is a MatchFunc that accepts packets with the first byte in [20..63]
+// as defied in RFC7983
+func MatchDTLS(b []byte) bool {
+ return MatchRange(20, 63)(b)
+}
+
+// MatchTURN is a MatchFunc that accepts packets with the first byte in [64..79]
+// as defied in RFC7983
+func MatchTURN(b []byte) bool {
+ return MatchRange(64, 79)(b)
+}
+
+// MatchSRTPOrSRTCP is a MatchFunc that accepts packets with the first byte in [128..191]
+// as defied in RFC7983
+func MatchSRTPOrSRTCP(b []byte) bool {
+ return MatchRange(128, 191)(b)
+}
+
+func isRTCP(buf []byte) bool {
+ // Not long enough to determine RTP/RTCP
+ if len(buf) < 4 {
+ return false
+ }
+
+ var rtcpPacketType uint8
+ r := bytes.NewReader([]byte{buf[1]})
+ if err := binary.Read(r, binary.BigEndian, &rtcpPacketType); err != nil {
+ return false
+ } else if rtcpPacketType >= 192 && rtcpPacketType <= 223 {
+ return true
+ }
+
+ return false
+}
+
+// MatchSRTP is a MatchFunc that only matches SRTP and not SRTCP
+func MatchSRTP(buf []byte) bool {
+ return MatchSRTPOrSRTCP(buf) && !isRTCP(buf)
+}
+
+// MatchSRTCP is a MatchFunc that only matches SRTCP and not SRTP
+func MatchSRTCP(buf []byte) bool {
+ return MatchSRTPOrSRTCP(buf) && isRTCP(buf)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/internal/util/util.go b/vendor/github.com/pion/webrtc/v3/internal/util/util.go
new file mode 100644
index 0000000..6e12b64
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/internal/util/util.go
@@ -0,0 +1,72 @@
+// Package util provides auxiliary functions internally used in webrtc package
+package util
+
+import (
+ "errors"
+ "strings"
+
+ "github.com/pion/randutil"
+)
+
+const (
+ runesAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+)
+
+// Use global random generator to properly seed by crypto grade random.
+var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals
+
+// MathRandAlpha generates a mathmatical random alphabet sequence of the requested length.
+func MathRandAlpha(n int) string {
+ return globalMathRandomGenerator.GenerateString(n, runesAlpha)
+}
+
+// RandUint32 generates a mathmatical random uint32.
+func RandUint32() uint32 {
+ return globalMathRandomGenerator.Uint32()
+}
+
+// FlattenErrs flattens multiple errors into one
+func FlattenErrs(errs []error) error {
+ errs2 := []error{}
+ for _, e := range errs {
+ if e != nil {
+ errs2 = append(errs2, e)
+ }
+ }
+ if len(errs2) == 0 {
+ return nil
+ }
+ return multiError(errs2)
+}
+
+type multiError []error
+
+func (me multiError) Error() string {
+ var errstrings []string
+
+ for _, err := range me {
+ if err != nil {
+ errstrings = append(errstrings, err.Error())
+ }
+ }
+
+ if len(errstrings) == 0 {
+ return "multiError must contain multiple error but is empty"
+ }
+
+ return strings.Join(errstrings, "\n")
+}
+
+func (me multiError) Is(err error) bool {
+ for _, e := range me {
+ if errors.Is(e, err) {
+ return true
+ }
+ if me2, ok := e.(multiError); ok {
+ if me2.Is(err) {
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/pion/webrtc/v3/js_compare.go b/vendor/github.com/pion/webrtc/v3/js_compare.go
new file mode 100644
index 0000000..31e3980
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/js_compare.go
@@ -0,0 +1,13 @@
+// +build js,go1.14
+
+package webrtc
+
+import "syscall/js"
+
+func jsValueIsUndefined(v js.Value) bool {
+ return v.IsUndefined()
+}
+
+func jsValueIsNull(v js.Value) bool {
+ return v.IsNull()
+}
diff --git a/vendor/github.com/pion/webrtc/v3/js_compare_legacy.go b/vendor/github.com/pion/webrtc/v3/js_compare_legacy.go
new file mode 100644
index 0000000..e2e247a
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/js_compare_legacy.go
@@ -0,0 +1,13 @@
+// +build js,!go1.14
+
+package webrtc
+
+import "syscall/js"
+
+func jsValueIsUndefined(v js.Value) bool {
+ return v == js.Undefined()
+}
+
+func jsValueIsNull(v js.Value) bool {
+ return v == js.Null()
+}
diff --git a/vendor/github.com/pion/webrtc/v3/js_utils.go b/vendor/github.com/pion/webrtc/v3/js_utils.go
new file mode 100644
index 0000000..25e8939
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/js_utils.go
@@ -0,0 +1,168 @@
+// +build js,wasm
+
+package webrtc
+
+import (
+ "fmt"
+ "syscall/js"
+)
+
+// awaitPromise accepts a js.Value representing a Promise. If the promise
+// resolves, it returns (result, nil). If the promise rejects, it returns
+// (js.Undefined, error). awaitPromise has a synchronous-like API but does not
+// block the JavaScript event loop.
+func awaitPromise(promise js.Value) (js.Value, error) {
+ resultsChan := make(chan js.Value)
+ errChan := make(chan js.Error)
+
+ thenFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go func() {
+ resultsChan <- args[0]
+ }()
+ return js.Undefined()
+ })
+ defer thenFunc.Release()
+ promise.Call("then", thenFunc)
+
+ catchFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go func() {
+ errChan <- js.Error{args[0]}
+ }()
+ return js.Undefined()
+ })
+ defer catchFunc.Release()
+ promise.Call("catch", catchFunc)
+
+ select {
+ case result := <-resultsChan:
+ return result, nil
+ case err := <-errChan:
+ return js.Undefined(), err
+ }
+}
+
+func valueToUint16Pointer(val js.Value) *uint16 {
+ if jsValueIsNull(val) || jsValueIsUndefined(val) {
+ return nil
+ }
+ convertedVal := uint16(val.Int())
+ return &convertedVal
+}
+
+func valueToStringPointer(val js.Value) *string {
+ if jsValueIsNull(val) || jsValueIsUndefined(val) {
+ return nil
+ }
+ stringVal := val.String()
+ return &stringVal
+}
+
+func stringToValueOrUndefined(val string) js.Value {
+ if val == "" {
+ return js.Undefined()
+ }
+ return js.ValueOf(val)
+}
+
+func uint8ToValueOrUndefined(val uint8) js.Value {
+ if val == 0 {
+ return js.Undefined()
+ }
+ return js.ValueOf(val)
+}
+
+func interfaceToValueOrUndefined(val interface{}) js.Value {
+ if val == nil {
+ return js.Undefined()
+ }
+ return js.ValueOf(val)
+}
+
+func valueToStringOrZero(val js.Value) string {
+ if jsValueIsUndefined(val) || jsValueIsNull(val) {
+ return ""
+ }
+ return val.String()
+}
+
+func valueToUint8OrZero(val js.Value) uint8 {
+ if jsValueIsUndefined(val) || jsValueIsNull(val) {
+ return 0
+ }
+ return uint8(val.Int())
+}
+
+func valueToUint16OrZero(val js.Value) uint16 {
+ if jsValueIsNull(val) || jsValueIsUndefined(val) {
+ return 0
+ }
+ return uint16(val.Int())
+}
+
+func valueToUint32OrZero(val js.Value) uint32 {
+ if jsValueIsNull(val) || jsValueIsUndefined(val) {
+ return 0
+ }
+ return uint32(val.Int())
+}
+
+func valueToStrings(val js.Value) []string {
+ result := make([]string, val.Length())
+ for i := 0; i < val.Length(); i++ {
+ result[i] = val.Index(i).String()
+ }
+ return result
+}
+
+func stringPointerToValue(val *string) js.Value {
+ if val == nil {
+ return js.Undefined()
+ }
+ return js.ValueOf(*val)
+}
+
+func uint16PointerToValue(val *uint16) js.Value {
+ if val == nil {
+ return js.Undefined()
+ }
+ return js.ValueOf(*val)
+}
+
+func boolPointerToValue(val *bool) js.Value {
+ if val == nil {
+ return js.Undefined()
+ }
+ return js.ValueOf(*val)
+}
+
+func stringsToValue(strings []string) js.Value {
+ val := make([]interface{}, len(strings))
+ for i, s := range strings {
+ val[i] = s
+ }
+ return js.ValueOf(val)
+}
+
+func stringEnumToValueOrUndefined(s string) js.Value {
+ if s == "unknown" {
+ return js.Undefined()
+ }
+ return js.ValueOf(s)
+}
+
+// Converts the return value of recover() to an error.
+func recoveryToError(e interface{}) error {
+ switch e := e.(type) {
+ case error:
+ return e
+ default:
+ return fmt.Errorf("recovered with non-error value: (%T) %s", e, e)
+ }
+}
+
+func uint8ArrayValueToBytes(val js.Value) []byte {
+ result := make([]byte, val.Length())
+ js.CopyBytesToGo(result, val)
+
+ return result
+}
diff --git a/vendor/github.com/pion/webrtc/v3/mediaengine.go b/vendor/github.com/pion/webrtc/v3/mediaengine.go
new file mode 100644
index 0000000..bc3ed99
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/mediaengine.go
@@ -0,0 +1,533 @@
+// +build !js
+
+package webrtc
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/pion/rtp"
+ "github.com/pion/rtp/codecs"
+ "github.com/pion/sdp/v3"
+)
+
+const (
+ // MimeTypeH264 H264 MIME type.
+ // Note: Matching should be case insensitive.
+ MimeTypeH264 = "video/H264"
+ // MimeTypeOpus Opus MIME type
+ // Note: Matching should be case insensitive.
+ MimeTypeOpus = "audio/opus"
+ // MimeTypeVP8 VP8 MIME type
+ // Note: Matching should be case insensitive.
+ MimeTypeVP8 = "video/VP8"
+ // MimeTypeVP9 VP9 MIME type
+ // Note: Matching should be case insensitive.
+ MimeTypeVP9 = "video/VP9"
+ // MimeTypeG722 G722 MIME type
+ // Note: Matching should be case insensitive.
+ MimeTypeG722 = "audio/G722"
+ // MimeTypePCMU PCMU MIME type
+ // Note: Matching should be case insensitive.
+ MimeTypePCMU = "audio/PCMU"
+ // MimeTypePCMA PCMA MIME type
+ // Note: Matching should be case insensitive.
+ MimeTypePCMA = "audio/PCMA"
+)
+
+type mediaEngineHeaderExtension struct {
+ uri string
+ isAudio, isVideo bool
+
+ // If set only Transceivers of this direction are allowed
+ allowedDirections []RTPTransceiverDirection
+}
+
+// A MediaEngine defines the codecs supported by a PeerConnection, and the
+// configuration of those codecs. A MediaEngine must not be shared between
+// PeerConnections.
+type MediaEngine struct {
+ // If we have attempted to negotiate a codec type yet.
+ negotiatedVideo, negotiatedAudio bool
+
+ videoCodecs, audioCodecs []RTPCodecParameters
+ negotiatedVideoCodecs, negotiatedAudioCodecs []RTPCodecParameters
+
+ headerExtensions []mediaEngineHeaderExtension
+ negotiatedHeaderExtensions map[int]mediaEngineHeaderExtension
+}
+
+// RegisterDefaultCodecs registers the default codecs supported by Pion WebRTC.
+// RegisterDefaultCodecs is not safe for concurrent use.
+func (m *MediaEngine) RegisterDefaultCodecs() error {
+ // Default Pion Audio Codecs
+ for _, codec := range []RTPCodecParameters{
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil},
+ PayloadType: 111,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeG722, 8000, 0, "", nil},
+ PayloadType: 9,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypePCMU, 8000, 0, "", nil},
+ PayloadType: 0,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypePCMA, 8000, 0, "", nil},
+ PayloadType: 8,
+ },
+ } {
+ if err := m.RegisterCodec(codec, RTPCodecTypeAudio); err != nil {
+ return err
+ }
+ }
+
+ videoRTCPFeedback := []RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}}
+ for _, codec := range []RTPCodecParameters{
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", videoRTCPFeedback},
+ PayloadType: 96,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
+ PayloadType: 97,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=0", videoRTCPFeedback},
+ PayloadType: 98,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=98", nil},
+ PayloadType: 99,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=1", videoRTCPFeedback},
+ PayloadType: 100,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=100", nil},
+ PayloadType: 101,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", videoRTCPFeedback},
+ PayloadType: 102,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=102", nil},
+ PayloadType: 121,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback},
+ PayloadType: 127,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil},
+ PayloadType: 120,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", videoRTCPFeedback},
+ PayloadType: 125,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=125", nil},
+ PayloadType: 107,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", videoRTCPFeedback},
+ PayloadType: 108,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=108", nil},
+ PayloadType: 109,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback},
+ PayloadType: 127,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil},
+ PayloadType: 120,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032", videoRTCPFeedback},
+ PayloadType: 123,
+ },
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=123", nil},
+ PayloadType: 118,
+ },
+
+ {
+ RTPCodecCapability: RTPCodecCapability{"video/ulpfec", 90000, 0, "", nil},
+ PayloadType: 116,
+ },
+ } {
+ if err := m.RegisterCodec(codec, RTPCodecTypeVideo); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// addCodec will append codec if it not exists
+func (m *MediaEngine) addCodec(codecs []RTPCodecParameters, codec RTPCodecParameters) []RTPCodecParameters {
+ for _, c := range codecs {
+ if c.MimeType == codec.MimeType && c.PayloadType == codec.PayloadType {
+ return codecs
+ }
+ }
+ return append(codecs, codec)
+}
+
+// RegisterCodec adds codec to the MediaEngine
+// These are the list of codecs supported by this PeerConnection.
+// RegisterCodec is not safe for concurrent use.
+func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType) error {
+ codec.statsID = fmt.Sprintf("RTPCodec-%d", time.Now().UnixNano())
+ switch typ {
+ case RTPCodecTypeAudio:
+ m.audioCodecs = m.addCodec(m.audioCodecs, codec)
+ case RTPCodecTypeVideo:
+ m.videoCodecs = m.addCodec(m.videoCodecs, codec)
+ default:
+ return ErrUnknownType
+ }
+ return nil
+}
+
+// RegisterHeaderExtension adds a header extension to the MediaEngine
+// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete
+func (m *MediaEngine) RegisterHeaderExtension(extension RTPHeaderExtensionCapability, typ RTPCodecType, allowedDirections ...RTPTransceiverDirection) error {
+ if m.negotiatedHeaderExtensions == nil {
+ m.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{}
+ }
+
+ if len(allowedDirections) == 0 {
+ allowedDirections = []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendonly}
+ }
+
+ for _, direction := range allowedDirections {
+ if direction != RTPTransceiverDirectionRecvonly && direction != RTPTransceiverDirectionSendonly {
+ return ErrRegisterHeaderExtensionInvalidDirection
+ }
+ }
+
+ extensionIndex := -1
+ for i := range m.headerExtensions {
+ if extension.URI == m.headerExtensions[i].uri {
+ extensionIndex = i
+ }
+ }
+
+ if extensionIndex == -1 {
+ m.headerExtensions = append(m.headerExtensions, mediaEngineHeaderExtension{})
+ extensionIndex = len(m.headerExtensions) - 1
+ }
+
+ if typ == RTPCodecTypeAudio {
+ m.headerExtensions[extensionIndex].isAudio = true
+ } else if typ == RTPCodecTypeVideo {
+ m.headerExtensions[extensionIndex].isVideo = true
+ }
+
+ m.headerExtensions[extensionIndex].uri = extension.URI
+ m.headerExtensions[extensionIndex].allowedDirections = allowedDirections
+
+ return nil
+}
+
+// RegisterFeedback adds feedback mechanism to already registered codecs.
+func (m *MediaEngine) RegisterFeedback(feedback RTCPFeedback, typ RTPCodecType) {
+ switch typ {
+ case RTPCodecTypeVideo:
+ for i, v := range m.videoCodecs {
+ v.RTCPFeedback = append(v.RTCPFeedback, feedback)
+ m.videoCodecs[i] = v
+ }
+ case RTPCodecTypeAudio:
+ for i, v := range m.audioCodecs {
+ v.RTCPFeedback = append(v.RTCPFeedback, feedback)
+ m.audioCodecs[i] = v
+ }
+ }
+}
+
+// getHeaderExtensionID returns the negotiated ID for a header extension.
+// If the Header Extension isn't enabled ok will be false
+func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapability) (val int, audioNegotiated, videoNegotiated bool) {
+ if m.negotiatedHeaderExtensions == nil {
+ return 0, false, false
+ }
+
+ for id, h := range m.negotiatedHeaderExtensions {
+ if extension.URI == h.uri {
+ return id, h.isAudio, h.isVideo
+ }
+ }
+
+ return
+}
+
+// copy copies any user modifiable state of the MediaEngine
+// all internal state is reset
+func (m *MediaEngine) copy() *MediaEngine {
+ cloned := &MediaEngine{
+ videoCodecs: append([]RTPCodecParameters{}, m.videoCodecs...),
+ audioCodecs: append([]RTPCodecParameters{}, m.audioCodecs...),
+ headerExtensions: append([]mediaEngineHeaderExtension{}, m.headerExtensions...),
+ }
+ if len(m.headerExtensions) > 0 {
+ cloned.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{}
+ }
+ return cloned
+}
+
+func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParameters, RTPCodecType, error) {
+ for _, codec := range m.negotiatedVideoCodecs {
+ if codec.PayloadType == payloadType {
+ return codec, RTPCodecTypeVideo, nil
+ }
+ }
+ for _, codec := range m.negotiatedAudioCodecs {
+ if codec.PayloadType == payloadType {
+ return codec, RTPCodecTypeAudio, nil
+ }
+ }
+
+ return RTPCodecParameters{}, 0, ErrCodecNotFound
+}
+
+func (m *MediaEngine) collectStats(collector *statsReportCollector) {
+ statsLoop := func(codecs []RTPCodecParameters) {
+ for _, codec := range codecs {
+ collector.Collecting()
+ stats := CodecStats{
+ Timestamp: statsTimestampFrom(time.Now()),
+ Type: StatsTypeCodec,
+ ID: codec.statsID,
+ PayloadType: codec.PayloadType,
+ MimeType: codec.MimeType,
+ ClockRate: codec.ClockRate,
+ Channels: uint8(codec.Channels),
+ SDPFmtpLine: codec.SDPFmtpLine,
+ }
+
+ collector.Collect(stats.ID, stats)
+ }
+ }
+
+ statsLoop(m.videoCodecs)
+ statsLoop(m.audioCodecs)
+}
+
+// Look up a codec and enable if it exists
+func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCodecType) (codecMatchType, error) {
+ codecs := m.videoCodecs
+ if typ == RTPCodecTypeAudio {
+ codecs = m.audioCodecs
+ }
+
+ if strings.HasPrefix(remoteCodec.RTPCodecCapability.SDPFmtpLine, "apt=") {
+ payloadType, err := strconv.Atoi(strings.TrimPrefix(remoteCodec.RTPCodecCapability.SDPFmtpLine, "apt="))
+ if err != nil {
+ return codecMatchNone, err
+ }
+
+ if _, _, err = m.getCodecByPayload(PayloadType(payloadType)); err != nil {
+ return codecMatchNone, nil // not an error, we just ignore this codec we don't support
+ }
+ }
+
+ _, matchType := codecParametersFuzzySearch(remoteCodec, codecs)
+ return matchType, nil
+}
+
+// Look up a header extension and enable if it exists
+func (m *MediaEngine) updateHeaderExtension(id int, extension string, typ RTPCodecType) error {
+ if m.negotiatedHeaderExtensions == nil {
+ return nil
+ }
+
+ for _, localExtension := range m.headerExtensions {
+ if localExtension.uri == extension {
+ h := mediaEngineHeaderExtension{uri: extension, allowedDirections: localExtension.allowedDirections}
+ if existingValue, ok := m.negotiatedHeaderExtensions[id]; ok {
+ h = existingValue
+ }
+
+ switch {
+ case localExtension.isAudio && typ == RTPCodecTypeAudio:
+ h.isAudio = true
+ case localExtension.isVideo && typ == RTPCodecTypeVideo:
+ h.isVideo = true
+ }
+
+ m.negotiatedHeaderExtensions[id] = h
+ }
+ }
+ return nil
+}
+
+func (m *MediaEngine) pushCodecs(codecs []RTPCodecParameters, typ RTPCodecType) {
+ for _, codec := range codecs {
+ if typ == RTPCodecTypeAudio {
+ m.negotiatedAudioCodecs = m.addCodec(m.negotiatedAudioCodecs, codec)
+ } else if typ == RTPCodecTypeVideo {
+ m.negotiatedVideoCodecs = m.addCodec(m.negotiatedVideoCodecs, codec)
+ }
+ }
+}
+
+// Update the MediaEngine from a remote description
+func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) error {
+ for _, media := range desc.MediaDescriptions {
+ var typ RTPCodecType
+ switch {
+ case !m.negotiatedAudio && strings.EqualFold(media.MediaName.Media, "audio"):
+ m.negotiatedAudio = true
+ typ = RTPCodecTypeAudio
+ case !m.negotiatedVideo && strings.EqualFold(media.MediaName.Media, "video"):
+ m.negotiatedVideo = true
+ typ = RTPCodecTypeVideo
+ default:
+ continue
+ }
+
+ codecs, err := codecsFromMediaDescription(media)
+ if err != nil {
+ return err
+ }
+
+ exactMatches := make([]RTPCodecParameters, 0, len(codecs))
+ partialMatches := make([]RTPCodecParameters, 0, len(codecs))
+
+ for _, codec := range codecs {
+ matchType, mErr := m.matchRemoteCodec(codec, typ)
+ if mErr != nil {
+ return mErr
+ }
+
+ if matchType == codecMatchExact {
+ exactMatches = append(exactMatches, codec)
+ } else if matchType == codecMatchPartial {
+ partialMatches = append(partialMatches, codec)
+ }
+ }
+
+ // use exact matches when they exist, otherwise fall back to partial
+ switch {
+ case len(exactMatches) > 0:
+ m.pushCodecs(exactMatches, typ)
+ case len(partialMatches) > 0:
+ m.pushCodecs(partialMatches, typ)
+ default:
+ // no match, not negotiated
+ continue
+ }
+
+ extensions, err := rtpExtensionsFromMediaDescription(media)
+ if err != nil {
+ return err
+ }
+
+ for extension, id := range extensions {
+ if err = m.updateHeaderExtension(id, extension, typ); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecParameters {
+ if typ == RTPCodecTypeVideo {
+ if m.negotiatedVideo {
+ return m.negotiatedVideoCodecs
+ }
+
+ return m.videoCodecs
+ } else if typ == RTPCodecTypeAudio {
+ if m.negotiatedAudio {
+ return m.negotiatedAudioCodecs
+ }
+
+ return m.audioCodecs
+ }
+
+ return nil
+}
+
+func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters {
+ headerExtensions := make([]RTPHeaderExtensionParameter, 0)
+
+ if m.negotiatedVideo && typ == RTPCodecTypeVideo ||
+ m.negotiatedAudio && typ == RTPCodecTypeAudio {
+ for id, e := range m.negotiatedHeaderExtensions {
+ if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) {
+ headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri})
+ }
+ }
+ } else {
+ for id, e := range m.headerExtensions {
+ if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) {
+ headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id + 1, URI: e.uri})
+ }
+ }
+ }
+
+ return RTPParameters{
+ HeaderExtensions: headerExtensions,
+ Codecs: m.getCodecsByKind(typ),
+ }
+}
+
+func (m *MediaEngine) getRTPParametersByPayloadType(payloadType PayloadType) (RTPParameters, error) {
+ codec, typ, err := m.getCodecByPayload(payloadType)
+ if err != nil {
+ return RTPParameters{}, err
+ }
+
+ headerExtensions := make([]RTPHeaderExtensionParameter, 0)
+ for id, e := range m.negotiatedHeaderExtensions {
+ if e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo {
+ headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri})
+ }
+ }
+
+ return RTPParameters{
+ HeaderExtensions: headerExtensions,
+ Codecs: []RTPCodecParameters{codec},
+ }, nil
+}
+
+func payloaderForCodec(codec RTPCodecCapability) (rtp.Payloader, error) {
+ switch strings.ToLower(codec.MimeType) {
+ case strings.ToLower(MimeTypeH264):
+ return &codecs.H264Payloader{}, nil
+ case strings.ToLower(MimeTypeOpus):
+ return &codecs.OpusPayloader{}, nil
+ case strings.ToLower(MimeTypeVP8):
+ return &codecs.VP8Payloader{}, nil
+ case strings.ToLower(MimeTypeVP9):
+ return &codecs.VP9Payloader{}, nil
+ case strings.ToLower(MimeTypeG722):
+ return &codecs.G722Payloader{}, nil
+ case strings.ToLower(MimeTypePCMU), strings.ToLower(MimeTypePCMA):
+ return &codecs.G711Payloader{}, nil
+ default:
+ return nil, ErrNoPayloaderForCodec
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/networktype.go b/vendor/github.com/pion/webrtc/v3/networktype.go
new file mode 100644
index 0000000..e5dd840
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/networktype.go
@@ -0,0 +1,104 @@
+package webrtc
+
+import (
+ "fmt"
+
+ "github.com/pion/ice/v2"
+)
+
+func supportedNetworkTypes() []NetworkType {
+ return []NetworkType{
+ NetworkTypeUDP4,
+ NetworkTypeUDP6,
+ // NetworkTypeTCP4, // Not supported yet
+ // NetworkTypeTCP6, // Not supported yet
+ }
+}
+
+// NetworkType represents the type of network
+type NetworkType int
+
+const (
+ // NetworkTypeUDP4 indicates UDP over IPv4.
+ NetworkTypeUDP4 NetworkType = iota + 1
+
+ // NetworkTypeUDP6 indicates UDP over IPv6.
+ NetworkTypeUDP6
+
+ // NetworkTypeTCP4 indicates TCP over IPv4.
+ NetworkTypeTCP4
+
+ // NetworkTypeTCP6 indicates TCP over IPv6.
+ NetworkTypeTCP6
+)
+
+// This is done this way because of a linter.
+const (
+ networkTypeUDP4Str = "udp4"
+ networkTypeUDP6Str = "udp6"
+ networkTypeTCP4Str = "tcp4"
+ networkTypeTCP6Str = "tcp6"
+)
+
+func (t NetworkType) String() string {
+ switch t {
+ case NetworkTypeUDP4:
+ return networkTypeUDP4Str
+ case NetworkTypeUDP6:
+ return networkTypeUDP6Str
+ case NetworkTypeTCP4:
+ return networkTypeTCP4Str
+ case NetworkTypeTCP6:
+ return networkTypeTCP6Str
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// Protocol returns udp or tcp
+func (t NetworkType) Protocol() string {
+ switch t {
+ case NetworkTypeUDP4:
+ return "udp"
+ case NetworkTypeUDP6:
+ return "udp"
+ case NetworkTypeTCP4:
+ return "tcp"
+ case NetworkTypeTCP6:
+ return "tcp"
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// NewNetworkType allows create network type from string
+// It will be useful for getting custom network types from external config.
+func NewNetworkType(raw string) (NetworkType, error) {
+ switch raw {
+ case networkTypeUDP4Str:
+ return NetworkTypeUDP4, nil
+ case networkTypeUDP6Str:
+ return NetworkTypeUDP6, nil
+ case networkTypeTCP4Str:
+ return NetworkTypeTCP4, nil
+ case networkTypeTCP6Str:
+ return NetworkTypeTCP6, nil
+ default:
+ return NetworkType(Unknown), fmt.Errorf("%w: %s", errNetworkTypeUnknown, raw)
+ }
+}
+
+func getNetworkType(iceNetworkType ice.NetworkType) (NetworkType, error) {
+ switch iceNetworkType {
+ case ice.NetworkTypeUDP4:
+ return NetworkTypeUDP4, nil
+ case ice.NetworkTypeUDP6:
+ return NetworkTypeUDP6, nil
+ case ice.NetworkTypeTCP4:
+ return NetworkTypeTCP4, nil
+ case ice.NetworkTypeTCP6:
+ return NetworkTypeTCP6, nil
+ default:
+ return NetworkType(Unknown), fmt.Errorf("%w: %s", errNetworkTypeUnknown, iceNetworkType.String())
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/oauthcredential.go b/vendor/github.com/pion/webrtc/v3/oauthcredential.go
new file mode 100644
index 0000000..46170c7
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/oauthcredential.go
@@ -0,0 +1,15 @@
+package webrtc
+
+// OAuthCredential represents OAuth credential information which is used by
+// the STUN/TURN client to connect to an ICE server as defined in
+// https://tools.ietf.org/html/rfc7635. Note that the kid parameter is not
+// located in OAuthCredential, but in ICEServer's username member.
+type OAuthCredential struct {
+ // MACKey is a base64-url encoded format. It is used in STUN message
+ // integrity hash calculation.
+ MACKey string
+
+ // AccessToken is a base64-encoded format. This is an encrypted
+ // self-contained token that is opaque to the application.
+ AccessToken string
+}
diff --git a/vendor/github.com/pion/webrtc/v3/offeransweroptions.go b/vendor/github.com/pion/webrtc/v3/offeransweroptions.go
new file mode 100644
index 0000000..2a34aed
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/offeransweroptions.go
@@ -0,0 +1,26 @@
+package webrtc
+
+// OfferAnswerOptions is a base structure which describes the options that
+// can be used to control the offer/answer creation process.
+type OfferAnswerOptions struct {
+ // VoiceActivityDetection allows the application to provide information
+ // about whether it wishes voice detection feature to be enabled or disabled.
+ VoiceActivityDetection bool
+}
+
+// AnswerOptions structure describes the options used to control the answer
+// creation process.
+type AnswerOptions struct {
+ OfferAnswerOptions
+}
+
+// OfferOptions structure describes the options used to control the offer
+// creation process
+type OfferOptions struct {
+ OfferAnswerOptions
+
+ // ICERestart forces the underlying ice gathering process to be restarted.
+ // When this value is true, the generated description will have ICE
+ // credentials that are different from the current credentials
+ ICERestart bool
+}
diff --git a/vendor/github.com/pion/webrtc/v3/operations.go b/vendor/github.com/pion/webrtc/v3/operations.go
new file mode 100644
index 0000000..82bc832
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/operations.go
@@ -0,0 +1,87 @@
+package webrtc
+
+import (
+ "sync"
+)
+
+// Operation is a function
+type operation func()
+
+// Operations is a task executor.
+type operations struct {
+ mu sync.Mutex
+ busy bool
+ ops []operation
+}
+
+func newOperations() *operations {
+ return &operations{}
+}
+
+// Enqueue adds a new action to be executed. If there are no actions scheduled,
+// the execution will start immediately in a new goroutine.
+func (o *operations) Enqueue(op operation) {
+ if op == nil {
+ return
+ }
+
+ o.mu.Lock()
+ running := o.busy
+ o.ops = append(o.ops, op)
+ o.busy = true
+ o.mu.Unlock()
+
+ if !running {
+ go o.start()
+ }
+}
+
+// IsEmpty checks if there are tasks in the queue
+func (o *operations) IsEmpty() bool {
+ o.mu.Lock()
+ defer o.mu.Unlock()
+ return len(o.ops) == 0
+}
+
+// Done blocks until all currently enqueued operations are finished executing.
+// For more complex synchronization, use Enqueue directly.
+func (o *operations) Done() {
+ var wg sync.WaitGroup
+ wg.Add(1)
+ o.Enqueue(func() {
+ wg.Done()
+ })
+ wg.Wait()
+}
+
+func (o *operations) pop() func() {
+ o.mu.Lock()
+ defer o.mu.Unlock()
+ if len(o.ops) == 0 {
+ return nil
+ }
+
+ fn := o.ops[0]
+ o.ops = o.ops[1:]
+ return fn
+}
+
+func (o *operations) start() {
+ defer func() {
+ o.mu.Lock()
+ defer o.mu.Unlock()
+ if len(o.ops) == 0 {
+ o.busy = false
+ return
+ }
+ // either a new operation was enqueued while we
+ // were busy, or an operation panicked
+ go o.start()
+ }()
+
+ fn := o.pop()
+ for fn != nil {
+ fn()
+ fn = o.pop()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/package.json b/vendor/github.com/pion/webrtc/v3/package.json
new file mode 100644
index 0000000..429f3ef
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "webrtc",
+ "repository": "git@github.com:pion/webrtc.git",
+ "private": true,
+ "devDependencies": {
+ "wrtc": "0.4.7"
+ },
+ "dependencies": {
+ "request": "2.88.2"
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/peerconnection.go b/vendor/github.com/pion/webrtc/v3/peerconnection.go
new file mode 100644
index 0000000..3f02bba
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/peerconnection.go
@@ -0,0 +1,2323 @@
+// +build !js
+
+package webrtc
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/interceptor"
+ "github.com/pion/logging"
+ "github.com/pion/rtcp"
+ "github.com/pion/sdp/v3"
+ "github.com/pion/webrtc/v3/internal/util"
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+// PeerConnection represents a WebRTC connection that establishes a
+// peer-to-peer communications with another PeerConnection instance in a
+// browser, or to another endpoint implementing the required protocols.
+type PeerConnection struct {
+ statsID string
+ mu sync.RWMutex
+
+ // ops is an operations queue which will ensure the enqueued actions are
+ // executed in order. It is used for asynchronously, but serially processing
+ // remote and local descriptions
+ ops *operations
+
+ configuration Configuration
+
+ currentLocalDescription *SessionDescription
+ pendingLocalDescription *SessionDescription
+ currentRemoteDescription *SessionDescription
+ pendingRemoteDescription *SessionDescription
+ signalingState SignalingState
+ iceConnectionState ICEConnectionState
+ connectionState PeerConnectionState
+
+ idpLoginURL *string
+
+ isClosed *atomicBool
+ isNegotiationNeeded *atomicBool
+ negotiationNeededState negotiationNeededState
+
+ lastOffer string
+ lastAnswer string
+
+ // a value containing the last known greater mid value
+ // we internally generate mids as numbers. Needed since JSEP
+ // requires that when reusing a media section a new unique mid
+ // should be defined (see JSEP 3.4.1).
+ greaterMid int
+
+ rtpTransceivers []*RTPTransceiver
+
+ onSignalingStateChangeHandler func(SignalingState)
+ onICEConnectionStateChangeHandler func(ICEConnectionState)
+ onConnectionStateChangeHandler func(PeerConnectionState)
+ onTrackHandler func(*TrackRemote, *RTPReceiver)
+ onDataChannelHandler func(*DataChannel)
+ onNegotiationNeededHandler atomic.Value // func()
+
+ iceGatherer *ICEGatherer
+ iceTransport *ICETransport
+ dtlsTransport *DTLSTransport
+ sctpTransport *SCTPTransport
+
+ // A reference to the associated API state used by this connection
+ api *API
+ log logging.LeveledLogger
+
+ interceptorRTCPWriter interceptor.RTCPWriter
+}
+
+// NewPeerConnection creates a PeerConnection with the default codecs and
+// interceptors. See RegisterDefaultCodecs and RegisterDefaultInterceptors.
+//
+// If you wish to customize the set of available codecs or the set of
+// active interceptors, create a MediaEngine and call api.NewPeerConnection
+// instead of this function.
+func NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
+ m := &MediaEngine{}
+ if err := m.RegisterDefaultCodecs(); err != nil {
+ return nil, err
+ }
+
+ i := &interceptor.Registry{}
+ if err := RegisterDefaultInterceptors(m, i); err != nil {
+ return nil, err
+ }
+
+ api := NewAPI(WithMediaEngine(m), WithInterceptorRegistry(i))
+ return api.NewPeerConnection(configuration)
+}
+
+// NewPeerConnection creates a new PeerConnection with the provided configuration against the received API object
+func (api *API) NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
+ // https://w3c.github.io/webrtc-pc/#constructor (Step #2)
+ // Some variables defined explicitly despite their implicit zero values to
+ // allow better readability to understand what is happening.
+ pc := &PeerConnection{
+ statsID: fmt.Sprintf("PeerConnection-%d", time.Now().UnixNano()),
+ configuration: Configuration{
+ ICEServers: []ICEServer{},
+ ICETransportPolicy: ICETransportPolicyAll,
+ BundlePolicy: BundlePolicyBalanced,
+ RTCPMuxPolicy: RTCPMuxPolicyRequire,
+ Certificates: []Certificate{},
+ ICECandidatePoolSize: 0,
+ },
+ ops: newOperations(),
+ isClosed: &atomicBool{},
+ isNegotiationNeeded: &atomicBool{},
+ negotiationNeededState: negotiationNeededStateEmpty,
+ lastOffer: "",
+ lastAnswer: "",
+ greaterMid: -1,
+ signalingState: SignalingStateStable,
+ iceConnectionState: ICEConnectionStateNew,
+ connectionState: PeerConnectionStateNew,
+
+ api: api,
+ log: api.settingEngine.LoggerFactory.NewLogger("pc"),
+ }
+
+ if !api.settingEngine.disableMediaEngineCopy {
+ pc.api = &API{
+ settingEngine: api.settingEngine,
+ mediaEngine: api.mediaEngine.copy(),
+ interceptor: api.interceptor,
+ }
+ }
+
+ var err error
+ if err = pc.initConfiguration(configuration); err != nil {
+ return nil, err
+ }
+
+ pc.iceGatherer, err = pc.createICEGatherer()
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the ice transport
+ iceTransport := pc.createICETransport()
+ pc.iceTransport = iceTransport
+
+ // Create the DTLS transport
+ dtlsTransport, err := pc.api.NewDTLSTransport(pc.iceTransport, pc.configuration.Certificates)
+ if err != nil {
+ return nil, err
+ }
+ pc.dtlsTransport = dtlsTransport
+
+ // Create the SCTP transport
+ pc.sctpTransport = pc.api.NewSCTPTransport(pc.dtlsTransport)
+
+ // Wire up the on datachannel handler
+ pc.sctpTransport.OnDataChannel(func(d *DataChannel) {
+ pc.mu.RLock()
+ handler := pc.onDataChannelHandler
+ pc.mu.RUnlock()
+ if handler != nil {
+ handler(d)
+ }
+ })
+
+ pc.interceptorRTCPWriter = api.interceptor.BindRTCPWriter(interceptor.RTCPWriterFunc(pc.writeRTCP))
+
+ return pc, nil
+}
+
+// initConfiguration defines validation of the specified Configuration and
+// its assignment to the internal configuration variable. This function differs
+// from its SetConfiguration counterpart because most of the checks do not
+// include verification statements related to the existing state. Thus the
+// function describes only minor verification of some the struct variables.
+func (pc *PeerConnection) initConfiguration(configuration Configuration) error {
+ if configuration.PeerIdentity != "" {
+ pc.configuration.PeerIdentity = configuration.PeerIdentity
+ }
+
+ // https://www.w3.org/TR/webrtc/#constructor (step #3)
+ if len(configuration.Certificates) > 0 {
+ now := time.Now()
+ for _, x509Cert := range configuration.Certificates {
+ if !x509Cert.Expires().IsZero() && now.After(x509Cert.Expires()) {
+ return &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}
+ }
+ pc.configuration.Certificates = append(pc.configuration.Certificates, x509Cert)
+ }
+ } else {
+ sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ return &rtcerr.UnknownError{Err: err}
+ }
+ certificate, err := GenerateCertificate(sk)
+ if err != nil {
+ return err
+ }
+ pc.configuration.Certificates = []Certificate{*certificate}
+ }
+
+ if configuration.BundlePolicy != BundlePolicy(Unknown) {
+ pc.configuration.BundlePolicy = configuration.BundlePolicy
+ }
+
+ if configuration.RTCPMuxPolicy != RTCPMuxPolicy(Unknown) {
+ pc.configuration.RTCPMuxPolicy = configuration.RTCPMuxPolicy
+ }
+
+ if configuration.ICECandidatePoolSize != 0 {
+ pc.configuration.ICECandidatePoolSize = configuration.ICECandidatePoolSize
+ }
+
+ if configuration.ICETransportPolicy != ICETransportPolicy(Unknown) {
+ pc.configuration.ICETransportPolicy = configuration.ICETransportPolicy
+ }
+
+ if configuration.SDPSemantics != SDPSemantics(Unknown) {
+ pc.configuration.SDPSemantics = configuration.SDPSemantics
+ }
+
+ sanitizedICEServers := configuration.getICEServers()
+ if len(sanitizedICEServers) > 0 {
+ for _, server := range sanitizedICEServers {
+ if err := server.validate(); err != nil {
+ return err
+ }
+ }
+ pc.configuration.ICEServers = sanitizedICEServers
+ }
+
+ return nil
+}
+
+// OnSignalingStateChange sets an event handler which is invoked when the
+// peer connection's signaling state changes
+func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState)) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ pc.onSignalingStateChangeHandler = f
+}
+
+func (pc *PeerConnection) onSignalingStateChange(newState SignalingState) {
+ pc.mu.RLock()
+ handler := pc.onSignalingStateChangeHandler
+ pc.mu.RUnlock()
+
+ pc.log.Infof("signaling state changed to %s", newState)
+ if handler != nil {
+ go handler(newState)
+ }
+}
+
+// OnDataChannel sets an event handler which is invoked when a data
+// channel message arrives from a remote peer.
+func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ pc.onDataChannelHandler = f
+}
+
+// OnNegotiationNeeded sets an event handler which is invoked when
+// a change has occurred which requires session negotiation
+func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
+ pc.onNegotiationNeededHandler.Store(f)
+}
+
+func (pc *PeerConnection) onNegotiationNeeded() {
+ // https://w3c.github.io/webrtc-pc/#updating-the-negotiation-needed-flag
+ // non-canon step 1
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ if pc.negotiationNeededState == negotiationNeededStateRun {
+ pc.negotiationNeededState = negotiationNeededStateQueue
+ return
+ } else if pc.negotiationNeededState == negotiationNeededStateQueue {
+ return
+ }
+
+ pc.negotiationNeededState = negotiationNeededStateRun
+
+ pc.ops.Enqueue(pc.negotiationNeededOp)
+}
+
+func (pc *PeerConnection) negotiationNeededOp() {
+ // Don't run NegotiatedNeeded checks if OnNegotiationNeeded is not set
+ if handler := pc.onNegotiationNeededHandler.Load(); handler == nil {
+ return
+ }
+
+ // https://www.w3.org/TR/webrtc/#updating-the-negotiation-needed-flag
+ // Step 2.1
+ if pc.isClosed.get() {
+ return
+ }
+ // non-canon step 2.2
+ if !pc.ops.IsEmpty() {
+ pc.ops.Enqueue(pc.negotiationNeededOp)
+ return
+ }
+
+ // non-canon, run again if there was a request
+ defer func() {
+ pc.mu.Lock()
+ if pc.negotiationNeededState == negotiationNeededStateQueue {
+ defer pc.onNegotiationNeeded()
+ }
+ pc.negotiationNeededState = negotiationNeededStateEmpty
+ pc.mu.Unlock()
+ }()
+
+ // Step 2.3
+ if pc.SignalingState() != SignalingStateStable {
+ return
+ }
+
+ // Step 2.4
+ if !pc.checkNegotiationNeeded() {
+ pc.isNegotiationNeeded.set(false)
+ return
+ }
+
+ // Step 2.5
+ if pc.isNegotiationNeeded.get() {
+ return
+ }
+
+ // Step 2.6
+ pc.isNegotiationNeeded.set(true)
+
+ // Step 2.7
+ if handler, ok := pc.onNegotiationNeededHandler.Load().(func()); ok && handler != nil {
+ handler()
+ }
+}
+
+func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:gocognit
+ // To check if negotiation is needed for connection, perform the following checks:
+ // Skip 1, 2 steps
+ // Step 3
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+
+ localDesc := pc.currentLocalDescription
+ remoteDesc := pc.currentRemoteDescription
+
+ if localDesc == nil {
+ return true
+ }
+
+ pc.sctpTransport.lock.Lock()
+ lenDataChannel := len(pc.sctpTransport.dataChannels)
+ pc.sctpTransport.lock.Unlock()
+
+ if lenDataChannel != 0 && haveDataChannel(localDesc) == nil {
+ return true
+ }
+
+ for _, t := range pc.rtpTransceivers {
+ // https://www.w3.org/TR/webrtc/#dfn-update-the-negotiation-needed-flag
+ // Step 5.1
+ // if t.stopping && !t.stopped {
+ // return true
+ // }
+ m := getByMid(t.Mid(), localDesc)
+ // Step 5.2
+ if !t.stopped && m == nil {
+ return true
+ }
+ if !t.stopped && m != nil {
+ // Step 5.3.1
+ if t.Direction() == RTPTransceiverDirectionSendrecv || t.Direction() == RTPTransceiverDirectionSendonly {
+ descMsid, okMsid := m.Attribute(sdp.AttrKeyMsid)
+ track := t.Sender().Track()
+ if !okMsid || descMsid != track.StreamID()+" "+track.ID() {
+ return true
+ }
+ }
+ switch localDesc.Type {
+ case SDPTypeOffer:
+ // Step 5.3.2
+ rm := getByMid(t.Mid(), remoteDesc)
+ if rm == nil {
+ return true
+ }
+
+ if getPeerDirection(m) != t.Direction() && getPeerDirection(rm) != t.Direction().Revers() {
+ return true
+ }
+ case SDPTypeAnswer:
+ // Step 5.3.3
+ if _, ok := m.Attribute(t.Direction().String()); !ok {
+ return true
+ }
+ default:
+ }
+ }
+ // Step 5.4
+ if t.stopped && t.Mid() != "" {
+ if getByMid(t.Mid(), localDesc) != nil || getByMid(t.Mid(), remoteDesc) != nil {
+ return true
+ }
+ }
+ }
+ // Step 6
+ return false
+}
+
+// OnICECandidate sets an event handler which is invoked when a new ICE
+// candidate is found.
+// Take note that the handler is gonna be called with a nil pointer when
+// gathering is finished.
+func (pc *PeerConnection) OnICECandidate(f func(*ICECandidate)) {
+ pc.iceGatherer.OnLocalCandidate(f)
+}
+
+// OnICEGatheringStateChange sets an event handler which is invoked when the
+// ICE candidate gathering state has changed.
+func (pc *PeerConnection) OnICEGatheringStateChange(f func(ICEGathererState)) {
+ pc.iceGatherer.OnStateChange(f)
+}
+
+// OnTrack sets an event handler which is called when remote track
+// arrives from a remote peer.
+func (pc *PeerConnection) OnTrack(f func(*TrackRemote, *RTPReceiver)) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ pc.onTrackHandler = f
+}
+
+func (pc *PeerConnection) onTrack(t *TrackRemote, r *RTPReceiver) {
+ pc.mu.RLock()
+ handler := pc.onTrackHandler
+ pc.mu.RUnlock()
+
+ pc.log.Debugf("got new track: %+v", t)
+ if t != nil {
+ if handler != nil {
+ go handler(t, r)
+ } else {
+ pc.log.Warnf("OnTrack unset, unable to handle incoming media streams")
+ }
+ }
+}
+
+// OnICEConnectionStateChange sets an event handler which is called
+// when an ICE connection state is changed.
+func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState)) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ pc.onICEConnectionStateChangeHandler = f
+}
+
+func (pc *PeerConnection) onICEConnectionStateChange(cs ICEConnectionState) {
+ pc.mu.Lock()
+ pc.iceConnectionState = cs
+ handler := pc.onICEConnectionStateChangeHandler
+ pc.mu.Unlock()
+
+ pc.log.Infof("ICE connection state changed: %s", cs)
+ if handler != nil {
+ go handler(cs)
+ }
+}
+
+// OnConnectionStateChange sets an event handler which is called
+// when the PeerConnectionState has changed
+func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ pc.onConnectionStateChangeHandler = f
+}
+
+// SetConfiguration updates the configuration of this PeerConnection object.
+func (pc *PeerConnection) SetConfiguration(configuration Configuration) error { //nolint:gocognit
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
+ if pc.isClosed.get() {
+ return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
+ if configuration.PeerIdentity != "" {
+ if configuration.PeerIdentity != pc.configuration.PeerIdentity {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}
+ }
+ pc.configuration.PeerIdentity = configuration.PeerIdentity
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
+ if len(configuration.Certificates) > 0 {
+ if len(configuration.Certificates) != len(pc.configuration.Certificates) {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
+ }
+
+ for i, certificate := range configuration.Certificates {
+ if !pc.configuration.Certificates[i].Equals(certificate) {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
+ }
+ }
+ pc.configuration.Certificates = configuration.Certificates
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)
+ if configuration.BundlePolicy != BundlePolicy(Unknown) {
+ if configuration.BundlePolicy != pc.configuration.BundlePolicy {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}
+ }
+ pc.configuration.BundlePolicy = configuration.BundlePolicy
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
+ if configuration.RTCPMuxPolicy != RTCPMuxPolicy(Unknown) {
+ if configuration.RTCPMuxPolicy != pc.configuration.RTCPMuxPolicy {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy}
+ }
+ pc.configuration.RTCPMuxPolicy = configuration.RTCPMuxPolicy
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #7)
+ if configuration.ICECandidatePoolSize != 0 {
+ if pc.configuration.ICECandidatePoolSize != configuration.ICECandidatePoolSize &&
+ pc.LocalDescription() != nil {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize}
+ }
+ pc.configuration.ICECandidatePoolSize = configuration.ICECandidatePoolSize
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #8)
+ if configuration.ICETransportPolicy != ICETransportPolicy(Unknown) {
+ pc.configuration.ICETransportPolicy = configuration.ICETransportPolicy
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11)
+ if len(configuration.ICEServers) > 0 {
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3)
+ for _, server := range configuration.ICEServers {
+ if err := server.validate(); err != nil {
+ return err
+ }
+ }
+ pc.configuration.ICEServers = configuration.ICEServers
+ }
+ return nil
+}
+
+// GetConfiguration returns a Configuration object representing the current
+// configuration of this PeerConnection object. The returned object is a
+// copy and direct mutation on it will not take affect until SetConfiguration
+// has been called with Configuration passed as its only argument.
+// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getconfiguration
+func (pc *PeerConnection) GetConfiguration() Configuration {
+ return pc.configuration
+}
+
+func (pc *PeerConnection) getStatsID() string {
+ pc.mu.RLock()
+ defer pc.mu.RUnlock()
+ return pc.statsID
+}
+
+func (pc *PeerConnection) hasLocalDescriptionChanged(desc *SessionDescription) bool {
+ for _, t := range pc.GetTransceivers() {
+ m := getByMid(t.Mid(), desc)
+ if m == nil {
+ return true
+ }
+
+ if getPeerDirection(m) != t.Direction() {
+ return true
+ }
+ }
+
+ return false
+}
+
+var errExcessiveRetries = errors.New("excessive retries in CreateOffer")
+
+// CreateOffer starts the PeerConnection and generates the localDescription
+// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-createoffer
+func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription, error) { //nolint:gocognit
+ useIdentity := pc.idpLoginURL != nil
+ switch {
+ case useIdentity:
+ return SessionDescription{}, errIdentityProviderNotImplemented
+ case pc.isClosed.get():
+ return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ if options != nil && options.ICERestart {
+ if err := pc.iceTransport.restart(); err != nil {
+ return SessionDescription{}, err
+ }
+ }
+
+ var (
+ d *sdp.SessionDescription
+ offer SessionDescription
+ err error
+ )
+
+ // This may be necessary to recompute if, for example, createOffer was called when only an
+ // audio RTCRtpTransceiver was added to connection, but while performing the in-parallel
+ // steps to create an offer, a video RTCRtpTransceiver was added, requiring additional
+ // inspection of video system resources.
+ count := 0
+ for {
+ // We cache current transceivers to ensure they aren't
+ // mutated during offer generation. We later check if they have
+ // been mutated and recompute the offer if necessary.
+ currentTransceivers := pc.GetTransceivers()
+
+ // in-parallel steps to create an offer
+ // https://w3c.github.io/webrtc-pc/#dfn-in-parallel-steps-to-create-an-offer
+ isPlanB := pc.configuration.SDPSemantics == SDPSemanticsPlanB
+ if pc.currentRemoteDescription != nil {
+ isPlanB = descriptionIsPlanB(pc.RemoteDescription())
+ }
+
+ // include unmatched local transceivers
+ if !isPlanB {
+ // update the greater mid if the remote description provides a greater one
+ if pc.currentRemoteDescription != nil {
+ var numericMid int
+ for _, media := range pc.currentRemoteDescription.parsed.MediaDescriptions {
+ mid := getMidValue(media)
+ if mid == "" {
+ continue
+ }
+ numericMid, err = strconv.Atoi(mid)
+ if err != nil {
+ continue
+ }
+ if numericMid > pc.greaterMid {
+ pc.greaterMid = numericMid
+ }
+ }
+ }
+ for _, t := range currentTransceivers {
+ if t.Mid() != "" {
+ continue
+ }
+ pc.greaterMid++
+ err = t.setMid(strconv.Itoa(pc.greaterMid))
+ if err != nil {
+ return SessionDescription{}, err
+ }
+ }
+ }
+
+ if pc.currentRemoteDescription == nil {
+ d, err = pc.generateUnmatchedSDP(currentTransceivers, useIdentity)
+ } else {
+ d, err = pc.generateMatchedSDP(currentTransceivers, useIdentity, true /*includeUnmatched */, connectionRoleFromDtlsRole(defaultDtlsRoleOffer))
+ }
+
+ if err != nil {
+ return SessionDescription{}, err
+ }
+
+ sdpBytes, err := d.Marshal()
+ if err != nil {
+ return SessionDescription{}, err
+ }
+
+ offer = SessionDescription{
+ Type: SDPTypeOffer,
+ SDP: string(sdpBytes),
+ parsed: d,
+ }
+
+ // Verify local media hasn't changed during offer
+ // generation. Recompute if necessary
+ if isPlanB || !pc.hasLocalDescriptionChanged(&offer) {
+ break
+ }
+ count++
+ if count >= 128 {
+ return SessionDescription{}, errExcessiveRetries
+ }
+ }
+
+ pc.lastOffer = offer.SDP
+ return offer, nil
+}
+
+func (pc *PeerConnection) createICEGatherer() (*ICEGatherer, error) {
+ g, err := pc.api.NewICEGatherer(ICEGatherOptions{
+ ICEServers: pc.configuration.getICEServers(),
+ ICEGatherPolicy: pc.configuration.ICETransportPolicy,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return g, nil
+}
+
+// Update the PeerConnectionState given the state of relevant transports
+// https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum
+func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnectionState, dtlsTransportState DTLSTransportState) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+
+ connectionState := PeerConnectionStateNew
+ switch {
+ // The RTCPeerConnection object's [[IsClosed]] slot is true.
+ case pc.isClosed.get():
+ connectionState = PeerConnectionStateClosed
+
+ // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state.
+ case iceConnectionState == ICEConnectionStateFailed || dtlsTransportState == DTLSTransportStateFailed:
+ connectionState = PeerConnectionStateFailed
+
+ // Any of the RTCIceTransports or RTCDtlsTransports are in the "disconnected"
+ // state and none of them are in the "failed" or "connecting" or "checking" state. */
+ case iceConnectionState == ICEConnectionStateDisconnected:
+ connectionState = PeerConnectionStateDisconnected
+
+ // All RTCIceTransports and RTCDtlsTransports are in the "connected", "completed" or "closed"
+ // state and at least one of them is in the "connected" or "completed" state.
+ case iceConnectionState == ICEConnectionStateConnected && dtlsTransportState == DTLSTransportStateConnected:
+ connectionState = PeerConnectionStateConnected
+
+ // Any of the RTCIceTransports or RTCDtlsTransports are in the "connecting" or
+ // "checking" state and none of them is in the "failed" state.
+ case iceConnectionState == ICEConnectionStateChecking && dtlsTransportState == DTLSTransportStateConnecting:
+ connectionState = PeerConnectionStateConnecting
+ }
+
+ if pc.connectionState == connectionState {
+ return
+ }
+
+ pc.log.Infof("peer connection state changed: %s", connectionState)
+ pc.connectionState = connectionState
+ handler := pc.onConnectionStateChangeHandler
+ if handler != nil {
+ go handler(connectionState)
+ }
+}
+
+func (pc *PeerConnection) createICETransport() *ICETransport {
+ t := pc.api.NewICETransport(pc.iceGatherer)
+ t.OnConnectionStateChange(func(state ICETransportState) {
+ var cs ICEConnectionState
+ switch state {
+ case ICETransportStateNew:
+ cs = ICEConnectionStateNew
+ case ICETransportStateChecking:
+ cs = ICEConnectionStateChecking
+ case ICETransportStateConnected:
+ cs = ICEConnectionStateConnected
+ case ICETransportStateCompleted:
+ cs = ICEConnectionStateCompleted
+ case ICETransportStateFailed:
+ cs = ICEConnectionStateFailed
+ case ICETransportStateDisconnected:
+ cs = ICEConnectionStateDisconnected
+ case ICETransportStateClosed:
+ cs = ICEConnectionStateClosed
+ default:
+ pc.log.Warnf("OnConnectionStateChange: unhandled ICE state: %s", state)
+ return
+ }
+ pc.onICEConnectionStateChange(cs)
+ pc.updateConnectionState(cs, pc.dtlsTransport.State())
+ })
+
+ return t
+}
+
+// CreateAnswer starts the PeerConnection and generates the localDescription
+func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (SessionDescription, error) {
+ useIdentity := pc.idpLoginURL != nil
+ switch {
+ case pc.RemoteDescription() == nil:
+ return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrNoRemoteDescription}
+ case useIdentity:
+ return SessionDescription{}, errIdentityProviderNotImplemented
+ case pc.isClosed.get():
+ return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ case pc.signalingState.Get() != SignalingStateHaveRemoteOffer && pc.signalingState.Get() != SignalingStateHaveLocalPranswer:
+ return SessionDescription{}, &rtcerr.InvalidStateError{Err: ErrIncorrectSignalingState}
+ }
+
+ connectionRole := connectionRoleFromDtlsRole(pc.api.settingEngine.answeringDTLSRole)
+ if connectionRole == sdp.ConnectionRole(0) {
+ connectionRole = connectionRoleFromDtlsRole(defaultDtlsRoleAnswer)
+ }
+
+ currentTransceivers := pc.GetTransceivers()
+ d, err := pc.generateMatchedSDP(currentTransceivers, useIdentity, false /*includeUnmatched */, connectionRole)
+ if err != nil {
+ return SessionDescription{}, err
+ }
+
+ sdpBytes, err := d.Marshal()
+ if err != nil {
+ return SessionDescription{}, err
+ }
+
+ desc := SessionDescription{
+ Type: SDPTypeAnswer,
+ SDP: string(sdpBytes),
+ parsed: d,
+ }
+ pc.lastAnswer = desc.SDP
+ return desc, nil
+}
+
+// 4.4.1.6 Set the SessionDescription
+func (pc *PeerConnection) setDescription(sd *SessionDescription, op stateChangeOp) error { //nolint:gocognit
+ switch {
+ case pc.isClosed.get():
+ return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ case NewSDPType(sd.Type.String()) == SDPType(Unknown):
+ return &rtcerr.TypeError{Err: fmt.Errorf("%w: '%d' is not a valid enum value of type SDPType", errPeerConnSDPTypeInvalidValue, sd.Type)}
+ }
+
+ nextState, err := func() (SignalingState, error) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+
+ cur := pc.SignalingState()
+ setLocal := stateChangeOpSetLocal
+ setRemote := stateChangeOpSetRemote
+ newSDPDoesNotMatchOffer := &rtcerr.InvalidModificationError{Err: errSDPDoesNotMatchOffer}
+ newSDPDoesNotMatchAnswer := &rtcerr.InvalidModificationError{Err: errSDPDoesNotMatchAnswer}
+
+ var nextState SignalingState
+ var err error
+ switch op {
+ case setLocal:
+ switch sd.Type {
+ // stable->SetLocal(offer)->have-local-offer
+ case SDPTypeOffer:
+ if sd.SDP != pc.lastOffer {
+ return nextState, newSDPDoesNotMatchOffer
+ }
+ nextState, err = checkNextSignalingState(cur, SignalingStateHaveLocalOffer, setLocal, sd.Type)
+ if err == nil {
+ pc.pendingLocalDescription = sd
+ }
+ // have-remote-offer->SetLocal(answer)->stable
+ // have-local-pranswer->SetLocal(answer)->stable
+ case SDPTypeAnswer:
+ if sd.SDP != pc.lastAnswer {
+ return nextState, newSDPDoesNotMatchAnswer
+ }
+ nextState, err = checkNextSignalingState(cur, SignalingStateStable, setLocal, sd.Type)
+ if err == nil {
+ pc.currentLocalDescription = sd
+ pc.currentRemoteDescription = pc.pendingRemoteDescription
+ pc.pendingRemoteDescription = nil
+ pc.pendingLocalDescription = nil
+ }
+ case SDPTypeRollback:
+ nextState, err = checkNextSignalingState(cur, SignalingStateStable, setLocal, sd.Type)
+ if err == nil {
+ pc.pendingLocalDescription = nil
+ }
+ // have-remote-offer->SetLocal(pranswer)->have-local-pranswer
+ case SDPTypePranswer:
+ if sd.SDP != pc.lastAnswer {
+ return nextState, newSDPDoesNotMatchAnswer
+ }
+ nextState, err = checkNextSignalingState(cur, SignalingStateHaveLocalPranswer, setLocal, sd.Type)
+ if err == nil {
+ pc.pendingLocalDescription = sd
+ }
+ default:
+ return nextState, &rtcerr.OperationError{Err: fmt.Errorf("%w: %s(%s)", errPeerConnStateChangeInvalid, op, sd.Type)}
+ }
+ case setRemote:
+ switch sd.Type {
+ // stable->SetRemote(offer)->have-remote-offer
+ case SDPTypeOffer:
+ nextState, err = checkNextSignalingState(cur, SignalingStateHaveRemoteOffer, setRemote, sd.Type)
+ if err == nil {
+ pc.pendingRemoteDescription = sd
+ }
+ // have-local-offer->SetRemote(answer)->stable
+ // have-remote-pranswer->SetRemote(answer)->stable
+ case SDPTypeAnswer:
+ nextState, err = checkNextSignalingState(cur, SignalingStateStable, setRemote, sd.Type)
+ if err == nil {
+ pc.currentRemoteDescription = sd
+ pc.currentLocalDescription = pc.pendingLocalDescription
+ pc.pendingRemoteDescription = nil
+ pc.pendingLocalDescription = nil
+ }
+ case SDPTypeRollback:
+ nextState, err = checkNextSignalingState(cur, SignalingStateStable, setRemote, sd.Type)
+ if err == nil {
+ pc.pendingRemoteDescription = nil
+ }
+ // have-local-offer->SetRemote(pranswer)->have-remote-pranswer
+ case SDPTypePranswer:
+ nextState, err = checkNextSignalingState(cur, SignalingStateHaveRemotePranswer, setRemote, sd.Type)
+ if err == nil {
+ pc.pendingRemoteDescription = sd
+ }
+ default:
+ return nextState, &rtcerr.OperationError{Err: fmt.Errorf("%w: %s(%s)", errPeerConnStateChangeInvalid, op, sd.Type)}
+ }
+ default:
+ return nextState, &rtcerr.OperationError{Err: fmt.Errorf("%w: %q", errPeerConnStateChangeUnhandled, op)}
+ }
+
+ return nextState, err
+ }()
+
+ if err == nil {
+ pc.signalingState.Set(nextState)
+ if pc.signalingState.Get() == SignalingStateStable {
+ pc.isNegotiationNeeded.set(false)
+ pc.onNegotiationNeeded()
+ }
+ pc.onSignalingStateChange(nextState)
+ }
+ return err
+}
+
+// SetLocalDescription sets the SessionDescription of the local peer
+func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error {
+ if pc.isClosed.get() {
+ return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ haveLocalDescription := pc.currentLocalDescription != nil
+
+ // JSEP 5.4
+ if desc.SDP == "" {
+ switch desc.Type {
+ case SDPTypeAnswer, SDPTypePranswer:
+ desc.SDP = pc.lastAnswer
+ case SDPTypeOffer:
+ desc.SDP = pc.lastOffer
+ default:
+ return &rtcerr.InvalidModificationError{
+ Err: fmt.Errorf("%w: %s", errPeerConnSDPTypeInvalidValueSetLocalDescription, desc.Type),
+ }
+ }
+ }
+
+ desc.parsed = &sdp.SessionDescription{}
+ if err := desc.parsed.Unmarshal([]byte(desc.SDP)); err != nil {
+ return err
+ }
+ if err := pc.setDescription(&desc, stateChangeOpSetLocal); err != nil {
+ return err
+ }
+
+ currentTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...)
+
+ weAnswer := desc.Type == SDPTypeAnswer
+ remoteDesc := pc.RemoteDescription()
+ if weAnswer && remoteDesc != nil {
+ if err := pc.startRTPSenders(currentTransceivers); err != nil {
+ return err
+ }
+ pc.ops.Enqueue(func() {
+ pc.startRTP(haveLocalDescription, remoteDesc, currentTransceivers)
+ })
+ }
+
+ if pc.iceGatherer.State() == ICEGathererStateNew {
+ return pc.iceGatherer.Gather()
+ }
+ return nil
+}
+
+// LocalDescription returns PendingLocalDescription if it is not null and
+// otherwise it returns CurrentLocalDescription. This property is used to
+// determine if SetLocalDescription has already been called.
+// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-localdescription
+func (pc *PeerConnection) LocalDescription() *SessionDescription {
+ if pendingLocalDescription := pc.PendingLocalDescription(); pendingLocalDescription != nil {
+ return pendingLocalDescription
+ }
+ return pc.CurrentLocalDescription()
+}
+
+// SetRemoteDescription sets the SessionDescription of the remote peer
+// nolint: gocyclo
+func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { //nolint:gocognit
+ if pc.isClosed.get() {
+ return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ isRenegotation := pc.currentRemoteDescription != nil
+
+ if _, err := desc.Unmarshal(); err != nil {
+ return err
+ }
+ if err := pc.setDescription(&desc, stateChangeOpSetRemote); err != nil {
+ return err
+ }
+
+ if err := pc.api.mediaEngine.updateFromRemoteDescription(*desc.parsed); err != nil {
+ return err
+ }
+
+ var t *RTPTransceiver
+ localTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...)
+ detectedPlanB := descriptionIsPlanB(pc.RemoteDescription())
+ weOffer := desc.Type == SDPTypeAnswer
+
+ if !weOffer && !detectedPlanB {
+ for _, media := range pc.RemoteDescription().parsed.MediaDescriptions {
+ midValue := getMidValue(media)
+ if midValue == "" {
+ return errPeerConnRemoteDescriptionWithoutMidValue
+ }
+
+ if media.MediaName.Media == mediaSectionApplication {
+ continue
+ }
+
+ kind := NewRTPCodecType(media.MediaName.Media)
+ direction := getPeerDirection(media)
+ if kind == 0 || direction == RTPTransceiverDirection(Unknown) {
+ continue
+ }
+
+ t, localTransceivers = findByMid(midValue, localTransceivers)
+ if t == nil {
+ t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers)
+ } else if direction == RTPTransceiverDirectionInactive {
+ if err := t.Stop(); err != nil {
+ return err
+ }
+ }
+
+ if t == nil {
+ receiver, err := pc.api.NewRTPReceiver(kind, pc.dtlsTransport)
+ if err != nil {
+ return err
+ }
+
+ localDirection := RTPTransceiverDirectionRecvonly
+ if direction == RTPTransceiverDirectionRecvonly {
+ localDirection = RTPTransceiverDirectionSendonly
+ }
+
+ t = pc.newRTPTransceiver(receiver, nil, localDirection, kind)
+
+ pc.onNegotiationNeeded()
+ } else if direction == RTPTransceiverDirectionRecvonly {
+ if t.Direction() == RTPTransceiverDirectionSendrecv {
+ t.setDirection(RTPTransceiverDirectionSendonly)
+ }
+ }
+
+ if t.Mid() == "" {
+ if err := t.setMid(midValue); err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ remoteUfrag, remotePwd, candidates, err := extractICEDetails(desc.parsed)
+ if err != nil {
+ return err
+ }
+
+ if isRenegotation && pc.iceTransport.haveRemoteCredentialsChange(remoteUfrag, remotePwd) {
+ // An ICE Restart only happens implicitly for a SetRemoteDescription of type offer
+ if !weOffer {
+ if err = pc.iceTransport.restart(); err != nil {
+ return err
+ }
+ }
+
+ if err = pc.iceTransport.setRemoteCredentials(remoteUfrag, remotePwd); err != nil {
+ return err
+ }
+ }
+
+ for i := range candidates {
+ if err = pc.iceTransport.AddRemoteCandidate(&candidates[i]); err != nil {
+ return err
+ }
+ }
+
+ currentTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...)
+
+ if isRenegotation {
+ if weOffer {
+ if err = pc.startRTPSenders(currentTransceivers); err != nil {
+ return err
+ }
+ pc.ops.Enqueue(func() {
+ pc.startRTP(true, &desc, currentTransceivers)
+ })
+ }
+ return nil
+ }
+
+ remoteIsLite := false
+ for _, a := range desc.parsed.Attributes {
+ if strings.TrimSpace(a.Key) == sdp.AttrKeyICELite {
+ remoteIsLite = true
+ }
+ }
+
+ fingerprint, fingerprintHash, err := extractFingerprint(desc.parsed)
+ if err != nil {
+ return err
+ }
+
+ iceRole := ICERoleControlled
+ // If one of the agents is lite and the other one is not, the lite agent must be the controlling agent.
+ // If both or neither agents are lite the offering agent is controlling.
+ // RFC 8445 S6.1.1
+ if (weOffer && remoteIsLite == pc.api.settingEngine.candidates.ICELite) || (remoteIsLite && !pc.api.settingEngine.candidates.ICELite) {
+ iceRole = ICERoleControlling
+ }
+
+ // Start the networking in a new routine since it will block until
+ // the connection is actually established.
+ if weOffer {
+ if err := pc.startRTPSenders(currentTransceivers); err != nil {
+ return err
+ }
+ }
+
+ pc.ops.Enqueue(func() {
+ pc.startTransports(iceRole, dtlsRoleFromRemoteSDP(desc.parsed), remoteUfrag, remotePwd, fingerprint, fingerprintHash)
+ if weOffer {
+ pc.startRTP(false, &desc, currentTransceivers)
+ }
+ })
+ return nil
+}
+
+func (pc *PeerConnection) startReceiver(incoming trackDetails, receiver *RTPReceiver) {
+ encodings := []RTPDecodingParameters{}
+ if incoming.ssrc != 0 {
+ encodings = append(encodings, RTPDecodingParameters{RTPCodingParameters{SSRC: incoming.ssrc}})
+ }
+ for _, rid := range incoming.rids {
+ encodings = append(encodings, RTPDecodingParameters{RTPCodingParameters{RID: rid}})
+ }
+
+ if err := receiver.Receive(RTPReceiveParameters{Encodings: encodings}); err != nil {
+ pc.log.Warnf("RTPReceiver Receive failed %s", err)
+ return
+ }
+
+ // set track id and label early so they can be set as new track information
+ // is received from the SDP.
+ for i := range receiver.tracks {
+ receiver.tracks[i].track.mu.Lock()
+ receiver.tracks[i].track.id = incoming.id
+ receiver.tracks[i].track.streamID = incoming.streamID
+ receiver.tracks[i].track.mu.Unlock()
+ }
+
+ // We can't block and wait for a single SSRC
+ if incoming.ssrc == 0 {
+ return
+ }
+
+ go func() {
+ if err := receiver.Track().determinePayloadType(); err != nil {
+ pc.log.Warnf("Could not determine PayloadType for SSRC %d", receiver.Track().SSRC())
+ return
+ }
+
+ params, err := pc.api.mediaEngine.getRTPParametersByPayloadType(receiver.Track().PayloadType())
+ if err != nil {
+ pc.log.Warnf("no codec could be found for payloadType %d", receiver.Track().PayloadType())
+ return
+ }
+
+ receiver.Track().mu.Lock()
+ receiver.Track().kind = receiver.kind
+ receiver.Track().codec = params.Codecs[0]
+ receiver.Track().params = params
+ receiver.Track().mu.Unlock()
+
+ pc.onTrack(receiver.Track(), receiver)
+ }()
+}
+
+// startRTPReceivers opens knows inbound SRTP streams from the RemoteDescription
+func (pc *PeerConnection) startRTPReceivers(incomingTracks []trackDetails, currentTransceivers []*RTPTransceiver) { //nolint:gocognit
+ localTransceivers := append([]*RTPTransceiver{}, currentTransceivers...)
+
+ remoteIsPlanB := false
+ switch pc.configuration.SDPSemantics {
+ case SDPSemanticsPlanB:
+ remoteIsPlanB = true
+ case SDPSemanticsUnifiedPlanWithFallback:
+ remoteIsPlanB = descriptionIsPlanB(pc.RemoteDescription())
+ default:
+ // none
+ }
+
+ // Ensure we haven't already started a transceiver for this ssrc
+ for i := range incomingTracks {
+ if len(incomingTracks) <= i {
+ break
+ }
+ incomingTrack := incomingTracks[i]
+
+ for _, t := range localTransceivers {
+ if (t.Receiver()) == nil || t.Receiver().Track() == nil || t.Receiver().Track().ssrc != incomingTrack.ssrc {
+ continue
+ }
+
+ incomingTracks = filterTrackWithSSRC(incomingTracks, incomingTrack.ssrc)
+ }
+ }
+
+ unhandledTracks := incomingTracks[:0]
+ for i := range incomingTracks {
+ trackHandled := false
+ for j := range localTransceivers {
+ t := localTransceivers[j]
+ incomingTrack := incomingTracks[i]
+
+ if t.Mid() != incomingTrack.mid {
+ continue
+ }
+
+ if (incomingTrack.kind != t.kind) ||
+ (t.Direction() != RTPTransceiverDirectionRecvonly && t.Direction() != RTPTransceiverDirectionSendrecv) ||
+ (t.Receiver()) == nil ||
+ (t.Receiver().haveReceived()) {
+ continue
+ }
+
+ pc.startReceiver(incomingTrack, t.Receiver())
+ trackHandled = true
+ break
+ }
+
+ if !trackHandled {
+ unhandledTracks = append(unhandledTracks, incomingTracks[i])
+ }
+ }
+
+ if remoteIsPlanB {
+ for _, incoming := range unhandledTracks {
+ t, err := pc.AddTransceiverFromKind(incoming.kind, RtpTransceiverInit{
+ Direction: RTPTransceiverDirectionSendrecv,
+ })
+ if err != nil {
+ pc.log.Warnf("Could not add transceiver for remote SSRC %d: %s", incoming.ssrc, err)
+ continue
+ }
+ pc.startReceiver(incoming, t.Receiver())
+ }
+ }
+}
+
+// startRTPSenders starts all outbound RTP streams
+func (pc *PeerConnection) startRTPSenders(currentTransceivers []*RTPTransceiver) error {
+ for _, transceiver := range currentTransceivers {
+ if transceiver.Sender() != nil && transceiver.Sender().isNegotiated() && !transceiver.Sender().hasSent() {
+ err := transceiver.Sender().Send(RTPSendParameters{
+ Encodings: []RTPEncodingParameters{
+ {
+ RTPCodingParameters{
+ SSRC: transceiver.Sender().ssrc,
+ PayloadType: transceiver.Sender().payloadType,
+ },
+ },
+ },
+ })
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+// Start SCTP subsystem
+func (pc *PeerConnection) startSCTP() {
+ // Start sctp
+ if err := pc.sctpTransport.Start(SCTPCapabilities{
+ MaxMessageSize: 0,
+ }); err != nil {
+ pc.log.Warnf("Failed to start SCTP: %s", err)
+ if err = pc.sctpTransport.Stop(); err != nil {
+ pc.log.Warnf("Failed to stop SCTPTransport: %s", err)
+ }
+
+ return
+ }
+
+ // DataChannels that need to be opened now that SCTP is available
+ // make a copy we may have incoming DataChannels mutating this while we open
+ pc.sctpTransport.lock.RLock()
+ dataChannels := append([]*DataChannel{}, pc.sctpTransport.dataChannels...)
+ pc.sctpTransport.lock.RUnlock()
+
+ var openedDCCount uint32
+ for _, d := range dataChannels {
+ if d.ReadyState() == DataChannelStateConnecting {
+ err := d.open(pc.sctpTransport)
+ if err != nil {
+ pc.log.Warnf("failed to open data channel: %s", err)
+ continue
+ }
+ openedDCCount++
+ }
+ }
+
+ pc.sctpTransport.lock.Lock()
+ pc.sctpTransport.dataChannelsOpened += openedDCCount
+ pc.sctpTransport.lock.Unlock()
+}
+
+func (pc *PeerConnection) handleUndeclaredSSRC(rtpStream io.Reader, ssrc SSRC) error { //nolint:gocognit
+ remoteDescription := pc.RemoteDescription()
+ if remoteDescription == nil {
+ return errPeerConnRemoteDescriptionNil
+ }
+
+ // If the remote SDP was only one media section the ssrc doesn't have to be explicitly declared
+ if len(remoteDescription.parsed.MediaDescriptions) == 1 {
+ onlyMediaSection := remoteDescription.parsed.MediaDescriptions[0]
+ for _, a := range onlyMediaSection.Attributes {
+ if a.Key == ssrcStr {
+ return errPeerConnSingleMediaSectionHasExplicitSSRC
+ }
+ }
+
+ incoming := trackDetails{
+ ssrc: ssrc,
+ kind: RTPCodecTypeVideo,
+ }
+ if onlyMediaSection.MediaName.Media == RTPCodecTypeAudio.String() {
+ incoming.kind = RTPCodecTypeAudio
+ }
+
+ t, err := pc.AddTransceiverFromKind(incoming.kind, RtpTransceiverInit{
+ Direction: RTPTransceiverDirectionSendrecv,
+ })
+ if err != nil {
+ return fmt.Errorf("%w: %d: %s", errPeerConnRemoteSSRCAddTransceiver, ssrc, err)
+ }
+ pc.startReceiver(incoming, t.Receiver())
+ return nil
+ }
+
+ midExtensionID, audioSupported, videoSupported := pc.api.mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESMidURI})
+ if !audioSupported && !videoSupported {
+ return errPeerConnSimulcastMidRTPExtensionRequired
+ }
+
+ streamIDExtensionID, audioSupported, videoSupported := pc.api.mediaEngine.getHeaderExtensionID(RTPHeaderExtensionCapability{sdp.SDESRTPStreamIDURI})
+ if !audioSupported && !videoSupported {
+ return errPeerConnSimulcastStreamIDRTPExtensionRequired
+ }
+
+ b := make([]byte, receiveMTU)
+ var mid, rid string
+ for readCount := 0; readCount <= simulcastProbeCount; readCount++ {
+ i, err := rtpStream.Read(b)
+ if err != nil {
+ return err
+ }
+
+ maybeMid, maybeRid, payloadType, err := handleUnknownRTPPacket(b[:i], uint8(midExtensionID), uint8(streamIDExtensionID))
+ if err != nil {
+ return err
+ }
+
+ if maybeMid != "" {
+ mid = maybeMid
+ }
+ if maybeRid != "" {
+ rid = maybeRid
+ }
+
+ if mid == "" || rid == "" {
+ continue
+ }
+
+ params, err := pc.api.mediaEngine.getRTPParametersByPayloadType(payloadType)
+ if err != nil {
+ return err
+ }
+
+ for _, t := range pc.GetTransceivers() {
+ if t.Mid() != mid || t.Receiver() == nil {
+ continue
+ }
+
+ track, err := t.Receiver().receiveForRid(rid, params, ssrc)
+ if err != nil {
+ return err
+ }
+ pc.onTrack(track, t.Receiver())
+ return nil
+ }
+ }
+
+ return errPeerConnSimulcastIncomingSSRCFailed
+}
+
+// undeclaredMediaProcessor handles RTP/RTCP packets that don't match any a:ssrc lines
+func (pc *PeerConnection) undeclaredMediaProcessor() {
+ go func() {
+ var simulcastRoutineCount uint64
+ for {
+ srtpSession, err := pc.dtlsTransport.getSRTPSession()
+ if err != nil {
+ pc.log.Warnf("undeclaredMediaProcessor failed to open SrtpSession: %v", err)
+ return
+ }
+
+ stream, ssrc, err := srtpSession.AcceptStream()
+ if err != nil {
+ pc.log.Warnf("Failed to accept RTP %v", err)
+ return
+ }
+
+ if pc.isClosed.get() {
+ if err = stream.Close(); err != nil {
+ pc.log.Warnf("Failed to close RTP stream %v", err)
+ }
+ continue
+ }
+
+ if atomic.AddUint64(&simulcastRoutineCount, 1) >= simulcastMaxProbeRoutines {
+ atomic.AddUint64(&simulcastRoutineCount, ^uint64(0))
+ pc.log.Warn(ErrSimulcastProbeOverflow.Error())
+ continue
+ }
+
+ go func(rtpStream io.Reader, ssrc SSRC) {
+ pc.dtlsTransport.storeSimulcastStream(stream)
+
+ if err := pc.handleUndeclaredSSRC(rtpStream, ssrc); err != nil {
+ pc.log.Errorf("Incoming unhandled RTP ssrc(%d), OnTrack will not be fired. %v", ssrc, err)
+ }
+ atomic.AddUint64(&simulcastRoutineCount, ^uint64(0))
+ }(stream, SSRC(ssrc))
+ }
+ }()
+
+ go func() {
+ for {
+ srtcpSession, err := pc.dtlsTransport.getSRTCPSession()
+ if err != nil {
+ pc.log.Warnf("undeclaredMediaProcessor failed to open SrtcpSession: %v", err)
+ return
+ }
+
+ _, ssrc, err := srtcpSession.AcceptStream()
+ if err != nil {
+ pc.log.Warnf("Failed to accept RTCP %v", err)
+ return
+ }
+ pc.log.Warnf("Incoming unhandled RTCP ssrc(%d), OnTrack will not be fired", ssrc)
+ }
+ }()
+}
+
+// RemoteDescription returns pendingRemoteDescription if it is not null and
+// otherwise it returns currentRemoteDescription. This property is used to
+// determine if setRemoteDescription has already been called.
+// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-remotedescription
+func (pc *PeerConnection) RemoteDescription() *SessionDescription {
+ pc.mu.RLock()
+ defer pc.mu.RUnlock()
+
+ if pc.pendingRemoteDescription != nil {
+ return pc.pendingRemoteDescription
+ }
+ return pc.currentRemoteDescription
+}
+
+// AddICECandidate accepts an ICE candidate string and adds it
+// to the existing set of candidates.
+func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) error {
+ if pc.RemoteDescription() == nil {
+ return &rtcerr.InvalidStateError{Err: ErrNoRemoteDescription}
+ }
+
+ candidateValue := strings.TrimPrefix(candidate.Candidate, "candidate:")
+
+ var iceCandidate *ICECandidate
+ if candidateValue != "" {
+ candidate, err := ice.UnmarshalCandidate(candidateValue)
+ if err != nil {
+ return err
+ }
+
+ c, err := newICECandidateFromICE(candidate)
+ if err != nil {
+ return err
+ }
+ iceCandidate = &c
+ }
+
+ return pc.iceTransport.AddRemoteCandidate(iceCandidate)
+}
+
+// ICEConnectionState returns the ICE connection state of the
+// PeerConnection instance.
+func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
+ pc.mu.RLock()
+ defer pc.mu.RUnlock()
+
+ return pc.iceConnectionState
+}
+
+// GetSenders returns the RTPSender that are currently attached to this PeerConnection
+func (pc *PeerConnection) GetSenders() []*RTPSender {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+
+ result := []*RTPSender{}
+ for _, transceiver := range pc.rtpTransceivers {
+ if transceiver.Sender() != nil {
+ result = append(result, transceiver.Sender())
+ }
+ }
+ return result
+}
+
+// GetReceivers returns the RTPReceivers that are currently attached to this PeerConnection
+func (pc *PeerConnection) GetReceivers() (receivers []*RTPReceiver) {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+
+ for _, transceiver := range pc.rtpTransceivers {
+ if transceiver.Receiver() != nil {
+ receivers = append(receivers, transceiver.Receiver())
+ }
+ }
+ return
+}
+
+// GetTransceivers returns the RtpTransceiver that are currently attached to this PeerConnection
+func (pc *PeerConnection) GetTransceivers() []*RTPTransceiver {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+
+ return pc.rtpTransceivers
+}
+
+// AddTrack adds a Track to the PeerConnection
+func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) {
+ if pc.isClosed.get() {
+ return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ var transceiver *RTPTransceiver
+ for _, t := range pc.GetTransceivers() {
+ if !t.stopped && t.kind == track.Kind() && t.Sender() == nil {
+ transceiver = t
+ break
+ }
+ }
+ if transceiver != nil {
+ sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport)
+ if err != nil {
+ return nil, err
+ }
+ transceiver.setSender(sender)
+ // we still need to call setSendingTrack to ensure direction has changed
+ if err := transceiver.setSendingTrack(track); err != nil {
+ return nil, err
+ }
+ pc.onNegotiationNeeded()
+
+ return sender, nil
+ }
+
+ transceiver, err := pc.AddTransceiverFromTrack(track)
+ if err != nil {
+ return nil, err
+ }
+
+ return transceiver.Sender(), nil
+}
+
+// RemoveTrack removes a Track from the PeerConnection
+func (pc *PeerConnection) RemoveTrack(sender *RTPSender) error {
+ if pc.isClosed.get() {
+ return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ var transceiver *RTPTransceiver
+ for _, t := range pc.GetTransceivers() {
+ if t.Sender() == sender {
+ transceiver = t
+ break
+ }
+ }
+
+ if transceiver == nil {
+ return &rtcerr.InvalidAccessError{Err: ErrSenderNotCreatedByConnection}
+ } else if err := sender.Stop(); err != nil {
+ return err
+ }
+
+ if err := transceiver.setSendingTrack(nil); err != nil {
+ return err
+ }
+
+ pc.onNegotiationNeeded()
+
+ return nil
+}
+
+// AddTransceiverFromKind Create a new RtpTransceiver(SendRecv or RecvOnly) and add it to the set of transceivers.
+func (pc *PeerConnection) AddTransceiverFromKind(kind RTPCodecType, init ...RtpTransceiverInit) (*RTPTransceiver, error) {
+ if pc.isClosed.get() {
+ return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ direction := RTPTransceiverDirectionSendrecv
+ if len(init) > 1 {
+ return nil, errPeerConnAddTransceiverFromKindOnlyAcceptsOne
+ } else if len(init) == 1 {
+ direction = init[0].Direction
+ }
+
+ switch direction {
+ case RTPTransceiverDirectionSendrecv:
+ codecs := pc.api.mediaEngine.getCodecsByKind(kind)
+ if len(codecs) == 0 {
+ return nil, ErrNoCodecsAvailable
+ }
+
+ track, err := NewTrackLocalStaticSample(codecs[0].RTPCodecCapability, util.MathRandAlpha(16), util.MathRandAlpha(16))
+ if err != nil {
+ return nil, err
+ }
+
+ return pc.AddTransceiverFromTrack(track, init...)
+ case RTPTransceiverDirectionRecvonly:
+ receiver, err := pc.api.NewRTPReceiver(kind, pc.dtlsTransport)
+ if err != nil {
+ return nil, err
+ }
+
+ t := pc.newRTPTransceiver(
+ receiver,
+ nil,
+ RTPTransceiverDirectionRecvonly,
+ kind,
+ )
+
+ pc.onNegotiationNeeded()
+
+ return t, nil
+ default:
+ return nil, errPeerConnAddTransceiverFromKindSupport
+ }
+}
+
+// AddTransceiverFromTrack Create a new RtpTransceiver(SendRecv or SendOnly) and add it to the set of transceivers.
+func (pc *PeerConnection) AddTransceiverFromTrack(track TrackLocal, init ...RtpTransceiverInit) (*RTPTransceiver, error) {
+ if pc.isClosed.get() {
+ return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ direction := RTPTransceiverDirectionSendrecv
+ if len(init) > 1 {
+ return nil, errPeerConnAddTransceiverFromTrackOnlyAcceptsOne
+ } else if len(init) == 1 {
+ direction = init[0].Direction
+ }
+
+ switch direction {
+ case RTPTransceiverDirectionSendrecv:
+ receiver, err := pc.api.NewRTPReceiver(track.Kind(), pc.dtlsTransport)
+ if err != nil {
+ return nil, err
+ }
+
+ sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport)
+ if err != nil {
+ return nil, err
+ }
+
+ t := pc.newRTPTransceiver(
+ receiver,
+ sender,
+ RTPTransceiverDirectionSendrecv,
+ track.Kind(),
+ )
+
+ pc.onNegotiationNeeded()
+
+ return t, nil
+
+ case RTPTransceiverDirectionSendonly:
+ sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport)
+ if err != nil {
+ return nil, err
+ }
+
+ t := pc.newRTPTransceiver(
+ nil,
+ sender,
+ RTPTransceiverDirectionSendonly,
+ track.Kind(),
+ )
+
+ pc.onNegotiationNeeded()
+
+ return t, nil
+ default:
+ return nil, errPeerConnAddTransceiverFromTrackSupport
+ }
+}
+
+// CreateDataChannel creates a new DataChannel object with the given label
+// and optional DataChannelInit used to configure properties of the
+// underlying channel such as data reliability.
+func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelInit) (*DataChannel, error) {
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #2)
+ if pc.isClosed.get() {
+ return nil, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ params := &DataChannelParameters{
+ Label: label,
+ Ordered: true,
+ }
+
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #19)
+ if options != nil {
+ params.ID = options.ID
+ }
+
+ if options != nil {
+ // Ordered indicates if data is allowed to be delivered out of order. The
+ // default value of true, guarantees that data will be delivered in order.
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #9)
+ if options.Ordered != nil {
+ params.Ordered = *options.Ordered
+ }
+
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #7)
+ if options.MaxPacketLifeTime != nil {
+ params.MaxPacketLifeTime = options.MaxPacketLifeTime
+ }
+
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #8)
+ if options.MaxRetransmits != nil {
+ params.MaxRetransmits = options.MaxRetransmits
+ }
+
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #10)
+ if options.Protocol != nil {
+ params.Protocol = *options.Protocol
+ }
+
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #11)
+ if len(params.Protocol) > 65535 {
+ return nil, &rtcerr.TypeError{Err: ErrProtocolTooLarge}
+ }
+
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #12)
+ if options.Negotiated != nil {
+ params.Negotiated = *options.Negotiated
+ }
+ }
+
+ d, err := pc.api.newDataChannel(params, pc.log)
+ if err != nil {
+ return nil, err
+ }
+
+ // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #16)
+ if d.maxPacketLifeTime != nil && d.maxRetransmits != nil {
+ return nil, &rtcerr.TypeError{Err: ErrRetransmitsOrPacketLifeTime}
+ }
+
+ pc.sctpTransport.lock.Lock()
+ pc.sctpTransport.dataChannels = append(pc.sctpTransport.dataChannels, d)
+ pc.sctpTransport.dataChannelsRequested++
+ pc.sctpTransport.lock.Unlock()
+
+ // If SCTP already connected open all the channels
+ if pc.sctpTransport.State() == SCTPTransportStateConnected {
+ if err = d.open(pc.sctpTransport); err != nil {
+ return nil, err
+ }
+ }
+
+ pc.onNegotiationNeeded()
+
+ return d, nil
+}
+
+// SetIdentityProvider is used to configure an identity provider to generate identity assertions
+func (pc *PeerConnection) SetIdentityProvider(provider string) error {
+ return errPeerConnSetIdentityProviderNotImplemented
+}
+
+// WriteRTCP sends a user provided RTCP packet to the connected peer. If no peer is connected the
+// packet is discarded. It also runs any configured interceptors.
+func (pc *PeerConnection) WriteRTCP(pkts []rtcp.Packet) error {
+ _, err := pc.interceptorRTCPWriter.Write(pkts, make(interceptor.Attributes))
+ return err
+}
+
+func (pc *PeerConnection) writeRTCP(pkts []rtcp.Packet, _ interceptor.Attributes) (int, error) {
+ raw, err := rtcp.Marshal(pkts)
+ if err != nil {
+ return 0, err
+ }
+
+ srtcpSession, err := pc.dtlsTransport.getSRTCPSession()
+ if err != nil {
+ return 0, nil
+ }
+
+ writeStream, err := srtcpSession.OpenWriteStream()
+ if err != nil {
+ return 0, fmt.Errorf("%w: %v", errPeerConnWriteRTCPOpenWriteStream, err)
+ }
+
+ if n, err := writeStream.Write(raw); err != nil {
+ return n, err
+ }
+ return 0, nil
+}
+
+// Close ends the PeerConnection
+func (pc *PeerConnection) Close() error {
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #1)
+ if pc.isClosed.get() {
+ return nil
+ }
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #2)
+ pc.isClosed.set(true)
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #3)
+ pc.signalingState.Set(SignalingStateClosed)
+
+ // Try closing everything and collect the errors
+ // Shutdown strategy:
+ // 1. All Conn close by closing their underlying Conn.
+ // 2. A Mux stops this chain. It won't close the underlying
+ // Conn if one of the endpoints is closed down. To
+ // continue the chain the Mux has to be closed.
+ closeErrs := make([]error, 4)
+
+ closeErrs = append(closeErrs, pc.api.interceptor.Close())
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #4)
+ for _, t := range pc.GetTransceivers() {
+ if !t.stopped {
+ closeErrs = append(closeErrs, t.Stop())
+ }
+ }
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #5)
+ pc.sctpTransport.lock.Lock()
+ for _, d := range pc.sctpTransport.dataChannels {
+ d.setReadyState(DataChannelStateClosed)
+ }
+ pc.sctpTransport.lock.Unlock()
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #6)
+ if pc.sctpTransport != nil {
+ closeErrs = append(closeErrs, pc.sctpTransport.Stop())
+ }
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #7)
+ closeErrs = append(closeErrs, pc.dtlsTransport.Stop())
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #8, #9, #10)
+ if pc.iceTransport != nil {
+ closeErrs = append(closeErrs, pc.iceTransport.Stop())
+ }
+
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-close (step #11)
+ pc.updateConnectionState(pc.ICEConnectionState(), pc.dtlsTransport.State())
+
+ return util.FlattenErrs(closeErrs)
+}
+
+func (pc *PeerConnection) newRTPTransceiver(
+ receiver *RTPReceiver,
+ sender *RTPSender,
+ direction RTPTransceiverDirection,
+ kind RTPCodecType,
+) *RTPTransceiver {
+ t := &RTPTransceiver{kind: kind}
+ t.setReceiver(receiver)
+ t.setSender(sender)
+ t.setDirection(direction)
+
+ pc.mu.Lock()
+ pc.rtpTransceivers = append(pc.rtpTransceivers, t)
+ pc.mu.Unlock()
+
+ return t
+}
+
+// CurrentLocalDescription represents the local description that was
+// successfully negotiated the last time the PeerConnection transitioned
+// into the stable state plus any local candidates that have been generated
+// by the ICEAgent since the offer or answer was created.
+func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ return populateLocalCandidates(pc.currentLocalDescription, pc.iceGatherer, pc.ICEGatheringState())
+}
+
+// PendingLocalDescription represents a local description that is in the
+// process of being negotiated plus any local candidates that have been
+// generated by the ICEAgent since the offer or answer was created. If the
+// PeerConnection is in the stable state, the value is null.
+func (pc *PeerConnection) PendingLocalDescription() *SessionDescription {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+ return populateLocalCandidates(pc.pendingLocalDescription, pc.iceGatherer, pc.ICEGatheringState())
+}
+
+// CurrentRemoteDescription represents the last remote description that was
+// successfully negotiated the last time the PeerConnection transitioned
+// into the stable state plus any remote candidates that have been supplied
+// via AddICECandidate() since the offer or answer was created.
+func (pc *PeerConnection) CurrentRemoteDescription() *SessionDescription {
+ return pc.currentRemoteDescription
+}
+
+// PendingRemoteDescription represents a remote description that is in the
+// process of being negotiated, complete with any remote candidates that
+// have been supplied via AddICECandidate() since the offer or answer was
+// created. If the PeerConnection is in the stable state, the value is
+// null.
+func (pc *PeerConnection) PendingRemoteDescription() *SessionDescription {
+ return pc.pendingRemoteDescription
+}
+
+// SignalingState attribute returns the signaling state of the
+// PeerConnection instance.
+func (pc *PeerConnection) SignalingState() SignalingState {
+ return pc.signalingState.Get()
+}
+
+// ICEGatheringState attribute returns the ICE gathering state of the
+// PeerConnection instance.
+func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
+ if pc.iceGatherer == nil {
+ return ICEGatheringStateNew
+ }
+
+ switch pc.iceGatherer.State() {
+ case ICEGathererStateNew:
+ return ICEGatheringStateNew
+ case ICEGathererStateGathering:
+ return ICEGatheringStateGathering
+ default:
+ return ICEGatheringStateComplete
+ }
+}
+
+// ConnectionState attribute returns the connection state of the
+// PeerConnection instance.
+func (pc *PeerConnection) ConnectionState() PeerConnectionState {
+ pc.mu.Lock()
+ defer pc.mu.Unlock()
+
+ return pc.connectionState
+}
+
+// GetStats return data providing statistics about the overall connection
+func (pc *PeerConnection) GetStats() StatsReport {
+ var (
+ dataChannelsAccepted uint32
+ dataChannelsClosed uint32
+ dataChannelsOpened uint32
+ dataChannelsRequested uint32
+ )
+ statsCollector := newStatsReportCollector()
+ statsCollector.Collecting()
+
+ pc.mu.Lock()
+ if pc.iceGatherer != nil {
+ pc.iceGatherer.collectStats(statsCollector)
+ }
+ if pc.iceTransport != nil {
+ pc.iceTransport.collectStats(statsCollector)
+ }
+
+ pc.sctpTransport.lock.Lock()
+ dataChannels := append([]*DataChannel{}, pc.sctpTransport.dataChannels...)
+ dataChannelsAccepted = pc.sctpTransport.dataChannelsAccepted
+ dataChannelsOpened = pc.sctpTransport.dataChannelsOpened
+ dataChannelsRequested = pc.sctpTransport.dataChannelsRequested
+ pc.sctpTransport.lock.Unlock()
+
+ for _, d := range dataChannels {
+ state := d.ReadyState()
+ if state != DataChannelStateConnecting && state != DataChannelStateOpen {
+ dataChannelsClosed++
+ }
+
+ d.collectStats(statsCollector)
+ }
+ pc.sctpTransport.collectStats(statsCollector)
+
+ stats := PeerConnectionStats{
+ Timestamp: statsTimestampNow(),
+ Type: StatsTypePeerConnection,
+ ID: pc.statsID,
+ DataChannelsAccepted: dataChannelsAccepted,
+ DataChannelsClosed: dataChannelsClosed,
+ DataChannelsOpened: dataChannelsOpened,
+ DataChannelsRequested: dataChannelsRequested,
+ }
+
+ statsCollector.Collect(stats.ID, stats)
+
+ certificates := pc.configuration.Certificates
+ for _, certificate := range certificates {
+ if err := certificate.collectStats(statsCollector); err != nil {
+ continue
+ }
+ }
+ pc.mu.Unlock()
+
+ pc.api.mediaEngine.collectStats(statsCollector)
+
+ return statsCollector.Ready()
+}
+
+// Start all transports. PeerConnection now has enough state
+func (pc *PeerConnection) startTransports(iceRole ICERole, dtlsRole DTLSRole, remoteUfrag, remotePwd, fingerprint, fingerprintHash string) {
+ // Start the ice transport
+ err := pc.iceTransport.Start(
+ pc.iceGatherer,
+ ICEParameters{
+ UsernameFragment: remoteUfrag,
+ Password: remotePwd,
+ ICELite: false,
+ },
+ &iceRole,
+ )
+ if err != nil {
+ pc.log.Warnf("Failed to start manager: %s", err)
+ return
+ }
+
+ // Start the dtls transport
+ err = pc.dtlsTransport.Start(DTLSParameters{
+ Role: dtlsRole,
+ Fingerprints: []DTLSFingerprint{{Algorithm: fingerprintHash, Value: fingerprint}},
+ })
+ pc.updateConnectionState(pc.ICEConnectionState(), pc.dtlsTransport.State())
+ if err != nil {
+ pc.log.Warnf("Failed to start manager: %s", err)
+ return
+ }
+}
+
+func (pc *PeerConnection) startRTP(isRenegotiation bool, remoteDesc *SessionDescription, currentTransceivers []*RTPTransceiver) {
+ trackDetails := trackDetailsFromSDP(pc.log, remoteDesc.parsed)
+ if isRenegotiation {
+ for _, t := range currentTransceivers {
+ if t.Receiver() == nil || t.Receiver().Track() == nil {
+ continue
+ }
+
+ t.Receiver().Track().mu.Lock()
+ ssrc := t.Receiver().Track().ssrc
+
+ if details := trackDetailsForSSRC(trackDetails, ssrc); details != nil {
+ t.Receiver().Track().id = details.id
+ t.Receiver().Track().streamID = details.streamID
+ t.Receiver().Track().mu.Unlock()
+ continue
+ }
+
+ t.Receiver().Track().mu.Unlock()
+
+ if err := t.Receiver().Stop(); err != nil {
+ pc.log.Warnf("Failed to stop RtpReceiver: %s", err)
+ continue
+ }
+
+ receiver, err := pc.api.NewRTPReceiver(t.Receiver().kind, pc.dtlsTransport)
+ if err != nil {
+ pc.log.Warnf("Failed to create new RtpReceiver: %s", err)
+ continue
+ }
+ t.setReceiver(receiver)
+ }
+ }
+
+ pc.startRTPReceivers(trackDetails, currentTransceivers)
+ if haveApplicationMediaSection(remoteDesc.parsed) {
+ pc.startSCTP()
+ }
+
+ if !isRenegotiation {
+ pc.undeclaredMediaProcessor()
+ }
+}
+
+// generateUnmatchedSDP generates an SDP that doesn't take remote state into account
+// This is used for the initial call for CreateOffer
+func (pc *PeerConnection) generateUnmatchedSDP(transceivers []*RTPTransceiver, useIdentity bool) (*sdp.SessionDescription, error) {
+ d, err := sdp.NewJSEPSessionDescription(useIdentity)
+ if err != nil {
+ return nil, err
+ }
+
+ iceParams, err := pc.iceGatherer.GetLocalParameters()
+ if err != nil {
+ return nil, err
+ }
+
+ candidates, err := pc.iceGatherer.GetLocalCandidates()
+ if err != nil {
+ return nil, err
+ }
+
+ isPlanB := pc.configuration.SDPSemantics == SDPSemanticsPlanB
+ mediaSections := []mediaSection{}
+
+ // Needed for pc.sctpTransport.dataChannelsRequested
+ pc.sctpTransport.lock.Lock()
+ defer pc.sctpTransport.lock.Unlock()
+
+ if isPlanB {
+ video := make([]*RTPTransceiver, 0)
+ audio := make([]*RTPTransceiver, 0)
+
+ for _, t := range transceivers {
+ if t.kind == RTPCodecTypeVideo {
+ video = append(video, t)
+ } else if t.kind == RTPCodecTypeAudio {
+ audio = append(audio, t)
+ }
+ if t.Sender() != nil {
+ t.Sender().setNegotiated()
+ }
+ }
+
+ if len(video) > 0 {
+ mediaSections = append(mediaSections, mediaSection{id: "video", transceivers: video})
+ }
+ if len(audio) > 0 {
+ mediaSections = append(mediaSections, mediaSection{id: "audio", transceivers: audio})
+ }
+
+ if pc.sctpTransport.dataChannelsRequested != 0 {
+ mediaSections = append(mediaSections, mediaSection{id: "data", data: true})
+ }
+ } else {
+ for _, t := range transceivers {
+ if t.Sender() != nil {
+ t.Sender().setNegotiated()
+ }
+ mediaSections = append(mediaSections, mediaSection{id: t.Mid(), transceivers: []*RTPTransceiver{t}})
+ }
+
+ if pc.sctpTransport.dataChannelsRequested != 0 {
+ mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), data: true})
+ }
+ }
+
+ dtlsFingerprints, err := pc.configuration.Certificates[0].GetFingerprints()
+ if err != nil {
+ return nil, err
+ }
+
+ return populateSDP(d, isPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, pc.api.mediaEngine, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), candidates, iceParams, mediaSections, pc.ICEGatheringState())
+}
+
+// generateMatchedSDP generates a SDP and takes the remote state into account
+// this is used everytime we have a RemoteDescription
+// nolint: gocyclo
+func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, useIdentity bool, includeUnmatched bool, connectionRole sdp.ConnectionRole) (*sdp.SessionDescription, error) { //nolint:gocognit
+ d, err := sdp.NewJSEPSessionDescription(useIdentity)
+ if err != nil {
+ return nil, err
+ }
+
+ iceParams, err := pc.iceGatherer.GetLocalParameters()
+ if err != nil {
+ return nil, err
+ }
+
+ candidates, err := pc.iceGatherer.GetLocalCandidates()
+ if err != nil {
+ return nil, err
+ }
+
+ var t *RTPTransceiver
+ localTransceivers := append([]*RTPTransceiver{}, transceivers...)
+ detectedPlanB := descriptionIsPlanB(pc.RemoteDescription())
+ mediaSections := []mediaSection{}
+ alreadyHaveApplicationMediaSection := false
+
+ for _, media := range pc.RemoteDescription().parsed.MediaDescriptions {
+ midValue := getMidValue(media)
+ if midValue == "" {
+ return nil, errPeerConnRemoteDescriptionWithoutMidValue
+ }
+
+ if media.MediaName.Media == mediaSectionApplication {
+ mediaSections = append(mediaSections, mediaSection{id: midValue, data: true})
+ alreadyHaveApplicationMediaSection = true
+ continue
+ }
+
+ kind := NewRTPCodecType(media.MediaName.Media)
+ direction := getPeerDirection(media)
+ if kind == 0 || direction == RTPTransceiverDirection(Unknown) {
+ continue
+ }
+
+ sdpSemantics := pc.configuration.SDPSemantics
+
+ switch {
+ case sdpSemantics == SDPSemanticsPlanB || sdpSemantics == SDPSemanticsUnifiedPlanWithFallback && detectedPlanB:
+ if !detectedPlanB {
+ return nil, &rtcerr.TypeError{Err: ErrIncorrectSDPSemantics}
+ }
+ // If we're responding to a plan-b offer, then we should try to fill up this
+ // media entry with all matching local transceivers
+ mediaTransceivers := []*RTPTransceiver{}
+ for {
+ // keep going until we can't get any more
+ t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers)
+ if t == nil {
+ if len(mediaTransceivers) == 0 {
+ t = &RTPTransceiver{kind: kind}
+ t.setDirection(RTPTransceiverDirectionInactive)
+ mediaTransceivers = append(mediaTransceivers, t)
+ }
+ break
+ }
+ if t.Sender() != nil {
+ t.Sender().setNegotiated()
+ }
+ mediaTransceivers = append(mediaTransceivers, t)
+ }
+ mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers})
+ case sdpSemantics == SDPSemanticsUnifiedPlan || sdpSemantics == SDPSemanticsUnifiedPlanWithFallback:
+ if detectedPlanB {
+ return nil, &rtcerr.TypeError{Err: ErrIncorrectSDPSemantics}
+ }
+ t, localTransceivers = findByMid(midValue, localTransceivers)
+ if t == nil {
+ return nil, fmt.Errorf("%w: %q", errPeerConnTranscieverMidNil, midValue)
+ }
+ if t.Sender() != nil {
+ t.Sender().setNegotiated()
+ }
+ mediaTransceivers := []*RTPTransceiver{t}
+ mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers, ridMap: getRids(media)})
+ }
+ }
+
+ // If we are offering also include unmatched local transceivers
+ if includeUnmatched {
+ if !detectedPlanB {
+ for _, t := range localTransceivers {
+ if t.Sender() != nil {
+ t.Sender().setNegotiated()
+ }
+ mediaSections = append(mediaSections, mediaSection{id: t.Mid(), transceivers: []*RTPTransceiver{t}})
+ }
+ }
+
+ if pc.sctpTransport.dataChannelsRequested != 0 && !alreadyHaveApplicationMediaSection {
+ if detectedPlanB {
+ mediaSections = append(mediaSections, mediaSection{id: "data", data: true})
+ } else {
+ mediaSections = append(mediaSections, mediaSection{id: strconv.Itoa(len(mediaSections)), data: true})
+ }
+ }
+ }
+
+ if pc.configuration.SDPSemantics == SDPSemanticsUnifiedPlanWithFallback && detectedPlanB {
+ pc.log.Info("Plan-B Offer detected; responding with Plan-B Answer")
+ }
+
+ dtlsFingerprints, err := pc.configuration.Certificates[0].GetFingerprints()
+ if err != nil {
+ return nil, err
+ }
+
+ return populateSDP(d, detectedPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, pc.api.mediaEngine, connectionRole, candidates, iceParams, mediaSections, pc.ICEGatheringState())
+}
+
+func (pc *PeerConnection) setGatherCompleteHandler(handler func()) {
+ pc.iceGatherer.onGatheringCompleteHandler.Store(handler)
+}
+
+// SCTP returns the SCTPTransport for this PeerConnection
+//
+// The SCTP transport over which SCTP data is sent and received. If SCTP has not been negotiated, the value is nil.
+// https://www.w3.org/TR/webrtc/#attributes-15
+func (pc *PeerConnection) SCTP() *SCTPTransport {
+ return pc.sctpTransport
+}
diff --git a/vendor/github.com/pion/webrtc/v3/peerconnection_js.go b/vendor/github.com/pion/webrtc/v3/peerconnection_js.go
new file mode 100644
index 0000000..c293194
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/peerconnection_js.go
@@ -0,0 +1,676 @@
+// +build js,wasm
+
+// Package webrtc implements the WebRTC 1.0 as defined in W3C WebRTC specification document.
+package webrtc
+
+import (
+ "syscall/js"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+// PeerConnection represents a WebRTC connection that establishes a
+// peer-to-peer communications with another PeerConnection instance in a
+// browser, or to another endpoint implementing the required protocols.
+type PeerConnection struct {
+ // Pointer to the underlying JavaScript RTCPeerConnection object.
+ underlying js.Value
+
+ // Keep track of handlers/callbacks so we can call Release as required by the
+ // syscall/js API. Initially nil.
+ onSignalingStateChangeHandler *js.Func
+ onDataChannelHandler *js.Func
+ onNegotiationNeededHandler *js.Func
+ onConnectionStateChangeHandler *js.Func
+ onICEConnectionStateChangeHandler *js.Func
+ onICECandidateHandler *js.Func
+ onICEGatheringStateChangeHandler *js.Func
+
+ // Used by GatheringCompletePromise
+ onGatherCompleteHandler func()
+
+ // A reference to the associated API state used by this connection
+ api *API
+}
+
+// NewPeerConnection creates a peerconnection.
+func NewPeerConnection(configuration Configuration) (*PeerConnection, error) {
+ api := NewAPI()
+ return api.NewPeerConnection(configuration)
+}
+
+// NewPeerConnection creates a new PeerConnection with the provided configuration against the received API object
+func (api *API) NewPeerConnection(configuration Configuration) (_ *PeerConnection, err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ configMap := configurationToValue(configuration)
+ underlying := js.Global().Get("window").Get("RTCPeerConnection").New(configMap)
+ return &PeerConnection{
+ underlying: underlying,
+ api: api,
+ }, nil
+}
+
+func (pc *PeerConnection) JSValue() js.Value {
+ return pc.underlying
+}
+
+// OnSignalingStateChange sets an event handler which is invoked when the
+// peer connection's signaling state changes
+func (pc *PeerConnection) OnSignalingStateChange(f func(SignalingState)) {
+ if pc.onSignalingStateChangeHandler != nil {
+ oldHandler := pc.onSignalingStateChangeHandler
+ defer oldHandler.Release()
+ }
+ onSignalingStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ state := newSignalingState(args[0].String())
+ go f(state)
+ return js.Undefined()
+ })
+ pc.onSignalingStateChangeHandler = &onSignalingStateChangeHandler
+ pc.underlying.Set("onsignalingstatechange", onSignalingStateChangeHandler)
+}
+
+// OnDataChannel sets an event handler which is invoked when a data
+// channel message arrives from a remote peer.
+func (pc *PeerConnection) OnDataChannel(f func(*DataChannel)) {
+ if pc.onDataChannelHandler != nil {
+ oldHandler := pc.onDataChannelHandler
+ defer oldHandler.Release()
+ }
+ onDataChannelHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ // pion/webrtc/projects/15
+ // This reference to the underlying DataChannel doesn't know
+ // about any other references to the same DataChannel. This might result in
+ // memory leaks where we don't clean up handler functions. Could possibly fix
+ // by keeping a mutex-protected list of all DataChannel references as a
+ // property of this PeerConnection, but at the cost of additional overhead.
+ dataChannel := &DataChannel{
+ underlying: args[0].Get("channel"),
+ api: pc.api,
+ }
+ go f(dataChannel)
+ return js.Undefined()
+ })
+ pc.onDataChannelHandler = &onDataChannelHandler
+ pc.underlying.Set("ondatachannel", onDataChannelHandler)
+}
+
+// OnNegotiationNeeded sets an event handler which is invoked when
+// a change has occurred which requires session negotiation
+func (pc *PeerConnection) OnNegotiationNeeded(f func()) {
+ if pc.onNegotiationNeededHandler != nil {
+ oldHandler := pc.onNegotiationNeededHandler
+ defer oldHandler.Release()
+ }
+ onNegotiationNeededHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go f()
+ return js.Undefined()
+ })
+ pc.onNegotiationNeededHandler = &onNegotiationNeededHandler
+ pc.underlying.Set("onnegotiationneeded", onNegotiationNeededHandler)
+}
+
+// OnICEConnectionStateChange sets an event handler which is called
+// when an ICE connection state is changed.
+func (pc *PeerConnection) OnICEConnectionStateChange(f func(ICEConnectionState)) {
+ if pc.onICEConnectionStateChangeHandler != nil {
+ oldHandler := pc.onICEConnectionStateChangeHandler
+ defer oldHandler.Release()
+ }
+ onICEConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ connectionState := NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
+ go f(connectionState)
+ return js.Undefined()
+ })
+ pc.onICEConnectionStateChangeHandler = &onICEConnectionStateChangeHandler
+ pc.underlying.Set("oniceconnectionstatechange", onICEConnectionStateChangeHandler)
+}
+
+// OnConnectionStateChange sets an event handler which is called
+// when an PeerConnectionState is changed.
+func (pc *PeerConnection) OnConnectionStateChange(f func(PeerConnectionState)) {
+ if pc.onConnectionStateChangeHandler != nil {
+ oldHandler := pc.onConnectionStateChangeHandler
+ defer oldHandler.Release()
+ }
+ onConnectionStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ connectionState := newPeerConnectionState(pc.underlying.Get("connectionState").String())
+ go f(connectionState)
+ return js.Undefined()
+ })
+ pc.onConnectionStateChangeHandler = &onConnectionStateChangeHandler
+ pc.underlying.Set("onconnectionstatechange", onConnectionStateChangeHandler)
+}
+
+func (pc *PeerConnection) checkConfiguration(configuration Configuration) error {
+ // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-setconfiguration (step #2)
+ if pc.ConnectionState() == PeerConnectionStateClosed {
+ return &rtcerr.InvalidStateError{Err: ErrConnectionClosed}
+ }
+
+ existingConfig := pc.GetConfiguration()
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #3)
+ if configuration.PeerIdentity != "" {
+ if configuration.PeerIdentity != existingConfig.PeerIdentity {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingPeerIdentity}
+ }
+ }
+
+ // https://github.com/pion/webrtc/issues/513
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #4)
+ // if len(configuration.Certificates) > 0 {
+ // if len(configuration.Certificates) != len(existingConfiguration.Certificates) {
+ // return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
+ // }
+
+ // for i, certificate := range configuration.Certificates {
+ // if !pc.configuration.Certificates[i].Equals(certificate) {
+ // return &rtcerr.InvalidModificationError{Err: ErrModifyingCertificates}
+ // }
+ // }
+ // pc.configuration.Certificates = configuration.Certificates
+ // }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #5)
+ if configuration.BundlePolicy != BundlePolicy(Unknown) {
+ if configuration.BundlePolicy != existingConfig.BundlePolicy {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingBundlePolicy}
+ }
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #6)
+ if configuration.RTCPMuxPolicy != RTCPMuxPolicy(Unknown) {
+ if configuration.RTCPMuxPolicy != existingConfig.RTCPMuxPolicy {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingRTCPMuxPolicy}
+ }
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #7)
+ if configuration.ICECandidatePoolSize != 0 {
+ if configuration.ICECandidatePoolSize != existingConfig.ICECandidatePoolSize &&
+ pc.LocalDescription() != nil {
+ return &rtcerr.InvalidModificationError{Err: ErrModifyingICECandidatePoolSize}
+ }
+ }
+
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11)
+ if len(configuration.ICEServers) > 0 {
+ // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3)
+ for _, server := range configuration.ICEServers {
+ if _, err := server.validate(); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// SetConfiguration updates the configuration of this PeerConnection object.
+func (pc *PeerConnection) SetConfiguration(configuration Configuration) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ if err := pc.checkConfiguration(configuration); err != nil {
+ return err
+ }
+ configMap := configurationToValue(configuration)
+ pc.underlying.Call("setConfiguration", configMap)
+ return nil
+}
+
+// GetConfiguration returns a Configuration object representing the current
+// configuration of this PeerConnection object. The returned object is a
+// copy and direct mutation on it will not take affect until SetConfiguration
+// has been called with Configuration passed as its only argument.
+// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getconfiguration
+func (pc *PeerConnection) GetConfiguration() Configuration {
+ return valueToConfiguration(pc.underlying.Call("getConfiguration"))
+}
+
+// CreateOffer starts the PeerConnection and generates the localDescription
+func (pc *PeerConnection) CreateOffer(options *OfferOptions) (_ SessionDescription, err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ promise := pc.underlying.Call("createOffer", offerOptionsToValue(options))
+ desc, err := awaitPromise(promise)
+ if err != nil {
+ return SessionDescription{}, err
+ }
+ return *valueToSessionDescription(desc), nil
+}
+
+// CreateAnswer starts the PeerConnection and generates the localDescription
+func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (_ SessionDescription, err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ promise := pc.underlying.Call("createAnswer", answerOptionsToValue(options))
+ desc, err := awaitPromise(promise)
+ if err != nil {
+ return SessionDescription{}, err
+ }
+ return *valueToSessionDescription(desc), nil
+}
+
+// SetLocalDescription sets the SessionDescription of the local peer
+func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ promise := pc.underlying.Call("setLocalDescription", sessionDescriptionToValue(&desc))
+ _, err = awaitPromise(promise)
+ return err
+}
+
+// LocalDescription returns PendingLocalDescription if it is not null and
+// otherwise it returns CurrentLocalDescription. This property is used to
+// determine if setLocalDescription has already been called.
+// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-localdescription
+func (pc *PeerConnection) LocalDescription() *SessionDescription {
+ return valueToSessionDescription(pc.underlying.Get("localDescription"))
+}
+
+// SetRemoteDescription sets the SessionDescription of the remote peer
+func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ promise := pc.underlying.Call("setRemoteDescription", sessionDescriptionToValue(&desc))
+ _, err = awaitPromise(promise)
+ return err
+}
+
+// RemoteDescription returns PendingRemoteDescription if it is not null and
+// otherwise it returns CurrentRemoteDescription. This property is used to
+// determine if setRemoteDescription has already been called.
+// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-remotedescription
+func (pc *PeerConnection) RemoteDescription() *SessionDescription {
+ return valueToSessionDescription(pc.underlying.Get("remoteDescription"))
+}
+
+// AddICECandidate accepts an ICE candidate string and adds it
+// to the existing set of candidates
+func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ promise := pc.underlying.Call("addIceCandidate", iceCandidateInitToValue(candidate))
+ _, err = awaitPromise(promise)
+ return err
+}
+
+// ICEConnectionState returns the ICE connection state of the
+// PeerConnection instance.
+func (pc *PeerConnection) ICEConnectionState() ICEConnectionState {
+ return NewICEConnectionState(pc.underlying.Get("iceConnectionState").String())
+}
+
+// OnICECandidate sets an event handler which is invoked when a new ICE
+// candidate is found.
+func (pc *PeerConnection) OnICECandidate(f func(candidate *ICECandidate)) {
+ if pc.onICECandidateHandler != nil {
+ oldHandler := pc.onICECandidateHandler
+ defer oldHandler.Release()
+ }
+ onICECandidateHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ candidate := valueToICECandidate(args[0].Get("candidate"))
+ if candidate == nil && pc.onGatherCompleteHandler != nil {
+ go pc.onGatherCompleteHandler()
+ }
+
+ go f(candidate)
+ return js.Undefined()
+ })
+ pc.onICECandidateHandler = &onICECandidateHandler
+ pc.underlying.Set("onicecandidate", onICECandidateHandler)
+}
+
+// OnICEGatheringStateChange sets an event handler which is invoked when the
+// ICE candidate gathering state has changed.
+func (pc *PeerConnection) OnICEGatheringStateChange(f func()) {
+ if pc.onICEGatheringStateChangeHandler != nil {
+ oldHandler := pc.onICEGatheringStateChangeHandler
+ defer oldHandler.Release()
+ }
+ onICEGatheringStateChangeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
+ go f()
+ return js.Undefined()
+ })
+ pc.onICEGatheringStateChangeHandler = &onICEGatheringStateChangeHandler
+ pc.underlying.Set("onicegatheringstatechange", onICEGatheringStateChangeHandler)
+}
+
+// CreateDataChannel creates a new DataChannel object with the given label
+// and optional DataChannelInit used to configure properties of the
+// underlying channel such as data reliability.
+func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelInit) (_ *DataChannel, err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ channel := pc.underlying.Call("createDataChannel", label, dataChannelInitToValue(options))
+ return &DataChannel{
+ underlying: channel,
+ api: pc.api,
+ }, nil
+}
+
+// SetIdentityProvider is used to configure an identity provider to generate identity assertions
+func (pc *PeerConnection) SetIdentityProvider(provider string) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+ pc.underlying.Call("setIdentityProvider", provider)
+ return nil
+}
+
+// Close ends the PeerConnection
+func (pc *PeerConnection) Close() (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = recoveryToError(e)
+ }
+ }()
+
+ pc.underlying.Call("close")
+
+ // Release any handlers as required by the syscall/js API.
+ if pc.onSignalingStateChangeHandler != nil {
+ pc.onSignalingStateChangeHandler.Release()
+ }
+ if pc.onDataChannelHandler != nil {
+ pc.onDataChannelHandler.Release()
+ }
+ if pc.onNegotiationNeededHandler != nil {
+ pc.onNegotiationNeededHandler.Release()
+ }
+ if pc.onConnectionStateChangeHandler != nil {
+ pc.onConnectionStateChangeHandler.Release()
+ }
+ if pc.onICEConnectionStateChangeHandler != nil {
+ pc.onICEConnectionStateChangeHandler.Release()
+ }
+ if pc.onICECandidateHandler != nil {
+ pc.onICECandidateHandler.Release()
+ }
+ if pc.onICEGatheringStateChangeHandler != nil {
+ pc.onICEGatheringStateChangeHandler.Release()
+ }
+
+ return nil
+}
+
+// CurrentLocalDescription represents the local description that was
+// successfully negotiated the last time the PeerConnection transitioned
+// into the stable state plus any local candidates that have been generated
+// by the ICEAgent since the offer or answer was created.
+func (pc *PeerConnection) CurrentLocalDescription() *SessionDescription {
+ desc := pc.underlying.Get("currentLocalDescription")
+ return valueToSessionDescription(desc)
+}
+
+// PendingLocalDescription represents a local description that is in the
+// process of being negotiated plus any local candidates that have been
+// generated by the ICEAgent since the offer or answer was created. If the
+// PeerConnection is in the stable state, the value is null.
+func (pc *PeerConnection) PendingLocalDescription() *SessionDescription {
+ desc := pc.underlying.Get("pendingLocalDescription")
+ return valueToSessionDescription(desc)
+}
+
+// CurrentRemoteDescription represents the last remote description that was
+// successfully negotiated the last time the PeerConnection transitioned
+// into the stable state plus any remote candidates that have been supplied
+// via AddICECandidate() since the offer or answer was created.
+func (pc *PeerConnection) CurrentRemoteDescription() *SessionDescription {
+ desc := pc.underlying.Get("currentRemoteDescription")
+ return valueToSessionDescription(desc)
+}
+
+// PendingRemoteDescription represents a remote description that is in the
+// process of being negotiated, complete with any remote candidates that
+// have been supplied via AddICECandidate() since the offer or answer was
+// created. If the PeerConnection is in the stable state, the value is
+// null.
+func (pc *PeerConnection) PendingRemoteDescription() *SessionDescription {
+ desc := pc.underlying.Get("pendingRemoteDescription")
+ return valueToSessionDescription(desc)
+}
+
+// SignalingState returns the signaling state of the PeerConnection instance.
+func (pc *PeerConnection) SignalingState() SignalingState {
+ rawState := pc.underlying.Get("signalingState").String()
+ return newSignalingState(rawState)
+}
+
+// ICEGatheringState attribute the ICE gathering state of the PeerConnection
+// instance.
+func (pc *PeerConnection) ICEGatheringState() ICEGatheringState {
+ rawState := pc.underlying.Get("iceGatheringState").String()
+ return NewICEGatheringState(rawState)
+}
+
+// ConnectionState attribute the connection state of the PeerConnection
+// instance.
+func (pc *PeerConnection) ConnectionState() PeerConnectionState {
+ rawState := pc.underlying.Get("connectionState").String()
+ return newPeerConnectionState(rawState)
+}
+
+func (pc *PeerConnection) setGatherCompleteHandler(handler func()) {
+ pc.onGatherCompleteHandler = handler
+
+ // If no onIceCandidate handler has been set provide an empty one
+ // otherwise our onGatherCompleteHandler will not be executed
+ if pc.onICECandidateHandler == nil {
+ pc.OnICECandidate(func(i *ICECandidate) {})
+ }
+}
+
+// Converts a Configuration to js.Value so it can be passed
+// through to the JavaScript WebRTC API. Any zero values are converted to
+// js.Undefined(), which will result in the default value being used.
+func configurationToValue(configuration Configuration) js.Value {
+ return js.ValueOf(map[string]interface{}{
+ "iceServers": iceServersToValue(configuration.ICEServers),
+ "iceTransportPolicy": stringEnumToValueOrUndefined(configuration.ICETransportPolicy.String()),
+ "bundlePolicy": stringEnumToValueOrUndefined(configuration.BundlePolicy.String()),
+ "rtcpMuxPolicy": stringEnumToValueOrUndefined(configuration.RTCPMuxPolicy.String()),
+ "peerIdentity": stringToValueOrUndefined(configuration.PeerIdentity),
+ "iceCandidatePoolSize": uint8ToValueOrUndefined(configuration.ICECandidatePoolSize),
+
+ // Note: Certificates are not currently supported.
+ // "certificates": configuration.Certificates,
+ })
+}
+
+func iceServersToValue(iceServers []ICEServer) js.Value {
+ if len(iceServers) == 0 {
+ return js.Undefined()
+ }
+ maps := make([]interface{}, len(iceServers))
+ for i, server := range iceServers {
+ maps[i] = iceServerToValue(server)
+ }
+ return js.ValueOf(maps)
+}
+
+func iceServerToValue(server ICEServer) js.Value {
+ return js.ValueOf(map[string]interface{}{
+ "urls": stringsToValue(server.URLs), // required
+ "username": stringToValueOrUndefined(server.Username),
+ // Note: credential and credentialType are not currently supported.
+ // "credential": interfaceToValueOrUndefined(server.Credential),
+ // "credentialType": stringEnumToValueOrUndefined(server.CredentialType.String()),
+ })
+}
+
+func valueToConfiguration(configValue js.Value) Configuration {
+ if jsValueIsNull(configValue) || jsValueIsUndefined(configValue) {
+ return Configuration{}
+ }
+ return Configuration{
+ ICEServers: valueToICEServers(configValue.Get("iceServers")),
+ ICETransportPolicy: NewICETransportPolicy(valueToStringOrZero(configValue.Get("iceTransportPolicy"))),
+ BundlePolicy: newBundlePolicy(valueToStringOrZero(configValue.Get("bundlePolicy"))),
+ RTCPMuxPolicy: newRTCPMuxPolicy(valueToStringOrZero(configValue.Get("rtcpMuxPolicy"))),
+ PeerIdentity: valueToStringOrZero(configValue.Get("peerIdentity")),
+ ICECandidatePoolSize: valueToUint8OrZero(configValue.Get("iceCandidatePoolSize")),
+
+ // Note: Certificates are not supported.
+ // Certificates []Certificate
+ }
+}
+
+func valueToICEServers(iceServersValue js.Value) []ICEServer {
+ if jsValueIsNull(iceServersValue) || jsValueIsUndefined(iceServersValue) {
+ return nil
+ }
+ iceServers := make([]ICEServer, iceServersValue.Length())
+ for i := 0; i < iceServersValue.Length(); i++ {
+ iceServers[i] = valueToICEServer(iceServersValue.Index(i))
+ }
+ return iceServers
+}
+
+func valueToICEServer(iceServerValue js.Value) ICEServer {
+ return ICEServer{
+ URLs: valueToStrings(iceServerValue.Get("urls")), // required
+ Username: valueToStringOrZero(iceServerValue.Get("username")),
+ // Note: Credential and CredentialType are not currently supported.
+ // Credential: iceServerValue.Get("credential"),
+ // CredentialType: newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType"))),
+ }
+}
+
+func valueToICECandidate(val js.Value) *ICECandidate {
+ if jsValueIsNull(val) || jsValueIsUndefined(val) {
+ return nil
+ }
+ if jsValueIsUndefined(val.Get("protocol")) && !jsValueIsUndefined(val.Get("candidate")) {
+ // Missing some fields, assume it's Firefox and parse SDP candidate.
+ c, err := ice.UnmarshalCandidate(val.Get("candidate").String())
+ if err != nil {
+ return nil
+ }
+
+ iceCandidate, err := newICECandidateFromICE(c)
+ if err != nil {
+ return nil
+ }
+
+ return &iceCandidate
+ }
+ protocol, _ := NewICEProtocol(val.Get("protocol").String())
+ candidateType, _ := NewICECandidateType(val.Get("type").String())
+ return &ICECandidate{
+ Foundation: val.Get("foundation").String(),
+ Priority: valueToUint32OrZero(val.Get("priority")),
+ Address: val.Get("address").String(),
+ Protocol: protocol,
+ Port: valueToUint16OrZero(val.Get("port")),
+ Typ: candidateType,
+ Component: stringToComponentIDOrZero(val.Get("component").String()),
+ RelatedAddress: val.Get("relatedAddress").String(),
+ RelatedPort: valueToUint16OrZero(val.Get("relatedPort")),
+ }
+}
+
+func stringToComponentIDOrZero(val string) uint16 {
+ // See: https://developer.mozilla.org/en-US/docs/Web/API/RTCIceComponent
+ switch val {
+ case "rtp":
+ return 1
+ case "rtcp":
+ return 2
+ }
+ return 0
+}
+
+func sessionDescriptionToValue(desc *SessionDescription) js.Value {
+ if desc == nil {
+ return js.Undefined()
+ }
+ return js.ValueOf(map[string]interface{}{
+ "type": desc.Type.String(),
+ "sdp": desc.SDP,
+ })
+}
+
+func valueToSessionDescription(descValue js.Value) *SessionDescription {
+ if jsValueIsNull(descValue) || jsValueIsUndefined(descValue) {
+ return nil
+ }
+ return &SessionDescription{
+ Type: NewSDPType(descValue.Get("type").String()),
+ SDP: descValue.Get("sdp").String(),
+ }
+}
+
+func offerOptionsToValue(offerOptions *OfferOptions) js.Value {
+ if offerOptions == nil {
+ return js.Undefined()
+ }
+ return js.ValueOf(map[string]interface{}{
+ "iceRestart": offerOptions.ICERestart,
+ "voiceActivityDetection": offerOptions.VoiceActivityDetection,
+ })
+}
+
+func answerOptionsToValue(answerOptions *AnswerOptions) js.Value {
+ if answerOptions == nil {
+ return js.Undefined()
+ }
+ return js.ValueOf(map[string]interface{}{
+ "voiceActivityDetection": answerOptions.VoiceActivityDetection,
+ })
+}
+
+func iceCandidateInitToValue(candidate ICECandidateInit) js.Value {
+ return js.ValueOf(map[string]interface{}{
+ "candidate": candidate.Candidate,
+ "sdpMid": stringPointerToValue(candidate.SDPMid),
+ "sdpMLineIndex": uint16PointerToValue(candidate.SDPMLineIndex),
+ "usernameFragment": stringPointerToValue(candidate.UsernameFragment),
+ })
+}
+
+func dataChannelInitToValue(options *DataChannelInit) js.Value {
+ if options == nil {
+ return js.Undefined()
+ }
+
+ maxPacketLifeTime := uint16PointerToValue(options.MaxPacketLifeTime)
+ return js.ValueOf(map[string]interface{}{
+ "ordered": boolPointerToValue(options.Ordered),
+ "maxPacketLifeTime": maxPacketLifeTime,
+ // See https://bugs.chromium.org/p/chromium/issues/detail?id=696681
+ // Chrome calls this "maxRetransmitTime"
+ "maxRetransmitTime": maxPacketLifeTime,
+ "maxRetransmits": uint16PointerToValue(options.MaxRetransmits),
+ "protocol": stringPointerToValue(options.Protocol),
+ "negotiated": boolPointerToValue(options.Negotiated),
+ "id": uint16PointerToValue(options.ID),
+ })
+}
diff --git a/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go b/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go
new file mode 100644
index 0000000..66ac20e
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go
@@ -0,0 +1,94 @@
+package webrtc
+
+// PeerConnectionState indicates the state of the PeerConnection.
+type PeerConnectionState int
+
+const (
+ // PeerConnectionStateNew indicates that any of the ICETransports or
+ // DTLSTransports are in the "new" state and none of the transports are
+ // in the "connecting", "checking", "failed" or "disconnected" state, or
+ // all transports are in the "closed" state, or there are no transports.
+ PeerConnectionStateNew PeerConnectionState = iota + 1
+
+ // PeerConnectionStateConnecting indicates that any of the
+ // ICETransports or DTLSTransports are in the "connecting" or
+ // "checking" state and none of them is in the "failed" state.
+ PeerConnectionStateConnecting
+
+ // PeerConnectionStateConnected indicates that all ICETransports and
+ // DTLSTransports are in the "connected", "completed" or "closed" state
+ // and at least one of them is in the "connected" or "completed" state.
+ PeerConnectionStateConnected
+
+ // PeerConnectionStateDisconnected indicates that any of the
+ // ICETransports or DTLSTransports are in the "disconnected" state
+ // and none of them are in the "failed" or "connecting" or "checking" state.
+ PeerConnectionStateDisconnected
+
+ // PeerConnectionStateFailed indicates that any of the ICETransports
+ // or DTLSTransports are in a "failed" state.
+ PeerConnectionStateFailed
+
+ // PeerConnectionStateClosed indicates the peer connection is closed
+ // and the isClosed member variable of PeerConnection is true.
+ PeerConnectionStateClosed
+)
+
+// This is done this way because of a linter.
+const (
+ peerConnectionStateNewStr = "new"
+ peerConnectionStateConnectingStr = "connecting"
+ peerConnectionStateConnectedStr = "connected"
+ peerConnectionStateDisconnectedStr = "disconnected"
+ peerConnectionStateFailedStr = "failed"
+ peerConnectionStateClosedStr = "closed"
+)
+
+func newPeerConnectionState(raw string) PeerConnectionState {
+ switch raw {
+ case peerConnectionStateNewStr:
+ return PeerConnectionStateNew
+ case peerConnectionStateConnectingStr:
+ return PeerConnectionStateConnecting
+ case peerConnectionStateConnectedStr:
+ return PeerConnectionStateConnected
+ case peerConnectionStateDisconnectedStr:
+ return PeerConnectionStateDisconnected
+ case peerConnectionStateFailedStr:
+ return PeerConnectionStateFailed
+ case peerConnectionStateClosedStr:
+ return PeerConnectionStateClosed
+ default:
+ return PeerConnectionState(Unknown)
+ }
+}
+
+func (t PeerConnectionState) String() string {
+ switch t {
+ case PeerConnectionStateNew:
+ return peerConnectionStateNewStr
+ case PeerConnectionStateConnecting:
+ return peerConnectionStateConnectingStr
+ case PeerConnectionStateConnected:
+ return peerConnectionStateConnectedStr
+ case PeerConnectionStateDisconnected:
+ return peerConnectionStateDisconnectedStr
+ case PeerConnectionStateFailed:
+ return peerConnectionStateFailedStr
+ case PeerConnectionStateClosed:
+ return peerConnectionStateClosedStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+type negotiationNeededState int
+
+const (
+ // NegotiationNeededStateEmpty not running and queue is empty
+ negotiationNeededStateEmpty = iota
+ // NegotiationNeededStateEmpty running and queue is empty
+ negotiationNeededStateRun
+ // NegotiationNeededStateEmpty running and queue
+ negotiationNeededStateQueue
+)
diff --git a/vendor/github.com/pion/webrtc/v3/pkg/media/media.go b/vendor/github.com/pion/webrtc/v3/pkg/media/media.go
new file mode 100644
index 0000000..bcd7e33
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/pkg/media/media.go
@@ -0,0 +1,25 @@
+// Package media provides media writer and filters
+package media
+
+import (
+ "time"
+
+ "github.com/pion/rtp"
+)
+
+// A Sample contains encoded media and timing information
+type Sample struct {
+ Data []byte
+ Timestamp time.Time
+ Duration time.Duration
+}
+
+// Writer defines an interface to handle
+// the creation of media files
+type Writer interface {
+ // Add the content of an RTP packet to the media
+ WriteRTP(packet *rtp.Packet) error
+ // Close the media
+ // Note: Close implementation must be idempotent
+ Close() error
+}
diff --git a/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go b/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go
new file mode 100644
index 0000000..aa94b7b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go
@@ -0,0 +1,160 @@
+// Package rtcerr implements the error wrappers defined throughout the
+// WebRTC 1.0 specifications.
+package rtcerr
+
+import (
+ "fmt"
+)
+
+// UnknownError indicates the operation failed for an unknown transient reason.
+type UnknownError struct {
+ Err error
+}
+
+func (e *UnknownError) Error() string {
+ return fmt.Sprintf("UnknownError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *UnknownError) Unwrap() error {
+ return e.Err
+}
+
+// InvalidStateError indicates the object is in an invalid state.
+type InvalidStateError struct {
+ Err error
+}
+
+func (e *InvalidStateError) Error() string {
+ return fmt.Sprintf("InvalidStateError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *InvalidStateError) Unwrap() error {
+ return e.Err
+}
+
+// InvalidAccessError indicates the object does not support the operation or
+// argument.
+type InvalidAccessError struct {
+ Err error
+}
+
+func (e *InvalidAccessError) Error() string {
+ return fmt.Sprintf("InvalidAccessError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *InvalidAccessError) Unwrap() error {
+ return e.Err
+}
+
+// NotSupportedError indicates the operation is not supported.
+type NotSupportedError struct {
+ Err error
+}
+
+func (e *NotSupportedError) Error() string {
+ return fmt.Sprintf("NotSupportedError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *NotSupportedError) Unwrap() error {
+ return e.Err
+}
+
+// InvalidModificationError indicates the object cannot be modified in this way.
+type InvalidModificationError struct {
+ Err error
+}
+
+func (e *InvalidModificationError) Error() string {
+ return fmt.Sprintf("InvalidModificationError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *InvalidModificationError) Unwrap() error {
+ return e.Err
+}
+
+// SyntaxError indicates the string did not match the expected pattern.
+type SyntaxError struct {
+ Err error
+}
+
+func (e *SyntaxError) Error() string {
+ return fmt.Sprintf("SyntaxError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *SyntaxError) Unwrap() error {
+ return e.Err
+}
+
+// TypeError indicates an error when a value is not of the expected type.
+type TypeError struct {
+ Err error
+}
+
+func (e *TypeError) Error() string {
+ return fmt.Sprintf("TypeError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *TypeError) Unwrap() error {
+ return e.Err
+}
+
+// OperationError indicates the operation failed for an operation-specific
+// reason.
+type OperationError struct {
+ Err error
+}
+
+func (e *OperationError) Error() string {
+ return fmt.Sprintf("OperationError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *OperationError) Unwrap() error {
+ return e.Err
+}
+
+// NotReadableError indicates the input/output read operation failed.
+type NotReadableError struct {
+ Err error
+}
+
+func (e *NotReadableError) Error() string {
+ return fmt.Sprintf("NotReadableError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *NotReadableError) Unwrap() error {
+ return e.Err
+}
+
+// RangeError indicates an error when a value is not in the set or range
+// of allowed values.
+type RangeError struct {
+ Err error
+}
+
+func (e *RangeError) Error() string {
+ return fmt.Sprintf("RangeError: %v", e.Err)
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's type contains
+// an Unwrap method returning error. Otherwise, Unwrap returns nil.
+func (e *RangeError) Unwrap() error {
+ return e.Err
+}
diff --git a/vendor/github.com/pion/webrtc/v3/renovate.json b/vendor/github.com/pion/webrtc/v3/renovate.json
new file mode 100644
index 0000000..4400fd9
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/renovate.json
@@ -0,0 +1,15 @@
+{
+ "extends": [
+ "config:base"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ],
+ "commitBody": "Generated by renovateBot",
+ "packageRules": [
+ {
+ "packagePatterns": ["^golang.org/x/"],
+ "schedule": ["on the first day of the month"]
+ }
+ ]
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go b/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go
new file mode 100644
index 0000000..b377738
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go
@@ -0,0 +1,31 @@
+package webrtc
+
+const (
+ // TypeRTCPFBTransportCC ..
+ TypeRTCPFBTransportCC = "transport-cc"
+
+ // TypeRTCPFBGoogREMB ..
+ TypeRTCPFBGoogREMB = "goog-remb"
+
+ // TypeRTCPFBACK ..
+ TypeRTCPFBACK = "ack"
+
+ // TypeRTCPFBCCM ..
+ TypeRTCPFBCCM = "ccm"
+
+ // TypeRTCPFBNACK ..
+ TypeRTCPFBNACK = "nack"
+)
+
+// RTCPFeedback signals the connection to use additional RTCP packet types.
+// https://draft.ortc.org/#dom-rtcrtcpfeedback
+type RTCPFeedback struct {
+ // Type is the type of feedback.
+ // see: https://draft.ortc.org/#dom-rtcrtcpfeedback
+ // valid: ack, ccm, nack, goog-remb, transport-cc
+ Type string
+
+ // The parameter value depends on the type.
+ // For example, type="nack" parameter="pli" will send Picture Loss Indicator packets.
+ Parameter string
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go b/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go
new file mode 100644
index 0000000..f74e440
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go
@@ -0,0 +1,66 @@
+package webrtc
+
+import (
+ "encoding/json"
+)
+
+// RTCPMuxPolicy affects what ICE candidates are gathered to support
+// non-multiplexed RTCP.
+type RTCPMuxPolicy int
+
+const (
+ // RTCPMuxPolicyNegotiate indicates to gather ICE candidates for both
+ // RTP and RTCP candidates. If the remote-endpoint is capable of
+ // multiplexing RTCP, multiplex RTCP on the RTP candidates. If it is not,
+ // use both the RTP and RTCP candidates separately.
+ RTCPMuxPolicyNegotiate RTCPMuxPolicy = iota + 1
+
+ // RTCPMuxPolicyRequire indicates to gather ICE candidates only for
+ // RTP and multiplex RTCP on the RTP candidates. If the remote endpoint is
+ // not capable of rtcp-mux, session negotiation will fail.
+ RTCPMuxPolicyRequire
+)
+
+// This is done this way because of a linter.
+const (
+ rtcpMuxPolicyNegotiateStr = "negotiate"
+ rtcpMuxPolicyRequireStr = "require"
+)
+
+func newRTCPMuxPolicy(raw string) RTCPMuxPolicy {
+ switch raw {
+ case rtcpMuxPolicyNegotiateStr:
+ return RTCPMuxPolicyNegotiate
+ case rtcpMuxPolicyRequireStr:
+ return RTCPMuxPolicyRequire
+ default:
+ return RTCPMuxPolicy(Unknown)
+ }
+}
+
+func (t RTCPMuxPolicy) String() string {
+ switch t {
+ case RTCPMuxPolicyNegotiate:
+ return rtcpMuxPolicyNegotiateStr
+ case RTCPMuxPolicyRequire:
+ return rtcpMuxPolicyRequireStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// UnmarshalJSON parses the JSON-encoded data and stores the result
+func (t *RTCPMuxPolicy) UnmarshalJSON(b []byte) error {
+ var val string
+ if err := json.Unmarshal(b, &val); err != nil {
+ return err
+ }
+
+ *t = newRTCPMuxPolicy(val)
+ return nil
+}
+
+// MarshalJSON returns the JSON encoding
+func (t RTCPMuxPolicy) MarshalJSON() ([]byte, error) {
+ return json.Marshal(t.String())
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go b/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go
new file mode 100644
index 0000000..dc42230
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go
@@ -0,0 +1,9 @@
+package webrtc
+
+// RTPCapabilities represents the capabilities of a transceiver
+//
+// https://w3c.github.io/webrtc-pc/#rtcrtpcapabilities
+type RTPCapabilities struct {
+ Codecs []RTPCodecCapability
+ HeaderExtensions []RTPHeaderExtensionCapability
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpcodec.go b/vendor/github.com/pion/webrtc/v3/rtpcodec.go
new file mode 100644
index 0000000..f05b98b
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpcodec.go
@@ -0,0 +1,118 @@
+package webrtc
+
+import (
+ "strings"
+)
+
+// RTPCodecType determines the type of a codec
+type RTPCodecType int
+
+const (
+
+ // RTPCodecTypeAudio indicates this is an audio codec
+ RTPCodecTypeAudio RTPCodecType = iota + 1
+
+ // RTPCodecTypeVideo indicates this is a video codec
+ RTPCodecTypeVideo
+)
+
+func (t RTPCodecType) String() string {
+ switch t {
+ case RTPCodecTypeAudio:
+ return "audio"
+ case RTPCodecTypeVideo:
+ return "video" //nolint: goconst
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// NewRTPCodecType creates a RTPCodecType from a string
+func NewRTPCodecType(r string) RTPCodecType {
+ switch {
+ case strings.EqualFold(r, RTPCodecTypeAudio.String()):
+ return RTPCodecTypeAudio
+ case strings.EqualFold(r, RTPCodecTypeVideo.String()):
+ return RTPCodecTypeVideo
+ default:
+ return RTPCodecType(0)
+ }
+}
+
+// RTPCodecCapability provides information about codec capabilities.
+//
+// https://w3c.github.io/webrtc-pc/#dictionary-rtcrtpcodeccapability-members
+type RTPCodecCapability struct {
+ MimeType string
+ ClockRate uint32
+ Channels uint16
+ SDPFmtpLine string
+ RTCPFeedback []RTCPFeedback
+}
+
+// RTPHeaderExtensionCapability is used to define a RFC5285 RTP header extension supported by the codec.
+//
+// https://w3c.github.io/webrtc-pc/#dom-rtcrtpcapabilities-headerextensions
+type RTPHeaderExtensionCapability struct {
+ URI string
+}
+
+// RTPHeaderExtensionParameter represents a negotiated RFC5285 RTP header extension.
+//
+// https://w3c.github.io/webrtc-pc/#dictionary-rtcrtpheaderextensionparameters-members
+type RTPHeaderExtensionParameter struct {
+ URI string
+ ID int
+}
+
+// RTPCodecParameters is a sequence containing the media codecs that an RtpSender
+// will choose from, as well as entries for RTX, RED and FEC mechanisms. This also
+// includes the PayloadType that has been negotiated
+//
+// https://w3c.github.io/webrtc-pc/#rtcrtpcodecparameters
+type RTPCodecParameters struct {
+ RTPCodecCapability
+ PayloadType PayloadType
+
+ statsID string
+}
+
+// RTPParameters is a list of negotiated codecs and header extensions
+//
+// https://w3c.github.io/webrtc-pc/#dictionary-rtcrtpparameters-members
+type RTPParameters struct {
+ HeaderExtensions []RTPHeaderExtensionParameter
+ Codecs []RTPCodecParameters
+}
+
+type codecMatchType int
+
+const (
+ codecMatchNone codecMatchType = 0
+ codecMatchPartial codecMatchType = 1
+ codecMatchExact codecMatchType = 2
+)
+
+// Do a fuzzy find for a codec in the list of codecs
+// Used for lookup up a codec in an existing list to find a match
+// Returns codecMatchExact, codecMatchPartial, or codecMatchNone
+func codecParametersFuzzySearch(needle RTPCodecParameters, haystack []RTPCodecParameters) (RTPCodecParameters, codecMatchType) {
+ // First attempt to match on MimeType + SDPFmtpLine
+ // Exact matches means fmtp line cannot be empty
+ for _, c := range haystack {
+ if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) &&
+ c.RTPCodecCapability.SDPFmtpLine == needle.RTPCodecCapability.SDPFmtpLine {
+ return c, codecMatchExact
+ }
+ }
+
+ // Fallback to just MimeType if either haystack or needle does not have fmtpline set
+ for _, c := range haystack {
+ if strings.EqualFold(c.RTPCodecCapability.MimeType, needle.RTPCodecCapability.MimeType) &&
+ (c.RTPCodecCapability.SDPFmtpLine == "" || needle.RTPCodecCapability.SDPFmtpLine == "") {
+ return c, codecMatchPartial
+ }
+ }
+
+ return RTPCodecParameters{}, codecMatchNone
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go b/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go
new file mode 100644
index 0000000..8a08c33
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go
@@ -0,0 +1,10 @@
+package webrtc
+
+// RTPCodingParameters provides information relating to both encoding and decoding.
+// This is a subset of the RFC since Pion WebRTC doesn't implement encoding/decoding itself
+// http://draft.ortc.org/#dom-rtcrtpcodingparameters
+type RTPCodingParameters struct {
+ RID string `json:"rid"`
+ SSRC SSRC `json:"ssrc"`
+ PayloadType PayloadType `json:"payloadType"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go b/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go
new file mode 100644
index 0000000..77aa1fc
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go
@@ -0,0 +1,8 @@
+package webrtc
+
+// RTPDecodingParameters provides information relating to both encoding and decoding.
+// This is a subset of the RFC since Pion WebRTC doesn't implement decoding itself
+// http://draft.ortc.org/#dom-rtcrtpdecodingparameters
+type RTPDecodingParameters struct {
+ RTPCodingParameters
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go b/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go
new file mode 100644
index 0000000..09481a5
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go
@@ -0,0 +1,8 @@
+package webrtc
+
+// RTPEncodingParameters provides information relating to both encoding and decoding.
+// This is a subset of the RFC since Pion WebRTC doesn't implement encoding itself
+// http://draft.ortc.org/#dom-rtcrtpencodingparameters
+type RTPEncodingParameters struct {
+ RTPCodingParameters
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go b/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go
new file mode 100644
index 0000000..badf6b7
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go
@@ -0,0 +1,6 @@
+package webrtc
+
+// RTPReceiveParameters contains the RTP stack settings used by receivers
+type RTPReceiveParameters struct {
+ Encodings []RTPDecodingParameters
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpreceiver.go b/vendor/github.com/pion/webrtc/v3/rtpreceiver.go
new file mode 100644
index 0000000..8d558e0
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpreceiver.go
@@ -0,0 +1,361 @@
+// +build !js
+
+package webrtc
+
+import (
+ "fmt"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/rtcp"
+ "github.com/pion/srtp/v2"
+ "github.com/pion/webrtc/v3/internal/util"
+)
+
+// trackStreams maintains a mapping of RTP/RTCP streams to a specific track
+// a RTPReceiver may contain multiple streams if we are dealing with Multicast
+type trackStreams struct {
+ track *TrackRemote
+
+ rtpReadStream *srtp.ReadStreamSRTP
+ rtpInterceptor interceptor.RTPReader
+
+ rtcpReadStream *srtp.ReadStreamSRTCP
+ rtcpInterceptor interceptor.RTCPReader
+}
+
+// RTPReceiver allows an application to inspect the receipt of a TrackRemote
+type RTPReceiver struct {
+ kind RTPCodecType
+ transport *DTLSTransport
+
+ tracks []trackStreams
+
+ closed, received chan interface{}
+ mu sync.RWMutex
+
+ // A reference to the associated api object
+ api *API
+}
+
+// NewRTPReceiver constructs a new RTPReceiver
+func (api *API) NewRTPReceiver(kind RTPCodecType, transport *DTLSTransport) (*RTPReceiver, error) {
+ if transport == nil {
+ return nil, errRTPReceiverDTLSTransportNil
+ }
+
+ r := &RTPReceiver{
+ kind: kind,
+ transport: transport,
+ api: api,
+ closed: make(chan interface{}),
+ received: make(chan interface{}),
+ tracks: []trackStreams{},
+ }
+
+ return r, nil
+}
+
+// Transport returns the currently-configured *DTLSTransport or nil
+// if one has not yet been configured
+func (r *RTPReceiver) Transport() *DTLSTransport {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+ return r.transport
+}
+
+// GetParameters describes the current configuration for the encoding and
+// transmission of media on the receiver's track.
+func (r *RTPReceiver) GetParameters() RTPParameters {
+ return r.api.mediaEngine.getRTPParametersByKind(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
+}
+
+// Track returns the RtpTransceiver TrackRemote
+func (r *RTPReceiver) Track() *TrackRemote {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ if len(r.tracks) != 1 {
+ return nil
+ }
+ return r.tracks[0].track
+}
+
+// Tracks returns the RtpTransceiver tracks
+// A RTPReceiver to support Simulcast may now have multiple tracks
+func (r *RTPReceiver) Tracks() []*TrackRemote {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ var tracks []*TrackRemote
+ for i := range r.tracks {
+ tracks = append(tracks, r.tracks[i].track)
+ }
+ return tracks
+}
+
+// Receive initialize the track and starts all the transports
+func (r *RTPReceiver) Receive(parameters RTPReceiveParameters) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ select {
+ case <-r.received:
+ return errRTPReceiverReceiveAlreadyCalled
+ default:
+ }
+ defer close(r.received)
+
+ if len(parameters.Encodings) == 1 && parameters.Encodings[0].SSRC != 0 {
+ t := trackStreams{
+ track: newTrackRemote(
+ r.kind,
+ parameters.Encodings[0].SSRC,
+ "",
+ r,
+ ),
+ }
+
+ globalParams := r.GetParameters()
+ codec := RTPCodecCapability{}
+ if len(globalParams.Codecs) != 0 {
+ codec = globalParams.Codecs[0].RTPCodecCapability
+ }
+
+ streamInfo := createStreamInfo("", parameters.Encodings[0].SSRC, 0, codec, globalParams.HeaderExtensions)
+ var err error
+ if t.rtpReadStream, t.rtpInterceptor, t.rtcpReadStream, t.rtcpInterceptor, err = r.streamsForSSRC(parameters.Encodings[0].SSRC, streamInfo); err != nil {
+ return err
+ }
+
+ r.tracks = append(r.tracks, t)
+ } else {
+ for _, encoding := range parameters.Encodings {
+ r.tracks = append(r.tracks, trackStreams{
+ track: newTrackRemote(
+ r.kind,
+ 0,
+ encoding.RID,
+ r,
+ ),
+ })
+ }
+ }
+
+ return nil
+}
+
+// Read reads incoming RTCP for this RTPReceiver
+func (r *RTPReceiver) Read(b []byte) (n int, a interceptor.Attributes, err error) {
+ select {
+ case <-r.received:
+ return r.tracks[0].rtcpInterceptor.Read(b, a)
+ case <-r.closed:
+ return 0, nil, io.ErrClosedPipe
+ }
+}
+
+// ReadSimulcast reads incoming RTCP for this RTPReceiver for given rid
+func (r *RTPReceiver) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) {
+ select {
+ case <-r.received:
+ for _, t := range r.tracks {
+ if t.track != nil && t.track.rid == rid {
+ return t.rtcpInterceptor.Read(b, a)
+ }
+ }
+ return 0, nil, fmt.Errorf("%w: %s", errRTPReceiverForRIDTrackStreamNotFound, rid)
+ case <-r.closed:
+ return 0, nil, io.ErrClosedPipe
+ }
+}
+
+// ReadRTCP is a convenience method that wraps Read and unmarshal for you.
+// It also runs any configured interceptors.
+func (r *RTPReceiver) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
+ b := make([]byte, receiveMTU)
+ i, attributes, err := r.Read(b)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ pkts, err := rtcp.Unmarshal(b[:i])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return pkts, attributes, nil
+}
+
+// ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you
+func (r *RTPReceiver) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.Attributes, error) {
+ b := make([]byte, receiveMTU)
+ i, attributes, err := r.ReadSimulcast(b, rid)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ pkts, err := rtcp.Unmarshal(b[:i])
+ return pkts, attributes, err
+}
+
+func (r *RTPReceiver) haveReceived() bool {
+ select {
+ case <-r.received:
+ return true
+ default:
+ return false
+ }
+}
+
+// Stop irreversibly stops the RTPReceiver
+func (r *RTPReceiver) Stop() error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ var err error
+
+ select {
+ case <-r.closed:
+ return err
+ default:
+ }
+
+ select {
+ case <-r.received:
+ for i := range r.tracks {
+ errs := []error{}
+
+ if r.tracks[i].rtcpReadStream != nil {
+ errs = append(errs, r.tracks[i].rtcpReadStream.Close())
+ }
+
+ if r.tracks[i].rtpReadStream != nil {
+ errs = append(errs, r.tracks[i].rtpReadStream.Close())
+ }
+
+ err = util.FlattenErrs(errs)
+ }
+ default:
+ }
+
+ close(r.closed)
+ return err
+}
+
+func (r *RTPReceiver) streamsForTrack(t *TrackRemote) *trackStreams {
+ for i := range r.tracks {
+ if r.tracks[i].track == t {
+ return &r.tracks[i]
+ }
+ }
+ return nil
+}
+
+// readRTP should only be called by a track, this only exists so we can keep state in one place
+func (r *RTPReceiver) readRTP(b []byte, reader *TrackRemote) (n int, a interceptor.Attributes, err error) {
+ <-r.received
+ if t := r.streamsForTrack(reader); t != nil {
+ return t.rtpInterceptor.Read(b, a)
+ }
+
+ return 0, nil, fmt.Errorf("%w: %d", errRTPReceiverWithSSRCTrackStreamNotFound, reader.SSRC())
+}
+
+// receiveForRid is the sibling of Receive expect for RIDs instead of SSRCs
+// It populates all the internal state for the given RID
+func (r *RTPReceiver) receiveForRid(rid string, params RTPParameters, ssrc SSRC) (*TrackRemote, error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for i := range r.tracks {
+ if r.tracks[i].track.RID() == rid {
+ r.tracks[i].track.mu.Lock()
+ r.tracks[i].track.kind = r.kind
+ r.tracks[i].track.codec = params.Codecs[0]
+ r.tracks[i].track.params = params
+ r.tracks[i].track.ssrc = ssrc
+ streamInfo := createStreamInfo("", ssrc, params.Codecs[0].PayloadType, params.Codecs[0].RTPCodecCapability, params.HeaderExtensions)
+ r.tracks[i].track.mu.Unlock()
+
+ var err error
+ if r.tracks[i].rtpReadStream, r.tracks[i].rtpInterceptor, r.tracks[i].rtcpReadStream, r.tracks[i].rtcpInterceptor, err = r.streamsForSSRC(ssrc, streamInfo); err != nil {
+ return nil, err
+ }
+
+ return r.tracks[i].track, nil
+ }
+ }
+
+ return nil, fmt.Errorf("%w: %d", errRTPReceiverForSSRCTrackStreamNotFound, ssrc)
+}
+
+func (r *RTPReceiver) streamsForSSRC(ssrc SSRC, streamInfo interceptor.StreamInfo) (*srtp.ReadStreamSRTP, interceptor.RTPReader, *srtp.ReadStreamSRTCP, interceptor.RTCPReader, error) {
+ srtpSession, err := r.transport.getSRTPSession()
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ rtpReadStream, err := srtpSession.OpenReadStream(uint32(ssrc))
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ rtpInterceptor := r.api.interceptor.BindRemoteStream(&streamInfo, interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
+ n, err = rtpReadStream.Read(in)
+ return n, a, err
+ }))
+
+ srtcpSession, err := r.transport.getSRTCPSession()
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ rtcpReadStream, err := srtcpSession.OpenReadStream(uint32(ssrc))
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+
+ rtcpInterceptor := r.api.interceptor.BindRTCPReader(interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
+ n, err = rtcpReadStream.Read(in)
+ return n, a, err
+ }))
+
+ return rtpReadStream, rtpInterceptor, rtcpReadStream, rtcpInterceptor, nil
+}
+
+// SetReadDeadline sets the max amount of time the RTCP stream will block before returning. 0 is forever.
+func (r *RTPReceiver) SetReadDeadline(t time.Time) error {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ if err := r.tracks[0].rtcpReadStream.SetReadDeadline(t); err != nil {
+ return err
+ }
+ return nil
+}
+
+// SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever.
+func (r *RTPReceiver) SetReadDeadlineSimulcast(deadline time.Time, rid string) error {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ for _, t := range r.tracks {
+ if t.track != nil && t.track.rid == rid {
+ return t.rtcpReadStream.SetReadDeadline(deadline)
+ }
+ }
+ return fmt.Errorf("%w: %s", errRTPReceiverForRIDTrackStreamNotFound, rid)
+}
+
+// setRTPReadDeadline sets the max amount of time the RTP stream will block before returning. 0 is forever.
+// This should be fired by calling SetReadDeadline on the TrackRemote
+func (r *RTPReceiver) setRTPReadDeadline(deadline time.Time, reader *TrackRemote) error {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ if t := r.streamsForTrack(reader); t != nil {
+ return t.rtpReadStream.SetReadDeadline(deadline)
+ }
+ return fmt.Errorf("%w: %d", errRTPReceiverWithSSRCTrackStreamNotFound, reader.SSRC())
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpsender.go b/vendor/github.com/pion/webrtc/v3/rtpsender.go
new file mode 100644
index 0000000..f3d34c7
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpsender.go
@@ -0,0 +1,261 @@
+// +build !js
+
+package webrtc
+
+import (
+ "io"
+ "sync"
+ "time"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/randutil"
+ "github.com/pion/rtcp"
+ "github.com/pion/rtp"
+)
+
+// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer
+type RTPSender struct {
+ track TrackLocal
+
+ srtpStream *srtpWriterFuture
+ rtcpInterceptor interceptor.RTCPReader
+
+ context TrackLocalContext
+
+ transport *DTLSTransport
+
+ payloadType PayloadType
+ ssrc SSRC
+
+ // nolint:godox
+ // TODO(sgotti) remove this when in future we'll avoid replacing
+ // a transceiver sender since we can just check the
+ // transceiver negotiation status
+ negotiated bool
+
+ // A reference to the associated api object
+ api *API
+ id string
+
+ mu sync.RWMutex
+ sendCalled, stopCalled chan struct{}
+}
+
+// NewRTPSender constructs a new RTPSender
+func (api *API) NewRTPSender(track TrackLocal, transport *DTLSTransport) (*RTPSender, error) {
+ if track == nil {
+ return nil, errRTPSenderTrackNil
+ } else if transport == nil {
+ return nil, errRTPSenderDTLSTransportNil
+ }
+
+ id, err := randutil.GenerateCryptoRandomString(32, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ if err != nil {
+ return nil, err
+ }
+
+ r := &RTPSender{
+ track: track,
+ transport: transport,
+ api: api,
+ sendCalled: make(chan struct{}),
+ stopCalled: make(chan struct{}),
+ ssrc: SSRC(randutil.NewMathRandomGenerator().Uint32()),
+ id: id,
+ srtpStream: &srtpWriterFuture{},
+ }
+
+ r.srtpStream.rtpSender = r
+
+ r.rtcpInterceptor = r.api.interceptor.BindRTCPReader(interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
+ n, err = r.srtpStream.Read(in)
+ return n, a, err
+ }))
+
+ return r, nil
+}
+
+func (r *RTPSender) isNegotiated() bool {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+ return r.negotiated
+}
+
+func (r *RTPSender) setNegotiated() {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ r.negotiated = true
+}
+
+// Transport returns the currently-configured *DTLSTransport or nil
+// if one has not yet been configured
+func (r *RTPSender) Transport() *DTLSTransport {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+ return r.transport
+}
+
+// GetParameters describes the current configuration for the encoding and
+// transmission of media on the sender's track.
+func (r *RTPSender) GetParameters() RTPSendParameters {
+ return RTPSendParameters{
+ RTPParameters: r.api.mediaEngine.getRTPParametersByKind(
+ r.track.Kind(),
+ []RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
+ ),
+ Encodings: []RTPEncodingParameters{
+ {
+ RTPCodingParameters: RTPCodingParameters{
+ SSRC: r.ssrc,
+ PayloadType: r.payloadType,
+ },
+ },
+ },
+ }
+}
+
+// Track returns the RTCRtpTransceiver track, or nil
+func (r *RTPSender) Track() TrackLocal {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+ return r.track
+}
+
+// ReplaceTrack replaces the track currently being used as the sender's source with a new TrackLocal.
+// The new track must be of the same media kind (audio, video, etc) and switching the track should not
+// require negotiation.
+func (r *RTPSender) ReplaceTrack(track TrackLocal) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if r.hasSent() && r.track != nil {
+ if err := r.track.Unbind(r.context); err != nil {
+ return err
+ }
+ }
+
+ if !r.hasSent() || track == nil {
+ r.track = track
+ return nil
+ }
+
+ if _, err := track.Bind(r.context); err != nil {
+ // Re-bind the original track
+ if _, reBindErr := r.track.Bind(r.context); reBindErr != nil {
+ return reBindErr
+ }
+
+ return err
+ }
+
+ r.track = track
+ return nil
+}
+
+// Send Attempts to set the parameters controlling the sending of media.
+func (r *RTPSender) Send(parameters RTPSendParameters) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if r.hasSent() {
+ return errRTPSenderSendAlreadyCalled
+ }
+
+ writeStream := &interceptorToTrackLocalWriter{}
+ r.context = TrackLocalContext{
+ id: r.id,
+ params: r.api.mediaEngine.getRTPParametersByKind(r.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}),
+ ssrc: parameters.Encodings[0].SSRC,
+ writeStream: writeStream,
+ }
+
+ codec, err := r.track.Bind(r.context)
+ if err != nil {
+ return err
+ }
+ r.context.params.Codecs = []RTPCodecParameters{codec}
+
+ streamInfo := createStreamInfo(r.id, parameters.Encodings[0].SSRC, codec.PayloadType, codec.RTPCodecCapability, parameters.HeaderExtensions)
+ rtpInterceptor := r.api.interceptor.BindLocalStream(&streamInfo, interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
+ return r.srtpStream.WriteRTP(header, payload)
+ }))
+ writeStream.interceptor.Store(rtpInterceptor)
+
+ close(r.sendCalled)
+ return nil
+}
+
+// Stop irreversibly stops the RTPSender
+func (r *RTPSender) Stop() error {
+ r.mu.Lock()
+
+ if stopped := r.hasStopped(); stopped {
+ r.mu.Unlock()
+ return nil
+ }
+
+ close(r.stopCalled)
+ r.mu.Unlock()
+
+ if !r.hasSent() {
+ return nil
+ }
+
+ if err := r.ReplaceTrack(nil); err != nil {
+ return err
+ }
+
+ return r.srtpStream.Close()
+}
+
+// Read reads incoming RTCP for this RTPReceiver
+func (r *RTPSender) Read(b []byte) (n int, a interceptor.Attributes, err error) {
+ select {
+ case <-r.sendCalled:
+ return r.rtcpInterceptor.Read(b, a)
+ case <-r.stopCalled:
+ return 0, nil, io.ErrClosedPipe
+ }
+}
+
+// ReadRTCP is a convenience method that wraps Read and unmarshals for you.
+func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
+ b := make([]byte, receiveMTU)
+ i, attributes, err := r.Read(b)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ pkts, err := rtcp.Unmarshal(b[:i])
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return pkts, attributes, nil
+}
+
+// SetReadDeadline sets the deadline for the Read operation.
+// Setting to zero means no deadline.
+func (r *RTPSender) SetReadDeadline(t time.Time) error {
+ return r.srtpStream.SetReadDeadline(t)
+}
+
+// hasSent tells if data has been ever sent for this instance
+func (r *RTPSender) hasSent() bool {
+ select {
+ case <-r.sendCalled:
+ return true
+ default:
+ return false
+ }
+}
+
+// hasStopped tells if stop has been called
+func (r *RTPSender) hasStopped() bool {
+ select {
+ case <-r.stopCalled:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go b/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go
new file mode 100644
index 0000000..cc55e5d
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go
@@ -0,0 +1,7 @@
+package webrtc
+
+// RTPSendParameters contains the RTP stack settings used by receivers
+type RTPSendParameters struct {
+ RTPParameters
+ Encodings []RTPEncodingParameters
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtptransceiver.go b/vendor/github.com/pion/webrtc/v3/rtptransceiver.go
new file mode 100644
index 0000000..ee9de2f
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtptransceiver.go
@@ -0,0 +1,187 @@
+// +build !js
+
+package webrtc
+
+import (
+ "fmt"
+ "sync/atomic"
+
+ "github.com/pion/rtp"
+)
+
+// RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid.
+type RTPTransceiver struct {
+ mid atomic.Value // string
+ sender atomic.Value // *RTPSender
+ receiver atomic.Value // *RTPReceiver
+ direction atomic.Value // RTPTransceiverDirection
+
+ stopped bool
+ kind RTPCodecType
+}
+
+// Sender returns the RTPTransceiver's RTPSender if it has one
+func (t *RTPTransceiver) Sender() *RTPSender {
+ if v := t.sender.Load(); v != nil {
+ return v.(*RTPSender)
+ }
+
+ return nil
+}
+
+// SetSender sets the RTPSender and Track to current transceiver
+func (t *RTPTransceiver) SetSender(s *RTPSender, track TrackLocal) error {
+ t.setSender(s)
+ return t.setSendingTrack(track)
+}
+
+func (t *RTPTransceiver) setSender(s *RTPSender) {
+ t.sender.Store(s)
+}
+
+// Receiver returns the RTPTransceiver's RTPReceiver if it has one
+func (t *RTPTransceiver) Receiver() *RTPReceiver {
+ if v := t.receiver.Load(); v != nil {
+ return v.(*RTPReceiver)
+ }
+
+ return nil
+}
+
+// setMid sets the RTPTransceiver's mid. If it was already set, will return an error.
+func (t *RTPTransceiver) setMid(mid string) error {
+ if currentMid := t.Mid(); currentMid != "" {
+ return fmt.Errorf("%w: %s to %s", errRTPTransceiverCannotChangeMid, currentMid, mid)
+ }
+ t.mid.Store(mid)
+ return nil
+}
+
+// Mid gets the Transceiver's mid value. When not already set, this value will be set in CreateOffer or CreateAnswer.
+func (t *RTPTransceiver) Mid() string {
+ if v := t.mid.Load(); v != nil {
+ return v.(string)
+ }
+ return ""
+}
+
+// Kind returns RTPTransceiver's kind.
+func (t *RTPTransceiver) Kind() RTPCodecType {
+ return t.kind
+}
+
+// Direction returns the RTPTransceiver's current direction
+func (t *RTPTransceiver) Direction() RTPTransceiverDirection {
+ return t.direction.Load().(RTPTransceiverDirection)
+}
+
+// Stop irreversibly stops the RTPTransceiver
+func (t *RTPTransceiver) Stop() error {
+ if t.Sender() != nil {
+ if err := t.Sender().Stop(); err != nil {
+ return err
+ }
+ }
+ if t.Receiver() != nil {
+ if err := t.Receiver().Stop(); err != nil {
+ return err
+ }
+ }
+
+ t.setDirection(RTPTransceiverDirectionInactive)
+ return nil
+}
+
+func (t *RTPTransceiver) setReceiver(r *RTPReceiver) {
+ t.receiver.Store(r)
+}
+
+func (t *RTPTransceiver) setDirection(d RTPTransceiverDirection) {
+ t.direction.Store(d)
+}
+
+func (t *RTPTransceiver) setSendingTrack(track TrackLocal) error {
+ if err := t.Sender().ReplaceTrack(track); err != nil {
+ return err
+ }
+ if track == nil {
+ t.setSender(nil)
+ }
+
+ switch {
+ case track != nil && t.Direction() == RTPTransceiverDirectionRecvonly:
+ t.setDirection(RTPTransceiverDirectionSendrecv)
+ case track != nil && t.Direction() == RTPTransceiverDirectionInactive:
+ t.setDirection(RTPTransceiverDirectionSendonly)
+ case track == nil && t.Direction() == RTPTransceiverDirectionSendrecv:
+ t.setDirection(RTPTransceiverDirectionRecvonly)
+ case track == nil && t.Direction() == RTPTransceiverDirectionSendonly:
+ t.setDirection(RTPTransceiverDirectionInactive)
+ default:
+ return errRTPTransceiverSetSendingInvalidState
+ }
+ return nil
+}
+
+func findByMid(mid string, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
+ for i, t := range localTransceivers {
+ if t.Mid() == mid {
+ return t, append(localTransceivers[:i], localTransceivers[i+1:]...)
+ }
+ }
+
+ return nil, localTransceivers
+}
+
+// Given a direction+type pluck a transceiver from the passed list
+// if no entry satisfies the requested type+direction return a inactive Transceiver
+func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransceiverDirection, localTransceivers []*RTPTransceiver) (*RTPTransceiver, []*RTPTransceiver) {
+ // Get direction order from most preferred to least
+ getPreferredDirections := func() []RTPTransceiverDirection {
+ switch remoteDirection {
+ case RTPTransceiverDirectionSendrecv:
+ return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv}
+ case RTPTransceiverDirectionSendonly:
+ return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly}
+ case RTPTransceiverDirectionRecvonly:
+ return []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionSendrecv}
+ default:
+ return []RTPTransceiverDirection{}
+ }
+ }
+
+ for _, possibleDirection := range getPreferredDirections() {
+ for i := range localTransceivers {
+ t := localTransceivers[i]
+ if t.Mid() == "" && t.kind == remoteKind && possibleDirection == t.Direction() {
+ return t, append(localTransceivers[:i], localTransceivers[i+1:]...)
+ }
+ }
+ }
+
+ return nil, localTransceivers
+}
+
+// handleUnknownRTPPacket consumes a single RTP Packet and returns information that is helpful
+// for demuxing and handling an unknown SSRC (usually for Simulcast)
+func handleUnknownRTPPacket(buf []byte, midExtensionID, streamIDExtensionID uint8) (mid, rid string, payloadType PayloadType, err error) {
+ rp := &rtp.Packet{}
+ if err = rp.Unmarshal(buf); err != nil {
+ return
+ }
+
+ if !rp.Header.Extension {
+ return
+ }
+
+ payloadType = PayloadType(rp.PayloadType)
+ if payload := rp.GetExtension(midExtensionID); payload != nil {
+ mid = string(payload)
+ }
+
+ if payload := rp.GetExtension(streamIDExtensionID); payload != nil {
+ rid = string(payload)
+ }
+
+ return
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go b/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go
new file mode 100644
index 0000000..3073014
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go
@@ -0,0 +1,85 @@
+package webrtc
+
+// RTPTransceiverDirection indicates the direction of the RTPTransceiver.
+type RTPTransceiverDirection int
+
+const (
+ // RTPTransceiverDirectionSendrecv indicates the RTPSender will offer
+ // to send RTP and RTPReceiver the will offer to receive RTP.
+ RTPTransceiverDirectionSendrecv RTPTransceiverDirection = iota + 1
+
+ // RTPTransceiverDirectionSendonly indicates the RTPSender will offer
+ // to send RTP.
+ RTPTransceiverDirectionSendonly
+
+ // RTPTransceiverDirectionRecvonly indicates the RTPReceiver the will
+ // offer to receive RTP.
+ RTPTransceiverDirectionRecvonly
+
+ // RTPTransceiverDirectionInactive indicates the RTPSender won't offer
+ // to send RTP and RTPReceiver the won't offer to receive RTP.
+ RTPTransceiverDirectionInactive
+)
+
+// This is done this way because of a linter.
+const (
+ rtpTransceiverDirectionSendrecvStr = "sendrecv"
+ rtpTransceiverDirectionSendonlyStr = "sendonly"
+ rtpTransceiverDirectionRecvonlyStr = "recvonly"
+ rtpTransceiverDirectionInactiveStr = "inactive"
+)
+
+// NewRTPTransceiverDirection defines a procedure for creating a new
+// RTPTransceiverDirection from a raw string naming the transceiver direction.
+func NewRTPTransceiverDirection(raw string) RTPTransceiverDirection {
+ switch raw {
+ case rtpTransceiverDirectionSendrecvStr:
+ return RTPTransceiverDirectionSendrecv
+ case rtpTransceiverDirectionSendonlyStr:
+ return RTPTransceiverDirectionSendonly
+ case rtpTransceiverDirectionRecvonlyStr:
+ return RTPTransceiverDirectionRecvonly
+ case rtpTransceiverDirectionInactiveStr:
+ return RTPTransceiverDirectionInactive
+ default:
+ return RTPTransceiverDirection(Unknown)
+ }
+}
+
+func (t RTPTransceiverDirection) String() string {
+ switch t {
+ case RTPTransceiverDirectionSendrecv:
+ return rtpTransceiverDirectionSendrecvStr
+ case RTPTransceiverDirectionSendonly:
+ return rtpTransceiverDirectionSendonlyStr
+ case RTPTransceiverDirectionRecvonly:
+ return rtpTransceiverDirectionRecvonlyStr
+ case RTPTransceiverDirectionInactive:
+ return rtpTransceiverDirectionInactiveStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// Revers indicate the opposite direction
+func (t RTPTransceiverDirection) Revers() RTPTransceiverDirection {
+ switch t {
+ case RTPTransceiverDirectionSendonly:
+ return RTPTransceiverDirectionRecvonly
+ case RTPTransceiverDirectionRecvonly:
+ return RTPTransceiverDirectionSendonly
+ default:
+ return t
+ }
+}
+
+func haveRTPTransceiverDirectionIntersection(haystack []RTPTransceiverDirection, needle []RTPTransceiverDirection) bool {
+ for _, n := range needle {
+ for _, h := range haystack {
+ if n == h {
+ return true
+ }
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go b/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go
new file mode 100644
index 0000000..3c439b7
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go
@@ -0,0 +1,12 @@
+package webrtc
+
+// RTPTransceiverInit dictionary is used when calling the WebRTC function addTransceiver() to provide configuration options for the new transceiver.
+type RTPTransceiverInit struct {
+ Direction RTPTransceiverDirection
+ SendEncodings []RTPEncodingParameters
+ // Streams []*Track
+}
+
+// RtpTransceiverInit is a temporary mapping while we fix case sensitivity
+// Deprecated: Use RTPTransceiverInit instead
+type RtpTransceiverInit = RTPTransceiverInit //nolint: stylecheck,golint
diff --git a/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go b/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go
new file mode 100644
index 0000000..34399d3
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go
@@ -0,0 +1,6 @@
+package webrtc
+
+// SCTPCapabilities indicates the capabilities of the SCTPTransport.
+type SCTPCapabilities struct {
+ MaxMessageSize uint32 `json:"maxMessageSize"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/sctptransport.go b/vendor/github.com/pion/webrtc/v3/sctptransport.go
new file mode 100644
index 0000000..d7b3764
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/sctptransport.go
@@ -0,0 +1,379 @@
+// +build !js
+
+package webrtc
+
+import (
+ "io"
+ "math"
+ "sync"
+ "time"
+
+ "github.com/pion/datachannel"
+ "github.com/pion/logging"
+ "github.com/pion/sctp"
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+const sctpMaxChannels = uint16(65535)
+
+// SCTPTransport provides details about the SCTP transport.
+type SCTPTransport struct {
+ lock sync.RWMutex
+
+ dtlsTransport *DTLSTransport
+
+ // State represents the current state of the SCTP transport.
+ state SCTPTransportState
+
+ // SCTPTransportState doesn't have an enum to distinguish between New/Connecting
+ // so we need a dedicated field
+ isStarted bool
+
+ // MaxMessageSize represents the maximum size of data that can be passed to
+ // DataChannel's send() method.
+ maxMessageSize float64
+
+ // MaxChannels represents the maximum amount of DataChannel's that can
+ // be used simultaneously.
+ maxChannels *uint16
+
+ // OnStateChange func()
+
+ onErrorHandler func(error)
+
+ association *sctp.Association
+ onDataChannelHandler func(*DataChannel)
+ onDataChannelOpenedHandler func(*DataChannel)
+
+ // DataChannels
+ dataChannels []*DataChannel
+ dataChannelsOpened uint32
+ dataChannelsRequested uint32
+ dataChannelsAccepted uint32
+
+ api *API
+ log logging.LeveledLogger
+}
+
+// NewSCTPTransport creates a new SCTPTransport.
+// This constructor is part of the ORTC API. It is not
+// meant to be used together with the basic WebRTC API.
+func (api *API) NewSCTPTransport(dtls *DTLSTransport) *SCTPTransport {
+ res := &SCTPTransport{
+ dtlsTransport: dtls,
+ state: SCTPTransportStateConnecting,
+ api: api,
+ log: api.settingEngine.LoggerFactory.NewLogger("ortc"),
+ }
+
+ res.updateMessageSize()
+ res.updateMaxChannels()
+
+ return res
+}
+
+// Transport returns the DTLSTransport instance the SCTPTransport is sending over.
+func (r *SCTPTransport) Transport() *DTLSTransport {
+ r.lock.RLock()
+ defer r.lock.RUnlock()
+
+ return r.dtlsTransport
+}
+
+// GetCapabilities returns the SCTPCapabilities of the SCTPTransport.
+func (r *SCTPTransport) GetCapabilities() SCTPCapabilities {
+ return SCTPCapabilities{
+ MaxMessageSize: 0,
+ }
+}
+
+// Start the SCTPTransport. Since both local and remote parties must mutually
+// create an SCTPTransport, SCTP SO (Simultaneous Open) is used to establish
+// a connection over SCTP.
+func (r *SCTPTransport) Start(remoteCaps SCTPCapabilities) error {
+ if r.isStarted {
+ return nil
+ }
+ r.isStarted = true
+
+ if err := r.ensureDTLS(); err != nil {
+ return err
+ }
+
+ sctpAssociation, err := sctp.Client(sctp.Config{
+ NetConn: r.Transport().conn,
+ LoggerFactory: r.api.settingEngine.LoggerFactory,
+ })
+ if err != nil {
+ return err
+ }
+
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ r.association = sctpAssociation
+ r.state = SCTPTransportStateConnected
+
+ go r.acceptDataChannels(sctpAssociation)
+
+ return nil
+}
+
+// Stop stops the SCTPTransport
+func (r *SCTPTransport) Stop() error {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ if r.association == nil {
+ return nil
+ }
+ err := r.association.Close()
+ if err != nil {
+ return err
+ }
+
+ r.association = nil
+ r.state = SCTPTransportStateClosed
+
+ return nil
+}
+
+func (r *SCTPTransport) ensureDTLS() error {
+ dtlsTransport := r.Transport()
+ if dtlsTransport == nil || dtlsTransport.conn == nil {
+ return errSCTPTransportDTLS
+ }
+
+ return nil
+}
+
+func (r *SCTPTransport) acceptDataChannels(a *sctp.Association) {
+ for {
+ dc, err := datachannel.Accept(a, &datachannel.Config{
+ LoggerFactory: r.api.settingEngine.LoggerFactory,
+ })
+ if err != nil {
+ if err != io.EOF {
+ r.log.Errorf("Failed to accept data channel: %v", err)
+ r.onError(err)
+ }
+ return
+ }
+
+ var (
+ maxRetransmits *uint16
+ maxPacketLifeTime *uint16
+ )
+ val := uint16(dc.Config.ReliabilityParameter)
+ ordered := true
+
+ switch dc.Config.ChannelType {
+ case datachannel.ChannelTypeReliable:
+ ordered = true
+ case datachannel.ChannelTypeReliableUnordered:
+ ordered = false
+ case datachannel.ChannelTypePartialReliableRexmit:
+ ordered = true
+ maxRetransmits = &val
+ case datachannel.ChannelTypePartialReliableRexmitUnordered:
+ ordered = false
+ maxRetransmits = &val
+ case datachannel.ChannelTypePartialReliableTimed:
+ ordered = true
+ maxPacketLifeTime = &val
+ case datachannel.ChannelTypePartialReliableTimedUnordered:
+ ordered = false
+ maxPacketLifeTime = &val
+ default:
+ }
+
+ sid := dc.StreamIdentifier()
+ rtcDC, err := r.api.newDataChannel(&DataChannelParameters{
+ ID: &sid,
+ Label: dc.Config.Label,
+ Protocol: dc.Config.Protocol,
+ Negotiated: dc.Config.Negotiated,
+ Ordered: ordered,
+ MaxPacketLifeTime: maxPacketLifeTime,
+ MaxRetransmits: maxRetransmits,
+ }, r.api.settingEngine.LoggerFactory.NewLogger("ortc"))
+ if err != nil {
+ r.log.Errorf("Failed to accept data channel: %v", err)
+ r.onError(err)
+ return
+ }
+
+ <-r.onDataChannel(rtcDC)
+ rtcDC.handleOpen(dc)
+
+ r.lock.Lock()
+ r.dataChannelsOpened++
+ handler := r.onDataChannelOpenedHandler
+ r.lock.Unlock()
+
+ if handler != nil {
+ handler(rtcDC)
+ }
+ }
+}
+
+// OnError sets an event handler which is invoked when
+// the SCTP connection error occurs.
+func (r *SCTPTransport) OnError(f func(err error)) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ r.onErrorHandler = f
+}
+
+func (r *SCTPTransport) onError(err error) {
+ r.lock.RLock()
+ handler := r.onErrorHandler
+ r.lock.RUnlock()
+
+ if handler != nil {
+ go handler(err)
+ }
+}
+
+// OnDataChannel sets an event handler which is invoked when a data
+// channel message arrives from a remote peer.
+func (r *SCTPTransport) OnDataChannel(f func(*DataChannel)) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ r.onDataChannelHandler = f
+}
+
+// OnDataChannelOpened sets an event handler which is invoked when a data
+// channel is opened
+func (r *SCTPTransport) OnDataChannelOpened(f func(*DataChannel)) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ r.onDataChannelOpenedHandler = f
+}
+
+func (r *SCTPTransport) onDataChannel(dc *DataChannel) (done chan struct{}) {
+ r.lock.Lock()
+ r.dataChannels = append(r.dataChannels, dc)
+ r.dataChannelsAccepted++
+ handler := r.onDataChannelHandler
+ r.lock.Unlock()
+
+ done = make(chan struct{})
+ if handler == nil || dc == nil {
+ close(done)
+ return
+ }
+
+ // Run this synchronously to allow setup done in onDataChannelFn()
+ // to complete before datachannel event handlers might be called.
+ go func() {
+ handler(dc)
+ close(done)
+ }()
+
+ return
+}
+
+func (r *SCTPTransport) updateMessageSize() {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ var remoteMaxMessageSize float64 = 65536 // pion/webrtc#758
+ var canSendSize float64 = 65536 // pion/webrtc#758
+
+ r.maxMessageSize = r.calcMessageSize(remoteMaxMessageSize, canSendSize)
+}
+
+func (r *SCTPTransport) calcMessageSize(remoteMaxMessageSize, canSendSize float64) float64 {
+ switch {
+ case remoteMaxMessageSize == 0 &&
+ canSendSize == 0:
+ return math.Inf(1)
+
+ case remoteMaxMessageSize == 0:
+ return canSendSize
+
+ case canSendSize == 0:
+ return remoteMaxMessageSize
+
+ case canSendSize > remoteMaxMessageSize:
+ return remoteMaxMessageSize
+
+ default:
+ return canSendSize
+ }
+}
+
+func (r *SCTPTransport) updateMaxChannels() {
+ val := sctpMaxChannels
+ r.maxChannels = &val
+}
+
+// MaxChannels is the maximum number of RTCDataChannels that can be open simultaneously.
+func (r *SCTPTransport) MaxChannels() uint16 {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ if r.maxChannels == nil {
+ return sctpMaxChannels
+ }
+
+ return *r.maxChannels
+}
+
+// State returns the current state of the SCTPTransport
+func (r *SCTPTransport) State() SCTPTransportState {
+ r.lock.RLock()
+ defer r.lock.RUnlock()
+ return r.state
+}
+
+func (r *SCTPTransport) collectStats(collector *statsReportCollector) {
+ r.lock.Lock()
+ association := r.association
+ r.lock.Unlock()
+
+ collector.Collecting()
+
+ stats := TransportStats{
+ Timestamp: statsTimestampFrom(time.Now()),
+ Type: StatsTypeTransport,
+ ID: "sctpTransport",
+ }
+
+ if association != nil {
+ stats.BytesSent = association.BytesSent()
+ stats.BytesReceived = association.BytesReceived()
+ }
+
+ collector.Collect(stats.ID, stats)
+}
+
+func (r *SCTPTransport) generateAndSetDataChannelID(dtlsRole DTLSRole, idOut **uint16) error {
+ isChannelWithID := func(id uint16) bool {
+ for _, d := range r.dataChannels {
+ if d.id != nil && *d.id == id {
+ return true
+ }
+ }
+ return false
+ }
+
+ var id uint16
+ if dtlsRole != DTLSRoleClient {
+ id++
+ }
+
+ max := r.MaxChannels()
+
+ r.lock.Lock()
+ defer r.lock.Unlock()
+ for ; id < max-1; id += 2 {
+ if isChannelWithID(id) {
+ continue
+ }
+ *idOut = &id
+ return nil
+ }
+
+ return &rtcerr.OperationError{Err: ErrMaxDataChannelID}
+}
diff --git a/vendor/github.com/pion/webrtc/v3/sctptransportstate.go b/vendor/github.com/pion/webrtc/v3/sctptransportstate.go
new file mode 100644
index 0000000..8dc7941
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/sctptransportstate.go
@@ -0,0 +1,54 @@
+package webrtc
+
+// SCTPTransportState indicates the state of the SCTP transport.
+type SCTPTransportState int
+
+const (
+ // SCTPTransportStateConnecting indicates the SCTPTransport is in the
+ // process of negotiating an association. This is the initial state of the
+ // SCTPTransportState when an SCTPTransport is created.
+ SCTPTransportStateConnecting SCTPTransportState = iota + 1
+
+ // SCTPTransportStateConnected indicates the negotiation of an
+ // association is completed.
+ SCTPTransportStateConnected
+
+ // SCTPTransportStateClosed indicates a SHUTDOWN or ABORT chunk is
+ // received or when the SCTP association has been closed intentionally,
+ // such as by closing the peer connection or applying a remote description
+ // that rejects data or changes the SCTP port.
+ SCTPTransportStateClosed
+)
+
+// This is done this way because of a linter.
+const (
+ sctpTransportStateConnectingStr = "connecting"
+ sctpTransportStateConnectedStr = "connected"
+ sctpTransportStateClosedStr = "closed"
+)
+
+func newSCTPTransportState(raw string) SCTPTransportState {
+ switch raw {
+ case sctpTransportStateConnectingStr:
+ return SCTPTransportStateConnecting
+ case sctpTransportStateConnectedStr:
+ return SCTPTransportStateConnected
+ case sctpTransportStateClosedStr:
+ return SCTPTransportStateClosed
+ default:
+ return SCTPTransportState(Unknown)
+ }
+}
+
+func (s SCTPTransportState) String() string {
+ switch s {
+ case SCTPTransportStateConnecting:
+ return sctpTransportStateConnectingStr
+ case SCTPTransportStateConnected:
+ return sctpTransportStateConnectedStr
+ case SCTPTransportStateClosed:
+ return sctpTransportStateClosedStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/sdp.go b/vendor/github.com/pion/webrtc/v3/sdp.go
new file mode 100644
index 0000000..7454696
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/sdp.go
@@ -0,0 +1,643 @@
+// +build !js
+
+package webrtc
+
+import (
+ "fmt"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/logging"
+ "github.com/pion/sdp/v3"
+)
+
+// trackDetails represents any media source that can be represented in a SDP
+// This isn't keyed by SSRC because it also needs to support rid based sources
+type trackDetails struct {
+ mid string
+ kind RTPCodecType
+ streamID string
+ id string
+ ssrc SSRC
+ rids []string
+}
+
+func trackDetailsForSSRC(trackDetails []trackDetails, ssrc SSRC) *trackDetails {
+ for i := range trackDetails {
+ if trackDetails[i].ssrc == ssrc {
+ return &trackDetails[i]
+ }
+ }
+ return nil
+}
+
+func filterTrackWithSSRC(incomingTracks []trackDetails, ssrc SSRC) []trackDetails {
+ filtered := []trackDetails{}
+ for i := range incomingTracks {
+ if incomingTracks[i].ssrc != ssrc {
+ filtered = append(filtered, incomingTracks[i])
+ }
+ }
+ return filtered
+}
+
+// extract all trackDetails from an SDP.
+func trackDetailsFromSDP(log logging.LeveledLogger, s *sdp.SessionDescription) []trackDetails { // nolint:gocognit
+ incomingTracks := []trackDetails{}
+ rtxRepairFlows := map[uint32]bool{}
+
+ for _, media := range s.MediaDescriptions {
+ // Plan B can have multiple tracks in a signle media section
+ streamID := ""
+ trackID := ""
+
+ // If media section is recvonly or inactive skip
+ if _, ok := media.Attribute(sdp.AttrKeyRecvOnly); ok {
+ continue
+ } else if _, ok := media.Attribute(sdp.AttrKeyInactive); ok {
+ continue
+ }
+
+ midValue := getMidValue(media)
+ if midValue == "" {
+ continue
+ }
+
+ codecType := NewRTPCodecType(media.MediaName.Media)
+ if codecType == 0 {
+ continue
+ }
+
+ for _, attr := range media.Attributes {
+ switch attr.Key {
+ case sdp.AttrKeySSRCGroup:
+ split := strings.Split(attr.Value, " ")
+ if split[0] == sdp.SemanticTokenFlowIdentification {
+ // Add rtx ssrcs to blacklist, to avoid adding them as tracks
+ // Essentially lines like `a=ssrc-group:FID 2231627014 632943048` are processed by this section
+ // as this declares that the second SSRC (632943048) is a rtx repair flow (RFC4588) for the first
+ // (2231627014) as specified in RFC5576
+ if len(split) == 3 {
+ _, err := strconv.ParseUint(split[1], 10, 32)
+ if err != nil {
+ log.Warnf("Failed to parse SSRC: %v", err)
+ continue
+ }
+ rtxRepairFlow, err := strconv.ParseUint(split[2], 10, 32)
+ if err != nil {
+ log.Warnf("Failed to parse SSRC: %v", err)
+ continue
+ }
+ rtxRepairFlows[uint32(rtxRepairFlow)] = true
+ incomingTracks = filterTrackWithSSRC(incomingTracks, SSRC(rtxRepairFlow)) // Remove if rtx was added as track before
+ }
+ }
+
+ // Handle `a=msid:<stream_id> <track_label>` for Unified plan. The first value is the same as MediaStream.id
+ // in the browser and can be used to figure out which tracks belong to the same stream. The browser should
+ // figure this out automatically when an ontrack event is emitted on RTCPeerConnection.
+ case sdp.AttrKeyMsid:
+ split := strings.Split(attr.Value, " ")
+ if len(split) == 2 {
+ streamID = split[0]
+ trackID = split[1]
+ }
+
+ case sdp.AttrKeySSRC:
+ split := strings.Split(attr.Value, " ")
+ ssrc, err := strconv.ParseUint(split[0], 10, 32)
+ if err != nil {
+ log.Warnf("Failed to parse SSRC: %v", err)
+ continue
+ }
+
+ if rtxRepairFlow := rtxRepairFlows[uint32(ssrc)]; rtxRepairFlow {
+ continue // This ssrc is a RTX repair flow, ignore
+ }
+
+ if len(split) == 3 && strings.HasPrefix(split[1], "msid:") {
+ streamID = split[1][len("msid:"):]
+ trackID = split[2]
+ }
+
+ isNewTrack := true
+ trackDetails := &trackDetails{}
+ for i := range incomingTracks {
+ if incomingTracks[i].ssrc == SSRC(ssrc) {
+ trackDetails = &incomingTracks[i]
+ isNewTrack = false
+ }
+ }
+
+ trackDetails.mid = midValue
+ trackDetails.kind = codecType
+ trackDetails.streamID = streamID
+ trackDetails.id = trackID
+ trackDetails.ssrc = SSRC(ssrc)
+
+ if isNewTrack {
+ incomingTracks = append(incomingTracks, *trackDetails)
+ }
+ }
+ }
+
+ if rids := getRids(media); len(rids) != 0 && trackID != "" && streamID != "" {
+ newTrack := trackDetails{
+ mid: midValue,
+ kind: codecType,
+ streamID: streamID,
+ id: trackID,
+ rids: []string{},
+ }
+ for rid := range rids {
+ newTrack.rids = append(newTrack.rids, rid)
+ }
+
+ incomingTracks = append(incomingTracks, newTrack)
+ }
+ }
+ return incomingTracks
+}
+
+func getRids(media *sdp.MediaDescription) map[string]string {
+ rids := map[string]string{}
+ for _, attr := range media.Attributes {
+ if attr.Key == "rid" {
+ split := strings.Split(attr.Value, " ")
+ rids[split[0]] = attr.Value
+ }
+ }
+ return rids
+}
+
+func addCandidatesToMediaDescriptions(candidates []ICECandidate, m *sdp.MediaDescription, iceGatheringState ICEGatheringState) error {
+ appendCandidateIfNew := func(c ice.Candidate, attributes []sdp.Attribute) {
+ marshaled := c.Marshal()
+ for _, a := range attributes {
+ if marshaled == a.Value {
+ return
+ }
+ }
+
+ m.WithValueAttribute("candidate", marshaled)
+ }
+
+ for _, c := range candidates {
+ candidate, err := c.toICE()
+ if err != nil {
+ return err
+ }
+
+ candidate.SetComponent(1)
+ appendCandidateIfNew(candidate, m.Attributes)
+
+ candidate.SetComponent(2)
+ appendCandidateIfNew(candidate, m.Attributes)
+ }
+
+ if iceGatheringState != ICEGatheringStateComplete {
+ return nil
+ }
+ for _, a := range m.Attributes {
+ if a.Key == "end-of-candidates" {
+ return nil
+ }
+ }
+
+ m.WithPropertyAttribute("end-of-candidates")
+ return nil
+}
+
+func addDataMediaSection(d *sdp.SessionDescription, shouldAddCandidates bool, dtlsFingerprints []DTLSFingerprint, midValue string, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole, iceGatheringState ICEGatheringState) error {
+ media := (&sdp.MediaDescription{
+ MediaName: sdp.MediaName{
+ Media: mediaSectionApplication,
+ Port: sdp.RangedPort{Value: 9},
+ Protos: []string{"UDP", "DTLS", "SCTP"},
+ Formats: []string{"webrtc-datachannel"},
+ },
+ ConnectionInformation: &sdp.ConnectionInformation{
+ NetworkType: "IN",
+ AddressType: "IP4",
+ Address: &sdp.Address{
+ Address: "0.0.0.0",
+ },
+ },
+ }).
+ WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()).
+ WithValueAttribute(sdp.AttrKeyMID, midValue).
+ WithPropertyAttribute(RTPTransceiverDirectionSendrecv.String()).
+ WithPropertyAttribute("sctp-port:5000").
+ WithICECredentials(iceParams.UsernameFragment, iceParams.Password)
+
+ for _, f := range dtlsFingerprints {
+ media = media.WithFingerprint(f.Algorithm, strings.ToUpper(f.Value))
+ }
+
+ if shouldAddCandidates {
+ if err := addCandidatesToMediaDescriptions(candidates, media, iceGatheringState); err != nil {
+ return err
+ }
+ }
+
+ d.WithMedia(media)
+ return nil
+}
+
+func populateLocalCandidates(sessionDescription *SessionDescription, i *ICEGatherer, iceGatheringState ICEGatheringState) *SessionDescription {
+ if sessionDescription == nil || i == nil {
+ return sessionDescription
+ }
+
+ candidates, err := i.GetLocalCandidates()
+ if err != nil {
+ return sessionDescription
+ }
+
+ parsed := sessionDescription.parsed
+ if len(parsed.MediaDescriptions) > 0 {
+ m := parsed.MediaDescriptions[0]
+ if err = addCandidatesToMediaDescriptions(candidates, m, iceGatheringState); err != nil {
+ return sessionDescription
+ }
+ }
+
+ sdp, err := parsed.Marshal()
+ if err != nil {
+ return sessionDescription
+ }
+
+ return &SessionDescription{
+ SDP: string(sdp),
+ Type: sessionDescription.Type,
+ }
+}
+
+func addTransceiverSDP(d *sdp.SessionDescription, isPlanB, shouldAddCandidates bool, dtlsFingerprints []DTLSFingerprint, mediaEngine *MediaEngine, midValue string, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole, iceGatheringState ICEGatheringState, mediaSection mediaSection) (bool, error) {
+ transceivers := mediaSection.transceivers
+ if len(transceivers) < 1 {
+ return false, errSDPZeroTransceivers
+ }
+ // Use the first transceiver to generate the section attributes
+ t := transceivers[0]
+ media := sdp.NewJSEPMediaDescription(t.kind.String(), []string{}).
+ WithValueAttribute(sdp.AttrKeyConnectionSetup, dtlsRole.String()).
+ WithValueAttribute(sdp.AttrKeyMID, midValue).
+ WithICECredentials(iceParams.UsernameFragment, iceParams.Password).
+ WithPropertyAttribute(sdp.AttrKeyRTCPMux).
+ WithPropertyAttribute(sdp.AttrKeyRTCPRsize)
+
+ codecs := mediaEngine.getCodecsByKind(t.kind)
+ for _, codec := range codecs {
+ name := strings.TrimPrefix(codec.MimeType, "audio/")
+ name = strings.TrimPrefix(name, "video/")
+ media.WithCodec(uint8(codec.PayloadType), name, codec.ClockRate, codec.Channels, codec.SDPFmtpLine)
+
+ for _, feedback := range codec.RTPCodecCapability.RTCPFeedback {
+ media.WithValueAttribute("rtcp-fb", fmt.Sprintf("%d %s %s", codec.PayloadType, feedback.Type, feedback.Parameter))
+ }
+ }
+ if len(codecs) == 0 {
+ // Explicitly reject track if we don't have the codec
+ d.WithMedia(&sdp.MediaDescription{
+ MediaName: sdp.MediaName{
+ Media: t.kind.String(),
+ Port: sdp.RangedPort{Value: 0},
+ Protos: []string{"UDP", "TLS", "RTP", "SAVPF"},
+ Formats: []string{"0"},
+ },
+ })
+ return false, nil
+ }
+
+ directions := []RTPTransceiverDirection{}
+ if t.Sender() != nil {
+ directions = append(directions, RTPTransceiverDirectionSendonly)
+ }
+ if t.Receiver() != nil {
+ directions = append(directions, RTPTransceiverDirectionRecvonly)
+ }
+
+ parameters := mediaEngine.getRTPParametersByKind(t.kind, directions)
+ for _, rtpExtension := range parameters.HeaderExtensions {
+ extURL, err := url.Parse(rtpExtension.URI)
+ if err != nil {
+ return false, err
+ }
+ media.WithExtMap(sdp.ExtMap{Value: rtpExtension.ID, URI: extURL})
+ }
+
+ if len(mediaSection.ridMap) > 0 {
+ recvRids := make([]string, 0, len(mediaSection.ridMap))
+
+ for rid := range mediaSection.ridMap {
+ media.WithValueAttribute("rid", rid+" recv")
+ recvRids = append(recvRids, rid)
+ }
+ // Simulcast
+ media.WithValueAttribute("simulcast", "recv "+strings.Join(recvRids, ";"))
+ }
+
+ for _, mt := range transceivers {
+ if mt.Sender() != nil && mt.Sender().Track() != nil {
+ track := mt.Sender().Track()
+ media = media.WithMediaSource(uint32(mt.Sender().ssrc), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID())
+ if !isPlanB {
+ media = media.WithPropertyAttribute("msid:" + track.StreamID() + " " + track.ID())
+ break
+ }
+ }
+ }
+
+ media = media.WithPropertyAttribute(t.Direction().String())
+
+ for _, fingerprint := range dtlsFingerprints {
+ media = media.WithFingerprint(fingerprint.Algorithm, strings.ToUpper(fingerprint.Value))
+ }
+
+ if shouldAddCandidates {
+ if err := addCandidatesToMediaDescriptions(candidates, media, iceGatheringState); err != nil {
+ return false, err
+ }
+ }
+
+ d.WithMedia(media)
+
+ return true, nil
+}
+
+type mediaSection struct {
+ id string
+ transceivers []*RTPTransceiver
+ data bool
+ ridMap map[string]string
+}
+
+// populateSDP serializes a PeerConnections state into an SDP
+func populateSDP(d *sdp.SessionDescription, isPlanB bool, dtlsFingerprints []DTLSFingerprint, mediaDescriptionFingerprint bool, isICELite bool, mediaEngine *MediaEngine, connectionRole sdp.ConnectionRole, candidates []ICECandidate, iceParams ICEParameters, mediaSections []mediaSection, iceGatheringState ICEGatheringState) (*sdp.SessionDescription, error) {
+ var err error
+ mediaDtlsFingerprints := []DTLSFingerprint{}
+
+ if mediaDescriptionFingerprint {
+ mediaDtlsFingerprints = dtlsFingerprints
+ }
+
+ bundleValue := "BUNDLE"
+ bundleCount := 0
+ appendBundle := func(midValue string) {
+ bundleValue += " " + midValue
+ bundleCount++
+ }
+
+ for i, m := range mediaSections {
+ if m.data && len(m.transceivers) != 0 {
+ return nil, errSDPMediaSectionMediaDataChanInvalid
+ } else if !isPlanB && len(m.transceivers) > 1 {
+ return nil, errSDPMediaSectionMultipleTrackInvalid
+ }
+
+ shouldAddID := true
+ shouldAddCanidates := i == 0
+ if m.data {
+ if err = addDataMediaSection(d, shouldAddCanidates, mediaDtlsFingerprints, m.id, iceParams, candidates, connectionRole, iceGatheringState); err != nil {
+ return nil, err
+ }
+ } else {
+ shouldAddID, err = addTransceiverSDP(d, isPlanB, shouldAddCanidates, mediaDtlsFingerprints, mediaEngine, m.id, iceParams, candidates, connectionRole, iceGatheringState, m)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if shouldAddID {
+ appendBundle(m.id)
+ }
+ }
+
+ if !mediaDescriptionFingerprint {
+ for _, fingerprint := range dtlsFingerprints {
+ d.WithFingerprint(fingerprint.Algorithm, strings.ToUpper(fingerprint.Value))
+ }
+ }
+
+ if isICELite {
+ // RFC 5245 S15.3
+ d = d.WithValueAttribute(sdp.AttrKeyICELite, sdp.AttrKeyICELite)
+ }
+
+ return d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue), nil
+}
+
+func getMidValue(media *sdp.MediaDescription) string {
+ for _, attr := range media.Attributes {
+ if attr.Key == "mid" {
+ return attr.Value
+ }
+ }
+ return ""
+}
+
+func descriptionIsPlanB(desc *SessionDescription) bool {
+ if desc == nil || desc.parsed == nil {
+ return false
+ }
+
+ detectionRegex := regexp.MustCompile(`(?i)^(audio|video|data)$`)
+ for _, media := range desc.parsed.MediaDescriptions {
+ if len(detectionRegex.FindStringSubmatch(getMidValue(media))) == 2 {
+ return true
+ }
+ }
+ return false
+}
+
+func getPeerDirection(media *sdp.MediaDescription) RTPTransceiverDirection {
+ for _, a := range media.Attributes {
+ if direction := NewRTPTransceiverDirection(a.Key); direction != RTPTransceiverDirection(Unknown) {
+ return direction
+ }
+ }
+ return RTPTransceiverDirection(Unknown)
+}
+
+func extractFingerprint(desc *sdp.SessionDescription) (string, string, error) {
+ fingerprints := []string{}
+
+ if fingerprint, haveFingerprint := desc.Attribute("fingerprint"); haveFingerprint {
+ fingerprints = append(fingerprints, fingerprint)
+ }
+
+ for _, m := range desc.MediaDescriptions {
+ if fingerprint, haveFingerprint := m.Attribute("fingerprint"); haveFingerprint {
+ fingerprints = append(fingerprints, fingerprint)
+ }
+ }
+
+ if len(fingerprints) < 1 {
+ return "", "", ErrSessionDescriptionNoFingerprint
+ }
+
+ for _, m := range fingerprints {
+ if m != fingerprints[0] {
+ return "", "", ErrSessionDescriptionConflictingFingerprints
+ }
+ }
+
+ parts := strings.Split(fingerprints[0], " ")
+ if len(parts) != 2 {
+ return "", "", ErrSessionDescriptionInvalidFingerprint
+ }
+ return parts[1], parts[0], nil
+}
+
+func extractICEDetails(desc *sdp.SessionDescription) (string, string, []ICECandidate, error) {
+ candidates := []ICECandidate{}
+ remotePwds := []string{}
+ remoteUfrags := []string{}
+
+ if ufrag, haveUfrag := desc.Attribute("ice-ufrag"); haveUfrag {
+ remoteUfrags = append(remoteUfrags, ufrag)
+ }
+ if pwd, havePwd := desc.Attribute("ice-pwd"); havePwd {
+ remotePwds = append(remotePwds, pwd)
+ }
+
+ for _, m := range desc.MediaDescriptions {
+ if ufrag, haveUfrag := m.Attribute("ice-ufrag"); haveUfrag {
+ remoteUfrags = append(remoteUfrags, ufrag)
+ }
+ if pwd, havePwd := m.Attribute("ice-pwd"); havePwd {
+ remotePwds = append(remotePwds, pwd)
+ }
+
+ for _, a := range m.Attributes {
+ if a.IsICECandidate() {
+ c, err := ice.UnmarshalCandidate(a.Value)
+ if err != nil {
+ return "", "", nil, err
+ }
+
+ candidate, err := newICECandidateFromICE(c)
+ if err != nil {
+ return "", "", nil, err
+ }
+
+ candidates = append(candidates, candidate)
+ }
+ }
+ }
+
+ if len(remoteUfrags) == 0 {
+ return "", "", nil, ErrSessionDescriptionMissingIceUfrag
+ } else if len(remotePwds) == 0 {
+ return "", "", nil, ErrSessionDescriptionMissingIcePwd
+ }
+
+ for _, m := range remoteUfrags {
+ if m != remoteUfrags[0] {
+ return "", "", nil, ErrSessionDescriptionConflictingIceUfrag
+ }
+ }
+
+ for _, m := range remotePwds {
+ if m != remotePwds[0] {
+ return "", "", nil, ErrSessionDescriptionConflictingIcePwd
+ }
+ }
+
+ return remoteUfrags[0], remotePwds[0], candidates, nil
+}
+
+func haveApplicationMediaSection(desc *sdp.SessionDescription) bool {
+ for _, m := range desc.MediaDescriptions {
+ if m.MediaName.Media == mediaSectionApplication {
+ return true
+ }
+ }
+
+ return false
+}
+
+func getByMid(searchMid string, desc *SessionDescription) *sdp.MediaDescription {
+ for _, m := range desc.parsed.MediaDescriptions {
+ if mid, ok := m.Attribute(sdp.AttrKeyMID); ok && mid == searchMid {
+ return m
+ }
+ }
+ return nil
+}
+
+// haveDataChannel return MediaDescription with MediaName equal application
+func haveDataChannel(desc *SessionDescription) *sdp.MediaDescription {
+ for _, d := range desc.parsed.MediaDescriptions {
+ if d.MediaName.Media == mediaSectionApplication {
+ return d
+ }
+ }
+ return nil
+}
+
+func codecsFromMediaDescription(m *sdp.MediaDescription) (out []RTPCodecParameters, err error) {
+ s := &sdp.SessionDescription{
+ MediaDescriptions: []*sdp.MediaDescription{m},
+ }
+
+ for _, payloadStr := range m.MediaName.Formats {
+ payloadType, err := strconv.Atoi(payloadStr)
+ if err != nil {
+ return nil, err
+ }
+
+ codec, err := s.GetCodecForPayloadType(uint8(payloadType))
+ if err != nil {
+ if payloadType == 0 {
+ continue
+ }
+ return nil, err
+ }
+
+ channels := uint16(0)
+ val, err := strconv.Atoi(codec.EncodingParameters)
+ if err == nil {
+ channels = uint16(val)
+ }
+
+ feedback := []RTCPFeedback{}
+ for _, raw := range codec.RTCPFeedback {
+ split := strings.Split(raw, " ")
+ entry := RTCPFeedback{Type: split[0]}
+ if len(split) == 2 {
+ entry.Parameter = split[1]
+ }
+
+ feedback = append(feedback, entry)
+ }
+
+ out = append(out, RTPCodecParameters{
+ RTPCodecCapability: RTPCodecCapability{m.MediaName.Media + "/" + codec.Name, codec.ClockRate, channels, codec.Fmtp, feedback},
+ PayloadType: PayloadType(payloadType),
+ })
+ }
+
+ return out, nil
+}
+
+func rtpExtensionsFromMediaDescription(m *sdp.MediaDescription) (map[string]int, error) {
+ out := map[string]int{}
+
+ for _, a := range m.Attributes {
+ if a.Key == sdp.AttrKeyExtMap {
+ e := sdp.ExtMap{}
+ if err := e.Unmarshal(a.String()); err != nil {
+ return nil, err
+ }
+
+ out[e.URI.String()] = e.Value
+ }
+ }
+
+ return out, nil
+}
diff --git a/vendor/github.com/pion/webrtc/v3/sdpsemantics.go b/vendor/github.com/pion/webrtc/v3/sdpsemantics.go
new file mode 100644
index 0000000..b8d396c
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/sdpsemantics.go
@@ -0,0 +1,74 @@
+package webrtc
+
+import (
+ "encoding/json"
+)
+
+// SDPSemantics determines which style of SDP offers and answers
+// can be used
+type SDPSemantics int
+
+const (
+ // SDPSemanticsUnifiedPlan uses unified-plan offers and answers
+ // (the default in Chrome since M72)
+ // https://tools.ietf.org/html/draft-roach-mmusic-unified-plan-00
+ SDPSemanticsUnifiedPlan SDPSemantics = iota
+
+ // SDPSemanticsPlanB uses plan-b offers and answers
+ // NB: This format should be considered deprecated
+ // https://tools.ietf.org/html/draft-uberti-rtcweb-plan-00
+ SDPSemanticsPlanB
+
+ // SDPSemanticsUnifiedPlanWithFallback prefers unified-plan
+ // offers and answers, but will respond to a plan-b offer
+ // with a plan-b answer
+ SDPSemanticsUnifiedPlanWithFallback
+)
+
+const (
+ sdpSemanticsUnifiedPlanWithFallback = "unified-plan-with-fallback"
+ sdpSemanticsUnifiedPlan = "unified-plan"
+ sdpSemanticsPlanB = "plan-b"
+)
+
+func newSDPSemantics(raw string) SDPSemantics {
+ switch raw {
+ case sdpSemanticsUnifiedPlan:
+ return SDPSemanticsUnifiedPlan
+ case sdpSemanticsPlanB:
+ return SDPSemanticsPlanB
+ case sdpSemanticsUnifiedPlanWithFallback:
+ return SDPSemanticsUnifiedPlanWithFallback
+ default:
+ return SDPSemantics(Unknown)
+ }
+}
+
+func (s SDPSemantics) String() string {
+ switch s {
+ case SDPSemanticsUnifiedPlanWithFallback:
+ return sdpSemanticsUnifiedPlanWithFallback
+ case SDPSemanticsUnifiedPlan:
+ return sdpSemanticsUnifiedPlan
+ case SDPSemanticsPlanB:
+ return sdpSemanticsPlanB
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// UnmarshalJSON parses the JSON-encoded data and stores the result
+func (s *SDPSemantics) UnmarshalJSON(b []byte) error {
+ var val string
+ if err := json.Unmarshal(b, &val); err != nil {
+ return err
+ }
+
+ *s = newSDPSemantics(val)
+ return nil
+}
+
+// MarshalJSON returns the JSON encoding
+func (s SDPSemantics) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.String())
+}
diff --git a/vendor/github.com/pion/webrtc/v3/sdptype.go b/vendor/github.com/pion/webrtc/v3/sdptype.go
new file mode 100644
index 0000000..bd49b6d
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/sdptype.go
@@ -0,0 +1,100 @@
+package webrtc
+
+import (
+ "encoding/json"
+ "strings"
+)
+
+// SDPType describes the type of an SessionDescription.
+type SDPType int
+
+const (
+ // SDPTypeOffer indicates that a description MUST be treated as an SDP
+ // offer.
+ SDPTypeOffer SDPType = iota + 1
+
+ // SDPTypePranswer indicates that a description MUST be treated as an
+ // SDP answer, but not a final answer. A description used as an SDP
+ // pranswer may be applied as a response to an SDP offer, or an update to
+ // a previously sent SDP pranswer.
+ SDPTypePranswer
+
+ // SDPTypeAnswer indicates that a description MUST be treated as an SDP
+ // final answer, and the offer-answer exchange MUST be considered complete.
+ // A description used as an SDP answer may be applied as a response to an
+ // SDP offer or as an update to a previously sent SDP pranswer.
+ SDPTypeAnswer
+
+ // SDPTypeRollback indicates that a description MUST be treated as
+ // canceling the current SDP negotiation and moving the SDP offer and
+ // answer back to what it was in the previous stable state. Note the
+ // local or remote SDP descriptions in the previous stable state could be
+ // null if there has not yet been a successful offer-answer negotiation.
+ SDPTypeRollback
+)
+
+// This is done this way because of a linter.
+const (
+ sdpTypeOfferStr = "offer"
+ sdpTypePranswerStr = "pranswer"
+ sdpTypeAnswerStr = "answer"
+ sdpTypeRollbackStr = "rollback"
+)
+
+// NewSDPType creates an SDPType from a string
+func NewSDPType(raw string) SDPType {
+ switch raw {
+ case sdpTypeOfferStr:
+ return SDPTypeOffer
+ case sdpTypePranswerStr:
+ return SDPTypePranswer
+ case sdpTypeAnswerStr:
+ return SDPTypeAnswer
+ case sdpTypeRollbackStr:
+ return SDPTypeRollback
+ default:
+ return SDPType(Unknown)
+ }
+}
+
+func (t SDPType) String() string {
+ switch t {
+ case SDPTypeOffer:
+ return sdpTypeOfferStr
+ case SDPTypePranswer:
+ return sdpTypePranswerStr
+ case SDPTypeAnswer:
+ return sdpTypeAnswerStr
+ case SDPTypeRollback:
+ return sdpTypeRollbackStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// MarshalJSON enables JSON marshaling of a SDPType
+func (t SDPType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(t.String())
+}
+
+// UnmarshalJSON enables JSON unmarshaling of a SDPType
+func (t *SDPType) UnmarshalJSON(b []byte) error {
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ switch strings.ToLower(s) {
+ default:
+ return ErrUnknownType
+ case "offer":
+ *t = SDPTypeOffer
+ case "pranswer":
+ *t = SDPTypePranswer
+ case "answer":
+ *t = SDPTypeAnswer
+ case "rollback":
+ *t = SDPTypeRollback
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/pion/webrtc/v3/sessiondescription.go b/vendor/github.com/pion/webrtc/v3/sessiondescription.go
new file mode 100644
index 0000000..5b93391
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/sessiondescription.go
@@ -0,0 +1,21 @@
+package webrtc
+
+import (
+ "github.com/pion/sdp/v3"
+)
+
+// SessionDescription is used to expose local and remote session descriptions.
+type SessionDescription struct {
+ Type SDPType `json:"type"`
+ SDP string `json:"sdp"`
+
+ // This will never be initialized by callers, internal use only
+ parsed *sdp.SessionDescription
+}
+
+// Unmarshal is a helper to deserialize the sdp
+func (sd *SessionDescription) Unmarshal() (*sdp.SessionDescription, error) {
+ sd.parsed = &sdp.SessionDescription{}
+ err := sd.parsed.Unmarshal([]byte(sd.SDP))
+ return sd.parsed, err
+}
diff --git a/vendor/github.com/pion/webrtc/v3/settingengine.go b/vendor/github.com/pion/webrtc/v3/settingengine.go
new file mode 100644
index 0000000..8091e6a
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/settingengine.go
@@ -0,0 +1,265 @@
+// +build !js
+
+package webrtc
+
+import (
+ "io"
+ "time"
+
+ "github.com/pion/ice/v2"
+ "github.com/pion/logging"
+ "github.com/pion/transport/packetio"
+ "github.com/pion/transport/vnet"
+ "golang.org/x/net/proxy"
+)
+
+// SettingEngine allows influencing behavior in ways that are not
+// supported by the WebRTC API. This allows us to support additional
+// use-cases without deviating from the WebRTC API elsewhere.
+type SettingEngine struct {
+ ephemeralUDP struct {
+ PortMin uint16
+ PortMax uint16
+ }
+ detach struct {
+ DataChannels bool
+ }
+ timeout struct {
+ ICEDisconnectedTimeout *time.Duration
+ ICEFailedTimeout *time.Duration
+ ICEKeepaliveInterval *time.Duration
+ ICEHostAcceptanceMinWait *time.Duration
+ ICESrflxAcceptanceMinWait *time.Duration
+ ICEPrflxAcceptanceMinWait *time.Duration
+ ICERelayAcceptanceMinWait *time.Duration
+ }
+ candidates struct {
+ ICELite bool
+ ICENetworkTypes []NetworkType
+ InterfaceFilter func(string) bool
+ NAT1To1IPs []string
+ NAT1To1IPCandidateType ICECandidateType
+ MulticastDNSMode ice.MulticastDNSMode
+ MulticastDNSHostName string
+ UsernameFragment string
+ Password string
+ }
+ replayProtection struct {
+ DTLS *uint
+ SRTP *uint
+ SRTCP *uint
+ }
+ sdpMediaLevelFingerprints bool
+ answeringDTLSRole DTLSRole
+ disableCertificateFingerprintVerification bool
+ disableSRTPReplayProtection bool
+ disableSRTCPReplayProtection bool
+ vnet *vnet.Net
+ BufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser
+ LoggerFactory logging.LoggerFactory
+ iceTCPMux ice.TCPMux
+ iceProxyDialer proxy.Dialer
+ disableMediaEngineCopy bool
+}
+
+// DetachDataChannels enables detaching data channels. When enabled
+// data channels have to be detached in the OnOpen callback using the
+// DataChannel.Detach method.
+func (e *SettingEngine) DetachDataChannels() {
+ e.detach.DataChannels = true
+}
+
+// SetICETimeouts sets the behavior around ICE Timeouts
+// * disconnectedTimeout is the duration without network activity before a Agent is considered disconnected. Default is 5 Seconds
+// * failedTimeout is the duration without network activity before a Agent is considered failed after disconnected. Default is 25 Seconds
+// * keepAliveInterval is how often the ICE Agent sends extra traffic if there is no activity, if media is flowing no traffic will be sent. Default is 2 seconds
+func (e *SettingEngine) SetICETimeouts(disconnectedTimeout, failedTimeout, keepAliveInterval time.Duration) {
+ e.timeout.ICEDisconnectedTimeout = &disconnectedTimeout
+ e.timeout.ICEFailedTimeout = &failedTimeout
+ e.timeout.ICEKeepaliveInterval = &keepAliveInterval
+}
+
+// SetHostAcceptanceMinWait sets the ICEHostAcceptanceMinWait
+func (e *SettingEngine) SetHostAcceptanceMinWait(t time.Duration) {
+ e.timeout.ICEHostAcceptanceMinWait = &t
+}
+
+// SetSrflxAcceptanceMinWait sets the ICESrflxAcceptanceMinWait
+func (e *SettingEngine) SetSrflxAcceptanceMinWait(t time.Duration) {
+ e.timeout.ICESrflxAcceptanceMinWait = &t
+}
+
+// SetPrflxAcceptanceMinWait sets the ICEPrflxAcceptanceMinWait
+func (e *SettingEngine) SetPrflxAcceptanceMinWait(t time.Duration) {
+ e.timeout.ICEPrflxAcceptanceMinWait = &t
+}
+
+// SetRelayAcceptanceMinWait sets the ICERelayAcceptanceMinWait
+func (e *SettingEngine) SetRelayAcceptanceMinWait(t time.Duration) {
+ e.timeout.ICERelayAcceptanceMinWait = &t
+}
+
+// SetEphemeralUDPPortRange limits the pool of ephemeral ports that
+// ICE UDP connections can allocate from. This affects both host candidates,
+// and the local address of server reflexive candidates.
+func (e *SettingEngine) SetEphemeralUDPPortRange(portMin, portMax uint16) error {
+ if portMax < portMin {
+ return ice.ErrPort
+ }
+
+ e.ephemeralUDP.PortMin = portMin
+ e.ephemeralUDP.PortMax = portMax
+ return nil
+}
+
+// SetLite configures whether or not the ice agent should be a lite agent
+func (e *SettingEngine) SetLite(lite bool) {
+ e.candidates.ICELite = lite
+}
+
+// SetNetworkTypes configures what types of candidate networks are supported
+// during local and server reflexive gathering.
+func (e *SettingEngine) SetNetworkTypes(candidateTypes []NetworkType) {
+ e.candidates.ICENetworkTypes = candidateTypes
+}
+
+// SetInterfaceFilter sets the filtering functions when gathering ICE candidates
+// This can be used to exclude certain network interfaces from ICE. Which may be
+// useful if you know a certain interface will never succeed, or if you wish to reduce
+// the amount of information you wish to expose to the remote peer
+func (e *SettingEngine) SetInterfaceFilter(filter func(string) bool) {
+ e.candidates.InterfaceFilter = filter
+}
+
+// SetNAT1To1IPs sets a list of external IP addresses of 1:1 (D)NAT
+// and a candidate type for which the external IP address is used.
+// This is useful when you are host a server using Pion on an AWS EC2 instance
+// which has a private address, behind a 1:1 DNAT with a public IP (e.g.
+// Elastic IP). In this case, you can give the public IP address so that
+// Pion will use the public IP address in its candidate instead of the private
+// IP address. The second argument, candidateType, is used to tell Pion which
+// type of candidate should use the given public IP address.
+// Two types of candidates are supported:
+//
+// ICECandidateTypeHost:
+// The public IP address will be used for the host candidate in the SDP.
+// ICECandidateTypeSrflx:
+// A server reflexive candidate with the given public IP address will be added
+// to the SDP.
+//
+// Please note that if you choose ICECandidateTypeHost, then the private IP address
+// won't be advertised with the peer. Also, this option cannot be used along with mDNS.
+//
+// If you choose ICECandidateTypeSrflx, it simply adds a server reflexive candidate
+// with the public IP. The host candidate is still available along with mDNS
+// capabilities unaffected. Also, you cannot give STUN server URL at the same time.
+// It will result in an error otherwise.
+func (e *SettingEngine) SetNAT1To1IPs(ips []string, candidateType ICECandidateType) {
+ e.candidates.NAT1To1IPs = ips
+ e.candidates.NAT1To1IPCandidateType = candidateType
+}
+
+// SetAnsweringDTLSRole sets the DTLS role that is selected when offering
+// The DTLS role controls if the WebRTC Client as a client or server. This
+// may be useful when interacting with non-compliant clients or debugging issues.
+//
+// DTLSRoleActive:
+// Act as DTLS Client, send the ClientHello and starts the handshake
+// DTLSRolePassive:
+// Act as DTLS Server, wait for ClientHello
+func (e *SettingEngine) SetAnsweringDTLSRole(role DTLSRole) error {
+ if role != DTLSRoleClient && role != DTLSRoleServer {
+ return errSettingEngineSetAnsweringDTLSRole
+ }
+
+ e.answeringDTLSRole = role
+ return nil
+}
+
+// SetVNet sets the VNet instance that is passed to pion/ice
+//
+// VNet is a virtual network layer for Pion, allowing users to simulate
+// different topologies, latency, loss and jitter. This can be useful for
+// learning WebRTC concepts or testing your application in a lab environment
+func (e *SettingEngine) SetVNet(vnet *vnet.Net) {
+ e.vnet = vnet
+}
+
+// SetICEMulticastDNSMode controls if pion/ice queries and generates mDNS ICE Candidates
+func (e *SettingEngine) SetICEMulticastDNSMode(multicastDNSMode ice.MulticastDNSMode) {
+ e.candidates.MulticastDNSMode = multicastDNSMode
+}
+
+// SetMulticastDNSHostName sets a static HostName to be used by pion/ice instead of generating one on startup
+//
+// This should only be used for a single PeerConnection. Having multiple PeerConnections with the same HostName will cause
+// undefined behavior
+func (e *SettingEngine) SetMulticastDNSHostName(hostName string) {
+ e.candidates.MulticastDNSHostName = hostName
+}
+
+// SetICECredentials sets a staic uFrag/uPwd to be used by pion/ice
+//
+// This is useful if you want to do signalless WebRTC session, or having a reproducible environment with static credentials
+func (e *SettingEngine) SetICECredentials(usernameFragment, password string) {
+ e.candidates.UsernameFragment = usernameFragment
+ e.candidates.Password = password
+}
+
+// DisableCertificateFingerprintVerification disables fingerprint verification after DTLS Handshake has finished
+func (e *SettingEngine) DisableCertificateFingerprintVerification(isDisabled bool) {
+ e.disableCertificateFingerprintVerification = isDisabled
+}
+
+// SetDTLSReplayProtectionWindow sets a replay attack protection window size of DTLS connection.
+func (e *SettingEngine) SetDTLSReplayProtectionWindow(n uint) {
+ e.replayProtection.DTLS = &n
+}
+
+// SetSRTPReplayProtectionWindow sets a replay attack protection window size of SRTP session.
+func (e *SettingEngine) SetSRTPReplayProtectionWindow(n uint) {
+ e.disableSRTPReplayProtection = false
+ e.replayProtection.SRTP = &n
+}
+
+// SetSRTCPReplayProtectionWindow sets a replay attack protection window size of SRTCP session.
+func (e *SettingEngine) SetSRTCPReplayProtectionWindow(n uint) {
+ e.disableSRTCPReplayProtection = false
+ e.replayProtection.SRTCP = &n
+}
+
+// DisableSRTPReplayProtection disables SRTP replay protection.
+func (e *SettingEngine) DisableSRTPReplayProtection(isDisabled bool) {
+ e.disableSRTPReplayProtection = isDisabled
+}
+
+// DisableSRTCPReplayProtection disables SRTCP replay protection.
+func (e *SettingEngine) DisableSRTCPReplayProtection(isDisabled bool) {
+ e.disableSRTCPReplayProtection = isDisabled
+}
+
+// SetSDPMediaLevelFingerprints configures the logic for DTLS Fingerprint insertion
+// If true, fingerprints will be inserted in the sdp at the fingerprint
+// level, instead of the session level. This helps with compatibility with
+// some webrtc implementations.
+func (e *SettingEngine) SetSDPMediaLevelFingerprints(sdpMediaLevelFingerprints bool) {
+ e.sdpMediaLevelFingerprints = sdpMediaLevelFingerprints
+}
+
+// SetICETCPMux enables ICE-TCP when set to a non-nil value. Make sure that
+// NetworkTypeTCP4 or NetworkTypeTCP6 is enabled as well.
+func (e *SettingEngine) SetICETCPMux(tcpMux ice.TCPMux) {
+ e.iceTCPMux = tcpMux
+}
+
+// SetICEProxyDialer sets the proxy dialer interface based on golang.org/x/net/proxy.
+func (e *SettingEngine) SetICEProxyDialer(d proxy.Dialer) {
+ e.iceProxyDialer = d
+}
+
+// DisableMediaEngineCopy stops the MediaEngine from being copied. This allows a user to modify
+// the MediaEngine after the PeerConnection has been constructed. This is useful if you wish to
+// modify codecs after signaling. Make sure not to share MediaEngines between PeerConnections.
+func (e *SettingEngine) DisableMediaEngineCopy(isDisabled bool) {
+ e.disableMediaEngineCopy = isDisabled
+}
diff --git a/vendor/github.com/pion/webrtc/v3/settingengine_js.go b/vendor/github.com/pion/webrtc/v3/settingengine_js.go
new file mode 100644
index 0000000..5b77d66
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/settingengine_js.go
@@ -0,0 +1,19 @@
+// +build js,wasm
+
+package webrtc
+
+// SettingEngine allows influencing behavior in ways that are not
+// supported by the WebRTC API. This allows us to support additional
+// use-cases without deviating from the WebRTC API elsewhere.
+type SettingEngine struct {
+ detach struct {
+ DataChannels bool
+ }
+}
+
+// DetachDataChannels enables detaching data channels. When enabled
+// data channels have to be detached in the OnOpen callback using the
+// DataChannel.Detach method.
+func (e *SettingEngine) DetachDataChannels() {
+ e.detach.DataChannels = true
+}
diff --git a/vendor/github.com/pion/webrtc/v3/signalingstate.go b/vendor/github.com/pion/webrtc/v3/signalingstate.go
new file mode 100644
index 0000000..b64dffc
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/signalingstate.go
@@ -0,0 +1,188 @@
+package webrtc
+
+import (
+ "fmt"
+ "sync/atomic"
+
+ "github.com/pion/webrtc/v3/pkg/rtcerr"
+)
+
+type stateChangeOp int
+
+const (
+ stateChangeOpSetLocal stateChangeOp = iota + 1
+ stateChangeOpSetRemote
+)
+
+func (op stateChangeOp) String() string {
+ switch op {
+ case stateChangeOpSetLocal:
+ return "SetLocal"
+ case stateChangeOpSetRemote:
+ return "SetRemote"
+ default:
+ return "Unknown State Change Operation"
+ }
+}
+
+// SignalingState indicates the signaling state of the offer/answer process.
+type SignalingState int32
+
+const (
+ // SignalingStateStable indicates there is no offer/answer exchange in
+ // progress. This is also the initial state, in which case the local and
+ // remote descriptions are nil.
+ SignalingStateStable SignalingState = iota + 1
+
+ // SignalingStateHaveLocalOffer indicates that a local description, of
+ // type "offer", has been successfully applied.
+ SignalingStateHaveLocalOffer
+
+ // SignalingStateHaveRemoteOffer indicates that a remote description, of
+ // type "offer", has been successfully applied.
+ SignalingStateHaveRemoteOffer
+
+ // SignalingStateHaveLocalPranswer indicates that a remote description
+ // of type "offer" has been successfully applied and a local description
+ // of type "pranswer" has been successfully applied.
+ SignalingStateHaveLocalPranswer
+
+ // SignalingStateHaveRemotePranswer indicates that a local description
+ // of type "offer" has been successfully applied and a remote description
+ // of type "pranswer" has been successfully applied.
+ SignalingStateHaveRemotePranswer
+
+ // SignalingStateClosed indicates The PeerConnection has been closed.
+ SignalingStateClosed
+)
+
+// This is done this way because of a linter.
+const (
+ signalingStateStableStr = "stable"
+ signalingStateHaveLocalOfferStr = "have-local-offer"
+ signalingStateHaveRemoteOfferStr = "have-remote-offer"
+ signalingStateHaveLocalPranswerStr = "have-local-pranswer"
+ signalingStateHaveRemotePranswerStr = "have-remote-pranswer"
+ signalingStateClosedStr = "closed"
+)
+
+func newSignalingState(raw string) SignalingState {
+ switch raw {
+ case signalingStateStableStr:
+ return SignalingStateStable
+ case signalingStateHaveLocalOfferStr:
+ return SignalingStateHaveLocalOffer
+ case signalingStateHaveRemoteOfferStr:
+ return SignalingStateHaveRemoteOffer
+ case signalingStateHaveLocalPranswerStr:
+ return SignalingStateHaveLocalPranswer
+ case signalingStateHaveRemotePranswerStr:
+ return SignalingStateHaveRemotePranswer
+ case signalingStateClosedStr:
+ return SignalingStateClosed
+ default:
+ return SignalingState(Unknown)
+ }
+}
+
+func (t SignalingState) String() string {
+ switch t {
+ case SignalingStateStable:
+ return signalingStateStableStr
+ case SignalingStateHaveLocalOffer:
+ return signalingStateHaveLocalOfferStr
+ case SignalingStateHaveRemoteOffer:
+ return signalingStateHaveRemoteOfferStr
+ case SignalingStateHaveLocalPranswer:
+ return signalingStateHaveLocalPranswerStr
+ case SignalingStateHaveRemotePranswer:
+ return signalingStateHaveRemotePranswerStr
+ case SignalingStateClosed:
+ return signalingStateClosedStr
+ default:
+ return ErrUnknownType.Error()
+ }
+}
+
+// Get thread safe read value
+func (t *SignalingState) Get() SignalingState {
+ return SignalingState(atomic.LoadInt32((*int32)(t)))
+}
+
+// Set thread safe write value
+func (t *SignalingState) Set(state SignalingState) {
+ atomic.StoreInt32((*int32)(t), int32(state))
+}
+
+func checkNextSignalingState(cur, next SignalingState, op stateChangeOp, sdpType SDPType) (SignalingState, error) { // nolint:gocognit
+ // Special case for rollbacks
+ if sdpType == SDPTypeRollback && cur == SignalingStateStable {
+ return cur, &rtcerr.InvalidModificationError{
+ Err: errSignalingStateCannotRollback,
+ }
+ }
+
+ // 4.3.1 valid state transitions
+ switch cur { // nolint:exhaustive
+ case SignalingStateStable:
+ switch op {
+ case stateChangeOpSetLocal:
+ // stable->SetLocal(offer)->have-local-offer
+ if sdpType == SDPTypeOffer && next == SignalingStateHaveLocalOffer {
+ return next, nil
+ }
+ case stateChangeOpSetRemote:
+ // stable->SetRemote(offer)->have-remote-offer
+ if sdpType == SDPTypeOffer && next == SignalingStateHaveRemoteOffer {
+ return next, nil
+ }
+ }
+ case SignalingStateHaveLocalOffer:
+ if op == stateChangeOpSetRemote {
+ switch sdpType { // nolint:exhaustive
+ // have-local-offer->SetRemote(answer)->stable
+ case SDPTypeAnswer:
+ if next == SignalingStateStable {
+ return next, nil
+ }
+ // have-local-offer->SetRemote(pranswer)->have-remote-pranswer
+ case SDPTypePranswer:
+ if next == SignalingStateHaveRemotePranswer {
+ return next, nil
+ }
+ }
+ }
+ case SignalingStateHaveRemotePranswer:
+ if op == stateChangeOpSetRemote && sdpType == SDPTypeAnswer {
+ // have-remote-pranswer->SetRemote(answer)->stable
+ if next == SignalingStateStable {
+ return next, nil
+ }
+ }
+ case SignalingStateHaveRemoteOffer:
+ if op == stateChangeOpSetLocal {
+ switch sdpType { // nolint:exhaustive
+ // have-remote-offer->SetLocal(answer)->stable
+ case SDPTypeAnswer:
+ if next == SignalingStateStable {
+ return next, nil
+ }
+ // have-remote-offer->SetLocal(pranswer)->have-local-pranswer
+ case SDPTypePranswer:
+ if next == SignalingStateHaveLocalPranswer {
+ return next, nil
+ }
+ }
+ }
+ case SignalingStateHaveLocalPranswer:
+ if op == stateChangeOpSetLocal && sdpType == SDPTypeAnswer {
+ // have-local-pranswer->SetLocal(answer)->stable
+ if next == SignalingStateStable {
+ return next, nil
+ }
+ }
+ }
+ return cur, &rtcerr.InvalidModificationError{
+ Err: fmt.Errorf("%w: %s->%s(%s)->%s", errSignalingStateProposedTransitionInvalid, cur, op, sdpType, next),
+ }
+}
diff --git a/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go b/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go
new file mode 100644
index 0000000..4d8bafe
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go
@@ -0,0 +1,118 @@
+// +build !js
+
+package webrtc
+
+import (
+ "io"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/rtp"
+ "github.com/pion/srtp/v2"
+)
+
+// srtpWriterFuture blocks Read/Write calls until
+// the SRTP Session is available
+type srtpWriterFuture struct {
+ rtpSender *RTPSender
+ rtcpReadStream atomic.Value // *srtp.ReadStreamSRTCP
+ rtpWriteStream atomic.Value // *srtp.WriteStreamSRTP
+}
+
+func (s *srtpWriterFuture) init(returnWhenNoSRTP bool) error {
+ if returnWhenNoSRTP {
+ select {
+ case <-s.rtpSender.stopCalled:
+ return io.ErrClosedPipe
+ case <-s.rtpSender.transport.srtpReady:
+ default:
+ return nil
+ }
+ } else {
+ select {
+ case <-s.rtpSender.stopCalled:
+ return io.ErrClosedPipe
+ case <-s.rtpSender.transport.srtpReady:
+ }
+ }
+
+ srtcpSession, err := s.rtpSender.transport.getSRTCPSession()
+ if err != nil {
+ return err
+ }
+
+ rtcpReadStream, err := srtcpSession.OpenReadStream(uint32(s.rtpSender.ssrc))
+ if err != nil {
+ return err
+ }
+
+ srtpSession, err := s.rtpSender.transport.getSRTPSession()
+ if err != nil {
+ return err
+ }
+
+ rtpWriteStream, err := srtpSession.OpenWriteStream()
+ if err != nil {
+ return err
+ }
+
+ s.rtcpReadStream.Store(rtcpReadStream)
+ s.rtpWriteStream.Store(rtpWriteStream)
+ return nil
+}
+
+func (s *srtpWriterFuture) Close() error {
+ if value := s.rtcpReadStream.Load(); value != nil {
+ return value.(*srtp.ReadStreamSRTCP).Close()
+ }
+
+ return nil
+}
+
+func (s *srtpWriterFuture) Read(b []byte) (n int, err error) {
+ if value := s.rtcpReadStream.Load(); value != nil {
+ return value.(*srtp.ReadStreamSRTCP).Read(b)
+ }
+
+ if err := s.init(false); err != nil || s.rtcpReadStream.Load() == nil {
+ return 0, err
+ }
+
+ return s.Read(b)
+}
+
+func (s *srtpWriterFuture) SetReadDeadline(t time.Time) error {
+ if value := s.rtcpReadStream.Load(); value != nil {
+ return value.(*srtp.ReadStreamSRTCP).SetReadDeadline(t)
+ }
+
+ if err := s.init(false); err != nil || s.rtcpReadStream.Load() == nil {
+ return err
+ }
+
+ return s.SetReadDeadline(t)
+}
+
+func (s *srtpWriterFuture) WriteRTP(header *rtp.Header, payload []byte) (int, error) {
+ if value := s.rtpWriteStream.Load(); value != nil {
+ return value.(*srtp.WriteStreamSRTP).WriteRTP(header, payload)
+ }
+
+ if err := s.init(true); err != nil || s.rtpWriteStream.Load() == nil {
+ return 0, err
+ }
+
+ return s.WriteRTP(header, payload)
+}
+
+func (s *srtpWriterFuture) Write(b []byte) (int, error) {
+ if value := s.rtpWriteStream.Load(); value != nil {
+ return value.(*srtp.WriteStreamSRTP).Write(b)
+ }
+
+ if err := s.init(true); err != nil || s.rtpWriteStream.Load() == nil {
+ return 0, err
+ }
+
+ return s.Write(b)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/stats.go b/vendor/github.com/pion/webrtc/v3/stats.go
new file mode 100644
index 0000000..a218d9e
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/stats.go
@@ -0,0 +1,1452 @@
+package webrtc
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/pion/ice/v2"
+)
+
+// A Stats object contains a set of statistics copies out of a monitored component
+// of the WebRTC stack at a specific time.
+type Stats interface{}
+
+// StatsType indicates the type of the object that a Stats object represents.
+type StatsType string
+
+const (
+ // StatsTypeCodec is used by CodecStats.
+ StatsTypeCodec StatsType = "codec"
+
+ // StatsTypeInboundRTP is used by InboundRTPStreamStats.
+ StatsTypeInboundRTP StatsType = "inbound-rtp"
+
+ // StatsTypeOutboundRTP is used by OutboundRTPStreamStats.
+ StatsTypeOutboundRTP StatsType = "outbound-rtp"
+
+ // StatsTypeRemoteInboundRTP is used by RemoteInboundRTPStreamStats.
+ StatsTypeRemoteInboundRTP StatsType = "remote-inbound-rtp"
+
+ // StatsTypeRemoteOutboundRTP is used by RemoteOutboundRTPStreamStats.
+ StatsTypeRemoteOutboundRTP StatsType = "remote-outbound-rtp"
+
+ // StatsTypeCSRC is used by RTPContributingSourceStats.
+ StatsTypeCSRC StatsType = "csrc"
+
+ // StatsTypePeerConnection used by PeerConnectionStats.
+ StatsTypePeerConnection StatsType = "peer-connection"
+
+ // StatsTypeDataChannel is used by DataChannelStats.
+ StatsTypeDataChannel StatsType = "data-channel"
+
+ // StatsTypeStream is used by MediaStreamStats.
+ StatsTypeStream StatsType = "stream"
+
+ // StatsTypeTrack is used by SenderVideoTrackAttachmentStats and SenderAudioTrackAttachmentStats.
+ StatsTypeTrack StatsType = "track"
+
+ // StatsTypeSender is used by by the AudioSenderStats or VideoSenderStats depending on kind.
+ StatsTypeSender StatsType = "sender"
+
+ // StatsTypeReceiver is used by the AudioReceiverStats or VideoReceiverStats depending on kind.
+ StatsTypeReceiver StatsType = "receiver"
+
+ // StatsTypeTransport is used by TransportStats.
+ StatsTypeTransport StatsType = "transport"
+
+ // StatsTypeCandidatePair is used by ICECandidatePairStats.
+ StatsTypeCandidatePair StatsType = "candidate-pair"
+
+ // StatsTypeLocalCandidate is used by ICECandidateStats for the local candidate.
+ StatsTypeLocalCandidate StatsType = "local-candidate"
+
+ // StatsTypeRemoteCandidate is used by ICECandidateStats for the remote candidate.
+ StatsTypeRemoteCandidate StatsType = "remote-candidate"
+
+ // StatsTypeCertificate is used by CertificateStats.
+ StatsTypeCertificate StatsType = "certificate"
+)
+
+// StatsTimestamp is a timestamp represented by the floating point number of
+// milliseconds since the epoch.
+type StatsTimestamp float64
+
+// Time returns the time.Time represented by this timestamp.
+func (s StatsTimestamp) Time() time.Time {
+ millis := float64(s)
+ nanos := int64(millis * float64(time.Millisecond))
+
+ return time.Unix(0, nanos).UTC()
+}
+
+func statsTimestampFrom(t time.Time) StatsTimestamp {
+ return StatsTimestamp(t.UnixNano() / int64(time.Millisecond))
+}
+
+func statsTimestampNow() StatsTimestamp {
+ return statsTimestampFrom(time.Now())
+}
+
+// StatsReport collects Stats objects indexed by their ID.
+type StatsReport map[string]Stats
+
+type statsReportCollector struct {
+ collectingGroup sync.WaitGroup
+ report StatsReport
+ mux sync.Mutex
+}
+
+func newStatsReportCollector() *statsReportCollector {
+ return &statsReportCollector{report: make(StatsReport)}
+}
+
+func (src *statsReportCollector) Collecting() {
+ src.collectingGroup.Add(1)
+}
+
+func (src *statsReportCollector) Collect(id string, stats Stats) {
+ src.mux.Lock()
+ defer src.mux.Unlock()
+
+ src.report[id] = stats
+ src.collectingGroup.Done()
+}
+
+func (src *statsReportCollector) Done() {
+ src.collectingGroup.Done()
+}
+
+func (src *statsReportCollector) Ready() StatsReport {
+ src.collectingGroup.Wait()
+ src.mux.Lock()
+ defer src.mux.Unlock()
+ return src.report
+}
+
+// CodecType specifies whether a CodecStats objects represents a media format
+// that is being encoded or decoded
+type CodecType string
+
+const (
+ // CodecTypeEncode means the attached CodecStats represents a media format that
+ // is being encoded, or that the implementation is prepared to encode.
+ CodecTypeEncode CodecType = "encode"
+
+ // CodecTypeDecode means the attached CodecStats represents a media format
+ // that the implementation is prepared to decode.
+ CodecTypeDecode CodecType = "decode"
+)
+
+// CodecStats contains statistics for a codec that is currently being used by RTP streams
+// being sent or received by this PeerConnection object.
+type CodecStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // PayloadType as used in RTP encoding or decoding
+ PayloadType PayloadType `json:"payloadType"`
+
+ // CodecType of this CodecStats
+ CodecType CodecType `json:"codecType"`
+
+ // TransportID is the unique identifier of the transport on which this codec is
+ // being used, which can be used to look up the corresponding TransportStats object.
+ TransportID string `json:"transportId"`
+
+ // MimeType is the codec MIME media type/subtype. e.g., video/vp8 or equivalent.
+ MimeType string `json:"mimeType"`
+
+ // ClockRate represents the media sampling rate.
+ ClockRate uint32 `json:"clockRate"`
+
+ // Channels is 2 for stereo, missing for most other cases.
+ Channels uint8 `json:"channels"`
+
+ // SDPFmtpLine is the a=fmtp line in the SDP corresponding to the codec,
+ // i.e., after the colon following the PT.
+ SDPFmtpLine string `json:"sdpFmtpLine"`
+
+ // Implementation identifies the implementation used. This is useful for diagnosing
+ // interoperability issues.
+ Implementation string `json:"implementation"`
+}
+
+// InboundRTPStreamStats contains statistics for an inbound RTP stream that is
+// currently received with this PeerConnection object.
+type InboundRTPStreamStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // SSRC is the 32-bit unsigned integer value used to identify the source of the
+ // stream of RTP packets that this stats object concerns.
+ SSRC SSRC `json:"ssrc"`
+
+ // Kind is either "audio" or "video"
+ Kind string `json:"kind"`
+
+ // It is a unique identifier that is associated to the object that was inspected
+ // to produce the TransportStats associated with this RTP stream.
+ TransportID string `json:"transportId"`
+
+ // CodecID is a unique identifier that is associated to the object that was inspected
+ // to produce the CodecStats associated with this RTP stream.
+ CodecID string `json:"codecId"`
+
+ // FIRCount counts the total number of Full Intra Request (FIR) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ FIRCount uint32 `json:"firCount"`
+
+ // PLICount counts the total number of Picture Loss Indication (PLI) packets
+ // received by the sender. This metric is only valid for video and is sent by receiver.
+ PLICount uint32 `json:"pliCount"`
+
+ // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets
+ // received by the sender and is sent by receiver.
+ NACKCount uint32 `json:"nackCount"`
+
+ // SLICount counts the total number of Slice Loss Indication (SLI) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ SLICount uint32 `json:"sliCount"`
+
+ // QPSum is the sum of the QP values of frames passed. The count of frames is
+ // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats.
+ QPSum uint64 `json:"qpSum"`
+
+ // PacketsReceived is the total number of RTP packets received for this SSRC.
+ PacketsReceived uint32 `json:"packetsReceived"`
+
+ // PacketsLost is the total number of RTP packets lost for this SSRC. Note that
+ // because of how this is estimated, it can be negative if more packets are received than sent.
+ PacketsLost int32 `json:"packetsLost"`
+
+ // Jitter is the packet jitter measured in seconds for this SSRC
+ Jitter float64 `json:"jitter"`
+
+ // PacketsDiscarded is the cumulative number of RTP packets discarded by the jitter
+ // buffer due to late or early-arrival, i.e., these packets are not played out.
+ // RTP packets discarded due to packet duplication are not reported in this metric.
+ PacketsDiscarded uint32 `json:"packetsDiscarded"`
+
+ // PacketsRepaired is the cumulative number of lost RTP packets repaired after applying
+ // an error-resilience mechanism. It is measured for the primary source RTP packets
+ // and only counted for RTP packets that have no further chance of repair.
+ PacketsRepaired uint32 `json:"packetsRepaired"`
+
+ // BurstPacketsLost is the cumulative number of RTP packets lost during loss bursts.
+ BurstPacketsLost uint32 `json:"burstPacketsLost"`
+
+ // BurstPacketsDiscarded is the cumulative number of RTP packets discarded during discard bursts.
+ BurstPacketsDiscarded uint32 `json:"burstPacketsDiscarded"`
+
+ // BurstLossCount is the cumulative number of bursts of lost RTP packets.
+ BurstLossCount uint32 `json:"burstLossCount"`
+
+ // BurstDiscardCount is the cumulative number of bursts of discarded RTP packets.
+ BurstDiscardCount uint32 `json:"burstDiscardCount"`
+
+ // BurstLossRate is the fraction of RTP packets lost during bursts to the
+ // total number of RTP packets expected in the bursts.
+ BurstLossRate float64 `json:"burstLossRate"`
+
+ // BurstDiscardRate is the fraction of RTP packets discarded during bursts to
+ // the total number of RTP packets expected in bursts.
+ BurstDiscardRate float64 `json:"burstDiscardRate"`
+
+ // GapLossRate is the fraction of RTP packets lost during the gap periods.
+ GapLossRate float64 `json:"gapLossRate"`
+
+ // GapDiscardRate is the fraction of RTP packets discarded during the gap periods.
+ GapDiscardRate float64 `json:"gapDiscardRate"`
+
+ // TrackID is the identifier of the stats object representing the receiving track,
+ // a ReceiverAudioTrackAttachmentStats or ReceiverVideoTrackAttachmentStats.
+ TrackID string `json:"trackId"`
+
+ // ReceiverID is the stats ID used to look up the AudioReceiverStats or VideoReceiverStats
+ // object receiving this stream.
+ ReceiverID string `json:"receiverId"`
+
+ // RemoteID is used for looking up the remote RemoteOutboundRTPStreamStats object
+ // for the same SSRC.
+ RemoteID string `json:"remoteId"`
+
+ // FramesDecoded represents the total number of frames correctly decoded for this SSRC,
+ // i.e., frames that would be displayed if no frames are dropped. Only valid for video.
+ FramesDecoded uint32 `json:"framesDecoded"`
+
+ // LastPacketReceivedTimestamp represents the timestamp at which the last packet was
+ // received for this SSRC. This differs from Timestamp, which represents the time
+ // at which the statistics were generated by the local endpoint.
+ LastPacketReceivedTimestamp StatsTimestamp `json:"lastPacketReceivedTimestamp"`
+
+ // AverageRTCPInterval is the average RTCP interval between two consecutive compound RTCP packets.
+ // This is calculated by the sending endpoint when sending compound RTCP reports.
+ // Compound packets must contain at least a RTCP RR or SR packet and an SDES packet
+ // with the CNAME item.
+ AverageRTCPInterval float64 `json:"averageRtcpInterval"`
+
+ // FECPacketsReceived is the total number of RTP FEC packets received for this SSRC.
+ // This counter can also be incremented when receiving FEC packets in-band with media packets (e.g., with Opus).
+ FECPacketsReceived uint32 `json:"fecPacketsReceived"`
+
+ // BytesReceived is the total number of bytes received for this SSRC.
+ BytesReceived uint64 `json:"bytesReceived"`
+
+ // PacketsFailedDecryption is the cumulative number of RTP packets that failed
+ // to be decrypted. These packets are not counted by PacketsDiscarded.
+ PacketsFailedDecryption uint32 `json:"packetsFailedDecryption"`
+
+ // PacketsDuplicated is the cumulative number of packets discarded because they
+ // are duplicated. Duplicate packets are not counted in PacketsDiscarded.
+ //
+ // Duplicated packets have the same RTP sequence number and content as a previously
+ // received packet. If multiple duplicates of a packet are received, all of them are counted.
+ // An improved estimate of lost packets can be calculated by adding PacketsDuplicated to PacketsLost.
+ PacketsDuplicated uint32 `json:"packetsDuplicated"`
+
+ // PerDSCPPacketsReceived is the total number of packets received for this SSRC,
+ // per Differentiated Services code point (DSCP) [RFC2474]. DSCPs are identified
+ // as decimal integers in string form. Note that due to network remapping and bleaching,
+ // these numbers are not expected to match the numbers seen on sending. Not all
+ // OSes make this information available.
+ PerDSCPPacketsReceived map[string]uint32 `json:"perDscpPacketsReceived"`
+}
+
+// QualityLimitationReason lists the reason for limiting the resolution and/or framerate.
+// Only valid for video.
+type QualityLimitationReason string
+
+const (
+ // QualityLimitationReasonNone means the resolution and/or framerate is not limited.
+ QualityLimitationReasonNone QualityLimitationReason = "none"
+
+ // QualityLimitationReasonCPU means the resolution and/or framerate is primarily limited due to CPU load.
+ QualityLimitationReasonCPU QualityLimitationReason = "cpu"
+
+ // QualityLimitationReasonBandwidth means the resolution and/or framerate is primarily limited due to congestion cues during bandwidth estimation. Typical, congestion control algorithms use inter-arrival time, round-trip time, packet or other congestion cues to perform bandwidth estimation.
+ QualityLimitationReasonBandwidth QualityLimitationReason = "bandwidth"
+
+ // QualityLimitationReasonOther means the resolution and/or framerate is primarily limited for a reason other than the above.
+ QualityLimitationReasonOther QualityLimitationReason = "other"
+)
+
+// OutboundRTPStreamStats contains statistics for an outbound RTP stream that is
+// currently sent with this PeerConnection object.
+type OutboundRTPStreamStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // SSRC is the 32-bit unsigned integer value used to identify the source of the
+ // stream of RTP packets that this stats object concerns.
+ SSRC SSRC `json:"ssrc"`
+
+ // Kind is either "audio" or "video"
+ Kind string `json:"kind"`
+
+ // It is a unique identifier that is associated to the object that was inspected
+ // to produce the TransportStats associated with this RTP stream.
+ TransportID string `json:"transportId"`
+
+ // CodecID is a unique identifier that is associated to the object that was inspected
+ // to produce the CodecStats associated with this RTP stream.
+ CodecID string `json:"codecId"`
+
+ // FIRCount counts the total number of Full Intra Request (FIR) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ FIRCount uint32 `json:"firCount"`
+
+ // PLICount counts the total number of Picture Loss Indication (PLI) packets
+ // received by the sender. This metric is only valid for video and is sent by receiver.
+ PLICount uint32 `json:"pliCount"`
+
+ // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets
+ // received by the sender and is sent by receiver.
+ NACKCount uint32 `json:"nackCount"`
+
+ // SLICount counts the total number of Slice Loss Indication (SLI) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ SLICount uint32 `json:"sliCount"`
+
+ // QPSum is the sum of the QP values of frames passed. The count of frames is
+ // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats.
+ QPSum uint64 `json:"qpSum"`
+
+ // PacketsSent is the total number of RTP packets sent for this SSRC.
+ PacketsSent uint32 `json:"packetsSent"`
+
+ // PacketsDiscardedOnSend is the total number of RTP packets for this SSRC that
+ // have been discarded due to socket errors, i.e. a socket error occurred when handing
+ // the packets to the socket. This might happen due to various reasons, including
+ // full buffer or no available memory.
+ PacketsDiscardedOnSend uint32 `json:"packetsDiscardedOnSend"`
+
+ // FECPacketsSent is the total number of RTP FEC packets sent for this SSRC.
+ // This counter can also be incremented when sending FEC packets in-band with
+ // media packets (e.g., with Opus).
+ FECPacketsSent uint32 `json:"fecPacketsSent"`
+
+ // BytesSent is the total number of bytes sent for this SSRC.
+ BytesSent uint64 `json:"bytesSent"`
+
+ // BytesDiscardedOnSend is the total number of bytes for this SSRC that have
+ // been discarded due to socket errors, i.e. a socket error occurred when handing
+ // the packets containing the bytes to the socket. This might happen due to various
+ // reasons, including full buffer or no available memory.
+ BytesDiscardedOnSend uint64 `json:"bytesDiscardedOnSend"`
+
+ // TrackID is the identifier of the stats object representing the current track
+ // attachment to the sender of this stream, a SenderAudioTrackAttachmentStats
+ // or SenderVideoTrackAttachmentStats.
+ TrackID string `json:"trackId"`
+
+ // SenderID is the stats ID used to look up the AudioSenderStats or VideoSenderStats
+ // object sending this stream.
+ SenderID string `json:"senderId"`
+
+ // RemoteID is used for looking up the remote RemoteInboundRTPStreamStats object
+ // for the same SSRC.
+ RemoteID string `json:"remoteId"`
+
+ // LastPacketSentTimestamp represents the timestamp at which the last packet was
+ // sent for this SSRC. This differs from timestamp, which represents the time at
+ // which the statistics were generated by the local endpoint.
+ LastPacketSentTimestamp StatsTimestamp `json:"lastPacketSentTimestamp"`
+
+ // TargetBitrate is the current target bitrate configured for this particular SSRC
+ // and is the Transport Independent Application Specific (TIAS) bitrate [RFC3890].
+ // Typically, the target bitrate is a configuration parameter provided to the codec's
+ // encoder and does not count the size of the IP or other transport layers like TCP or UDP.
+ // It is measured in bits per second and the bitrate is calculated over a 1 second window.
+ TargetBitrate float64 `json:"targetBitrate"`
+
+ // FramesEncoded represents the total number of frames successfully encoded for this RTP media stream.
+ // Only valid for video.
+ FramesEncoded uint32 `json:"framesEncoded"`
+
+ // TotalEncodeTime is the total number of seconds that has been spent encoding the
+ // framesEncoded frames of this stream. The average encode time can be calculated by
+ // dividing this value with FramesEncoded. The time it takes to encode one frame is the
+ // time passed between feeding the encoder a frame and the encoder returning encoded data
+ // for that frame. This does not include any additional time it may take to packetize the resulting data.
+ TotalEncodeTime float64 `json:"totalEncodeTime"`
+
+ // AverageRTCPInterval is the average RTCP interval between two consecutive compound RTCP
+ // packets. This is calculated by the sending endpoint when sending compound RTCP reports.
+ // Compound packets must contain at least a RTCP RR or SR packet and an SDES packet with the CNAME item.
+ AverageRTCPInterval float64 `json:"averageRtcpInterval"`
+
+ // QualityLimitationReason is the current reason for limiting the resolution and/or framerate,
+ // or "none" if not limited. Only valid for video.
+ QualityLimitationReason QualityLimitationReason `json:"qualityLimitationReason"`
+
+ // QualityLimitationDurations is record of the total time, in seconds, that this
+ // stream has spent in each quality limitation state. The record includes a mapping
+ // for all QualityLimitationReason types, including "none". Only valid for video.
+ QualityLimitationDurations map[string]float64 `json:"qualityLimitationDurations"`
+
+ // PerDSCPPacketsSent is the total number of packets sent for this SSRC, per DSCP.
+ // DSCPs are identified as decimal integers in string form.
+ PerDSCPPacketsSent map[string]uint32 `json:"perDscpPacketsSent"`
+}
+
+// RemoteInboundRTPStreamStats contains statistics for the remote endpoint's inbound
+// RTP stream corresponding to an outbound stream that is currently sent with this
+// PeerConnection object. It is measured at the remote endpoint and reported in an RTCP
+// Receiver Report (RR) or RTCP Extended Report (XR).
+type RemoteInboundRTPStreamStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // SSRC is the 32-bit unsigned integer value used to identify the source of the
+ // stream of RTP packets that this stats object concerns.
+ SSRC SSRC `json:"ssrc"`
+
+ // Kind is either "audio" or "video"
+ Kind string `json:"kind"`
+
+ // It is a unique identifier that is associated to the object that was inspected
+ // to produce the TransportStats associated with this RTP stream.
+ TransportID string `json:"transportId"`
+
+ // CodecID is a unique identifier that is associated to the object that was inspected
+ // to produce the CodecStats associated with this RTP stream.
+ CodecID string `json:"codecId"`
+
+ // FIRCount counts the total number of Full Intra Request (FIR) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ FIRCount uint32 `json:"firCount"`
+
+ // PLICount counts the total number of Picture Loss Indication (PLI) packets
+ // received by the sender. This metric is only valid for video and is sent by receiver.
+ PLICount uint32 `json:"pliCount"`
+
+ // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets
+ // received by the sender and is sent by receiver.
+ NACKCount uint32 `json:"nackCount"`
+
+ // SLICount counts the total number of Slice Loss Indication (SLI) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ SLICount uint32 `json:"sliCount"`
+
+ // QPSum is the sum of the QP values of frames passed. The count of frames is
+ // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats.
+ QPSum uint64 `json:"qpSum"`
+
+ // PacketsReceived is the total number of RTP packets received for this SSRC.
+ PacketsReceived uint32 `json:"packetsReceived"`
+
+ // PacketsLost is the total number of RTP packets lost for this SSRC. Note that
+ // because of how this is estimated, it can be negative if more packets are received than sent.
+ PacketsLost int32 `json:"packetsLost"`
+
+ // Jitter is the packet jitter measured in seconds for this SSRC
+ Jitter float64 `json:"jitter"`
+
+ // PacketsDiscarded is the cumulative number of RTP packets discarded by the jitter
+ // buffer due to late or early-arrival, i.e., these packets are not played out.
+ // RTP packets discarded due to packet duplication are not reported in this metric.
+ PacketsDiscarded uint32 `json:"packetsDiscarded"`
+
+ // PacketsRepaired is the cumulative number of lost RTP packets repaired after applying
+ // an error-resilience mechanism. It is measured for the primary source RTP packets
+ // and only counted for RTP packets that have no further chance of repair.
+ PacketsRepaired uint32 `json:"packetsRepaired"`
+
+ // BurstPacketsLost is the cumulative number of RTP packets lost during loss bursts.
+ BurstPacketsLost uint32 `json:"burstPacketsLost"`
+
+ // BurstPacketsDiscarded is the cumulative number of RTP packets discarded during discard bursts.
+ BurstPacketsDiscarded uint32 `json:"burstPacketsDiscarded"`
+
+ // BurstLossCount is the cumulative number of bursts of lost RTP packets.
+ BurstLossCount uint32 `json:"burstLossCount"`
+
+ // BurstDiscardCount is the cumulative number of bursts of discarded RTP packets.
+ BurstDiscardCount uint32 `json:"burstDiscardCount"`
+
+ // BurstLossRate is the fraction of RTP packets lost during bursts to the
+ // total number of RTP packets expected in the bursts.
+ BurstLossRate float64 `json:"burstLossRate"`
+
+ // BurstDiscardRate is the fraction of RTP packets discarded during bursts to
+ // the total number of RTP packets expected in bursts.
+ BurstDiscardRate float64 `json:"burstDiscardRate"`
+
+ // GapLossRate is the fraction of RTP packets lost during the gap periods.
+ GapLossRate float64 `json:"gapLossRate"`
+
+ // GapDiscardRate is the fraction of RTP packets discarded during the gap periods.
+ GapDiscardRate float64 `json:"gapDiscardRate"`
+
+ // LocalID is used for looking up the local OutboundRTPStreamStats object for the same SSRC.
+ LocalID string `json:"localId"`
+
+ // RoundTripTime is the estimated round trip time for this SSRC based on the
+ // RTCP timestamps in the RTCP Receiver Report (RR) and measured in seconds.
+ RoundTripTime float64 `json:"roundTripTime"`
+
+ // FractionLost is the the fraction packet loss reported for this SSRC.
+ FractionLost float64 `json:"fractionLost"`
+}
+
+// RemoteOutboundRTPStreamStats contains statistics for the remote endpoint's outbound
+// RTP stream corresponding to an inbound stream that is currently received with this
+// PeerConnection object. It is measured at the remote endpoint and reported in an
+// RTCP Sender Report (SR).
+type RemoteOutboundRTPStreamStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // SSRC is the 32-bit unsigned integer value used to identify the source of the
+ // stream of RTP packets that this stats object concerns.
+ SSRC SSRC `json:"ssrc"`
+
+ // Kind is either "audio" or "video"
+ Kind string `json:"kind"`
+
+ // It is a unique identifier that is associated to the object that was inspected
+ // to produce the TransportStats associated with this RTP stream.
+ TransportID string `json:"transportId"`
+
+ // CodecID is a unique identifier that is associated to the object that was inspected
+ // to produce the CodecStats associated with this RTP stream.
+ CodecID string `json:"codecId"`
+
+ // FIRCount counts the total number of Full Intra Request (FIR) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ FIRCount uint32 `json:"firCount"`
+
+ // PLICount counts the total number of Picture Loss Indication (PLI) packets
+ // received by the sender. This metric is only valid for video and is sent by receiver.
+ PLICount uint32 `json:"pliCount"`
+
+ // NACKCount counts the total number of Negative ACKnowledgement (NACK) packets
+ // received by the sender and is sent by receiver.
+ NACKCount uint32 `json:"nackCount"`
+
+ // SLICount counts the total number of Slice Loss Indication (SLI) packets received
+ // by the sender. This metric is only valid for video and is sent by receiver.
+ SLICount uint32 `json:"sliCount"`
+
+ // QPSum is the sum of the QP values of frames passed. The count of frames is
+ // in FramesDecoded for inbound stream stats, and in FramesEncoded for outbound stream stats.
+ QPSum uint64 `json:"qpSum"`
+
+ // PacketsSent is the total number of RTP packets sent for this SSRC.
+ PacketsSent uint32 `json:"packetsSent"`
+
+ // PacketsDiscardedOnSend is the total number of RTP packets for this SSRC that
+ // have been discarded due to socket errors, i.e. a socket error occurred when handing
+ // the packets to the socket. This might happen due to various reasons, including
+ // full buffer or no available memory.
+ PacketsDiscardedOnSend uint32 `json:"packetsDiscardedOnSend"`
+
+ // FECPacketsSent is the total number of RTP FEC packets sent for this SSRC.
+ // This counter can also be incremented when sending FEC packets in-band with
+ // media packets (e.g., with Opus).
+ FECPacketsSent uint32 `json:"fecPacketsSent"`
+
+ // BytesSent is the total number of bytes sent for this SSRC.
+ BytesSent uint64 `json:"bytesSent"`
+
+ // BytesDiscardedOnSend is the total number of bytes for this SSRC that have
+ // been discarded due to socket errors, i.e. a socket error occurred when handing
+ // the packets containing the bytes to the socket. This might happen due to various
+ // reasons, including full buffer or no available memory.
+ BytesDiscardedOnSend uint64 `json:"bytesDiscardedOnSend"`
+
+ // LocalID is used for looking up the local InboundRTPStreamStats object for the same SSRC.
+ LocalID string `json:"localId"`
+
+ // RemoteTimestamp represents the remote timestamp at which these statistics were
+ // sent by the remote endpoint. This differs from timestamp, which represents the
+ // time at which the statistics were generated or received by the local endpoint.
+ // The RemoteTimestamp, if present, is derived from the NTP timestamp in an RTCP
+ // Sender Report (SR) packet, which reflects the remote endpoint's clock.
+ // That clock may not be synchronized with the local clock.
+ RemoteTimestamp StatsTimestamp `json:"remoteTimestamp"`
+}
+
+// RTPContributingSourceStats contains statistics for a contributing source (CSRC) that contributed
+// to an inbound RTP stream.
+type RTPContributingSourceStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // ContributorSSRC is the SSRC identifier of the contributing source represented
+ // by this stats object. It is a 32-bit unsigned integer that appears in the CSRC
+ // list of any packets the relevant source contributed to.
+ ContributorSSRC SSRC `json:"contributorSsrc"`
+
+ // InboundRTPStreamID is the ID of the InboundRTPStreamStats object representing
+ // the inbound RTP stream that this contributing source is contributing to.
+ InboundRTPStreamID string `json:"inboundRtpStreamId"`
+
+ // PacketsContributedTo is the total number of RTP packets that this contributing
+ // source contributed to. This value is incremented each time a packet is counted
+ // by InboundRTPStreamStats.packetsReceived, and the packet's CSRC list contains
+ // the SSRC identifier of this contributing source, ContributorSSRC.
+ PacketsContributedTo uint32 `json:"packetsContributedTo"`
+
+ // AudioLevel is present if the last received RTP packet that this source contributed
+ // to contained an [RFC6465] mixer-to-client audio level header extension. The value
+ // of audioLevel is between 0..1 (linear), where 1.0 represents 0 dBov, 0 represents
+ // silence, and 0.5 represents approximately 6 dBSPL change in the sound pressure level from 0 dBov.
+ AudioLevel float64 `json:"audioLevel"`
+}
+
+// PeerConnectionStats contains statistics related to the PeerConnection object.
+type PeerConnectionStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // DataChannelsOpened represents the number of unique DataChannels that have
+ // entered the "open" state during their lifetime.
+ DataChannelsOpened uint32 `json:"dataChannelsOpened"`
+
+ // DataChannelsClosed represents the number of unique DataChannels that have
+ // left the "open" state during their lifetime (due to being closed by either
+ // end or the underlying transport being closed). DataChannels that transition
+ // from "connecting" to "closing" or "closed" without ever being "open"
+ // are not counted in this number.
+ DataChannelsClosed uint32 `json:"dataChannelsClosed"`
+
+ // DataChannelsRequested Represents the number of unique DataChannels returned
+ // from a successful createDataChannel() call on the PeerConnection. If the
+ // underlying data transport is not established, these may be in the "connecting" state.
+ DataChannelsRequested uint32 `json:"dataChannelsRequested"`
+
+ // DataChannelsAccepted represents the number of unique DataChannels signaled
+ // in a "datachannel" event on the PeerConnection.
+ DataChannelsAccepted uint32 `json:"dataChannelsAccepted"`
+}
+
+// DataChannelStats contains statistics related to each DataChannel ID.
+type DataChannelStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // Label is the "label" value of the DataChannel object.
+ Label string `json:"label"`
+
+ // Protocol is the "protocol" value of the DataChannel object.
+ Protocol string `json:"protocol"`
+
+ // DataChannelIdentifier is the "id" attribute of the DataChannel object.
+ DataChannelIdentifier int32 `json:"dataChannelIdentifier"`
+
+ // TransportID the ID of the TransportStats object for transport used to carry this datachannel.
+ TransportID string `json:"transportId"`
+
+ // State is the "readyState" value of the DataChannel object.
+ State DataChannelState `json:"state"`
+
+ // MessagesSent represents the total number of API "message" events sent.
+ MessagesSent uint32 `json:"messagesSent"`
+
+ // BytesSent represents the total number of payload bytes sent on this
+ // datachannel not including headers or padding.
+ BytesSent uint64 `json:"bytesSent"`
+
+ // MessagesReceived represents the total number of API "message" events received.
+ MessagesReceived uint32 `json:"messagesReceived"`
+
+ // BytesReceived represents the total number of bytes received on this
+ // datachannel not including headers or padding.
+ BytesReceived uint64 `json:"bytesReceived"`
+}
+
+// MediaStreamStats contains statistics related to a specific MediaStream.
+type MediaStreamStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // StreamIdentifier is the "id" property of the MediaStream
+ StreamIdentifier string `json:"streamIdentifier"`
+
+ // TrackIDs is a list of the identifiers of the stats object representing the
+ // stream's tracks, either ReceiverAudioTrackAttachmentStats or ReceiverVideoTrackAttachmentStats.
+ TrackIDs []string `json:"trackIds"`
+}
+
+// AudioSenderStats represents the stats about one audio sender of a PeerConnection
+// object for which one calls GetStats.
+//
+// It appears in the stats as soon as the RTPSender is added by either AddTrack
+// or AddTransceiver, or by media negotiation.
+type AudioSenderStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // TrackIdentifier represents the id property of the track.
+ TrackIdentifier string `json:"trackIdentifier"`
+
+ // RemoteSource is true if the source is remote, for instance if it is sourced
+ // from another host via a PeerConnection. False otherwise. Only applicable for 'track' stats.
+ RemoteSource bool `json:"remoteSource"`
+
+ // Ended reflects the "ended" state of the track.
+ Ended bool `json:"ended"`
+
+ // Kind is either "audio" or "video". This reflects the "kind" attribute of the MediaStreamTrack.
+ Kind string `json:"kind"`
+
+ // AudioLevel represents the output audio level of the track.
+ //
+ // The value is a value between 0..1 (linear), where 1.0 represents 0 dBov,
+ // 0 represents silence, and 0.5 represents approximately 6 dBSPL change in
+ // the sound pressure level from 0 dBov.
+ //
+ // If the track is sourced from an Receiver, does no audio processing, has a
+ // constant level, and has a volume setting of 1.0, the audio level is expected
+ // to be the same as the audio level of the source SSRC, while if the volume setting
+ // is 0.5, the AudioLevel is expected to be half that value.
+ //
+ // For outgoing audio tracks, the AudioLevel is the level of the audio being sent.
+ AudioLevel float64 `json:"audioLevel"`
+
+ // TotalAudioEnergy is the total energy of all the audio samples sent/received
+ // for this object, calculated by duration * Math.pow(energy/maxEnergy, 2) for
+ // each audio sample seen.
+ TotalAudioEnergy float64 `json:"totalAudioEnergy"`
+
+ // VoiceActivityFlag represents whether the last RTP packet sent or played out
+ // by this track contained voice activity or not based on the presence of the
+ // V bit in the extension header, as defined in [RFC6464].
+ //
+ // This value indicates the voice activity in the latest RTP packet played out
+ // from a given SSRC, and is defined in RTPSynchronizationSource.voiceActivityFlag.
+ VoiceActivityFlag bool `json:"voiceActivityFlag"`
+
+ // TotalSamplesDuration represents the total duration in seconds of all samples
+ // that have sent or received (and thus counted by TotalSamplesSent or TotalSamplesReceived).
+ // Can be used with TotalAudioEnergy to compute an average audio level over different intervals.
+ TotalSamplesDuration float64 `json:"totalSamplesDuration"`
+
+ // EchoReturnLoss is only present while the sender is sending a track sourced from
+ // a microphone where echo cancellation is applied. Calculated in decibels.
+ EchoReturnLoss float64 `json:"echoReturnLoss"`
+
+ // EchoReturnLossEnhancement is only present while the sender is sending a track
+ // sourced from a microphone where echo cancellation is applied. Calculated in decibels.
+ EchoReturnLossEnhancement float64 `json:"echoReturnLossEnhancement"`
+
+ // TotalSamplesSent is the total number of samples that have been sent by this sender.
+ TotalSamplesSent uint64 `json:"totalSamplesSent"`
+}
+
+// SenderAudioTrackAttachmentStats object represents the stats about one attachment
+// of an audio MediaStreamTrack to the PeerConnection object for which one calls GetStats.
+//
+// It appears in the stats as soon as it is attached (via AddTrack, via AddTransceiver,
+// via ReplaceTrack on an RTPSender object).
+//
+// If an audio track is attached twice (via AddTransceiver or ReplaceTrack), there
+// will be two SenderAudioTrackAttachmentStats objects, one for each attachment.
+// They will have the same "TrackIdentifier" attribute, but different "ID" attributes.
+//
+// If the track is detached from the PeerConnection (via removeTrack or via replaceTrack),
+// it continues to appear, but with the "ObjectDeleted" member set to true.
+type SenderAudioTrackAttachmentStats AudioSenderStats
+
+// VideoSenderStats represents the stats about one video sender of a PeerConnection
+// object for which one calls GetStats.
+//
+// It appears in the stats as soon as the sender is added by either AddTrack or
+// AddTransceiver, or by media negotiation.
+type VideoSenderStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // FramesCaptured represents the total number of frames captured, before encoding,
+ // for this RTPSender (or for this MediaStreamTrack, if type is "track"). For example,
+ // if type is "sender" and this sender's track represents a camera, then this is the
+ // number of frames produced by the camera for this track while being sent by this sender,
+ // combined with the number of frames produced by all tracks previously attached to this
+ // sender while being sent by this sender. Framerates can vary due to hardware limitations
+ // or environmental factors such as lighting conditions.
+ FramesCaptured uint32 `json:"framesCaptured"`
+
+ // FramesSent represents the total number of frames sent by this RTPSender
+ // (or for this MediaStreamTrack, if type is "track").
+ FramesSent uint32 `json:"framesSent"`
+
+ // HugeFramesSent represents the total number of huge frames sent by this RTPSender
+ // (or for this MediaStreamTrack, if type is "track"). Huge frames, by definition,
+ // are frames that have an encoded size at least 2.5 times the average size of the frames.
+ // The average size of the frames is defined as the target bitrate per second divided
+ // by the target fps at the time the frame was encoded. These are usually complex
+ // to encode frames with a lot of changes in the picture. This can be used to estimate,
+ // e.g slide changes in the streamed presentation. If a huge frame is also a key frame,
+ // then both counters HugeFramesSent and KeyFramesSent are incremented.
+ HugeFramesSent uint32 `json:"hugeFramesSent"`
+
+ // KeyFramesSent represents the total number of key frames sent by this RTPSender
+ // (or for this MediaStreamTrack, if type is "track"), such as Infra-frames in
+ // VP8 [RFC6386] or I-frames in H.264 [RFC6184]. This is a subset of FramesSent.
+ // FramesSent - KeyFramesSent gives you the number of delta frames sent.
+ KeyFramesSent uint32 `json:"keyFramesSent"`
+}
+
+// SenderVideoTrackAttachmentStats represents the stats about one attachment of a
+// video MediaStreamTrack to the PeerConnection object for which one calls GetStats.
+//
+// It appears in the stats as soon as it is attached (via AddTrack, via AddTransceiver,
+// via ReplaceTrack on an RTPSender object).
+//
+// If a video track is attached twice (via AddTransceiver or ReplaceTrack), there
+// will be two SenderVideoTrackAttachmentStats objects, one for each attachment.
+// They will have the same "TrackIdentifier" attribute, but different "ID" attributes.
+//
+// If the track is detached from the PeerConnection (via RemoveTrack or via ReplaceTrack),
+// it continues to appear, but with the "ObjectDeleted" member set to true.
+type SenderVideoTrackAttachmentStats VideoSenderStats
+
+// AudioReceiverStats contains audio metrics related to a specific receiver.
+type AudioReceiverStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // AudioLevel represents the output audio level of the track.
+ //
+ // The value is a value between 0..1 (linear), where 1.0 represents 0 dBov,
+ // 0 represents silence, and 0.5 represents approximately 6 dBSPL change in
+ // the sound pressure level from 0 dBov.
+ //
+ // If the track is sourced from an Receiver, does no audio processing, has a
+ // constant level, and has a volume setting of 1.0, the audio level is expected
+ // to be the same as the audio level of the source SSRC, while if the volume setting
+ // is 0.5, the AudioLevel is expected to be half that value.
+ //
+ // For outgoing audio tracks, the AudioLevel is the level of the audio being sent.
+ AudioLevel float64 `json:"audioLevel"`
+
+ // TotalAudioEnergy is the total energy of all the audio samples sent/received
+ // for this object, calculated by duration * Math.pow(energy/maxEnergy, 2) for
+ // each audio sample seen.
+ TotalAudioEnergy float64 `json:"totalAudioEnergy"`
+
+ // VoiceActivityFlag represents whether the last RTP packet sent or played out
+ // by this track contained voice activity or not based on the presence of the
+ // V bit in the extension header, as defined in [RFC6464].
+ //
+ // This value indicates the voice activity in the latest RTP packet played out
+ // from a given SSRC, and is defined in RTPSynchronizationSource.voiceActivityFlag.
+ VoiceActivityFlag bool `json:"voiceActivityFlag"`
+
+ // TotalSamplesDuration represents the total duration in seconds of all samples
+ // that have sent or received (and thus counted by TotalSamplesSent or TotalSamplesReceived).
+ // Can be used with TotalAudioEnergy to compute an average audio level over different intervals.
+ TotalSamplesDuration float64 `json:"totalSamplesDuration"`
+
+ // EstimatedPlayoutTimestamp is the estimated playout time of this receiver's
+ // track. The playout time is the NTP timestamp of the last playable sample that
+ // has a known timestamp (from an RTCP SR packet mapping RTP timestamps to NTP
+ // timestamps), extrapolated with the time elapsed since it was ready to be played out.
+ // This is the "current time" of the track in NTP clock time of the sender and
+ // can be present even if there is no audio currently playing.
+ //
+ // This can be useful for estimating how much audio and video is out of
+ // sync for two tracks from the same source:
+ // AudioTrackStats.EstimatedPlayoutTimestamp - VideoTrackStats.EstimatedPlayoutTimestamp
+ EstimatedPlayoutTimestamp StatsTimestamp `json:"estimatedPlayoutTimestamp"`
+
+ // JitterBufferDelay is the sum of the time, in seconds, each sample takes from
+ // the time it is received and to the time it exits the jitter buffer.
+ // This increases upon samples exiting, having completed their time in the buffer
+ // (incrementing JitterBufferEmittedCount). The average jitter buffer delay can
+ // be calculated by dividing the JitterBufferDelay with the JitterBufferEmittedCount.
+ JitterBufferDelay float64 `json:"jitterBufferDelay"`
+
+ // JitterBufferEmittedCount is the total number of samples that have come out
+ // of the jitter buffer (increasing JitterBufferDelay).
+ JitterBufferEmittedCount uint64 `json:"jitterBufferEmittedCount"`
+
+ // TotalSamplesReceived is the total number of samples that have been received
+ // by this receiver. This includes ConcealedSamples.
+ TotalSamplesReceived uint64 `json:"totalSamplesReceived"`
+
+ // ConcealedSamples is the total number of samples that are concealed samples.
+ // A concealed sample is a sample that is based on data that was synthesized
+ // to conceal packet loss and does not represent incoming data.
+ ConcealedSamples uint64 `json:"concealedSamples"`
+
+ // ConcealmentEvents is the number of concealment events. This counter increases
+ // every time a concealed sample is synthesized after a non-concealed sample.
+ // That is, multiple consecutive concealed samples will increase the concealedSamples
+ // count multiple times but is a single concealment event.
+ ConcealmentEvents uint64 `json:"concealmentEvents"`
+}
+
+// VideoReceiverStats contains video metrics related to a specific receiver.
+type VideoReceiverStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // FrameWidth represents the width of the last processed frame for this track.
+ // Before the first frame is processed this attribute is missing.
+ FrameWidth uint32 `json:"frameWidth"`
+
+ // FrameHeight represents the height of the last processed frame for this track.
+ // Before the first frame is processed this attribute is missing.
+ FrameHeight uint32 `json:"frameHeight"`
+
+ // FramesPerSecond represents the nominal FPS value before the degradation preference
+ // is applied. It is the number of complete frames in the last second. For sending
+ // tracks it is the current captured FPS and for the receiving tracks it is the
+ // current decoding framerate.
+ FramesPerSecond float64 `json:"framesPerSecond"`
+
+ // EstimatedPlayoutTimestamp is the estimated playout time of this receiver's
+ // track. The playout time is the NTP timestamp of the last playable sample that
+ // has a known timestamp (from an RTCP SR packet mapping RTP timestamps to NTP
+ // timestamps), extrapolated with the time elapsed since it was ready to be played out.
+ // This is the "current time" of the track in NTP clock time of the sender and
+ // can be present even if there is no audio currently playing.
+ //
+ // This can be useful for estimating how much audio and video is out of
+ // sync for two tracks from the same source:
+ // AudioTrackStats.EstimatedPlayoutTimestamp - VideoTrackStats.EstimatedPlayoutTimestamp
+ EstimatedPlayoutTimestamp StatsTimestamp `json:"estimatedPlayoutTimestamp"`
+
+ // JitterBufferDelay is the sum of the time, in seconds, each sample takes from
+ // the time it is received and to the time it exits the jitter buffer.
+ // This increases upon samples exiting, having completed their time in the buffer
+ // (incrementing JitterBufferEmittedCount). The average jitter buffer delay can
+ // be calculated by dividing the JitterBufferDelay with the JitterBufferEmittedCount.
+ JitterBufferDelay float64 `json:"jitterBufferDelay"`
+
+ // JitterBufferEmittedCount is the total number of samples that have come out
+ // of the jitter buffer (increasing JitterBufferDelay).
+ JitterBufferEmittedCount uint64 `json:"jitterBufferEmittedCount"`
+
+ // FramesReceived Represents the total number of complete frames received for
+ // this receiver. This metric is incremented when the complete frame is received.
+ FramesReceived uint32 `json:"framesReceived"`
+
+ // KeyFramesReceived represents the total number of complete key frames received
+ // for this MediaStreamTrack, such as Infra-frames in VP8 [RFC6386] or I-frames
+ // in H.264 [RFC6184]. This is a subset of framesReceived. `framesReceived - keyFramesReceived`
+ // gives you the number of delta frames received. This metric is incremented when
+ // the complete key frame is received. It is not incremented if a partial key
+ // frames is received and sent for decoding, i.e., the frame could not be recovered
+ // via retransmission or FEC.
+ KeyFramesReceived uint32 `json:"keyFramesReceived"`
+
+ // FramesDecoded represents the total number of frames correctly decoded for this
+ // SSRC, i.e., frames that would be displayed if no frames are dropped.
+ FramesDecoded uint32 `json:"framesDecoded"`
+
+ // FramesDropped is the total number of frames dropped predecode or dropped
+ // because the frame missed its display deadline for this receiver's track.
+ FramesDropped uint32 `json:"framesDropped"`
+
+ // The cumulative number of partial frames lost. This metric is incremented when
+ // the frame is sent to the decoder. If the partial frame is received and recovered
+ // via retransmission or FEC before decoding, the FramesReceived counter is incremented.
+ PartialFramesLost uint32 `json:"partialFramesLost"`
+
+ // FullFramesLost is the cumulative number of full frames lost.
+ FullFramesLost uint32 `json:"fullFramesLost"`
+}
+
+// TransportStats contains transport statistics related to the PeerConnection object.
+type TransportStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // PacketsSent represents the total number of packets sent over this transport.
+ PacketsSent uint32 `json:"packetsSent"`
+
+ // PacketsReceived represents the total number of packets received on this transport.
+ PacketsReceived uint32 `json:"packetsReceived"`
+
+ // BytesSent represents the total number of payload bytes sent on this PeerConnection
+ // not including headers or padding.
+ BytesSent uint64 `json:"bytesSent"`
+
+ // BytesReceived represents the total number of bytes received on this PeerConnection
+ // not including headers or padding.
+ BytesReceived uint64 `json:"bytesReceived"`
+
+ // RTCPTransportStatsID is the ID of the transport that gives stats for the RTCP
+ // component If RTP and RTCP are not multiplexed and this record has only
+ // the RTP component stats.
+ RTCPTransportStatsID string `json:"rtcpTransportStatsId"`
+
+ // ICERole is set to the current value of the "role" attribute of the underlying
+ // DTLSTransport's "transport".
+ ICERole ICERole `json:"iceRole"`
+
+ // DTLSState is set to the current value of the "state" attribute of the underlying DTLSTransport.
+ DTLSState DTLSTransportState `json:"dtlsState"`
+
+ // SelectedCandidatePairID is a unique identifier that is associated to the object
+ // that was inspected to produce the ICECandidatePairStats associated with this transport.
+ SelectedCandidatePairID string `json:"selectedCandidatePairId"`
+
+ // LocalCertificateID is the ID of the CertificateStats for the local certificate.
+ // Present only if DTLS is negotiated.
+ LocalCertificateID string `json:"localCertificateId"`
+
+ // LocalCertificateID is the ID of the CertificateStats for the remote certificate.
+ // Present only if DTLS is negotiated.
+ RemoteCertificateID string `json:"remoteCertificateId"`
+
+ // DTLSCipher is the descriptive name of the cipher suite used for the DTLS transport,
+ // as defined in the "Description" column of the IANA cipher suite registry.
+ DTLSCipher string `json:"dtlsCipher"`
+
+ // SRTPCipher is the descriptive name of the protection profile used for the SRTP
+ // transport, as defined in the "Profile" column of the IANA DTLS-SRTP protection
+ // profile registry.
+ SRTPCipher string `json:"srtpCipher"`
+}
+
+// StatsICECandidatePairState is the state of an ICE candidate pair used in the
+// ICECandidatePairStats object.
+type StatsICECandidatePairState string
+
+func toStatsICECandidatePairState(state ice.CandidatePairState) (StatsICECandidatePairState, error) {
+ switch state {
+ case ice.CandidatePairStateWaiting:
+ return StatsICECandidatePairStateWaiting, nil
+ case ice.CandidatePairStateInProgress:
+ return StatsICECandidatePairStateInProgress, nil
+ case ice.CandidatePairStateFailed:
+ return StatsICECandidatePairStateFailed, nil
+ case ice.CandidatePairStateSucceeded:
+ return StatsICECandidatePairStateSucceeded, nil
+ default:
+ // NOTE: this should never happen[tm]
+ err := fmt.Errorf("%w: %s", errStatsICECandidateStateInvalid, state.String())
+ return StatsICECandidatePairState("Unknown"), err
+ }
+}
+
+const (
+ // StatsICECandidatePairStateFrozen means a check for this pair hasn't been
+ // performed, and it can't yet be performed until some other check succeeds,
+ // allowing this pair to unfreeze and move into the Waiting state.
+ StatsICECandidatePairStateFrozen StatsICECandidatePairState = "frozen"
+
+ // StatsICECandidatePairStateWaiting means a check has not been performed for
+ // this pair, and can be performed as soon as it is the highest-priority Waiting
+ // pair on the check list.
+ StatsICECandidatePairStateWaiting StatsICECandidatePairState = "waiting"
+
+ // StatsICECandidatePairStateInProgress means a check has been sent for this pair,
+ // but the transaction is in progress.
+ StatsICECandidatePairStateInProgress StatsICECandidatePairState = "in-progress"
+
+ // StatsICECandidatePairStateFailed means a check for this pair was already done
+ // and failed, either never producing any response or producing an unrecoverable
+ // failure response.
+ StatsICECandidatePairStateFailed StatsICECandidatePairState = "failed"
+
+ // StatsICECandidatePairStateSucceeded means a check for this pair was already
+ // done and produced a successful result.
+ StatsICECandidatePairStateSucceeded StatsICECandidatePairState = "succeeded"
+)
+
+// ICECandidatePairStats contains ICE candidate pair statistics related
+// to the ICETransport objects.
+type ICECandidatePairStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // TransportID is a unique identifier that is associated to the object that
+ // was inspected to produce the TransportStats associated with this candidate pair.
+ TransportID string `json:"transportId"`
+
+ // LocalCandidateID is a unique identifier that is associated to the object
+ // that was inspected to produce the ICECandidateStats for the local candidate
+ // associated with this candidate pair.
+ LocalCandidateID string `json:"localCandidateId"`
+
+ // RemoteCandidateID is a unique identifier that is associated to the object
+ // that was inspected to produce the ICECandidateStats for the remote candidate
+ // associated with this candidate pair.
+ RemoteCandidateID string `json:"remoteCandidateId"`
+
+ // State represents the state of the checklist for the local and remote
+ // candidates in a pair.
+ State StatsICECandidatePairState `json:"state"`
+
+ // Nominated is true when this valid pair that should be used for media
+ // if it is the highest-priority one amongst those whose nominated flag is set
+ Nominated bool `json:"nominated"`
+
+ // PacketsSent represents the total number of packets sent on this candidate pair.
+ PacketsSent uint32 `json:"packetsSent"`
+
+ // PacketsReceived represents the total number of packets received on this candidate pair.
+ PacketsReceived uint32 `json:"packetsReceived"`
+
+ // BytesSent represents the total number of payload bytes sent on this candidate pair
+ // not including headers or padding.
+ BytesSent uint64 `json:"bytesSent"`
+
+ // BytesReceived represents the total number of payload bytes received on this candidate pair
+ // not including headers or padding.
+ BytesReceived uint64 `json:"bytesReceived"`
+
+ // LastPacketSentTimestamp represents the timestamp at which the last packet was
+ // sent on this particular candidate pair, excluding STUN packets.
+ LastPacketSentTimestamp StatsTimestamp `json:"lastPacketSentTimestamp"`
+
+ // LastPacketReceivedTimestamp represents the timestamp at which the last packet
+ // was received on this particular candidate pair, excluding STUN packets.
+ LastPacketReceivedTimestamp StatsTimestamp `json:"lastPacketReceivedTimestamp"`
+
+ // FirstRequestTimestamp represents the timestamp at which the first STUN request
+ // was sent on this particular candidate pair.
+ FirstRequestTimestamp StatsTimestamp `json:"firstRequestTimestamp"`
+
+ // LastRequestTimestamp represents the timestamp at which the last STUN request
+ // was sent on this particular candidate pair. The average interval between two
+ // consecutive connectivity checks sent can be calculated with
+ // (LastRequestTimestamp - FirstRequestTimestamp) / RequestsSent.
+ LastRequestTimestamp StatsTimestamp `json:"lastRequestTimestamp"`
+
+ // LastResponseTimestamp represents the timestamp at which the last STUN response
+ // was received on this particular candidate pair.
+ LastResponseTimestamp StatsTimestamp `json:"lastResponseTimestamp"`
+
+ // TotalRoundTripTime represents the sum of all round trip time measurements
+ // in seconds since the beginning of the session, based on STUN connectivity
+ // check responses (ResponsesReceived), including those that reply to requests
+ // that are sent in order to verify consent. The average round trip time can
+ // be computed from TotalRoundTripTime by dividing it by ResponsesReceived.
+ TotalRoundTripTime float64 `json:"totalRoundTripTime"`
+
+ // CurrentRoundTripTime represents the latest round trip time measured in seconds,
+ // computed from both STUN connectivity checks, including those that are sent
+ // for consent verification.
+ CurrentRoundTripTime float64 `json:"currentRoundTripTime"`
+
+ // AvailableOutgoingBitrate is calculated by the underlying congestion control
+ // by combining the available bitrate for all the outgoing RTP streams using
+ // this candidate pair. The bitrate measurement does not count the size of the
+ // IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
+ // in RFC 3890, i.e., it is measured in bits per second and the bitrate is calculated
+ // over a 1 second window.
+ AvailableOutgoingBitrate float64 `json:"availableOutgoingBitrate"`
+
+ // AvailableIncomingBitrate is calculated by the underlying congestion control
+ // by combining the available bitrate for all the incoming RTP streams using
+ // this candidate pair. The bitrate measurement does not count the size of the
+ // IP or other transport layers like TCP or UDP. It is similar to the TIAS defined
+ // in RFC 3890, i.e., it is measured in bits per second and the bitrate is
+ // calculated over a 1 second window.
+ AvailableIncomingBitrate float64 `json:"availableIncomingBitrate"`
+
+ // CircuitBreakerTriggerCount represents the number of times the circuit breaker
+ // is triggered for this particular 5-tuple, ceasing transmission.
+ CircuitBreakerTriggerCount uint32 `json:"circuitBreakerTriggerCount"`
+
+ // RequestsReceived represents the total number of connectivity check requests
+ // received (including retransmissions). It is impossible for the receiver to
+ // tell whether the request was sent in order to check connectivity or check
+ // consent, so all connectivity checks requests are counted here.
+ RequestsReceived uint64 `json:"requestsReceived"`
+
+ // RequestsSent represents the total number of connectivity check requests
+ // sent (not including retransmissions).
+ RequestsSent uint64 `json:"requestsSent"`
+
+ // ResponsesReceived represents the total number of connectivity check responses received.
+ ResponsesReceived uint64 `json:"responsesReceived"`
+
+ // ResponsesSent represents the total number of connectivity check responses sent.
+ // Since we cannot distinguish connectivity check requests and consent requests,
+ // all responses are counted.
+ ResponsesSent uint64 `json:"responsesSent"`
+
+ // RetransmissionsReceived represents the total number of connectivity check
+ // request retransmissions received.
+ RetransmissionsReceived uint64 `json:"retransmissionsReceived"`
+
+ // RetransmissionsSent represents the total number of connectivity check
+ // request retransmissions sent.
+ RetransmissionsSent uint64 `json:"retransmissionsSent"`
+
+ // ConsentRequestsSent represents the total number of consent requests sent.
+ ConsentRequestsSent uint64 `json:"consentRequestsSent"`
+
+ // ConsentExpiredTimestamp represents the timestamp at which the latest valid
+ // STUN binding response expired.
+ ConsentExpiredTimestamp StatsTimestamp `json:"consentExpiredTimestamp"`
+}
+
+// ICECandidateStats contains ICE candidate statistics related to the ICETransport objects.
+type ICECandidateStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // TransportID is a unique identifier that is associated to the object that
+ // was inspected to produce the TransportStats associated with this candidate.
+ TransportID string `json:"transportId"`
+
+ // NetworkType represents the type of network interface used by the base of a
+ // local candidate (the address the ICE agent sends from). Only present for
+ // local candidates; it's not possible to know what type of network interface
+ // a remote candidate is using.
+ //
+ // Note:
+ // This stat only tells you about the network interface used by the first "hop";
+ // it's possible that a connection will be bottlenecked by another type of network.
+ // For example, when using Wi-Fi tethering, the networkType of the relevant candidate
+ // would be "wifi", even when the next hop is over a cellular connection.
+ NetworkType NetworkType `json:"networkType"`
+
+ // IP is the IP address of the candidate, allowing for IPv4 addresses and
+ // IPv6 addresses, but fully qualified domain names (FQDNs) are not allowed.
+ IP string `json:"ip"`
+
+ // Port is the port number of the candidate.
+ Port int32 `json:"port"`
+
+ // Protocol is one of udp and tcp.
+ Protocol string `json:"protocol"`
+
+ // CandidateType is the "Type" field of the ICECandidate.
+ CandidateType ICECandidateType `json:"candidateType"`
+
+ // Priority is the "Priority" field of the ICECandidate.
+ Priority int32 `json:"priority"`
+
+ // URL is the URL of the TURN or STUN server indicated in the that translated
+ // this IP address. It is the URL address surfaced in an PeerConnectionICEEvent.
+ URL string `json:"url"`
+
+ // RelayProtocol is the protocol used by the endpoint to communicate with the
+ // TURN server. This is only present for local candidates. Valid values for
+ // the TURN URL protocol is one of udp, tcp, or tls.
+ RelayProtocol string `json:"relayProtocol"`
+
+ // Deleted is true if the candidate has been deleted/freed. For host candidates,
+ // this means that any network resources (typically a socket) associated with the
+ // candidate have been released. For TURN candidates, this means the TURN allocation
+ // is no longer active.
+ //
+ // Only defined for local candidates. For remote candidates, this property is not applicable.
+ Deleted bool `json:"deleted"`
+}
+
+// CertificateStats contains information about a certificate used by an ICETransport.
+type CertificateStats struct {
+ // Timestamp is the timestamp associated with this object.
+ Timestamp StatsTimestamp `json:"timestamp"`
+
+ // Type is the object's StatsType
+ Type StatsType `json:"type"`
+
+ // ID is a unique id that is associated with the component inspected to produce
+ // this Stats object. Two Stats objects will have the same ID if they were produced
+ // by inspecting the same underlying object.
+ ID string `json:"id"`
+
+ // Fingerprint is the fingerprint of the certificate.
+ Fingerprint string `json:"fingerprint"`
+
+ // FingerprintAlgorithm is the hash function used to compute the certificate fingerprint. For instance, "sha-256".
+ FingerprintAlgorithm string `json:"fingerprintAlgorithm"`
+
+ // Base64Certificate is the DER-encoded base-64 representation of the certificate.
+ Base64Certificate string `json:"base64Certificate"`
+
+ // IssuerCertificateID refers to the stats object that contains the next certificate
+ // in the certificate chain. If the current certificate is at the end of the chain
+ // (i.e. a self-signed certificate), this will not be set.
+ IssuerCertificateID string `json:"issuerCertificateId"`
+}
diff --git a/vendor/github.com/pion/webrtc/v3/stats_go.go b/vendor/github.com/pion/webrtc/v3/stats_go.go
new file mode 100644
index 0000000..e1f622e
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/stats_go.go
@@ -0,0 +1,93 @@
+// +build !js
+
+package webrtc
+
+// GetConnectionStats is a helper method to return the associated stats for a given PeerConnection
+func (r StatsReport) GetConnectionStats(conn *PeerConnection) (PeerConnectionStats, bool) {
+ statsID := conn.getStatsID()
+ stats, ok := r[statsID]
+ if !ok {
+ return PeerConnectionStats{}, false
+ }
+
+ pcStats, ok := stats.(PeerConnectionStats)
+ if !ok {
+ return PeerConnectionStats{}, false
+ }
+ return pcStats, true
+}
+
+// GetDataChannelStats is a helper method to return the associated stats for a given DataChannel
+func (r StatsReport) GetDataChannelStats(dc *DataChannel) (DataChannelStats, bool) {
+ statsID := dc.getStatsID()
+ stats, ok := r[statsID]
+ if !ok {
+ return DataChannelStats{}, false
+ }
+
+ dcStats, ok := stats.(DataChannelStats)
+ if !ok {
+ return DataChannelStats{}, false
+ }
+ return dcStats, true
+}
+
+// GetICECandidateStats is a helper method to return the associated stats for a given ICECandidate
+func (r StatsReport) GetICECandidateStats(c *ICECandidate) (ICECandidateStats, bool) {
+ statsID := c.statsID
+ stats, ok := r[statsID]
+ if !ok {
+ return ICECandidateStats{}, false
+ }
+
+ candidateStats, ok := stats.(ICECandidateStats)
+ if !ok {
+ return ICECandidateStats{}, false
+ }
+ return candidateStats, true
+}
+
+// GetICECandidatePairStats is a helper method to return the associated stats for a given ICECandidatePair
+func (r StatsReport) GetICECandidatePairStats(c *ICECandidatePair) (ICECandidatePairStats, bool) {
+ statsID := c.statsID
+ stats, ok := r[statsID]
+ if !ok {
+ return ICECandidatePairStats{}, false
+ }
+
+ candidateStats, ok := stats.(ICECandidatePairStats)
+ if !ok {
+ return ICECandidatePairStats{}, false
+ }
+ return candidateStats, true
+}
+
+// GetCertificateStats is a helper method to return the associated stats for a given Certificate
+func (r StatsReport) GetCertificateStats(c *Certificate) (CertificateStats, bool) {
+ statsID := c.statsID
+ stats, ok := r[statsID]
+ if !ok {
+ return CertificateStats{}, false
+ }
+
+ certificateStats, ok := stats.(CertificateStats)
+ if !ok {
+ return CertificateStats{}, false
+ }
+ return certificateStats, true
+}
+
+// GetCodecStats is a helper method to return the associated stats for a given Codec
+func (r StatsReport) GetCodecStats(c *RTPCodecParameters) (CodecStats, bool) {
+ statsID := c.statsID
+ stats, ok := r[statsID]
+ if !ok {
+ return CodecStats{}, false
+ }
+
+ codecStats, ok := stats.(CodecStats)
+ if !ok {
+ return CodecStats{}, false
+ }
+ return codecStats, true
+}
diff --git a/vendor/github.com/pion/webrtc/v3/track_local.go b/vendor/github.com/pion/webrtc/v3/track_local.go
new file mode 100644
index 0000000..e6e1da1
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/track_local.go
@@ -0,0 +1,75 @@
+package webrtc
+
+import "github.com/pion/rtp"
+
+// TrackLocalWriter is the Writer for outbound RTP Packets
+type TrackLocalWriter interface {
+ // WriteRTP encrypts a RTP packet and writes to the connection
+ WriteRTP(header *rtp.Header, payload []byte) (int, error)
+
+ // Write encrypts and writes a full RTP packet
+ Write(b []byte) (int, error)
+}
+
+// TrackLocalContext is the Context passed when a TrackLocal has been Binded/Unbinded from a PeerConnection, and used
+// in Interceptors.
+type TrackLocalContext struct {
+ id string
+ params RTPParameters
+ ssrc SSRC
+ writeStream TrackLocalWriter
+}
+
+// CodecParameters returns the negotiated RTPCodecParameters. These are the codecs supported by both
+// PeerConnections and the SSRC/PayloadTypes
+func (t *TrackLocalContext) CodecParameters() []RTPCodecParameters {
+ return t.params.Codecs
+}
+
+// HeaderExtensions returns the negotiated RTPHeaderExtensionParameters. These are the header extensions supported by
+// both PeerConnections and the SSRC/PayloadTypes
+func (t *TrackLocalContext) HeaderExtensions() []RTPHeaderExtensionParameter {
+ return t.params.HeaderExtensions
+}
+
+// SSRC requires the negotiated SSRC of this track
+// This track may have multiple if RTX is enabled
+func (t *TrackLocalContext) SSRC() SSRC {
+ return t.ssrc
+}
+
+// WriteStream returns the WriteStream for this TrackLocal. The implementer writes the outbound
+// media packets to it
+func (t *TrackLocalContext) WriteStream() TrackLocalWriter {
+ return t.writeStream
+}
+
+// ID is a unique identifier that is used for both Bind/Unbind
+func (t *TrackLocalContext) ID() string {
+ return t.id
+}
+
+// TrackLocal is an interface that controls how the user can send media
+// The user can provide their own TrackLocal implementatiosn, or use
+// the implementations in pkg/media
+type TrackLocal interface {
+ // Bind should implement the way how the media data flows from the Track to the PeerConnection
+ // This will be called internally after signaling is complete and the list of available
+ // codecs has been determined
+ Bind(TrackLocalContext) (RTPCodecParameters, error)
+
+ // Unbind should implement the teardown logic when the track is no longer needed. This happens
+ // because a track has been stopped.
+ Unbind(TrackLocalContext) error
+
+ // ID is the unique identifier for this Track. This should be unique for the
+ // stream, but doesn't have to globally unique. A common example would be 'audio' or 'video'
+ // and StreamID would be 'desktop' or 'webcam'
+ ID() string
+
+ // StreamID is the group this track belongs too. This must be unique
+ StreamID() string
+
+ // Kind controls if this TrackLocal is audio or video
+ Kind() RTPCodecType
+}
diff --git a/vendor/github.com/pion/webrtc/v3/track_local_static.go b/vendor/github.com/pion/webrtc/v3/track_local_static.go
new file mode 100644
index 0000000..cf5aa22
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/track_local_static.go
@@ -0,0 +1,267 @@
+// +build !js
+
+package webrtc
+
+import (
+ "strings"
+ "sync"
+
+ "github.com/pion/rtp"
+ "github.com/pion/webrtc/v3/internal/util"
+ "github.com/pion/webrtc/v3/pkg/media"
+)
+
+// trackBinding is a single bind for a Track
+// Bind can be called multiple times, this stores the
+// result for a single bind call so that it can be used when writing
+type trackBinding struct {
+ id string
+ ssrc SSRC
+ payloadType PayloadType
+ writeStream TrackLocalWriter
+}
+
+// TrackLocalStaticRTP is a TrackLocal that has a pre-set codec and accepts RTP Packets.
+// If you wish to send a media.Sample use TrackLocalStaticSample
+type TrackLocalStaticRTP struct {
+ mu sync.RWMutex
+ bindings []trackBinding
+ codec RTPCodecCapability
+ id, streamID string
+}
+
+// NewTrackLocalStaticRTP returns a TrackLocalStaticRTP.
+func NewTrackLocalStaticRTP(c RTPCodecCapability, id, streamID string) (*TrackLocalStaticRTP, error) {
+ return &TrackLocalStaticRTP{
+ codec: c,
+ bindings: []trackBinding{},
+ id: id,
+ streamID: streamID,
+ }, nil
+}
+
+// Bind is called by the PeerConnection after negotiation is complete
+// This asserts that the code requested is supported by the remote peer.
+// If so it setups all the state (SSRC and PayloadType) to have a call
+func (s *TrackLocalStaticRTP) Bind(t TrackLocalContext) (RTPCodecParameters, error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ parameters := RTPCodecParameters{RTPCodecCapability: s.codec}
+ if codec, matchType := codecParametersFuzzySearch(parameters, t.CodecParameters()); matchType != codecMatchNone {
+ s.bindings = append(s.bindings, trackBinding{
+ ssrc: t.SSRC(),
+ payloadType: codec.PayloadType,
+ writeStream: t.WriteStream(),
+ id: t.ID(),
+ })
+ return codec, nil
+ }
+
+ return RTPCodecParameters{}, ErrUnsupportedCodec
+}
+
+// Unbind implements the teardown logic when the track is no longer needed. This happens
+// because a track has been stopped.
+func (s *TrackLocalStaticRTP) Unbind(t TrackLocalContext) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ for i := range s.bindings {
+ if s.bindings[i].id == t.ID() {
+ s.bindings[i] = s.bindings[len(s.bindings)-1]
+ s.bindings = s.bindings[:len(s.bindings)-1]
+ return nil
+ }
+ }
+
+ return ErrUnbindFailed
+}
+
+// ID is the unique identifier for this Track. This should be unique for the
+// stream, but doesn't have to globally unique. A common example would be 'audio' or 'video'
+// and StreamID would be 'desktop' or 'webcam'
+func (s *TrackLocalStaticRTP) ID() string { return s.id }
+
+// StreamID is the group this track belongs too. This must be unique
+func (s *TrackLocalStaticRTP) StreamID() string { return s.streamID }
+
+// Kind controls if this TrackLocal is audio or video
+func (s *TrackLocalStaticRTP) Kind() RTPCodecType {
+ switch {
+ case strings.HasPrefix(s.codec.MimeType, "audio/"):
+ return RTPCodecTypeAudio
+ case strings.HasPrefix(s.codec.MimeType, "video/"):
+ return RTPCodecTypeVideo
+ default:
+ return RTPCodecType(0)
+ }
+}
+
+// Codec gets the Codec of the track
+func (s *TrackLocalStaticRTP) Codec() RTPCodecCapability {
+ return s.codec
+}
+
+// packetPool is a pool of packets used by WriteRTP and Write below
+// nolint:gochecknoglobals
+var rtpPacketPool = sync.Pool{
+ New: func() interface{} {
+ return &rtp.Packet{}
+ },
+}
+
+// WriteRTP writes a RTP Packet to the TrackLocalStaticRTP
+// If one PeerConnection fails the packets will still be sent to
+// all PeerConnections. The error message will contain the ID of the failed
+// PeerConnections so you can remove them
+func (s *TrackLocalStaticRTP) WriteRTP(p *rtp.Packet) error {
+ ipacket := rtpPacketPool.Get()
+ packet := ipacket.(*rtp.Packet)
+ defer func() {
+ *packet = rtp.Packet{}
+ rtpPacketPool.Put(ipacket)
+ }()
+ *packet = *p
+ return s.writeRTP(packet)
+}
+
+// writeRTP is like WriteRTP, except that it may modify the packet p
+func (s *TrackLocalStaticRTP) writeRTP(p *rtp.Packet) error {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ writeErrs := []error{}
+
+ for _, b := range s.bindings {
+ p.Header.SSRC = uint32(b.ssrc)
+ p.Header.PayloadType = uint8(b.payloadType)
+ if _, err := b.writeStream.WriteRTP(&p.Header, p.Payload); err != nil {
+ writeErrs = append(writeErrs, err)
+ }
+ }
+
+ return util.FlattenErrs(writeErrs)
+}
+
+// Write writes a RTP Packet as a buffer to the TrackLocalStaticRTP
+// If one PeerConnection fails the packets will still be sent to
+// all PeerConnections. The error message will contain the ID of the failed
+// PeerConnections so you can remove them
+func (s *TrackLocalStaticRTP) Write(b []byte) (n int, err error) {
+ ipacket := rtpPacketPool.Get()
+ packet := ipacket.(*rtp.Packet)
+ defer func() {
+ *packet = rtp.Packet{}
+ rtpPacketPool.Put(ipacket)
+ }()
+
+ if err = packet.Unmarshal(b); err != nil {
+ return 0, err
+ }
+
+ return len(b), s.writeRTP(packet)
+}
+
+// TrackLocalStaticSample is a TrackLocal that has a pre-set codec and accepts Samples.
+// If you wish to send a RTP Packet use TrackLocalStaticRTP
+type TrackLocalStaticSample struct {
+ packetizer rtp.Packetizer
+ rtpTrack *TrackLocalStaticRTP
+ clockRate float64
+}
+
+// NewTrackLocalStaticSample returns a TrackLocalStaticSample
+func NewTrackLocalStaticSample(c RTPCodecCapability, id, streamID string) (*TrackLocalStaticSample, error) {
+ rtpTrack, err := NewTrackLocalStaticRTP(c, id, streamID)
+ if err != nil {
+ return nil, err
+ }
+
+ return &TrackLocalStaticSample{
+ rtpTrack: rtpTrack,
+ }, nil
+}
+
+// ID is the unique identifier for this Track. This should be unique for the
+// stream, but doesn't have to globally unique. A common example would be 'audio' or 'video'
+// and StreamID would be 'desktop' or 'webcam'
+func (s *TrackLocalStaticSample) ID() string { return s.rtpTrack.ID() }
+
+// StreamID is the group this track belongs too. This must be unique
+func (s *TrackLocalStaticSample) StreamID() string { return s.rtpTrack.StreamID() }
+
+// Kind controls if this TrackLocal is audio or video
+func (s *TrackLocalStaticSample) Kind() RTPCodecType { return s.rtpTrack.Kind() }
+
+// Codec gets the Codec of the track
+func (s *TrackLocalStaticSample) Codec() RTPCodecCapability {
+ return s.rtpTrack.Codec()
+}
+
+// Bind is called by the PeerConnection after negotiation is complete
+// This asserts that the code requested is supported by the remote peer.
+// If so it setups all the state (SSRC and PayloadType) to have a call
+func (s *TrackLocalStaticSample) Bind(t TrackLocalContext) (RTPCodecParameters, error) {
+ codec, err := s.rtpTrack.Bind(t)
+ if err != nil {
+ return codec, err
+ }
+
+ s.rtpTrack.mu.Lock()
+ defer s.rtpTrack.mu.Unlock()
+
+ // We only need one packetizer
+ if s.packetizer != nil {
+ return codec, nil
+ }
+
+ payloader, err := payloaderForCodec(codec.RTPCodecCapability)
+ if err != nil {
+ return codec, err
+ }
+
+ s.packetizer = rtp.NewPacketizer(
+ rtpOutboundMTU,
+ 0, // Value is handled when writing
+ 0, // Value is handled when writing
+ payloader,
+ rtp.NewRandomSequencer(),
+ codec.ClockRate,
+ )
+ s.clockRate = float64(codec.RTPCodecCapability.ClockRate)
+ return codec, nil
+}
+
+// Unbind implements the teardown logic when the track is no longer needed. This happens
+// because a track has been stopped.
+func (s *TrackLocalStaticSample) Unbind(t TrackLocalContext) error {
+ return s.rtpTrack.Unbind(t)
+}
+
+// WriteSample writes a Sample to the TrackLocalStaticSample
+// If one PeerConnection fails the packets will still be sent to
+// all PeerConnections. The error message will contain the ID of the failed
+// PeerConnections so you can remove them
+func (s *TrackLocalStaticSample) WriteSample(sample media.Sample) error {
+ s.rtpTrack.mu.RLock()
+ p := s.packetizer
+ clockRate := s.clockRate
+ s.rtpTrack.mu.RUnlock()
+
+ if p == nil {
+ return nil
+ }
+
+ samples := sample.Duration.Seconds() * clockRate
+ packets := p.(rtp.Packetizer).Packetize(sample.Data, uint32(samples))
+
+ writeErrs := []error{}
+ for _, p := range packets {
+ if err := s.rtpTrack.WriteRTP(p); err != nil {
+ writeErrs = append(writeErrs, err)
+ }
+ }
+
+ return util.FlattenErrs(writeErrs)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/track_remote.go b/vendor/github.com/pion/webrtc/v3/track_remote.go
new file mode 100644
index 0000000..6733b7c
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/track_remote.go
@@ -0,0 +1,183 @@
+// +build !js
+
+package webrtc
+
+import (
+ "sync"
+ "time"
+
+ "github.com/pion/interceptor"
+ "github.com/pion/rtp"
+)
+
+// TrackRemote represents a single inbound source of media
+type TrackRemote struct {
+ mu sync.RWMutex
+
+ id string
+ streamID string
+
+ payloadType PayloadType
+ kind RTPCodecType
+ ssrc SSRC
+ codec RTPCodecParameters
+ params RTPParameters
+ rid string
+
+ receiver *RTPReceiver
+ peeked []byte
+ peekedAttributes interceptor.Attributes
+}
+
+func newTrackRemote(kind RTPCodecType, ssrc SSRC, rid string, receiver *RTPReceiver) *TrackRemote {
+ return &TrackRemote{
+ kind: kind,
+ ssrc: ssrc,
+ rid: rid,
+ receiver: receiver,
+ }
+}
+
+// ID is the unique identifier for this Track. This should be unique for the
+// stream, but doesn't have to globally unique. A common example would be 'audio' or 'video'
+// and StreamID would be 'desktop' or 'webcam'
+func (t *TrackRemote) ID() string {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.id
+}
+
+// RID gets the RTP Stream ID of this Track
+// With Simulcast you will have multiple tracks with the same ID, but different RID values.
+// In many cases a TrackRemote will not have an RID, so it is important to assert it is non-zero
+func (t *TrackRemote) RID() string {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+
+ return t.rid
+}
+
+// PayloadType gets the PayloadType of the track
+func (t *TrackRemote) PayloadType() PayloadType {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.payloadType
+}
+
+// Kind gets the Kind of the track
+func (t *TrackRemote) Kind() RTPCodecType {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.kind
+}
+
+// StreamID is the group this track belongs too. This must be unique
+func (t *TrackRemote) StreamID() string {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.streamID
+}
+
+// SSRC gets the SSRC of the track
+func (t *TrackRemote) SSRC() SSRC {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.ssrc
+}
+
+// Msid gets the Msid of the track
+func (t *TrackRemote) Msid() string {
+ return t.StreamID() + " " + t.ID()
+}
+
+// Codec gets the Codec of the track
+func (t *TrackRemote) Codec() RTPCodecParameters {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.codec
+}
+
+// Read reads data from the track.
+func (t *TrackRemote) Read(b []byte) (n int, attributes interceptor.Attributes, err error) {
+ t.mu.RLock()
+ r := t.receiver
+ peeked := t.peeked != nil
+ t.mu.RUnlock()
+
+ if peeked {
+ t.mu.Lock()
+ data := t.peeked
+ attributes = t.peekedAttributes
+
+ t.peeked = nil
+ t.peekedAttributes = nil
+ t.mu.Unlock()
+ // someone else may have stolen our packet when we
+ // released the lock. Deal with it.
+ if data != nil {
+ n = copy(b, data)
+ return
+ }
+ }
+
+ return r.readRTP(b, t)
+}
+
+// ReadRTP is a convenience method that wraps Read and unmarshals for you.
+func (t *TrackRemote) ReadRTP() (*rtp.Packet, interceptor.Attributes, error) {
+ b := make([]byte, receiveMTU)
+ i, attributes, err := t.Read(b)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ r := &rtp.Packet{}
+ if err := r.Unmarshal(b[:i]); err != nil {
+ return nil, nil, err
+ }
+ return r, attributes, nil
+}
+
+// determinePayloadType blocks and reads a single packet to determine the PayloadType for this Track
+// this is useful because we can't announce it to the user until we know the payloadType
+func (t *TrackRemote) determinePayloadType() error {
+ b := make([]byte, receiveMTU)
+ n, _, err := t.peek(b)
+ if err != nil {
+ return err
+ }
+ r := rtp.Packet{}
+ if err := r.Unmarshal(b[:n]); err != nil {
+ return err
+ }
+
+ t.mu.Lock()
+ t.payloadType = PayloadType(r.PayloadType)
+ defer t.mu.Unlock()
+
+ return nil
+}
+
+// peek is like Read, but it doesn't discard the packet read
+func (t *TrackRemote) peek(b []byte) (n int, a interceptor.Attributes, err error) {
+ n, a, err = t.Read(b)
+ if err != nil {
+ return
+ }
+
+ t.mu.Lock()
+ // this might overwrite data if somebody peeked between the Read
+ // and us getting the lock. Oh well, we'll just drop a packet in
+ // that case.
+ data := make([]byte, n)
+ n = copy(data, b[:n])
+ t.peeked = data
+ t.peekedAttributes = a
+ t.mu.Unlock()
+ return
+}
+
+// SetReadDeadline sets the max amount of time the RTP stream will block before returning. 0 is forever.
+func (t *TrackRemote) SetReadDeadline(deadline time.Time) error {
+ return t.receiver.setRTPReadDeadline(deadline, t)
+}
diff --git a/vendor/github.com/pion/webrtc/v3/webrtc.go b/vendor/github.com/pion/webrtc/v3/webrtc.go
new file mode 100644
index 0000000..ff32a55
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/webrtc.go
@@ -0,0 +1,17 @@
+// Package webrtc implements the WebRTC 1.0 as defined in W3C WebRTC specification document.
+package webrtc
+
+// SSRC represents a synchronization source
+// A synchronization source is a randomly chosen
+// value meant to be globally unique within a particular
+// RTP session. Used to identify a single stream of media.
+//
+// https://tools.ietf.org/html/rfc3550#section-3
+type SSRC uint32
+
+// PayloadType identifies the format of the RTP payload and determines
+// its interpretation by the application. Each codec in a RTP Session
+// will have a different PayloadType
+//
+// https://tools.ietf.org/html/rfc3550#section-3
+type PayloadType uint8
diff --git a/vendor/github.com/pion/webrtc/v3/yarn.lock b/vendor/github.com/pion/webrtc/v3/yarn.lock
new file mode 100644
index 0000000..90f73c2
--- /dev/null
+++ b/vendor/github.com/pion/webrtc/v3/yarn.lock
@@ -0,0 +1,795 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+abbrev@1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+ integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
+ajv@^6.5.5:
+ version "6.12.2"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
+ integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-regex@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+ integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+ integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+aproba@^1.0.3:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+ integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
+are-we-there-yet@~1.1.2:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
+ integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
+ dependencies:
+ delegates "^1.0.0"
+ readable-stream "^2.0.6"
+
+asn1@~0.2.3:
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+ integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+ dependencies:
+ safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+ integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+aws-sign2@~0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+ integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+
+aws4@^1.8.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
+ integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+ integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
+ dependencies:
+ tweetnacl "^0.14.3"
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+ integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+
+chownr@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
+ integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==
+
+code-point-at@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+ integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+ integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
+core-util-is@1.0.2, core-util-is@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+ integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
+ dependencies:
+ assert-plus "^1.0.0"
+
+debug@^2.1.2:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+deep-extend@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
+ integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
+delegates@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+ integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+
+detect-libc@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+ integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+
+domexception@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
+ integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
+ dependencies:
+ webidl-conversions "^4.0.2"
+
+ecc-jsbn@~0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+ integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
+ dependencies:
+ jsbn "~0.1.0"
+ safer-buffer "^2.1.0"
+
+extend@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extsprintf@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+ integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
+
+extsprintf@^1.2.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+ integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
+
+fast-deep-equal@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
+ integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+ integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+
+form-data@~2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+ integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.6"
+ mime-types "^2.1.12"
+
+fs-minipass@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
+ integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==
+ dependencies:
+ minipass "^2.2.1"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+gauge@~2.7.3:
+ version "2.7.4"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+ integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+ dependencies:
+ aproba "^1.0.3"
+ console-control-strings "^1.0.0"
+ has-unicode "^2.0.0"
+ object-assign "^4.1.0"
+ signal-exit "^3.0.0"
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wide-align "^1.1.0"
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
+ dependencies:
+ assert-plus "^1.0.0"
+
+glob@^7.1.3:
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+ integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+har-schema@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+ integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+
+har-validator@~5.1.3:
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
+ integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+ dependencies:
+ ajv "^6.5.5"
+ har-schema "^2.0.0"
+
+has-unicode@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+ integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
+http-signature@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+ integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+ dependencies:
+ assert-plus "^1.0.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+iconv-lite@^0.4.4:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+ignore-walk@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8"
+ integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==
+ dependencies:
+ minimatch "^3.0.4"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@~2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+ integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+ini@~1.3.0:
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
+ integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
+
+is-fullwidth-code-point@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+ integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+ dependencies:
+ number-is-nan "^1.0.0"
+
+is-fullwidth-code-point@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+ integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+ integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+ integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+ integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+ integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+
+json-stringify-safe@~5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+jsprim@^1.2.2:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+ integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.3.0"
+ json-schema "0.2.3"
+ verror "1.10.0"
+
+mime-db@1.44.0:
+ version "1.44.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
+ integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+ version "2.1.27"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
+ integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
+ dependencies:
+ mime-db "1.44.0"
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+ integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+ integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+minipass@^2.2.1, minipass@^2.3.4:
+ version "2.3.5"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848"
+ integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==
+ dependencies:
+ safe-buffer "^5.1.2"
+ yallist "^3.0.0"
+
+minizlib@^1.1.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614"
+ integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==
+ dependencies:
+ minipass "^2.2.1"
+
+mkdirp@^0.5.0, mkdirp@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+ integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+ dependencies:
+ minimist "0.0.8"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+needle@^2.2.1:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e"
+ integrity sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==
+ dependencies:
+ debug "^2.1.2"
+ iconv-lite "^0.4.4"
+ sax "^1.2.4"
+
+node-pre-gyp@^0.13.0:
+ version "0.13.0"
+ resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.13.0.tgz#df9ab7b68dd6498137717838e4f92a33fc9daa42"
+ integrity sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==
+ dependencies:
+ detect-libc "^1.0.2"
+ mkdirp "^0.5.1"
+ needle "^2.2.1"
+ nopt "^4.0.1"
+ npm-packlist "^1.1.6"
+ npmlog "^4.0.2"
+ rc "^1.2.7"
+ rimraf "^2.6.1"
+ semver "^5.3.0"
+ tar "^4"
+
+nopt@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
+ integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
+ dependencies:
+ abbrev "1"
+ osenv "^0.1.4"
+
+npm-bundled@^1.0.1:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd"
+ integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==
+
+npm-packlist@^1.1.6:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc"
+ integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==
+ dependencies:
+ ignore-walk "^3.0.1"
+ npm-bundled "^1.0.1"
+
+npmlog@^4.0.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+ integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+ dependencies:
+ are-we-there-yet "~1.1.2"
+ console-control-strings "~1.1.0"
+ gauge "~2.7.3"
+ set-blocking "~2.0.0"
+
+number-is-nan@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+ integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
+oauth-sign@~0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+ integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+object-assign@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+ integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+os-tmpdir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+osenv@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
+ integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.0"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+performance-now@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+ integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+
+process-nextick-args@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
+ integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==
+
+psl@^1.1.28:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
+ integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
+
+punycode@^2.1.0, punycode@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@~6.5.2:
+ version "6.5.2"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+ integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+rc@^1.2.7:
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
+ integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
+ dependencies:
+ deep-extend "^0.6.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+readable-stream@^2.0.6:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+ integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~2.0.0"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.1.1"
+ util-deprecate "~1.0.1"
+
+request@2.88.2:
+ version "2.88.2"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
+ integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.8.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.6"
+ extend "~3.0.2"
+ forever-agent "~0.6.1"
+ form-data "~2.3.2"
+ har-validator "~5.1.3"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.19"
+ oauth-sign "~0.9.0"
+ performance-now "^2.1.0"
+ qs "~6.5.2"
+ safe-buffer "^5.1.2"
+ tough-cookie "~2.5.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.3.2"
+
+rimraf@^2.6.1:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+ dependencies:
+ glob "^7.1.3"
+
+safe-buffer@^5.0.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sax@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+semver@^5.3.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
+ integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
+
+set-blocking@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+signal-exit@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+ integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+sshpk@^1.7.0:
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
+ integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ bcrypt-pbkdf "^1.0.0"
+ dashdash "^1.12.0"
+ ecc-jsbn "~0.1.1"
+ getpass "^0.1.1"
+ jsbn "~0.1.0"
+ safer-buffer "^2.0.2"
+ tweetnacl "~0.14.0"
+
+string-width@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+ integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+ dependencies:
+ code-point-at "^1.0.0"
+ is-fullwidth-code-point "^1.0.0"
+ strip-ansi "^3.0.0"
+
+"string-width@^1.0.2 || 2":
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
+ integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+ dependencies:
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^4.0.0"
+
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+ integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+ dependencies:
+ ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+ dependencies:
+ ansi-regex "^3.0.0"
+
+strip-json-comments@~2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+ integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+
+tar@^4:
+ version "4.4.8"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d"
+ integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==
+ dependencies:
+ chownr "^1.1.1"
+ fs-minipass "^1.2.5"
+ minipass "^2.3.4"
+ minizlib "^1.1.1"
+ mkdirp "^0.5.0"
+ safe-buffer "^5.1.2"
+ yallist "^3.0.2"
+
+tough-cookie@~2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+ integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+ dependencies:
+ psl "^1.1.28"
+ punycode "^2.1.1"
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+ integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+
+uri-js@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+ integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+ dependencies:
+ punycode "^2.1.0"
+
+util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+uuid@^3.3.2:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+ integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
+verror@1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+ integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+ dependencies:
+ assert-plus "^1.0.0"
+ core-util-is "1.0.2"
+ extsprintf "^1.2.0"
+
+webidl-conversions@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+ integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
+
+wide-align@^1.1.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
+ integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+ dependencies:
+ string-width "^1.0.2 || 2"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+wrtc@0.4.7:
+ version "0.4.7"
+ resolved "https://registry.yarnpkg.com/wrtc/-/wrtc-0.4.7.tgz#c61530cd662713e50bffe64b7a78673ce070426c"
+ integrity sha512-P6Hn7VT4lfSH49HxLHcHhDq+aFf/jd9dPY7lDHeFhZ22N3858EKuwm2jmnlPzpsRGEPaoF6XwkcxY5SYnt4f/g==
+ dependencies:
+ node-pre-gyp "^0.13.0"
+ optionalDependencies:
+ domexception "^1.0.1"
+
+yallist@^3.0.0, yallist@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9"
+ integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==
diff --git a/vendor/github.com/pkg/errors/.gitignore b/vendor/github.com/pkg/errors/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/pkg/errors/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/pkg/errors/.travis.yml b/vendor/github.com/pkg/errors/.travis.yml
new file mode 100644
index 0000000..9159de0
--- /dev/null
+++ b/vendor/github.com/pkg/errors/.travis.yml
@@ -0,0 +1,10 @@
+language: go
+go_import_path: github.com/pkg/errors
+go:
+ - 1.11.x
+ - 1.12.x
+ - 1.13.x
+ - tip
+
+script:
+ - make check
diff --git a/vendor/github.com/pkg/errors/LICENSE b/vendor/github.com/pkg/errors/LICENSE
new file mode 100644
index 0000000..835ba3e
--- /dev/null
+++ b/vendor/github.com/pkg/errors/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2015, Dave Cheney <dave@cheney.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* 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.
diff --git a/vendor/github.com/pkg/errors/Makefile b/vendor/github.com/pkg/errors/Makefile
new file mode 100644
index 0000000..ce9d7cd
--- /dev/null
+++ b/vendor/github.com/pkg/errors/Makefile
@@ -0,0 +1,44 @@
+PKGS := github.com/pkg/errors
+SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS))
+GO := go
+
+check: test vet gofmt misspell unconvert staticcheck ineffassign unparam
+
+test:
+ $(GO) test $(PKGS)
+
+vet: | test
+ $(GO) vet $(PKGS)
+
+staticcheck:
+ $(GO) get honnef.co/go/tools/cmd/staticcheck
+ staticcheck -checks all $(PKGS)
+
+misspell:
+ $(GO) get github.com/client9/misspell/cmd/misspell
+ misspell \
+ -locale GB \
+ -error \
+ *.md *.go
+
+unconvert:
+ $(GO) get github.com/mdempsky/unconvert
+ unconvert -v $(PKGS)
+
+ineffassign:
+ $(GO) get github.com/gordonklaus/ineffassign
+ find $(SRCDIRS) -name '*.go' | xargs ineffassign
+
+pedantic: check errcheck
+
+unparam:
+ $(GO) get mvdan.cc/unparam
+ unparam ./...
+
+errcheck:
+ $(GO) get github.com/kisielk/errcheck
+ errcheck $(PKGS)
+
+gofmt:
+ @echo Checking code is gofmted
+ @test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)"
diff --git a/vendor/github.com/pkg/errors/README.md b/vendor/github.com/pkg/errors/README.md
new file mode 100644
index 0000000..54dfdcb
--- /dev/null
+++ b/vendor/github.com/pkg/errors/README.md
@@ -0,0 +1,59 @@
+# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge)
+
+Package errors provides simple error handling primitives.
+
+`go get github.com/pkg/errors`
+
+The traditional error handling idiom in Go is roughly akin to
+```go
+if err != nil {
+ return err
+}
+```
+which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
+
+## Adding context to an error
+
+The errors.Wrap function returns a new error that adds context to the original error. For example
+```go
+_, err := ioutil.ReadAll(r)
+if err != nil {
+ return errors.Wrap(err, "read failed")
+}
+```
+## Retrieving the cause of an error
+
+Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
+```go
+type causer interface {
+ Cause() error
+}
+```
+`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
+```go
+switch err := errors.Cause(err).(type) {
+case *MyError:
+ // handle specifically
+default:
+ // unknown error
+}
+```
+
+[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
+
+## Roadmap
+
+With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows:
+
+- 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible)
+- 1.0. Final release.
+
+## Contributing
+
+Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports.
+
+Before sending a PR, please discuss your change by raising an issue.
+
+## License
+
+BSD-2-Clause
diff --git a/vendor/github.com/pkg/errors/appveyor.yml b/vendor/github.com/pkg/errors/appveyor.yml
new file mode 100644
index 0000000..a932ead
--- /dev/null
+++ b/vendor/github.com/pkg/errors/appveyor.yml
@@ -0,0 +1,32 @@
+version: build-{build}.{branch}
+
+clone_folder: C:\gopath\src\github.com\pkg\errors
+shallow_clone: true # for startup speed
+
+environment:
+ GOPATH: C:\gopath
+
+platform:
+ - x64
+
+# http://www.appveyor.com/docs/installed-software
+install:
+ # some helpful output for debugging builds
+ - go version
+ - go env
+ # pre-installed MinGW at C:\MinGW is 32bit only
+ # but MSYS2 at C:\msys64 has mingw64
+ - set PATH=C:\msys64\mingw64\bin;%PATH%
+ - gcc --version
+ - g++ --version
+
+build_script:
+ - go install -v ./...
+
+test_script:
+ - set PATH=C:\gopath\bin;%PATH%
+ - go test -v ./...
+
+#artifacts:
+# - path: '%GOPATH%\bin\*.exe'
+deploy: off
diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go
new file mode 100644
index 0000000..161aea2
--- /dev/null
+++ b/vendor/github.com/pkg/errors/errors.go
@@ -0,0 +1,288 @@
+// Package errors provides simple error handling primitives.
+//
+// The traditional error handling idiom in Go is roughly akin to
+//
+// if err != nil {
+// return err
+// }
+//
+// which when applied recursively up the call stack results in error reports
+// without context or debugging information. The errors package allows
+// programmers to add context to the failure path in their code in a way
+// that does not destroy the original value of the error.
+//
+// Adding context to an error
+//
+// The errors.Wrap function returns a new error that adds context to the
+// original error by recording a stack trace at the point Wrap is called,
+// together with the supplied message. For example
+//
+// _, err := ioutil.ReadAll(r)
+// if err != nil {
+// return errors.Wrap(err, "read failed")
+// }
+//
+// If additional control is required, the errors.WithStack and
+// errors.WithMessage functions destructure errors.Wrap into its component
+// operations: annotating an error with a stack trace and with a message,
+// respectively.
+//
+// Retrieving the cause of an error
+//
+// Using errors.Wrap constructs a stack of errors, adding context to the
+// preceding error. Depending on the nature of the error it may be necessary
+// to reverse the operation of errors.Wrap to retrieve the original error
+// for inspection. Any error value which implements this interface
+//
+// type causer interface {
+// Cause() error
+// }
+//
+// can be inspected by errors.Cause. errors.Cause will recursively retrieve
+// the topmost error that does not implement causer, which is assumed to be
+// the original cause. For example:
+//
+// switch err := errors.Cause(err).(type) {
+// case *MyError:
+// // handle specifically
+// default:
+// // unknown error
+// }
+//
+// Although the causer interface is not exported by this package, it is
+// considered a part of its stable public interface.
+//
+// Formatted printing of errors
+//
+// All error values returned from this package implement fmt.Formatter and can
+// be formatted by the fmt package. The following verbs are supported:
+//
+// %s print the error. If the error has a Cause it will be
+// printed recursively.
+// %v see %s
+// %+v extended format. Each Frame of the error's StackTrace will
+// be printed in detail.
+//
+// Retrieving the stack trace of an error or wrapper
+//
+// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
+// invoked. This information can be retrieved with the following interface:
+//
+// type stackTracer interface {
+// StackTrace() errors.StackTrace
+// }
+//
+// The returned errors.StackTrace type is defined as
+//
+// type StackTrace []Frame
+//
+// The Frame type represents a call site in the stack trace. Frame supports
+// the fmt.Formatter interface that can be used for printing information about
+// the stack trace of this error. For example:
+//
+// if err, ok := err.(stackTracer); ok {
+// for _, f := range err.StackTrace() {
+// fmt.Printf("%+s:%d\n", f, f)
+// }
+// }
+//
+// Although the stackTracer interface is not exported by this package, it is
+// considered a part of its stable public interface.
+//
+// See the documentation for Frame.Format for more details.
+package errors
+
+import (
+ "fmt"
+ "io"
+)
+
+// New returns an error with the supplied message.
+// New also records the stack trace at the point it was called.
+func New(message string) error {
+ return &fundamental{
+ msg: message,
+ stack: callers(),
+ }
+}
+
+// Errorf formats according to a format specifier and returns the string
+// as a value that satisfies error.
+// Errorf also records the stack trace at the point it was called.
+func Errorf(format string, args ...interface{}) error {
+ return &fundamental{
+ msg: fmt.Sprintf(format, args...),
+ stack: callers(),
+ }
+}
+
+// fundamental is an error that has a message and a stack, but no caller.
+type fundamental struct {
+ msg string
+ *stack
+}
+
+func (f *fundamental) Error() string { return f.msg }
+
+func (f *fundamental) Format(s fmt.State, verb rune) {
+ switch verb {
+ case 'v':
+ if s.Flag('+') {
+ io.WriteString(s, f.msg)
+ f.stack.Format(s, verb)
+ return
+ }
+ fallthrough
+ case 's':
+ io.WriteString(s, f.msg)
+ case 'q':
+ fmt.Fprintf(s, "%q", f.msg)
+ }
+}
+
+// WithStack annotates err with a stack trace at the point WithStack was called.
+// If err is nil, WithStack returns nil.
+func WithStack(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &withStack{
+ err,
+ callers(),
+ }
+}
+
+type withStack struct {
+ error
+ *stack
+}
+
+func (w *withStack) Cause() error { return w.error }
+
+// Unwrap provides compatibility for Go 1.13 error chains.
+func (w *withStack) Unwrap() error { return w.error }
+
+func (w *withStack) Format(s fmt.State, verb rune) {
+ switch verb {
+ case 'v':
+ if s.Flag('+') {
+ fmt.Fprintf(s, "%+v", w.Cause())
+ w.stack.Format(s, verb)
+ return
+ }
+ fallthrough
+ case 's':
+ io.WriteString(s, w.Error())
+ case 'q':
+ fmt.Fprintf(s, "%q", w.Error())
+ }
+}
+
+// Wrap returns an error annotating err with a stack trace
+// at the point Wrap is called, and the supplied message.
+// If err is nil, Wrap returns nil.
+func Wrap(err error, message string) error {
+ if err == nil {
+ return nil
+ }
+ err = &withMessage{
+ cause: err,
+ msg: message,
+ }
+ return &withStack{
+ err,
+ callers(),
+ }
+}
+
+// Wrapf returns an error annotating err with a stack trace
+// at the point Wrapf is called, and the format specifier.
+// If err is nil, Wrapf returns nil.
+func Wrapf(err error, format string, args ...interface{}) error {
+ if err == nil {
+ return nil
+ }
+ err = &withMessage{
+ cause: err,
+ msg: fmt.Sprintf(format, args...),
+ }
+ return &withStack{
+ err,
+ callers(),
+ }
+}
+
+// WithMessage annotates err with a new message.
+// If err is nil, WithMessage returns nil.
+func WithMessage(err error, message string) error {
+ if err == nil {
+ return nil
+ }
+ return &withMessage{
+ cause: err,
+ msg: message,
+ }
+}
+
+// WithMessagef annotates err with the format specifier.
+// If err is nil, WithMessagef returns nil.
+func WithMessagef(err error, format string, args ...interface{}) error {
+ if err == nil {
+ return nil
+ }
+ return &withMessage{
+ cause: err,
+ msg: fmt.Sprintf(format, args...),
+ }
+}
+
+type withMessage struct {
+ cause error
+ msg string
+}
+
+func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
+func (w *withMessage) Cause() error { return w.cause }
+
+// Unwrap provides compatibility for Go 1.13 error chains.
+func (w *withMessage) Unwrap() error { return w.cause }
+
+func (w *withMessage) Format(s fmt.State, verb rune) {
+ switch verb {
+ case 'v':
+ if s.Flag('+') {
+ fmt.Fprintf(s, "%+v\n", w.Cause())
+ io.WriteString(s, w.msg)
+ return
+ }
+ fallthrough
+ case 's', 'q':
+ io.WriteString(s, w.Error())
+ }
+}
+
+// Cause returns the underlying cause of the error, if possible.
+// An error value has a cause if it implements the following
+// interface:
+//
+// type causer interface {
+// Cause() error
+// }
+//
+// If the error does not implement Cause, the original error will
+// be returned. If the error is nil, nil will be returned without further
+// investigation.
+func Cause(err error) error {
+ type causer interface {
+ Cause() error
+ }
+
+ for err != nil {
+ cause, ok := err.(causer)
+ if !ok {
+ break
+ }
+ err = cause.Cause()
+ }
+ return err
+}
diff --git a/vendor/github.com/pkg/errors/go113.go b/vendor/github.com/pkg/errors/go113.go
new file mode 100644
index 0000000..be0d10d
--- /dev/null
+++ b/vendor/github.com/pkg/errors/go113.go
@@ -0,0 +1,38 @@
+// +build go1.13
+
+package errors
+
+import (
+ stderrors "errors"
+)
+
+// Is reports whether any error in err's chain matches target.
+//
+// The chain consists of err itself followed by the sequence of errors obtained by
+// repeatedly calling Unwrap.
+//
+// An error is considered to match a target if it is equal to that target or if
+// it implements a method Is(error) bool such that Is(target) returns true.
+func Is(err, target error) bool { return stderrors.Is(err, target) }
+
+// As finds the first error in err's chain that matches target, and if so, sets
+// target to that error value and returns true.
+//
+// The chain consists of err itself followed by the sequence of errors obtained by
+// repeatedly calling Unwrap.
+//
+// An error matches target if the error's concrete value is assignable to the value
+// pointed to by target, or if the error has a method As(interface{}) bool such that
+// As(target) returns true. In the latter case, the As method is responsible for
+// setting target.
+//
+// As will panic if target is not a non-nil pointer to either a type that implements
+// error, or to any interface type. As returns false if err is nil.
+func As(err error, target interface{}) bool { return stderrors.As(err, target) }
+
+// Unwrap returns the result of calling the Unwrap method on err, if err's
+// type contains an Unwrap method returning error.
+// Otherwise, Unwrap returns nil.
+func Unwrap(err error) error {
+ return stderrors.Unwrap(err)
+}
diff --git a/vendor/github.com/pkg/errors/stack.go b/vendor/github.com/pkg/errors/stack.go
new file mode 100644
index 0000000..779a834
--- /dev/null
+++ b/vendor/github.com/pkg/errors/stack.go
@@ -0,0 +1,177 @@
+package errors
+
+import (
+ "fmt"
+ "io"
+ "path"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+// Frame represents a program counter inside a stack frame.
+// For historical reasons if Frame is interpreted as a uintptr
+// its value represents the program counter + 1.
+type Frame uintptr
+
+// pc returns the program counter for this frame;
+// multiple frames may have the same PC value.
+func (f Frame) pc() uintptr { return uintptr(f) - 1 }
+
+// file returns the full path to the file that contains the
+// function for this Frame's pc.
+func (f Frame) file() string {
+ fn := runtime.FuncForPC(f.pc())
+ if fn == nil {
+ return "unknown"
+ }
+ file, _ := fn.FileLine(f.pc())
+ return file
+}
+
+// line returns the line number of source code of the
+// function for this Frame's pc.
+func (f Frame) line() int {
+ fn := runtime.FuncForPC(f.pc())
+ if fn == nil {
+ return 0
+ }
+ _, line := fn.FileLine(f.pc())
+ return line
+}
+
+// name returns the name of this function, if known.
+func (f Frame) name() string {
+ fn := runtime.FuncForPC(f.pc())
+ if fn == nil {
+ return "unknown"
+ }
+ return fn.Name()
+}
+
+// Format formats the frame according to the fmt.Formatter interface.
+//
+// %s source file
+// %d source line
+// %n function name
+// %v equivalent to %s:%d
+//
+// Format accepts flags that alter the printing of some verbs, as follows:
+//
+// %+s function name and path of source file relative to the compile time
+// GOPATH separated by \n\t (<funcname>\n\t<path>)
+// %+v equivalent to %+s:%d
+func (f Frame) Format(s fmt.State, verb rune) {
+ switch verb {
+ case 's':
+ switch {
+ case s.Flag('+'):
+ io.WriteString(s, f.name())
+ io.WriteString(s, "\n\t")
+ io.WriteString(s, f.file())
+ default:
+ io.WriteString(s, path.Base(f.file()))
+ }
+ case 'd':
+ io.WriteString(s, strconv.Itoa(f.line()))
+ case 'n':
+ io.WriteString(s, funcname(f.name()))
+ case 'v':
+ f.Format(s, 's')
+ io.WriteString(s, ":")
+ f.Format(s, 'd')
+ }
+}
+
+// MarshalText formats a stacktrace Frame as a text string. The output is the
+// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
+func (f Frame) MarshalText() ([]byte, error) {
+ name := f.name()
+ if name == "unknown" {
+ return []byte(name), nil
+ }
+ return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
+}
+
+// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
+type StackTrace []Frame
+
+// Format formats the stack of Frames according to the fmt.Formatter interface.
+//
+// %s lists source files for each Frame in the stack
+// %v lists the source file and line number for each Frame in the stack
+//
+// Format accepts flags that alter the printing of some verbs, as follows:
+//
+// %+v Prints filename, function, and line number for each Frame in the stack.
+func (st StackTrace) Format(s fmt.State, verb rune) {
+ switch verb {
+ case 'v':
+ switch {
+ case s.Flag('+'):
+ for _, f := range st {
+ io.WriteString(s, "\n")
+ f.Format(s, verb)
+ }
+ case s.Flag('#'):
+ fmt.Fprintf(s, "%#v", []Frame(st))
+ default:
+ st.formatSlice(s, verb)
+ }
+ case 's':
+ st.formatSlice(s, verb)
+ }
+}
+
+// formatSlice will format this StackTrace into the given buffer as a slice of
+// Frame, only valid when called with '%s' or '%v'.
+func (st StackTrace) formatSlice(s fmt.State, verb rune) {
+ io.WriteString(s, "[")
+ for i, f := range st {
+ if i > 0 {
+ io.WriteString(s, " ")
+ }
+ f.Format(s, verb)
+ }
+ io.WriteString(s, "]")
+}
+
+// stack represents a stack of program counters.
+type stack []uintptr
+
+func (s *stack) Format(st fmt.State, verb rune) {
+ switch verb {
+ case 'v':
+ switch {
+ case st.Flag('+'):
+ for _, pc := range *s {
+ f := Frame(pc)
+ fmt.Fprintf(st, "\n%+v", f)
+ }
+ }
+ }
+}
+
+func (s *stack) StackTrace() StackTrace {
+ f := make([]Frame, len(*s))
+ for i := 0; i < len(f); i++ {
+ f[i] = Frame((*s)[i])
+ }
+ return f
+}
+
+func callers() *stack {
+ const depth = 32
+ var pcs [depth]uintptr
+ n := runtime.Callers(3, pcs[:])
+ var st stack = pcs[0:n]
+ return &st
+}
+
+// funcname removes the path prefix component of a function's name reported by func.Name().
+func funcname(name string) string {
+ i := strings.LastIndex(name, "/")
+ name = name[i+1:]
+ i = strings.Index(name, ".")
+ return name[i+1:]
+}
diff --git a/vendor/github.com/smartystreets/assertions/.gitignore b/vendor/github.com/smartystreets/assertions/.gitignore
new file mode 100644
index 0000000..07d3c71
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+Thumbs.db
+*.iml
+/.idea
+coverage.out
diff --git a/vendor/github.com/smartystreets/assertions/.travis.yml b/vendor/github.com/smartystreets/assertions/.travis.yml
new file mode 100644
index 0000000..72df752
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/.travis.yml
@@ -0,0 +1,11 @@
+language: go
+
+go:
+ - 1.x
+
+install:
+ - go get -t ./...
+
+script: go test ./... -v
+
+sudo: false
diff --git a/vendor/github.com/smartystreets/assertions/CONTRIBUTING.md b/vendor/github.com/smartystreets/assertions/CONTRIBUTING.md
new file mode 100644
index 0000000..1820ecb
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/CONTRIBUTING.md
@@ -0,0 +1,12 @@
+# Contributing
+
+In general, the code posted to the [SmartyStreets github organization](https://github.com/smartystreets) is created to solve specific problems at SmartyStreets that are ancillary to our core products in the address verification industry and may or may not be useful to other organizations or developers. Our reason for posting said code isn't necessarily to solicit feedback or contributions from the community but more as a showcase of some of the approaches to solving problems we have adopted.
+
+Having stated that, we do consider issues raised by other githubbers as well as contributions submitted via pull requests. When submitting such a pull request, please follow these guidelines:
+
+- _Look before you leap:_ If the changes you plan to make are significant, it's in everyone's best interest for you to discuss them with a SmartyStreets team member prior to opening a pull request.
+- _License and ownership:_ If modifying the `LICENSE.md` file, limit your changes to fixing typographical mistakes. Do NOT modify the actual terms in the license or the copyright by **SmartyStreets, LLC**. Code submitted to SmartyStreets projects becomes property of SmartyStreets and must be compatible with the associated license.
+- _Testing:_ If the code you are submitting resides in packages/modules covered by automated tests, be sure to add passing tests that cover your changes and assert expected behavior and state. Submit the additional test cases as part of your change set.
+- _Style:_ Match your approach to **naming** and **formatting** with the surrounding code. Basically, the code you submit shouldn't stand out.
+ - "Naming" refers to such constructs as variables, methods, functions, classes, structs, interfaces, packages, modules, directories, files, etc...
+ - "Formatting" refers to such constructs as whitespace, horizontal line length, vertical function length, vertical file length, indentation, curly braces, etc...
diff --git a/vendor/github.com/smartystreets/assertions/LICENSE.md b/vendor/github.com/smartystreets/assertions/LICENSE.md
new file mode 100644
index 0000000..8ea6f94
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/LICENSE.md
@@ -0,0 +1,23 @@
+Copyright (c) 2016 SmartyStreets, LLC
+
+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.
+
+NOTE: Various optional and subordinate components carry their own licensing
+requirements and restrictions. Use of those components is subject to the terms
+and conditions outlined the respective license of each component.
diff --git a/vendor/github.com/smartystreets/assertions/README.md b/vendor/github.com/smartystreets/assertions/README.md
new file mode 100644
index 0000000..58e51e9
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/README.md
@@ -0,0 +1,611 @@
+# assertions
+--
+ import "github.com/smartystreets/assertions"
+
+Package assertions contains the implementations for all assertions which are
+referenced in goconvey's `convey` package
+(github.com/smartystreets/goconvey/convey) and gunit
+(github.com/smartystreets/gunit) for use with the So(...) method. They can also
+be used in traditional Go test functions and even in applications.
+
+https://smartystreets.com
+
+Many of the assertions lean heavily on work done by Aaron Jacobs in his
+excellent oglematchers library. (https://github.com/jacobsa/oglematchers) The
+ShouldResemble assertion leans heavily on work done by Daniel Jacques in his
+very helpful go-render library. (https://github.com/luci/go-render)
+
+## Usage
+
+#### func GoConveyMode
+
+```go
+func GoConveyMode(yes bool)
+```
+GoConveyMode provides control over JSON serialization of failures. When using
+the assertions in this package from the convey package JSON results are very
+helpful and can be rendered in a DIFF view. In that case, this function will be
+called with a true value to enable the JSON serialization. By default, the
+assertions in this package will not serializer a JSON result, making standalone
+usage more convenient.
+
+#### func ShouldAlmostEqual
+
+```go
+func ShouldAlmostEqual(actual interface{}, expected ...interface{}) string
+```
+ShouldAlmostEqual makes sure that two parameters are close enough to being
+equal. The acceptable delta may be specified with a third argument, or a very
+small default delta will be used.
+
+#### func ShouldBeBetween
+
+```go
+func ShouldBeBetween(actual interface{}, expected ...interface{}) string
+```
+ShouldBeBetween receives exactly three parameters: an actual value, a lower
+bound, and an upper bound. It ensures that the actual value is between both
+bounds (but not equal to either of them).
+
+#### func ShouldBeBetweenOrEqual
+
+```go
+func ShouldBeBetweenOrEqual(actual interface{}, expected ...interface{}) string
+```
+ShouldBeBetweenOrEqual receives exactly three parameters: an actual value, a
+lower bound, and an upper bound. It ensures that the actual value is between
+both bounds or equal to one of them.
+
+#### func ShouldBeBlank
+
+```go
+func ShouldBeBlank(actual interface{}, expected ...interface{}) string
+```
+ShouldBeBlank receives exactly 1 string parameter and ensures that it is equal
+to "".
+
+#### func ShouldBeChronological
+
+```go
+func ShouldBeChronological(actual interface{}, expected ...interface{}) string
+```
+ShouldBeChronological receives a []time.Time slice and asserts that the are in
+chronological order starting with the first time.Time as the earliest.
+
+#### func ShouldBeEmpty
+
+```go
+func ShouldBeEmpty(actual interface{}, expected ...interface{}) string
+```
+ShouldBeEmpty receives a single parameter (actual) and determines whether or not
+calling len(actual) would return `0`. It obeys the rules specified by the len
+function for determining length: http://golang.org/pkg/builtin/#len
+
+#### func ShouldBeError
+
+```go
+func ShouldBeError(actual interface{}, expected ...interface{}) string
+```
+ShouldBeError asserts that the first argument implements the error interface. It
+also compares the first argument against the second argument if provided (which
+must be an error message string or another error value).
+
+#### func ShouldBeFalse
+
+```go
+func ShouldBeFalse(actual interface{}, expected ...interface{}) string
+```
+ShouldBeFalse receives a single parameter and ensures that it is false.
+
+#### func ShouldBeGreaterThan
+
+```go
+func ShouldBeGreaterThan(actual interface{}, expected ...interface{}) string
+```
+ShouldBeGreaterThan receives exactly two parameters and ensures that the first
+is greater than the second.
+
+#### func ShouldBeGreaterThanOrEqualTo
+
+```go
+func ShouldBeGreaterThanOrEqualTo(actual interface{}, expected ...interface{}) string
+```
+ShouldBeGreaterThanOrEqualTo receives exactly two parameters and ensures that
+the first is greater than or equal to the second.
+
+#### func ShouldBeIn
+
+```go
+func ShouldBeIn(actual interface{}, expected ...interface{}) string
+```
+ShouldBeIn receives at least 2 parameters. The first is a proposed member of the
+collection that is passed in either as the second parameter, or of the
+collection that is comprised of all the remaining parameters. This assertion
+ensures that the proposed member is in the collection (using ShouldEqual).
+
+#### func ShouldBeLessThan
+
+```go
+func ShouldBeLessThan(actual interface{}, expected ...interface{}) string
+```
+ShouldBeLessThan receives exactly two parameters and ensures that the first is
+less than the second.
+
+#### func ShouldBeLessThanOrEqualTo
+
+```go
+func ShouldBeLessThanOrEqualTo(actual interface{}, expected ...interface{}) string
+```
+ShouldBeLessThan receives exactly two parameters and ensures that the first is
+less than or equal to the second.
+
+#### func ShouldBeNil
+
+```go
+func ShouldBeNil(actual interface{}, expected ...interface{}) string
+```
+ShouldBeNil receives a single parameter and ensures that it is nil.
+
+#### func ShouldBeTrue
+
+```go
+func ShouldBeTrue(actual interface{}, expected ...interface{}) string
+```
+ShouldBeTrue receives a single parameter and ensures that it is true.
+
+#### func ShouldBeZeroValue
+
+```go
+func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string
+```
+ShouldBeZeroValue receives a single parameter and ensures that it is the Go
+equivalent of the default value, or "zero" value.
+
+#### func ShouldContain
+
+```go
+func ShouldContain(actual interface{}, expected ...interface{}) string
+```
+ShouldContain receives exactly two parameters. The first is a slice and the
+second is a proposed member. Membership is determined using ShouldEqual.
+
+#### func ShouldContainKey
+
+```go
+func ShouldContainKey(actual interface{}, expected ...interface{}) string
+```
+ShouldContainKey receives exactly two parameters. The first is a map and the
+second is a proposed key. Keys are compared with a simple '=='.
+
+#### func ShouldContainSubstring
+
+```go
+func ShouldContainSubstring(actual interface{}, expected ...interface{}) string
+```
+ShouldContainSubstring receives exactly 2 string parameters and ensures that the
+first contains the second as a substring.
+
+#### func ShouldEndWith
+
+```go
+func ShouldEndWith(actual interface{}, expected ...interface{}) string
+```
+ShouldEndWith receives exactly 2 string parameters and ensures that the first
+ends with the second.
+
+#### func ShouldEqual
+
+```go
+func ShouldEqual(actual interface{}, expected ...interface{}) string
+```
+ShouldEqual receives exactly two parameters and does an equality check using the
+following semantics: 1. If the expected and actual values implement an Equal
+method in the form `func (this T) Equal(that T) bool` then call the method. If
+true, they are equal. 2. The expected and actual values are judged equal or not
+by oglematchers.Equals.
+
+#### func ShouldEqualJSON
+
+```go
+func ShouldEqualJSON(actual interface{}, expected ...interface{}) string
+```
+ShouldEqualJSON receives exactly two parameters and does an equality check by
+marshalling to JSON
+
+#### func ShouldEqualTrimSpace
+
+```go
+func ShouldEqualTrimSpace(actual interface{}, expected ...interface{}) string
+```
+ShouldEqualTrimSpace receives exactly 2 string parameters and ensures that the
+first is equal to the second after removing all leading and trailing whitespace
+using strings.TrimSpace(first).
+
+#### func ShouldEqualWithout
+
+```go
+func ShouldEqualWithout(actual interface{}, expected ...interface{}) string
+```
+ShouldEqualWithout receives exactly 3 string parameters and ensures that the
+first is equal to the second after removing all instances of the third from the
+first using strings.Replace(first, third, "", -1).
+
+#### func ShouldHappenAfter
+
+```go
+func ShouldHappenAfter(actual interface{}, expected ...interface{}) string
+```
+ShouldHappenAfter receives exactly 2 time.Time arguments and asserts that the
+first happens after the second.
+
+#### func ShouldHappenBefore
+
+```go
+func ShouldHappenBefore(actual interface{}, expected ...interface{}) string
+```
+ShouldHappenBefore receives exactly 2 time.Time arguments and asserts that the
+first happens before the second.
+
+#### func ShouldHappenBetween
+
+```go
+func ShouldHappenBetween(actual interface{}, expected ...interface{}) string
+```
+ShouldHappenBetween receives exactly 3 time.Time arguments and asserts that the
+first happens between (not on) the second and third.
+
+#### func ShouldHappenOnOrAfter
+
+```go
+func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) string
+```
+ShouldHappenOnOrAfter receives exactly 2 time.Time arguments and asserts that
+the first happens on or after the second.
+
+#### func ShouldHappenOnOrBefore
+
+```go
+func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) string
+```
+ShouldHappenOnOrBefore receives exactly 2 time.Time arguments and asserts that
+the first happens on or before the second.
+
+#### func ShouldHappenOnOrBetween
+
+```go
+func ShouldHappenOnOrBetween(actual interface{}, expected ...interface{}) string
+```
+ShouldHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that
+the first happens between or on the second and third.
+
+#### func ShouldHappenWithin
+
+```go
+func ShouldHappenWithin(actual interface{}, expected ...interface{}) string
+```
+ShouldHappenWithin receives a time.Time, a time.Duration, and a time.Time (3
+arguments) and asserts that the first time.Time happens within or on the
+duration specified relative to the other time.Time.
+
+#### func ShouldHaveLength
+
+```go
+func ShouldHaveLength(actual interface{}, expected ...interface{}) string
+```
+ShouldHaveLength receives 2 parameters. The first is a collection to check the
+length of, the second being the expected length. It obeys the rules specified by
+the len function for determining length: http://golang.org/pkg/builtin/#len
+
+#### func ShouldHaveSameTypeAs
+
+```go
+func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string
+```
+ShouldHaveSameTypeAs receives exactly two parameters and compares their
+underlying types for equality.
+
+#### func ShouldImplement
+
+```go
+func ShouldImplement(actual interface{}, expectedList ...interface{}) string
+```
+ShouldImplement receives exactly two parameters and ensures that the first
+implements the interface type of the second.
+
+#### func ShouldNotAlmostEqual
+
+```go
+func ShouldNotAlmostEqual(actual interface{}, expected ...interface{}) string
+```
+ShouldNotAlmostEqual is the inverse of ShouldAlmostEqual
+
+#### func ShouldNotBeBetween
+
+```go
+func ShouldNotBeBetween(actual interface{}, expected ...interface{}) string
+```
+ShouldNotBeBetween receives exactly three parameters: an actual value, a lower
+bound, and an upper bound. It ensures that the actual value is NOT between both
+bounds.
+
+#### func ShouldNotBeBetweenOrEqual
+
+```go
+func ShouldNotBeBetweenOrEqual(actual interface{}, expected ...interface{}) string
+```
+ShouldNotBeBetweenOrEqual receives exactly three parameters: an actual value, a
+lower bound, and an upper bound. It ensures that the actual value is nopt
+between the bounds nor equal to either of them.
+
+#### func ShouldNotBeBlank
+
+```go
+func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string
+```
+ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is
+equal to "".
+
+#### func ShouldNotBeEmpty
+
+```go
+func ShouldNotBeEmpty(actual interface{}, expected ...interface{}) string
+```
+ShouldNotBeEmpty receives a single parameter (actual) and determines whether or
+not calling len(actual) would return a value greater than zero. It obeys the
+rules specified by the `len` function for determining length:
+http://golang.org/pkg/builtin/#len
+
+#### func ShouldNotBeIn
+
+```go
+func ShouldNotBeIn(actual interface{}, expected ...interface{}) string
+```
+ShouldNotBeIn receives at least 2 parameters. The first is a proposed member of
+the collection that is passed in either as the second parameter, or of the
+collection that is comprised of all the remaining parameters. This assertion
+ensures that the proposed member is NOT in the collection (using ShouldEqual).
+
+#### func ShouldNotBeNil
+
+```go
+func ShouldNotBeNil(actual interface{}, expected ...interface{}) string
+```
+ShouldNotBeNil receives a single parameter and ensures that it is not nil.
+
+#### func ShouldNotBeZeroValue
+
+```go
+func ShouldNotBeZeroValue(actual interface{}, expected ...interface{}) string
+```
+ShouldBeZeroValue receives a single parameter and ensures that it is NOT the Go
+equivalent of the default value, or "zero" value.
+
+#### func ShouldNotContain
+
+```go
+func ShouldNotContain(actual interface{}, expected ...interface{}) string
+```
+ShouldNotContain receives exactly two parameters. The first is a slice and the
+second is a proposed member. Membership is determinied using ShouldEqual.
+
+#### func ShouldNotContainKey
+
+```go
+func ShouldNotContainKey(actual interface{}, expected ...interface{}) string
+```
+ShouldNotContainKey receives exactly two parameters. The first is a map and the
+second is a proposed absent key. Keys are compared with a simple '=='.
+
+#### func ShouldNotContainSubstring
+
+```go
+func ShouldNotContainSubstring(actual interface{}, expected ...interface{}) string
+```
+ShouldNotContainSubstring receives exactly 2 string parameters and ensures that
+the first does NOT contain the second as a substring.
+
+#### func ShouldNotEndWith
+
+```go
+func ShouldNotEndWith(actual interface{}, expected ...interface{}) string
+```
+ShouldEndWith receives exactly 2 string parameters and ensures that the first
+does not end with the second.
+
+#### func ShouldNotEqual
+
+```go
+func ShouldNotEqual(actual interface{}, expected ...interface{}) string
+```
+ShouldNotEqual receives exactly two parameters and does an inequality check. See
+ShouldEqual for details on how equality is determined.
+
+#### func ShouldNotHappenOnOrBetween
+
+```go
+func ShouldNotHappenOnOrBetween(actual interface{}, expected ...interface{}) string
+```
+ShouldNotHappenOnOrBetween receives exactly 3 time.Time arguments and asserts
+that the first does NOT happen between or on the second or third.
+
+#### func ShouldNotHappenWithin
+
+```go
+func ShouldNotHappenWithin(actual interface{}, expected ...interface{}) string
+```
+ShouldNotHappenWithin receives a time.Time, a time.Duration, and a time.Time (3
+arguments) and asserts that the first time.Time does NOT happen within or on the
+duration specified relative to the other time.Time.
+
+#### func ShouldNotHaveSameTypeAs
+
+```go
+func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string
+```
+ShouldNotHaveSameTypeAs receives exactly two parameters and compares their
+underlying types for inequality.
+
+#### func ShouldNotImplement
+
+```go
+func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string
+```
+ShouldNotImplement receives exactly two parameters and ensures that the first
+does NOT implement the interface type of the second.
+
+#### func ShouldNotPanic
+
+```go
+func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string)
+```
+ShouldNotPanic receives a void, niladic function and expects to execute the
+function without any panic.
+
+#### func ShouldNotPanicWith
+
+```go
+func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string)
+```
+ShouldNotPanicWith receives a void, niladic function and expects to recover a
+panic whose content differs from the second argument.
+
+#### func ShouldNotPointTo
+
+```go
+func ShouldNotPointTo(actual interface{}, expected ...interface{}) string
+```
+ShouldNotPointTo receives exactly two parameters and checks to see that they
+point to different addresess.
+
+#### func ShouldNotResemble
+
+```go
+func ShouldNotResemble(actual interface{}, expected ...interface{}) string
+```
+ShouldNotResemble receives exactly two parameters and does an inverse deep equal
+check (see reflect.DeepEqual)
+
+#### func ShouldNotStartWith
+
+```go
+func ShouldNotStartWith(actual interface{}, expected ...interface{}) string
+```
+ShouldNotStartWith receives exactly 2 string parameters and ensures that the
+first does not start with the second.
+
+#### func ShouldPanic
+
+```go
+func ShouldPanic(actual interface{}, expected ...interface{}) (message string)
+```
+ShouldPanic receives a void, niladic function and expects to recover a panic.
+
+#### func ShouldPanicWith
+
+```go
+func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string)
+```
+ShouldPanicWith receives a void, niladic function and expects to recover a panic
+with the second argument as the content.
+
+#### func ShouldPointTo
+
+```go
+func ShouldPointTo(actual interface{}, expected ...interface{}) string
+```
+ShouldPointTo receives exactly two parameters and checks to see that they point
+to the same address.
+
+#### func ShouldResemble
+
+```go
+func ShouldResemble(actual interface{}, expected ...interface{}) string
+```
+ShouldResemble receives exactly two parameters and does a deep equal check (see
+reflect.DeepEqual)
+
+#### func ShouldStartWith
+
+```go
+func ShouldStartWith(actual interface{}, expected ...interface{}) string
+```
+ShouldStartWith receives exactly 2 string parameters and ensures that the first
+starts with the second.
+
+#### func So
+
+```go
+func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string)
+```
+So is a convenience function (as opposed to an inconvenience function?) for
+running assertions on arbitrary arguments in any context, be it for testing or
+even application logging. It allows you to perform assertion-like behavior (and
+get nicely formatted messages detailing discrepancies) but without the program
+blowing up or panicking. All that is required is to import this package and call
+`So` with one of the assertions exported by this package as the second
+parameter. The first return parameter is a boolean indicating if the assertion
+was true. The second return parameter is the well-formatted message showing why
+an assertion was incorrect, or blank if the assertion was correct.
+
+Example:
+
+ if ok, message := So(x, ShouldBeGreaterThan, y); !ok {
+ log.Println(message)
+ }
+
+For an alternative implementation of So (that provides more flexible return
+options) see the `So` function in the package at
+github.com/smartystreets/assertions/assert.
+
+#### type Assertion
+
+```go
+type Assertion struct {
+}
+```
+
+
+#### func New
+
+```go
+func New(t testingT) *Assertion
+```
+New swallows the *testing.T struct and prints failed assertions using t.Error.
+Example: assertions.New(t).So(1, should.Equal, 1)
+
+#### func (*Assertion) Failed
+
+```go
+func (this *Assertion) Failed() bool
+```
+Failed reports whether any calls to So (on this Assertion instance) have failed.
+
+#### func (*Assertion) So
+
+```go
+func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool
+```
+So calls the standalone So function and additionally, calls t.Error in failure
+scenarios.
+
+#### type FailureView
+
+```go
+type FailureView struct {
+ Message string `json:"Message"`
+ Expected string `json:"Expected"`
+ Actual string `json:"Actual"`
+}
+```
+
+This struct is also declared in
+github.com/smartystreets/goconvey/convey/reporting. The json struct tags should
+be equal in both declarations.
+
+#### type Serializer
+
+```go
+type Serializer interface {
+ // contains filtered or unexported methods
+}
+```
diff --git a/vendor/github.com/smartystreets/assertions/collections.go b/vendor/github.com/smartystreets/assertions/collections.go
new file mode 100644
index 0000000..b534d4b
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/collections.go
@@ -0,0 +1,244 @@
+package assertions
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/smartystreets/assertions/internal/oglematchers"
+)
+
+// ShouldContain receives exactly two parameters. The first is a slice and the
+// second is a proposed member. Membership is determined using ShouldEqual.
+func ShouldContain(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ if matchError := oglematchers.Contains(expected[0]).Matches(actual); matchError != nil {
+ typeName := reflect.TypeOf(actual)
+
+ if fmt.Sprintf("%v", matchError) == "which is not a slice or array" {
+ return fmt.Sprintf(shouldHaveBeenAValidCollection, typeName)
+ }
+ return fmt.Sprintf(shouldHaveContained, typeName, expected[0])
+ }
+ return success
+}
+
+// ShouldNotContain receives exactly two parameters. The first is a slice and the
+// second is a proposed member. Membership is determinied using ShouldEqual.
+func ShouldNotContain(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+ typeName := reflect.TypeOf(actual)
+
+ if matchError := oglematchers.Contains(expected[0]).Matches(actual); matchError != nil {
+ if fmt.Sprintf("%v", matchError) == "which is not a slice or array" {
+ return fmt.Sprintf(shouldHaveBeenAValidCollection, typeName)
+ }
+ return success
+ }
+ return fmt.Sprintf(shouldNotHaveContained, typeName, expected[0])
+}
+
+// ShouldContainKey receives exactly two parameters. The first is a map and the
+// second is a proposed key. Keys are compared with a simple '=='.
+func ShouldContainKey(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ keys, isMap := mapKeys(actual)
+ if !isMap {
+ return fmt.Sprintf(shouldHaveBeenAValidMap, reflect.TypeOf(actual))
+ }
+
+ if !keyFound(keys, expected[0]) {
+ return fmt.Sprintf(shouldHaveContainedKey, reflect.TypeOf(actual), expected)
+ }
+
+ return ""
+}
+
+// ShouldNotContainKey receives exactly two parameters. The first is a map and the
+// second is a proposed absent key. Keys are compared with a simple '=='.
+func ShouldNotContainKey(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ keys, isMap := mapKeys(actual)
+ if !isMap {
+ return fmt.Sprintf(shouldHaveBeenAValidMap, reflect.TypeOf(actual))
+ }
+
+ if keyFound(keys, expected[0]) {
+ return fmt.Sprintf(shouldNotHaveContainedKey, reflect.TypeOf(actual), expected)
+ }
+
+ return ""
+}
+
+func mapKeys(m interface{}) ([]reflect.Value, bool) {
+ value := reflect.ValueOf(m)
+ if value.Kind() != reflect.Map {
+ return nil, false
+ }
+ return value.MapKeys(), true
+}
+func keyFound(keys []reflect.Value, expectedKey interface{}) bool {
+ found := false
+ for _, key := range keys {
+ if key.Interface() == expectedKey {
+ found = true
+ }
+ }
+ return found
+}
+
+// ShouldBeIn receives at least 2 parameters. The first is a proposed member of the collection
+// that is passed in either as the second parameter, or of the collection that is comprised
+// of all the remaining parameters. This assertion ensures that the proposed member is in
+// the collection (using ShouldEqual).
+func ShouldBeIn(actual interface{}, expected ...interface{}) string {
+ if fail := atLeast(1, expected); fail != success {
+ return fail
+ }
+
+ if len(expected) == 1 {
+ return shouldBeIn(actual, expected[0])
+ }
+ return shouldBeIn(actual, expected)
+}
+func shouldBeIn(actual interface{}, expected interface{}) string {
+ if matchError := oglematchers.Contains(actual).Matches(expected); matchError != nil {
+ return fmt.Sprintf(shouldHaveBeenIn, actual, reflect.TypeOf(expected))
+ }
+ return success
+}
+
+// ShouldNotBeIn receives at least 2 parameters. The first is a proposed member of the collection
+// that is passed in either as the second parameter, or of the collection that is comprised
+// of all the remaining parameters. This assertion ensures that the proposed member is NOT in
+// the collection (using ShouldEqual).
+func ShouldNotBeIn(actual interface{}, expected ...interface{}) string {
+ if fail := atLeast(1, expected); fail != success {
+ return fail
+ }
+
+ if len(expected) == 1 {
+ return shouldNotBeIn(actual, expected[0])
+ }
+ return shouldNotBeIn(actual, expected)
+}
+func shouldNotBeIn(actual interface{}, expected interface{}) string {
+ if matchError := oglematchers.Contains(actual).Matches(expected); matchError == nil {
+ return fmt.Sprintf(shouldNotHaveBeenIn, actual, reflect.TypeOf(expected))
+ }
+ return success
+}
+
+// ShouldBeEmpty receives a single parameter (actual) and determines whether or not
+// calling len(actual) would return `0`. It obeys the rules specified by the len
+// function for determining length: http://golang.org/pkg/builtin/#len
+func ShouldBeEmpty(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+
+ if actual == nil {
+ return success
+ }
+
+ value := reflect.ValueOf(actual)
+ switch value.Kind() {
+ case reflect.Slice:
+ if value.Len() == 0 {
+ return success
+ }
+ case reflect.Chan:
+ if value.Len() == 0 {
+ return success
+ }
+ case reflect.Map:
+ if value.Len() == 0 {
+ return success
+ }
+ case reflect.String:
+ if value.Len() == 0 {
+ return success
+ }
+ case reflect.Ptr:
+ elem := value.Elem()
+ kind := elem.Kind()
+ if (kind == reflect.Slice || kind == reflect.Array) && elem.Len() == 0 {
+ return success
+ }
+ }
+
+ return fmt.Sprintf(shouldHaveBeenEmpty, actual)
+}
+
+// ShouldNotBeEmpty receives a single parameter (actual) and determines whether or not
+// calling len(actual) would return a value greater than zero. It obeys the rules
+// specified by the `len` function for determining length: http://golang.org/pkg/builtin/#len
+func ShouldNotBeEmpty(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+
+ if empty := ShouldBeEmpty(actual, expected...); empty != success {
+ return success
+ }
+ return fmt.Sprintf(shouldNotHaveBeenEmpty, actual)
+}
+
+// ShouldHaveLength receives 2 parameters. The first is a collection to check
+// the length of, the second being the expected length. It obeys the rules
+// specified by the len function for determining length:
+// http://golang.org/pkg/builtin/#len
+func ShouldHaveLength(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ var expectedLen int64
+ lenValue := reflect.ValueOf(expected[0])
+ switch lenValue.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ expectedLen = lenValue.Int()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ expectedLen = int64(lenValue.Uint())
+ default:
+ return fmt.Sprintf(shouldHaveBeenAValidInteger, reflect.TypeOf(expected[0]))
+ }
+
+ if expectedLen < 0 {
+ return fmt.Sprintf(shouldHaveBeenAValidLength, expected[0])
+ }
+
+ value := reflect.ValueOf(actual)
+ switch value.Kind() {
+ case reflect.Slice,
+ reflect.Chan,
+ reflect.Map,
+ reflect.String:
+ if int64(value.Len()) == expectedLen {
+ return success
+ } else {
+ return fmt.Sprintf(shouldHaveHadLength, expectedLen, value.Len(), actual)
+ }
+ case reflect.Ptr:
+ elem := value.Elem()
+ kind := elem.Kind()
+ if kind == reflect.Slice || kind == reflect.Array {
+ if int64(elem.Len()) == expectedLen {
+ return success
+ } else {
+ return fmt.Sprintf(shouldHaveHadLength, expectedLen, elem.Len(), actual)
+ }
+ }
+ }
+ return fmt.Sprintf(shouldHaveBeenAValidCollection, reflect.TypeOf(actual))
+}
diff --git a/vendor/github.com/smartystreets/assertions/doc.go b/vendor/github.com/smartystreets/assertions/doc.go
new file mode 100644
index 0000000..ba30a92
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/doc.go
@@ -0,0 +1,109 @@
+// Package assertions contains the implementations for all assertions which
+// are referenced in goconvey's `convey` package
+// (github.com/smartystreets/goconvey/convey) and gunit (github.com/smartystreets/gunit)
+// for use with the So(...) method.
+// They can also be used in traditional Go test functions and even in
+// applications.
+//
+// https://smartystreets.com
+//
+// Many of the assertions lean heavily on work done by Aaron Jacobs in his excellent oglematchers library.
+// (https://github.com/jacobsa/oglematchers)
+// The ShouldResemble assertion leans heavily on work done by Daniel Jacques in his very helpful go-render library.
+// (https://github.com/luci/go-render)
+package assertions
+
+import (
+ "fmt"
+ "runtime"
+)
+
+// By default we use a no-op serializer. The actual Serializer provides a JSON
+// representation of failure results on selected assertions so the goconvey
+// web UI can display a convenient diff.
+var serializer Serializer = new(noopSerializer)
+
+// GoConveyMode provides control over JSON serialization of failures. When
+// using the assertions in this package from the convey package JSON results
+// are very helpful and can be rendered in a DIFF view. In that case, this function
+// will be called with a true value to enable the JSON serialization. By default,
+// the assertions in this package will not serializer a JSON result, making
+// standalone usage more convenient.
+func GoConveyMode(yes bool) {
+ if yes {
+ serializer = newSerializer()
+ } else {
+ serializer = new(noopSerializer)
+ }
+}
+
+type testingT interface {
+ Error(args ...interface{})
+}
+
+type Assertion struct {
+ t testingT
+ failed bool
+}
+
+// New swallows the *testing.T struct and prints failed assertions using t.Error.
+// Example: assertions.New(t).So(1, should.Equal, 1)
+func New(t testingT) *Assertion {
+ return &Assertion{t: t}
+}
+
+// Failed reports whether any calls to So (on this Assertion instance) have failed.
+func (this *Assertion) Failed() bool {
+ return this.failed
+}
+
+// So calls the standalone So function and additionally, calls t.Error in failure scenarios.
+func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool {
+ ok, result := So(actual, assert, expected...)
+ if !ok {
+ this.failed = true
+ _, file, line, _ := runtime.Caller(1)
+ this.t.Error(fmt.Sprintf("\n%s:%d\n%s", file, line, result))
+ }
+ return ok
+}
+
+// So is a convenience function (as opposed to an inconvenience function?)
+// for running assertions on arbitrary arguments in any context, be it for testing or even
+// application logging. It allows you to perform assertion-like behavior (and get nicely
+// formatted messages detailing discrepancies) but without the program blowing up or panicking.
+// All that is required is to import this package and call `So` with one of the assertions
+// exported by this package as the second parameter.
+// The first return parameter is a boolean indicating if the assertion was true. The second
+// return parameter is the well-formatted message showing why an assertion was incorrect, or
+// blank if the assertion was correct.
+//
+// Example:
+//
+// if ok, message := So(x, ShouldBeGreaterThan, y); !ok {
+// log.Println(message)
+// }
+//
+// For an alternative implementation of So (that provides more flexible return options)
+// see the `So` function in the package at github.com/smartystreets/assertions/assert.
+func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) {
+ if result := so(actual, assert, expected...); len(result) == 0 {
+ return true, result
+ } else {
+ return false, result
+ }
+}
+
+// so is like So, except that it only returns the string message, which is blank if the
+// assertion passed. Used to facilitate testing.
+func so(actual interface{}, assert func(interface{}, ...interface{}) string, expected ...interface{}) string {
+ return assert(actual, expected...)
+}
+
+// assertion is an alias for a function with a signature that the So()
+// function can handle. Any future or custom assertions should conform to this
+// method signature. The return value should be an empty string if the assertion
+// passes and a well-formed failure message if not.
+type assertion func(actual interface{}, expected ...interface{}) string
+
+////////////////////////////////////////////////////////////////////////////
diff --git a/vendor/github.com/smartystreets/assertions/equal_method.go b/vendor/github.com/smartystreets/assertions/equal_method.go
new file mode 100644
index 0000000..c4fc38f
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/equal_method.go
@@ -0,0 +1,75 @@
+package assertions
+
+import "reflect"
+
+type equalityMethodSpecification struct {
+ a interface{}
+ b interface{}
+
+ aType reflect.Type
+ bType reflect.Type
+
+ equalMethod reflect.Value
+}
+
+func newEqualityMethodSpecification(a, b interface{}) *equalityMethodSpecification {
+ return &equalityMethodSpecification{
+ a: a,
+ b: b,
+ }
+}
+
+func (this *equalityMethodSpecification) IsSatisfied() bool {
+ if !this.bothAreSameType() {
+ return false
+ }
+ if !this.typeHasEqualMethod() {
+ return false
+ }
+ if !this.equalMethodReceivesSameTypeForComparison() {
+ return false
+ }
+ if !this.equalMethodReturnsBool() {
+ return false
+ }
+ return true
+}
+
+func (this *equalityMethodSpecification) bothAreSameType() bool {
+ this.aType = reflect.TypeOf(this.a)
+ if this.aType == nil {
+ return false
+ }
+ if this.aType.Kind() == reflect.Ptr {
+ this.aType = this.aType.Elem()
+ }
+ this.bType = reflect.TypeOf(this.b)
+ return this.aType == this.bType
+}
+func (this *equalityMethodSpecification) typeHasEqualMethod() bool {
+ aInstance := reflect.ValueOf(this.a)
+ this.equalMethod = aInstance.MethodByName("Equal")
+ return this.equalMethod != reflect.Value{}
+}
+
+func (this *equalityMethodSpecification) equalMethodReceivesSameTypeForComparison() bool {
+ signature := this.equalMethod.Type()
+ return signature.NumIn() == 1 && signature.In(0) == this.aType
+}
+
+func (this *equalityMethodSpecification) equalMethodReturnsBool() bool {
+ signature := this.equalMethod.Type()
+ return signature.NumOut() == 1 && signature.Out(0) == reflect.TypeOf(true)
+}
+
+func (this *equalityMethodSpecification) AreEqual() bool {
+ a := reflect.ValueOf(this.a)
+ b := reflect.ValueOf(this.b)
+ return areEqual(a, b) && areEqual(b, a)
+}
+func areEqual(receiver reflect.Value, argument reflect.Value) bool {
+ equalMethod := receiver.MethodByName("Equal")
+ argumentList := []reflect.Value{argument}
+ result := equalMethod.Call(argumentList)
+ return result[0].Bool()
+}
diff --git a/vendor/github.com/smartystreets/assertions/equality.go b/vendor/github.com/smartystreets/assertions/equality.go
new file mode 100644
index 0000000..6c10d24
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/equality.go
@@ -0,0 +1,328 @@
+package assertions
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+ "strings"
+
+ "github.com/smartystreets/assertions/internal/go-render/render"
+ "github.com/smartystreets/assertions/internal/oglematchers"
+)
+
+// ShouldEqual receives exactly two parameters and does an equality check
+// using the following semantics:
+// 1. If the expected and actual values implement an Equal method in the form
+// `func (this T) Equal(that T) bool` then call the method. If true, they are equal.
+// 2. The expected and actual values are judged equal or not by oglematchers.Equals.
+func ShouldEqual(actual interface{}, expected ...interface{}) string {
+ if message := need(1, expected); message != success {
+ return message
+ }
+ return shouldEqual(actual, expected[0])
+}
+func shouldEqual(actual, expected interface{}) (message string) {
+ defer func() {
+ if r := recover(); r != nil {
+ message = serializer.serialize(expected, actual, fmt.Sprintf(shouldHaveBeenEqual, expected, actual))
+ }
+ }()
+
+ if specification := newEqualityMethodSpecification(expected, actual); specification.IsSatisfied() {
+ if specification.AreEqual() {
+ return success
+ } else {
+ message = fmt.Sprintf(shouldHaveBeenEqual, expected, actual)
+ return serializer.serialize(expected, actual, message)
+ }
+ }
+ if matchError := oglematchers.Equals(expected).Matches(actual); matchError != nil {
+ expectedSyntax := fmt.Sprintf("%v", expected)
+ actualSyntax := fmt.Sprintf("%v", actual)
+ if expectedSyntax == actualSyntax && reflect.TypeOf(expected) != reflect.TypeOf(actual) {
+ message = fmt.Sprintf(shouldHaveBeenEqualTypeMismatch, expected, expected, actual, actual)
+ } else {
+ message = fmt.Sprintf(shouldHaveBeenEqual, expected, actual)
+ }
+ return serializer.serialize(expected, actual, message)
+ }
+
+ return success
+}
+
+// ShouldNotEqual receives exactly two parameters and does an inequality check.
+// See ShouldEqual for details on how equality is determined.
+func ShouldNotEqual(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ } else if ShouldEqual(actual, expected[0]) == success {
+ return fmt.Sprintf(shouldNotHaveBeenEqual, actual, expected[0])
+ }
+ return success
+}
+
+// ShouldAlmostEqual makes sure that two parameters are close enough to being equal.
+// The acceptable delta may be specified with a third argument,
+// or a very small default delta will be used.
+func ShouldAlmostEqual(actual interface{}, expected ...interface{}) string {
+ actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...)
+
+ if err != "" {
+ return err
+ }
+
+ if math.Abs(actualFloat-expectedFloat) <= deltaFloat {
+ return success
+ } else {
+ return fmt.Sprintf(shouldHaveBeenAlmostEqual, actualFloat, expectedFloat)
+ }
+}
+
+// ShouldNotAlmostEqual is the inverse of ShouldAlmostEqual
+func ShouldNotAlmostEqual(actual interface{}, expected ...interface{}) string {
+ actualFloat, expectedFloat, deltaFloat, err := cleanAlmostEqualInput(actual, expected...)
+
+ if err != "" {
+ return err
+ }
+
+ if math.Abs(actualFloat-expectedFloat) > deltaFloat {
+ return success
+ } else {
+ return fmt.Sprintf(shouldHaveNotBeenAlmostEqual, actualFloat, expectedFloat)
+ }
+}
+
+func cleanAlmostEqualInput(actual interface{}, expected ...interface{}) (float64, float64, float64, string) {
+ deltaFloat := 0.0000000001
+
+ if len(expected) == 0 {
+ return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided neither)"
+ } else if len(expected) == 2 {
+ delta, err := getFloat(expected[1])
+
+ if err != nil {
+ return 0.0, 0.0, 0.0, "The delta value " + err.Error()
+ }
+
+ deltaFloat = delta
+ } else if len(expected) > 2 {
+ return 0.0, 0.0, 0.0, "This assertion requires exactly one comparison value and an optional delta (you provided more values)"
+ }
+
+ actualFloat, err := getFloat(actual)
+ if err != nil {
+ return 0.0, 0.0, 0.0, "The actual value " + err.Error()
+ }
+
+ expectedFloat, err := getFloat(expected[0])
+ if err != nil {
+ return 0.0, 0.0, 0.0, "The comparison value " + err.Error()
+ }
+
+ return actualFloat, expectedFloat, deltaFloat, ""
+}
+
+// returns the float value of any real number, or error if it is not a numerical type
+func getFloat(num interface{}) (float64, error) {
+ numValue := reflect.ValueOf(num)
+ numKind := numValue.Kind()
+
+ if numKind == reflect.Int ||
+ numKind == reflect.Int8 ||
+ numKind == reflect.Int16 ||
+ numKind == reflect.Int32 ||
+ numKind == reflect.Int64 {
+ return float64(numValue.Int()), nil
+ } else if numKind == reflect.Uint ||
+ numKind == reflect.Uint8 ||
+ numKind == reflect.Uint16 ||
+ numKind == reflect.Uint32 ||
+ numKind == reflect.Uint64 {
+ return float64(numValue.Uint()), nil
+ } else if numKind == reflect.Float32 ||
+ numKind == reflect.Float64 {
+ return numValue.Float(), nil
+ } else {
+ return 0.0, errors.New("must be a numerical type, but was: " + numKind.String())
+ }
+}
+
+// ShouldEqualJSON receives exactly two parameters and does an equality check by marshalling to JSON
+func ShouldEqualJSON(actual interface{}, expected ...interface{}) string {
+ if message := need(1, expected); message != success {
+ return message
+ }
+
+ expectedString, expectedErr := remarshal(expected[0].(string))
+ if expectedErr != nil {
+ return "Expected value not valid JSON: " + expectedErr.Error()
+ }
+
+ actualString, actualErr := remarshal(actual.(string))
+ if actualErr != nil {
+ return "Actual value not valid JSON: " + actualErr.Error()
+ }
+
+ return ShouldEqual(actualString, expectedString)
+}
+func remarshal(value string) (string, error) {
+ var structured map[string]interface{}
+ err := json.Unmarshal([]byte(value), &structured)
+ if err != nil {
+ return "", err
+ }
+ canonical, _ := json.Marshal(structured)
+ return string(canonical), nil
+}
+
+// ShouldResemble receives exactly two parameters and does a deep equal check (see reflect.DeepEqual)
+func ShouldResemble(actual interface{}, expected ...interface{}) string {
+ if message := need(1, expected); message != success {
+ return message
+ }
+
+ if matchError := oglematchers.DeepEquals(expected[0]).Matches(actual); matchError != nil {
+ return serializer.serializeDetailed(expected[0], actual,
+ fmt.Sprintf(shouldHaveResembled, render.Render(expected[0]), render.Render(actual)))
+ }
+
+ return success
+}
+
+// ShouldNotResemble receives exactly two parameters and does an inverse deep equal check (see reflect.DeepEqual)
+func ShouldNotResemble(actual interface{}, expected ...interface{}) string {
+ if message := need(1, expected); message != success {
+ return message
+ } else if ShouldResemble(actual, expected[0]) == success {
+ return fmt.Sprintf(shouldNotHaveResembled, render.Render(actual), render.Render(expected[0]))
+ }
+ return success
+}
+
+// ShouldPointTo receives exactly two parameters and checks to see that they point to the same address.
+func ShouldPointTo(actual interface{}, expected ...interface{}) string {
+ if message := need(1, expected); message != success {
+ return message
+ }
+ return shouldPointTo(actual, expected[0])
+
+}
+func shouldPointTo(actual, expected interface{}) string {
+ actualValue := reflect.ValueOf(actual)
+ expectedValue := reflect.ValueOf(expected)
+
+ if ShouldNotBeNil(actual) != success {
+ return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "nil")
+ } else if ShouldNotBeNil(expected) != success {
+ return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "nil")
+ } else if actualValue.Kind() != reflect.Ptr {
+ return fmt.Sprintf(shouldHaveBeenNonNilPointer, "first", "not")
+ } else if expectedValue.Kind() != reflect.Ptr {
+ return fmt.Sprintf(shouldHaveBeenNonNilPointer, "second", "not")
+ } else if ShouldEqual(actualValue.Pointer(), expectedValue.Pointer()) != success {
+ actualAddress := reflect.ValueOf(actual).Pointer()
+ expectedAddress := reflect.ValueOf(expected).Pointer()
+ return serializer.serialize(expectedAddress, actualAddress, fmt.Sprintf(shouldHavePointedTo,
+ actual, actualAddress,
+ expected, expectedAddress))
+ }
+ return success
+}
+
+// ShouldNotPointTo receives exactly two parameters and checks to see that they point to different addresess.
+func ShouldNotPointTo(actual interface{}, expected ...interface{}) string {
+ if message := need(1, expected); message != success {
+ return message
+ }
+ compare := ShouldPointTo(actual, expected[0])
+ if strings.HasPrefix(compare, shouldBePointers) {
+ return compare
+ } else if compare == success {
+ return fmt.Sprintf(shouldNotHavePointedTo, actual, expected[0], reflect.ValueOf(actual).Pointer())
+ }
+ return success
+}
+
+// ShouldBeNil receives a single parameter and ensures that it is nil.
+func ShouldBeNil(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ } else if actual == nil {
+ return success
+ } else if interfaceHasNilValue(actual) {
+ return success
+ }
+ return fmt.Sprintf(shouldHaveBeenNil, actual)
+}
+func interfaceHasNilValue(actual interface{}) bool {
+ value := reflect.ValueOf(actual)
+ kind := value.Kind()
+ nilable := kind == reflect.Slice ||
+ kind == reflect.Chan ||
+ kind == reflect.Func ||
+ kind == reflect.Ptr ||
+ kind == reflect.Map
+
+ // Careful: reflect.Value.IsNil() will panic unless it's an interface, chan, map, func, slice, or ptr
+ // Reference: http://golang.org/pkg/reflect/#Value.IsNil
+ return nilable && value.IsNil()
+}
+
+// ShouldNotBeNil receives a single parameter and ensures that it is not nil.
+func ShouldNotBeNil(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ } else if ShouldBeNil(actual) == success {
+ return fmt.Sprintf(shouldNotHaveBeenNil, actual)
+ }
+ return success
+}
+
+// ShouldBeTrue receives a single parameter and ensures that it is true.
+func ShouldBeTrue(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ } else if actual != true {
+ return fmt.Sprintf(shouldHaveBeenTrue, actual)
+ }
+ return success
+}
+
+// ShouldBeFalse receives a single parameter and ensures that it is false.
+func ShouldBeFalse(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ } else if actual != false {
+ return fmt.Sprintf(shouldHaveBeenFalse, actual)
+ }
+ return success
+}
+
+// ShouldBeZeroValue receives a single parameter and ensures that it is
+// the Go equivalent of the default value, or "zero" value.
+func ShouldBeZeroValue(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+ zeroVal := reflect.Zero(reflect.TypeOf(actual)).Interface()
+ if !reflect.DeepEqual(zeroVal, actual) {
+ return serializer.serialize(zeroVal, actual, fmt.Sprintf(shouldHaveBeenZeroValue, actual))
+ }
+ return success
+}
+
+// ShouldBeZeroValue receives a single parameter and ensures that it is NOT
+// the Go equivalent of the default value, or "zero" value.
+func ShouldNotBeZeroValue(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+ zeroVal := reflect.Zero(reflect.TypeOf(actual)).Interface()
+ if reflect.DeepEqual(zeroVal, actual) {
+ return serializer.serialize(zeroVal, actual, fmt.Sprintf(shouldNotHaveBeenZeroValue, actual))
+ }
+ return success
+}
diff --git a/vendor/github.com/smartystreets/assertions/filter.go b/vendor/github.com/smartystreets/assertions/filter.go
new file mode 100644
index 0000000..7c46ab8
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/filter.go
@@ -0,0 +1,31 @@
+package assertions
+
+import "fmt"
+
+const (
+ success = ""
+ needExactValues = "This assertion requires exactly %d comparison values (you provided %d)."
+ needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)."
+ needFewerValues = "This assertion allows %d or fewer comparison values (you provided %d)."
+)
+
+func need(needed int, expected []interface{}) string {
+ if len(expected) != needed {
+ return fmt.Sprintf(needExactValues, needed, len(expected))
+ }
+ return success
+}
+
+func atLeast(minimum int, expected []interface{}) string {
+ if len(expected) < 1 {
+ return needNonEmptyCollection
+ }
+ return success
+}
+
+func atMost(max int, expected []interface{}) string {
+ if len(expected) > max {
+ return fmt.Sprintf(needFewerValues, max, len(expected))
+ }
+ return success
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/go-render/LICENSE b/vendor/github.com/smartystreets/assertions/internal/go-render/LICENSE
new file mode 100644
index 0000000..6280ff0
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/go-render/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// 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
+// OWNER 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.
diff --git a/vendor/github.com/smartystreets/assertions/internal/go-render/render/render.go b/vendor/github.com/smartystreets/assertions/internal/go-render/render/render.go
new file mode 100644
index 0000000..313611e
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/go-render/render/render.go
@@ -0,0 +1,481 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package render
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "sort"
+ "strconv"
+)
+
+var builtinTypeMap = map[reflect.Kind]string{
+ reflect.Bool: "bool",
+ reflect.Complex128: "complex128",
+ reflect.Complex64: "complex64",
+ reflect.Float32: "float32",
+ reflect.Float64: "float64",
+ reflect.Int16: "int16",
+ reflect.Int32: "int32",
+ reflect.Int64: "int64",
+ reflect.Int8: "int8",
+ reflect.Int: "int",
+ reflect.String: "string",
+ reflect.Uint16: "uint16",
+ reflect.Uint32: "uint32",
+ reflect.Uint64: "uint64",
+ reflect.Uint8: "uint8",
+ reflect.Uint: "uint",
+ reflect.Uintptr: "uintptr",
+}
+
+var builtinTypeSet = map[string]struct{}{}
+
+func init() {
+ for _, v := range builtinTypeMap {
+ builtinTypeSet[v] = struct{}{}
+ }
+}
+
+var typeOfString = reflect.TypeOf("")
+var typeOfInt = reflect.TypeOf(int(1))
+var typeOfUint = reflect.TypeOf(uint(1))
+var typeOfFloat = reflect.TypeOf(10.1)
+
+// Render converts a structure to a string representation. Unline the "%#v"
+// format string, this resolves pointer types' contents in structs, maps, and
+// slices/arrays and prints their field values.
+func Render(v interface{}) string {
+ buf := bytes.Buffer{}
+ s := (*traverseState)(nil)
+ s.render(&buf, 0, reflect.ValueOf(v), false)
+ return buf.String()
+}
+
+// renderPointer is called to render a pointer value.
+//
+// This is overridable so that the test suite can have deterministic pointer
+// values in its expectations.
+var renderPointer = func(buf *bytes.Buffer, p uintptr) {
+ fmt.Fprintf(buf, "0x%016x", p)
+}
+
+// traverseState is used to note and avoid recursion as struct members are being
+// traversed.
+//
+// traverseState is allowed to be nil. Specifically, the root state is nil.
+type traverseState struct {
+ parent *traverseState
+ ptr uintptr
+}
+
+func (s *traverseState) forkFor(ptr uintptr) *traverseState {
+ for cur := s; cur != nil; cur = cur.parent {
+ if ptr == cur.ptr {
+ return nil
+ }
+ }
+
+ fs := &traverseState{
+ parent: s,
+ ptr: ptr,
+ }
+ return fs
+}
+
+func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value, implicit bool) {
+ if v.Kind() == reflect.Invalid {
+ buf.WriteString("nil")
+ return
+ }
+ vt := v.Type()
+
+ // If the type being rendered is a potentially recursive type (a type that
+ // can contain itself as a member), we need to avoid recursion.
+ //
+ // If we've already seen this type before, mark that this is the case and
+ // write a recursion placeholder instead of actually rendering it.
+ //
+ // If we haven't seen it before, fork our `seen` tracking so any higher-up
+ // renderers will also render it at least once, then mark that we've seen it
+ // to avoid recursing on lower layers.
+ pe := uintptr(0)
+ vk := vt.Kind()
+ switch vk {
+ case reflect.Ptr:
+ // Since structs and arrays aren't pointers, they can't directly be
+ // recursed, but they can contain pointers to themselves. Record their
+ // pointer to avoid this.
+ switch v.Elem().Kind() {
+ case reflect.Struct, reflect.Array:
+ pe = v.Pointer()
+ }
+
+ case reflect.Slice, reflect.Map:
+ pe = v.Pointer()
+ }
+ if pe != 0 {
+ s = s.forkFor(pe)
+ if s == nil {
+ buf.WriteString("<REC(")
+ if !implicit {
+ writeType(buf, ptrs, vt)
+ }
+ buf.WriteString(")>")
+ return
+ }
+ }
+
+ isAnon := func(t reflect.Type) bool {
+ if t.Name() != "" {
+ if _, ok := builtinTypeSet[t.Name()]; !ok {
+ return false
+ }
+ }
+ return t.Kind() != reflect.Interface
+ }
+
+ switch vk {
+ case reflect.Struct:
+ if !implicit {
+ writeType(buf, ptrs, vt)
+ }
+ buf.WriteRune('{')
+ if rendered, ok := renderTime(v); ok {
+ buf.WriteString(rendered)
+ } else {
+ structAnon := vt.Name() == ""
+ for i := 0; i < vt.NumField(); i++ {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ anon := structAnon && isAnon(vt.Field(i).Type)
+
+ if !anon {
+ buf.WriteString(vt.Field(i).Name)
+ buf.WriteRune(':')
+ }
+
+ s.render(buf, 0, v.Field(i), anon)
+ }
+ }
+ buf.WriteRune('}')
+
+ case reflect.Slice:
+ if v.IsNil() {
+ if !implicit {
+ writeType(buf, ptrs, vt)
+ buf.WriteString("(nil)")
+ } else {
+ buf.WriteString("nil")
+ }
+ return
+ }
+ fallthrough
+
+ case reflect.Array:
+ if !implicit {
+ writeType(buf, ptrs, vt)
+ }
+ anon := vt.Name() == "" && isAnon(vt.Elem())
+ buf.WriteString("{")
+ for i := 0; i < v.Len(); i++ {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+
+ s.render(buf, 0, v.Index(i), anon)
+ }
+ buf.WriteRune('}')
+
+ case reflect.Map:
+ if !implicit {
+ writeType(buf, ptrs, vt)
+ }
+ if v.IsNil() {
+ buf.WriteString("(nil)")
+ } else {
+ buf.WriteString("{")
+
+ mkeys := v.MapKeys()
+ tryAndSortMapKeys(vt, mkeys)
+
+ kt := vt.Key()
+ keyAnon := typeOfString.ConvertibleTo(kt) || typeOfInt.ConvertibleTo(kt) || typeOfUint.ConvertibleTo(kt) || typeOfFloat.ConvertibleTo(kt)
+ valAnon := vt.Name() == "" && isAnon(vt.Elem())
+ for i, mk := range mkeys {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+
+ s.render(buf, 0, mk, keyAnon)
+ buf.WriteString(":")
+ s.render(buf, 0, v.MapIndex(mk), valAnon)
+ }
+ buf.WriteRune('}')
+ }
+
+ case reflect.Ptr:
+ ptrs++
+ fallthrough
+ case reflect.Interface:
+ if v.IsNil() {
+ writeType(buf, ptrs, v.Type())
+ buf.WriteString("(nil)")
+ } else {
+ s.render(buf, ptrs, v.Elem(), false)
+ }
+
+ case reflect.Chan, reflect.Func, reflect.UnsafePointer:
+ writeType(buf, ptrs, vt)
+ buf.WriteRune('(')
+ renderPointer(buf, v.Pointer())
+ buf.WriteRune(')')
+
+ default:
+ tstr := vt.String()
+ implicit = implicit || (ptrs == 0 && builtinTypeMap[vk] == tstr)
+ if !implicit {
+ writeType(buf, ptrs, vt)
+ buf.WriteRune('(')
+ }
+
+ switch vk {
+ case reflect.String:
+ fmt.Fprintf(buf, "%q", v.String())
+ case reflect.Bool:
+ fmt.Fprintf(buf, "%v", v.Bool())
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ fmt.Fprintf(buf, "%d", v.Int())
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ fmt.Fprintf(buf, "%d", v.Uint())
+
+ case reflect.Float32, reflect.Float64:
+ fmt.Fprintf(buf, "%g", v.Float())
+
+ case reflect.Complex64, reflect.Complex128:
+ fmt.Fprintf(buf, "%g", v.Complex())
+ }
+
+ if !implicit {
+ buf.WriteRune(')')
+ }
+ }
+}
+
+func writeType(buf *bytes.Buffer, ptrs int, t reflect.Type) {
+ parens := ptrs > 0
+ switch t.Kind() {
+ case reflect.Chan, reflect.Func, reflect.UnsafePointer:
+ parens = true
+ }
+
+ if parens {
+ buf.WriteRune('(')
+ for i := 0; i < ptrs; i++ {
+ buf.WriteRune('*')
+ }
+ }
+
+ switch t.Kind() {
+ case reflect.Ptr:
+ if ptrs == 0 {
+ // This pointer was referenced from within writeType (e.g., as part of
+ // rendering a list), and so hasn't had its pointer asterisk accounted
+ // for.
+ buf.WriteRune('*')
+ }
+ writeType(buf, 0, t.Elem())
+
+ case reflect.Interface:
+ if n := t.Name(); n != "" {
+ buf.WriteString(t.String())
+ } else {
+ buf.WriteString("interface{}")
+ }
+
+ case reflect.Array:
+ buf.WriteRune('[')
+ buf.WriteString(strconv.FormatInt(int64(t.Len()), 10))
+ buf.WriteRune(']')
+ writeType(buf, 0, t.Elem())
+
+ case reflect.Slice:
+ if t == reflect.SliceOf(t.Elem()) {
+ buf.WriteString("[]")
+ writeType(buf, 0, t.Elem())
+ } else {
+ // Custom slice type, use type name.
+ buf.WriteString(t.String())
+ }
+
+ case reflect.Map:
+ if t == reflect.MapOf(t.Key(), t.Elem()) {
+ buf.WriteString("map[")
+ writeType(buf, 0, t.Key())
+ buf.WriteRune(']')
+ writeType(buf, 0, t.Elem())
+ } else {
+ // Custom map type, use type name.
+ buf.WriteString(t.String())
+ }
+
+ default:
+ buf.WriteString(t.String())
+ }
+
+ if parens {
+ buf.WriteRune(')')
+ }
+}
+
+type cmpFn func(a, b reflect.Value) int
+
+type sortableValueSlice struct {
+ cmp cmpFn
+ elements []reflect.Value
+}
+
+func (s sortableValueSlice) Len() int {
+ return len(s.elements)
+}
+
+func (s sortableValueSlice) Less(i, j int) bool {
+ return s.cmp(s.elements[i], s.elements[j]) < 0
+}
+
+func (s sortableValueSlice) Swap(i, j int) {
+ s.elements[i], s.elements[j] = s.elements[j], s.elements[i]
+}
+
+// cmpForType returns a cmpFn which sorts the data for some type t in the same
+// order that a go-native map key is compared for equality.
+func cmpForType(t reflect.Type) cmpFn {
+ switch t.Kind() {
+ case reflect.String:
+ return func(av, bv reflect.Value) int {
+ a, b := av.String(), bv.String()
+ if a < b {
+ return -1
+ } else if a > b {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Bool:
+ return func(av, bv reflect.Value) int {
+ a, b := av.Bool(), bv.Bool()
+ if !a && b {
+ return -1
+ } else if a && !b {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return func(av, bv reflect.Value) int {
+ a, b := av.Int(), bv.Int()
+ if a < b {
+ return -1
+ } else if a > b {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64, reflect.Uintptr, reflect.UnsafePointer:
+ return func(av, bv reflect.Value) int {
+ a, b := av.Uint(), bv.Uint()
+ if a < b {
+ return -1
+ } else if a > b {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Float32, reflect.Float64:
+ return func(av, bv reflect.Value) int {
+ a, b := av.Float(), bv.Float()
+ if a < b {
+ return -1
+ } else if a > b {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Interface:
+ return func(av, bv reflect.Value) int {
+ a, b := av.InterfaceData(), bv.InterfaceData()
+ if a[0] < b[0] {
+ return -1
+ } else if a[0] > b[0] {
+ return 1
+ }
+ if a[1] < b[1] {
+ return -1
+ } else if a[1] > b[1] {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Complex64, reflect.Complex128:
+ return func(av, bv reflect.Value) int {
+ a, b := av.Complex(), bv.Complex()
+ if real(a) < real(b) {
+ return -1
+ } else if real(a) > real(b) {
+ return 1
+ }
+ if imag(a) < imag(b) {
+ return -1
+ } else if imag(a) > imag(b) {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Ptr, reflect.Chan:
+ return func(av, bv reflect.Value) int {
+ a, b := av.Pointer(), bv.Pointer()
+ if a < b {
+ return -1
+ } else if a > b {
+ return 1
+ }
+ return 0
+ }
+
+ case reflect.Struct:
+ cmpLst := make([]cmpFn, t.NumField())
+ for i := range cmpLst {
+ cmpLst[i] = cmpForType(t.Field(i).Type)
+ }
+ return func(a, b reflect.Value) int {
+ for i, cmp := range cmpLst {
+ if rslt := cmp(a.Field(i), b.Field(i)); rslt != 0 {
+ return rslt
+ }
+ }
+ return 0
+ }
+ }
+
+ return nil
+}
+
+func tryAndSortMapKeys(mt reflect.Type, k []reflect.Value) {
+ if cmp := cmpForType(mt.Key()); cmp != nil {
+ sort.Sort(sortableValueSlice{cmp, k})
+ }
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/go-render/render/render_time.go b/vendor/github.com/smartystreets/assertions/internal/go-render/render/render_time.go
new file mode 100644
index 0000000..990c75d
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/go-render/render/render_time.go
@@ -0,0 +1,26 @@
+package render
+
+import (
+ "reflect"
+ "time"
+)
+
+func renderTime(value reflect.Value) (string, bool) {
+ if instant, ok := convertTime(value); !ok {
+ return "", false
+ } else if instant.IsZero() {
+ return "0", true
+ } else {
+ return instant.String(), true
+ }
+}
+
+func convertTime(value reflect.Value) (t time.Time, ok bool) {
+ if value.Type() == timeType {
+ defer func() { recover() }()
+ t, ok = value.Interface().(time.Time)
+ }
+ return
+}
+
+var timeType = reflect.TypeOf(time.Time{})
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/.gitignore b/vendor/github.com/smartystreets/assertions/internal/oglematchers/.gitignore
new file mode 100644
index 0000000..dd8fc74
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/.gitignore
@@ -0,0 +1,5 @@
+*.6
+6.out
+_obj/
+_test/
+_testmain.go
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/.travis.yml b/vendor/github.com/smartystreets/assertions/internal/oglematchers/.travis.yml
new file mode 100644
index 0000000..b972119
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/.travis.yml
@@ -0,0 +1,4 @@
+# Cf. http://docs.travis-ci.com/user/getting-started/
+# Cf. http://docs.travis-ci.com/user/languages/go/
+
+language: go
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE b/vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md b/vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md
new file mode 100644
index 0000000..215a2bb
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md
@@ -0,0 +1,58 @@
+[![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers)
+
+`oglematchers` is a package for the Go programming language containing a set of
+matchers, useful in a testing or mocking framework, inspired by and mostly
+compatible with [Google Test][googletest] for C++ and
+[Google JS Test][google-js-test]. The package is used by the
+[ogletest][ogletest] testing framework and [oglemock][oglemock] mocking
+framework, which may be more directly useful to you, but can be generically used
+elsewhere as well.
+
+A "matcher" is simply an object with a `Matches` method defining a set of golang
+values matched by the matcher, and a `Description` method describing that set.
+For example, here are some matchers:
+
+```go
+// Numbers
+Equals(17.13)
+LessThan(19)
+
+// Strings
+Equals("taco")
+HasSubstr("burrito")
+MatchesRegex("t.*o")
+
+// Combining matchers
+AnyOf(LessThan(17), GreaterThan(19))
+```
+
+There are lots more; see [here][reference] for a reference. You can also add
+your own simply by implementing the `oglematchers.Matcher` interface.
+
+
+Installation
+------------
+
+First, make sure you have installed Go 1.0.2 or newer. See
+[here][golang-install] for instructions.
+
+Use the following command to install `oglematchers` and keep it up to date:
+
+ go get -u github.com/smartystreets/assertions/internal/oglematchers
+
+
+Documentation
+-------------
+
+See [here][reference] for documentation. Alternatively, you can install the
+package and then use `godoc`:
+
+ godoc github.com/smartystreets/assertions/internal/oglematchers
+
+
+[reference]: http://godoc.org/github.com/smartystreets/assertions/internal/oglematchers
+[golang-install]: http://golang.org/doc/install.html
+[googletest]: http://code.google.com/p/googletest/
+[google-js-test]: http://code.google.com/p/google-js-test/
+[ogletest]: http://github.com/smartystreets/assertions/internal/ogletest
+[oglemock]: http://github.com/smartystreets/assertions/internal/oglemock
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go
new file mode 100644
index 0000000..2918b51
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go
@@ -0,0 +1,94 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// AnyOf accepts a set of values S and returns a matcher that follows the
+// algorithm below when considering a candidate c:
+//
+// 1. If there exists a value m in S such that m implements the Matcher
+// interface and m matches c, return true.
+//
+// 2. Otherwise, if there exists a value v in S such that v does not implement
+// the Matcher interface and the matcher Equals(v) matches c, return true.
+//
+// 3. Otherwise, if there is a value m in S such that m implements the Matcher
+// interface and m returns a fatal error for c, return that fatal error.
+//
+// 4. Otherwise, return false.
+//
+// This is akin to a logical OR operation for matchers, with non-matchers x
+// being treated as Equals(x).
+func AnyOf(vals ...interface{}) Matcher {
+ // Get ahold of a type variable for the Matcher interface.
+ var dummy *Matcher
+ matcherType := reflect.TypeOf(dummy).Elem()
+
+ // Create a matcher for each value, or use the value itself if it's already a
+ // matcher.
+ wrapped := make([]Matcher, len(vals))
+ for i, v := range vals {
+ t := reflect.TypeOf(v)
+ if t != nil && t.Implements(matcherType) {
+ wrapped[i] = v.(Matcher)
+ } else {
+ wrapped[i] = Equals(v)
+ }
+ }
+
+ return &anyOfMatcher{wrapped}
+}
+
+type anyOfMatcher struct {
+ wrapped []Matcher
+}
+
+func (m *anyOfMatcher) Description() string {
+ wrappedDescs := make([]string, len(m.wrapped))
+ for i, matcher := range m.wrapped {
+ wrappedDescs[i] = matcher.Description()
+ }
+
+ return fmt.Sprintf("or(%s)", strings.Join(wrappedDescs, ", "))
+}
+
+func (m *anyOfMatcher) Matches(c interface{}) (err error) {
+ err = errors.New("")
+
+ // Try each matcher in turn.
+ for _, matcher := range m.wrapped {
+ wrappedErr := matcher.Matches(c)
+
+ // Return immediately if there's a match.
+ if wrappedErr == nil {
+ err = nil
+ return
+ }
+
+ // Note the fatal error, if any.
+ if _, isFatal := wrappedErr.(*FatalError); isFatal {
+ err = wrappedErr
+ }
+ }
+
+ return
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go
new file mode 100644
index 0000000..87f107d
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go
@@ -0,0 +1,61 @@
+// Copyright 2012 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// Return a matcher that matches arrays slices with at least one element that
+// matches the supplied argument. If the argument x is not itself a Matcher,
+// this is equivalent to Contains(Equals(x)).
+func Contains(x interface{}) Matcher {
+ var result containsMatcher
+ var ok bool
+
+ if result.elementMatcher, ok = x.(Matcher); !ok {
+ result.elementMatcher = DeepEquals(x)
+ }
+
+ return &result
+}
+
+type containsMatcher struct {
+ elementMatcher Matcher
+}
+
+func (m *containsMatcher) Description() string {
+ return fmt.Sprintf("contains: %s", m.elementMatcher.Description())
+}
+
+func (m *containsMatcher) Matches(candidate interface{}) error {
+ // The candidate must be a slice or an array.
+ v := reflect.ValueOf(candidate)
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return NewFatalError("which is not a slice or array")
+ }
+
+ // Check each element.
+ for i := 0; i < v.Len(); i++ {
+ elem := v.Index(i)
+ if matchErr := m.elementMatcher.Matches(elem.Interface()); matchErr == nil {
+ return nil
+ }
+ }
+
+ return fmt.Errorf("")
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go
new file mode 100644
index 0000000..1d91bae
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go
@@ -0,0 +1,88 @@
+// Copyright 2012 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+var byteSliceType reflect.Type = reflect.TypeOf([]byte{})
+
+// DeepEquals returns a matcher that matches based on 'deep equality', as
+// defined by the reflect package. This matcher requires that values have
+// identical types to x.
+func DeepEquals(x interface{}) Matcher {
+ return &deepEqualsMatcher{x}
+}
+
+type deepEqualsMatcher struct {
+ x interface{}
+}
+
+func (m *deepEqualsMatcher) Description() string {
+ xDesc := fmt.Sprintf("%v", m.x)
+ xValue := reflect.ValueOf(m.x)
+
+ // Special case: fmt.Sprintf presents nil slices as "[]", but
+ // reflect.DeepEqual makes a distinction between nil and empty slices. Make
+ // this less confusing.
+ if xValue.Kind() == reflect.Slice && xValue.IsNil() {
+ xDesc = "<nil slice>"
+ }
+
+ return fmt.Sprintf("deep equals: %s", xDesc)
+}
+
+func (m *deepEqualsMatcher) Matches(c interface{}) error {
+ // Make sure the types match.
+ ct := reflect.TypeOf(c)
+ xt := reflect.TypeOf(m.x)
+
+ if ct != xt {
+ return NewFatalError(fmt.Sprintf("which is of type %v", ct))
+ }
+
+ // Special case: handle byte slices more efficiently.
+ cValue := reflect.ValueOf(c)
+ xValue := reflect.ValueOf(m.x)
+
+ if ct == byteSliceType && !cValue.IsNil() && !xValue.IsNil() {
+ xBytes := m.x.([]byte)
+ cBytes := c.([]byte)
+
+ if bytes.Equal(cBytes, xBytes) {
+ return nil
+ }
+
+ return errors.New("")
+ }
+
+ // Defer to the reflect package.
+ if reflect.DeepEqual(m.x, c) {
+ return nil
+ }
+
+ // Special case: if the comparison failed because c is the nil slice, given
+ // an indication of this (since its value is printed as "[]").
+ if cValue.Kind() == reflect.Slice && cValue.IsNil() {
+ return errors.New("which is nil")
+ }
+
+ return errors.New("")
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go
new file mode 100644
index 0000000..a510707
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/equals.go
@@ -0,0 +1,541 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+)
+
+// Equals(x) returns a matcher that matches values v such that v and x are
+// equivalent. This includes the case when the comparison v == x using Go's
+// built-in comparison operator is legal (except for structs, which this
+// matcher does not support), but for convenience the following rules also
+// apply:
+//
+// * Type checking is done based on underlying types rather than actual
+// types, so that e.g. two aliases for string can be compared:
+//
+// type stringAlias1 string
+// type stringAlias2 string
+//
+// a := "taco"
+// b := stringAlias1("taco")
+// c := stringAlias2("taco")
+//
+// ExpectTrue(a == b) // Legal, passes
+// ExpectTrue(b == c) // Illegal, doesn't compile
+//
+// ExpectThat(a, Equals(b)) // Passes
+// ExpectThat(b, Equals(c)) // Passes
+//
+// * Values of numeric type are treated as if they were abstract numbers, and
+// compared accordingly. Therefore Equals(17) will match int(17),
+// int16(17), uint(17), float32(17), complex64(17), and so on.
+//
+// If you want a stricter matcher that contains no such cleverness, see
+// IdenticalTo instead.
+//
+// Arrays are supported by this matcher, but do not participate in the
+// exceptions above. Two arrays compared with this matcher must have identical
+// types, and their element type must itself be comparable according to Go's ==
+// operator.
+func Equals(x interface{}) Matcher {
+ v := reflect.ValueOf(x)
+
+ // This matcher doesn't support structs.
+ if v.Kind() == reflect.Struct {
+ panic(fmt.Sprintf("oglematchers.Equals: unsupported kind %v", v.Kind()))
+ }
+
+ // The == operator is not defined for non-nil slices.
+ if v.Kind() == reflect.Slice && v.Pointer() != uintptr(0) {
+ panic(fmt.Sprintf("oglematchers.Equals: non-nil slice"))
+ }
+
+ return &equalsMatcher{v}
+}
+
+type equalsMatcher struct {
+ expectedValue reflect.Value
+}
+
+////////////////////////////////////////////////////////////////////////
+// Numeric types
+////////////////////////////////////////////////////////////////////////
+
+func isSignedInteger(v reflect.Value) bool {
+ k := v.Kind()
+ return k >= reflect.Int && k <= reflect.Int64
+}
+
+func isUnsignedInteger(v reflect.Value) bool {
+ k := v.Kind()
+ return k >= reflect.Uint && k <= reflect.Uintptr
+}
+
+func isInteger(v reflect.Value) bool {
+ return isSignedInteger(v) || isUnsignedInteger(v)
+}
+
+func isFloat(v reflect.Value) bool {
+ k := v.Kind()
+ return k == reflect.Float32 || k == reflect.Float64
+}
+
+func isComplex(v reflect.Value) bool {
+ k := v.Kind()
+ return k == reflect.Complex64 || k == reflect.Complex128
+}
+
+func checkAgainstInt64(e int64, c reflect.Value) (err error) {
+ err = errors.New("")
+
+ switch {
+ case isSignedInteger(c):
+ if c.Int() == e {
+ err = nil
+ }
+
+ case isUnsignedInteger(c):
+ u := c.Uint()
+ if u <= math.MaxInt64 && int64(u) == e {
+ err = nil
+ }
+
+ // Turn around the various floating point types so that the checkAgainst*
+ // functions for them can deal with precision issues.
+ case isFloat(c), isComplex(c):
+ return Equals(c.Interface()).Matches(e)
+
+ default:
+ err = NewFatalError("which is not numeric")
+ }
+
+ return
+}
+
+func checkAgainstUint64(e uint64, c reflect.Value) (err error) {
+ err = errors.New("")
+
+ switch {
+ case isSignedInteger(c):
+ i := c.Int()
+ if i >= 0 && uint64(i) == e {
+ err = nil
+ }
+
+ case isUnsignedInteger(c):
+ if c.Uint() == e {
+ err = nil
+ }
+
+ // Turn around the various floating point types so that the checkAgainst*
+ // functions for them can deal with precision issues.
+ case isFloat(c), isComplex(c):
+ return Equals(c.Interface()).Matches(e)
+
+ default:
+ err = NewFatalError("which is not numeric")
+ }
+
+ return
+}
+
+func checkAgainstFloat32(e float32, c reflect.Value) (err error) {
+ err = errors.New("")
+
+ switch {
+ case isSignedInteger(c):
+ if float32(c.Int()) == e {
+ err = nil
+ }
+
+ case isUnsignedInteger(c):
+ if float32(c.Uint()) == e {
+ err = nil
+ }
+
+ case isFloat(c):
+ // Compare using float32 to avoid a false sense of precision; otherwise
+ // e.g. Equals(float32(0.1)) won't match float32(0.1).
+ if float32(c.Float()) == e {
+ err = nil
+ }
+
+ case isComplex(c):
+ comp := c.Complex()
+ rl := real(comp)
+ im := imag(comp)
+
+ // Compare using float32 to avoid a false sense of precision; otherwise
+ // e.g. Equals(float32(0.1)) won't match (0.1 + 0i).
+ if im == 0 && float32(rl) == e {
+ err = nil
+ }
+
+ default:
+ err = NewFatalError("which is not numeric")
+ }
+
+ return
+}
+
+func checkAgainstFloat64(e float64, c reflect.Value) (err error) {
+ err = errors.New("")
+
+ ck := c.Kind()
+
+ switch {
+ case isSignedInteger(c):
+ if float64(c.Int()) == e {
+ err = nil
+ }
+
+ case isUnsignedInteger(c):
+ if float64(c.Uint()) == e {
+ err = nil
+ }
+
+ // If the actual value is lower precision, turn the comparison around so we
+ // apply the low-precision rules. Otherwise, e.g. Equals(0.1) may not match
+ // float32(0.1).
+ case ck == reflect.Float32 || ck == reflect.Complex64:
+ return Equals(c.Interface()).Matches(e)
+
+ // Otherwise, compare with double precision.
+ case isFloat(c):
+ if c.Float() == e {
+ err = nil
+ }
+
+ case isComplex(c):
+ comp := c.Complex()
+ rl := real(comp)
+ im := imag(comp)
+
+ if im == 0 && rl == e {
+ err = nil
+ }
+
+ default:
+ err = NewFatalError("which is not numeric")
+ }
+
+ return
+}
+
+func checkAgainstComplex64(e complex64, c reflect.Value) (err error) {
+ err = errors.New("")
+ realPart := real(e)
+ imaginaryPart := imag(e)
+
+ switch {
+ case isInteger(c) || isFloat(c):
+ // If we have no imaginary part, then we should just compare against the
+ // real part. Otherwise, we can't be equal.
+ if imaginaryPart != 0 {
+ return
+ }
+
+ return checkAgainstFloat32(realPart, c)
+
+ case isComplex(c):
+ // Compare using complex64 to avoid a false sense of precision; otherwise
+ // e.g. Equals(0.1 + 0i) won't match float32(0.1).
+ if complex64(c.Complex()) == e {
+ err = nil
+ }
+
+ default:
+ err = NewFatalError("which is not numeric")
+ }
+
+ return
+}
+
+func checkAgainstComplex128(e complex128, c reflect.Value) (err error) {
+ err = errors.New("")
+ realPart := real(e)
+ imaginaryPart := imag(e)
+
+ switch {
+ case isInteger(c) || isFloat(c):
+ // If we have no imaginary part, then we should just compare against the
+ // real part. Otherwise, we can't be equal.
+ if imaginaryPart != 0 {
+ return
+ }
+
+ return checkAgainstFloat64(realPart, c)
+
+ case isComplex(c):
+ if c.Complex() == e {
+ err = nil
+ }
+
+ default:
+ err = NewFatalError("which is not numeric")
+ }
+
+ return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Other types
+////////////////////////////////////////////////////////////////////////
+
+func checkAgainstBool(e bool, c reflect.Value) (err error) {
+ if c.Kind() != reflect.Bool {
+ err = NewFatalError("which is not a bool")
+ return
+ }
+
+ err = errors.New("")
+ if c.Bool() == e {
+ err = nil
+ }
+ return
+}
+
+func checkAgainstChan(e reflect.Value, c reflect.Value) (err error) {
+ // Create a description of e's type, e.g. "chan int".
+ typeStr := fmt.Sprintf("%s %s", e.Type().ChanDir(), e.Type().Elem())
+
+ // Make sure c is a chan of the correct type.
+ if c.Kind() != reflect.Chan ||
+ c.Type().ChanDir() != e.Type().ChanDir() ||
+ c.Type().Elem() != e.Type().Elem() {
+ err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr))
+ return
+ }
+
+ err = errors.New("")
+ if c.Pointer() == e.Pointer() {
+ err = nil
+ }
+ return
+}
+
+func checkAgainstFunc(e reflect.Value, c reflect.Value) (err error) {
+ // Make sure c is a function.
+ if c.Kind() != reflect.Func {
+ err = NewFatalError("which is not a function")
+ return
+ }
+
+ err = errors.New("")
+ if c.Pointer() == e.Pointer() {
+ err = nil
+ }
+ return
+}
+
+func checkAgainstMap(e reflect.Value, c reflect.Value) (err error) {
+ // Make sure c is a map.
+ if c.Kind() != reflect.Map {
+ err = NewFatalError("which is not a map")
+ return
+ }
+
+ err = errors.New("")
+ if c.Pointer() == e.Pointer() {
+ err = nil
+ }
+ return
+}
+
+func checkAgainstPtr(e reflect.Value, c reflect.Value) (err error) {
+ // Create a description of e's type, e.g. "*int".
+ typeStr := fmt.Sprintf("*%v", e.Type().Elem())
+
+ // Make sure c is a pointer of the correct type.
+ if c.Kind() != reflect.Ptr ||
+ c.Type().Elem() != e.Type().Elem() {
+ err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr))
+ return
+ }
+
+ err = errors.New("")
+ if c.Pointer() == e.Pointer() {
+ err = nil
+ }
+ return
+}
+
+func checkAgainstSlice(e reflect.Value, c reflect.Value) (err error) {
+ // Create a description of e's type, e.g. "[]int".
+ typeStr := fmt.Sprintf("[]%v", e.Type().Elem())
+
+ // Make sure c is a slice of the correct type.
+ if c.Kind() != reflect.Slice ||
+ c.Type().Elem() != e.Type().Elem() {
+ err = NewFatalError(fmt.Sprintf("which is not a %s", typeStr))
+ return
+ }
+
+ err = errors.New("")
+ if c.Pointer() == e.Pointer() {
+ err = nil
+ }
+ return
+}
+
+func checkAgainstString(e reflect.Value, c reflect.Value) (err error) {
+ // Make sure c is a string.
+ if c.Kind() != reflect.String {
+ err = NewFatalError("which is not a string")
+ return
+ }
+
+ err = errors.New("")
+ if c.String() == e.String() {
+ err = nil
+ }
+ return
+}
+
+func checkAgainstArray(e reflect.Value, c reflect.Value) (err error) {
+ // Create a description of e's type, e.g. "[2]int".
+ typeStr := fmt.Sprintf("%v", e.Type())
+
+ // Make sure c is the correct type.
+ if c.Type() != e.Type() {
+ err = NewFatalError(fmt.Sprintf("which is not %s", typeStr))
+ return
+ }
+
+ // Check for equality.
+ if e.Interface() != c.Interface() {
+ err = errors.New("")
+ return
+ }
+
+ return
+}
+
+func checkAgainstUnsafePointer(e reflect.Value, c reflect.Value) (err error) {
+ // Make sure c is a pointer.
+ if c.Kind() != reflect.UnsafePointer {
+ err = NewFatalError("which is not a unsafe.Pointer")
+ return
+ }
+
+ err = errors.New("")
+ if c.Pointer() == e.Pointer() {
+ err = nil
+ }
+ return
+}
+
+func checkForNil(c reflect.Value) (err error) {
+ err = errors.New("")
+
+ // Make sure it is legal to call IsNil.
+ switch c.Kind() {
+ case reflect.Invalid:
+ case reflect.Chan:
+ case reflect.Func:
+ case reflect.Interface:
+ case reflect.Map:
+ case reflect.Ptr:
+ case reflect.Slice:
+
+ default:
+ err = NewFatalError("which cannot be compared to nil")
+ return
+ }
+
+ // Ask whether the value is nil. Handle a nil literal (kind Invalid)
+ // specially, since it's not legal to call IsNil there.
+ if c.Kind() == reflect.Invalid || c.IsNil() {
+ err = nil
+ }
+ return
+}
+
+////////////////////////////////////////////////////////////////////////
+// Public implementation
+////////////////////////////////////////////////////////////////////////
+
+func (m *equalsMatcher) Matches(candidate interface{}) error {
+ e := m.expectedValue
+ c := reflect.ValueOf(candidate)
+ ek := e.Kind()
+
+ switch {
+ case ek == reflect.Bool:
+ return checkAgainstBool(e.Bool(), c)
+
+ case isSignedInteger(e):
+ return checkAgainstInt64(e.Int(), c)
+
+ case isUnsignedInteger(e):
+ return checkAgainstUint64(e.Uint(), c)
+
+ case ek == reflect.Float32:
+ return checkAgainstFloat32(float32(e.Float()), c)
+
+ case ek == reflect.Float64:
+ return checkAgainstFloat64(e.Float(), c)
+
+ case ek == reflect.Complex64:
+ return checkAgainstComplex64(complex64(e.Complex()), c)
+
+ case ek == reflect.Complex128:
+ return checkAgainstComplex128(complex128(e.Complex()), c)
+
+ case ek == reflect.Chan:
+ return checkAgainstChan(e, c)
+
+ case ek == reflect.Func:
+ return checkAgainstFunc(e, c)
+
+ case ek == reflect.Map:
+ return checkAgainstMap(e, c)
+
+ case ek == reflect.Ptr:
+ return checkAgainstPtr(e, c)
+
+ case ek == reflect.Slice:
+ return checkAgainstSlice(e, c)
+
+ case ek == reflect.String:
+ return checkAgainstString(e, c)
+
+ case ek == reflect.Array:
+ return checkAgainstArray(e, c)
+
+ case ek == reflect.UnsafePointer:
+ return checkAgainstUnsafePointer(e, c)
+
+ case ek == reflect.Invalid:
+ return checkForNil(c)
+ }
+
+ panic(fmt.Sprintf("equalsMatcher.Matches: unexpected kind: %v", ek))
+}
+
+func (m *equalsMatcher) Description() string {
+ // Special case: handle nil.
+ if !m.expectedValue.IsValid() {
+ return "is nil"
+ }
+
+ return fmt.Sprintf("%v", m.expectedValue.Interface())
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go
new file mode 100644
index 0000000..4b9d103
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go
@@ -0,0 +1,39 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// GreaterOrEqual returns a matcher that matches integer, floating point, or
+// strings values v such that v >= x. Comparison is not defined between numeric
+// and string types, but is defined between all integer and floating point
+// types.
+//
+// x must itself be an integer, floating point, or string type; otherwise,
+// GreaterOrEqual will panic.
+func GreaterOrEqual(x interface{}) Matcher {
+ desc := fmt.Sprintf("greater than or equal to %v", x)
+
+ // Special case: make it clear that strings are strings.
+ if reflect.TypeOf(x).Kind() == reflect.String {
+ desc = fmt.Sprintf("greater than or equal to \"%s\"", x)
+ }
+
+ return transformDescription(Not(LessThan(x)), desc)
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go
new file mode 100644
index 0000000..3eef321
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go
@@ -0,0 +1,39 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// GreaterThan returns a matcher that matches integer, floating point, or
+// strings values v such that v > x. Comparison is not defined between numeric
+// and string types, but is defined between all integer and floating point
+// types.
+//
+// x must itself be an integer, floating point, or string type; otherwise,
+// GreaterThan will panic.
+func GreaterThan(x interface{}) Matcher {
+ desc := fmt.Sprintf("greater than %v", x)
+
+ // Special case: make it clear that strings are strings.
+ if reflect.TypeOf(x).Kind() == reflect.String {
+ desc = fmt.Sprintf("greater than \"%s\"", x)
+ }
+
+ return transformDescription(Not(LessOrEqual(x)), desc)
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go
new file mode 100644
index 0000000..8402cde
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go
@@ -0,0 +1,41 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// LessOrEqual returns a matcher that matches integer, floating point, or
+// strings values v such that v <= x. Comparison is not defined between numeric
+// and string types, but is defined between all integer and floating point
+// types.
+//
+// x must itself be an integer, floating point, or string type; otherwise,
+// LessOrEqual will panic.
+func LessOrEqual(x interface{}) Matcher {
+ desc := fmt.Sprintf("less than or equal to %v", x)
+
+ // Special case: make it clear that strings are strings.
+ if reflect.TypeOf(x).Kind() == reflect.String {
+ desc = fmt.Sprintf("less than or equal to \"%s\"", x)
+ }
+
+ // Put LessThan last so that its error messages will be used in the event of
+ // failure.
+ return transformDescription(AnyOf(Equals(x), LessThan(x)), desc)
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go
new file mode 100644
index 0000000..8258e45
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go
@@ -0,0 +1,152 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+)
+
+// LessThan returns a matcher that matches integer, floating point, or strings
+// values v such that v < x. Comparison is not defined between numeric and
+// string types, but is defined between all integer and floating point types.
+//
+// x must itself be an integer, floating point, or string type; otherwise,
+// LessThan will panic.
+func LessThan(x interface{}) Matcher {
+ v := reflect.ValueOf(x)
+ kind := v.Kind()
+
+ switch {
+ case isInteger(v):
+ case isFloat(v):
+ case kind == reflect.String:
+
+ default:
+ panic(fmt.Sprintf("LessThan: unexpected kind %v", kind))
+ }
+
+ return &lessThanMatcher{v}
+}
+
+type lessThanMatcher struct {
+ limit reflect.Value
+}
+
+func (m *lessThanMatcher) Description() string {
+ // Special case: make it clear that strings are strings.
+ if m.limit.Kind() == reflect.String {
+ return fmt.Sprintf("less than \"%s\"", m.limit.String())
+ }
+
+ return fmt.Sprintf("less than %v", m.limit.Interface())
+}
+
+func compareIntegers(v1, v2 reflect.Value) (err error) {
+ err = errors.New("")
+
+ switch {
+ case isSignedInteger(v1) && isSignedInteger(v2):
+ if v1.Int() < v2.Int() {
+ err = nil
+ }
+ return
+
+ case isSignedInteger(v1) && isUnsignedInteger(v2):
+ if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() {
+ err = nil
+ }
+ return
+
+ case isUnsignedInteger(v1) && isSignedInteger(v2):
+ if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() {
+ err = nil
+ }
+ return
+
+ case isUnsignedInteger(v1) && isUnsignedInteger(v2):
+ if v1.Uint() < v2.Uint() {
+ err = nil
+ }
+ return
+ }
+
+ panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2))
+}
+
+func getFloat(v reflect.Value) float64 {
+ switch {
+ case isSignedInteger(v):
+ return float64(v.Int())
+
+ case isUnsignedInteger(v):
+ return float64(v.Uint())
+
+ case isFloat(v):
+ return v.Float()
+ }
+
+ panic(fmt.Sprintf("getFloat: %v", v))
+}
+
+func (m *lessThanMatcher) Matches(c interface{}) (err error) {
+ v1 := reflect.ValueOf(c)
+ v2 := m.limit
+
+ err = errors.New("")
+
+ // Handle strings as a special case.
+ if v1.Kind() == reflect.String && v2.Kind() == reflect.String {
+ if v1.String() < v2.String() {
+ err = nil
+ }
+ return
+ }
+
+ // If we get here, we require that we are dealing with integers or floats.
+ v1Legal := isInteger(v1) || isFloat(v1)
+ v2Legal := isInteger(v2) || isFloat(v2)
+ if !v1Legal || !v2Legal {
+ err = NewFatalError("which is not comparable")
+ return
+ }
+
+ // Handle the various comparison cases.
+ switch {
+ // Both integers
+ case isInteger(v1) && isInteger(v2):
+ return compareIntegers(v1, v2)
+
+ // At least one float32
+ case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32:
+ if float32(getFloat(v1)) < float32(getFloat(v2)) {
+ err = nil
+ }
+ return
+
+ // At least one float64
+ case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64:
+ if getFloat(v1) < getFloat(v2) {
+ err = nil
+ }
+ return
+ }
+
+ // We shouldn't get here.
+ panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2))
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go
new file mode 100644
index 0000000..78159a0
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go
@@ -0,0 +1,86 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package oglematchers provides a set of matchers useful in a testing or
+// mocking framework. These matchers are inspired by and mostly compatible with
+// Google Test for C++ and Google JS Test.
+//
+// This package is used by github.com/smartystreets/assertions/internal/ogletest and
+// github.com/smartystreets/assertions/internal/oglemock, which may be more directly useful if you're not
+// writing your own testing package or defining your own matchers.
+package oglematchers
+
+// A Matcher is some predicate implicitly defining a set of values that it
+// matches. For example, GreaterThan(17) matches all numeric values greater
+// than 17, and HasSubstr("taco") matches all strings with the substring
+// "taco".
+//
+// Matchers are typically exposed to tests via constructor functions like
+// HasSubstr. In order to implement such a function you can either define your
+// own matcher type or use NewMatcher.
+type Matcher interface {
+ // Check whether the supplied value belongs to the the set defined by the
+ // matcher. Return a non-nil error if and only if it does not.
+ //
+ // The error describes why the value doesn't match. The error text is a
+ // relative clause that is suitable for being placed after the value. For
+ // example, a predicate that matches strings with a particular substring may,
+ // when presented with a numerical value, return the following error text:
+ //
+ // "which is not a string"
+ //
+ // Then the failure message may look like:
+ //
+ // Expected: has substring "taco"
+ // Actual: 17, which is not a string
+ //
+ // If the error is self-apparent based on the description of the matcher, the
+ // error text may be empty (but the error still non-nil). For example:
+ //
+ // Expected: 17
+ // Actual: 19
+ //
+ // If you are implementing a new matcher, see also the documentation on
+ // FatalError.
+ Matches(candidate interface{}) error
+
+ // Description returns a string describing the property that values matching
+ // this matcher have, as a verb phrase where the subject is the value. For
+ // example, "is greather than 17" or "has substring "taco"".
+ Description() string
+}
+
+// FatalError is an implementation of the error interface that may be returned
+// from matchers, indicating the error should be propagated. Returning a
+// *FatalError indicates that the matcher doesn't process values of the
+// supplied type, or otherwise doesn't know how to handle the value.
+//
+// For example, if GreaterThan(17) returned false for the value "taco" without
+// a fatal error, then Not(GreaterThan(17)) would return true. This is
+// technically correct, but is surprising and may mask failures where the wrong
+// sort of matcher is accidentally used. Instead, GreaterThan(17) can return a
+// fatal error, which will be propagated by Not().
+type FatalError struct {
+ errorText string
+}
+
+// NewFatalError creates a FatalError struct with the supplied error text.
+func NewFatalError(s string) *FatalError {
+ return &FatalError{s}
+}
+
+func (e *FatalError) Error() string {
+ return e.errorText
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go
new file mode 100644
index 0000000..623789f
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go
@@ -0,0 +1,53 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+import (
+ "errors"
+ "fmt"
+)
+
+// Not returns a matcher that inverts the set of values matched by the wrapped
+// matcher. It does not transform the result for values for which the wrapped
+// matcher returns a fatal error.
+func Not(m Matcher) Matcher {
+ return &notMatcher{m}
+}
+
+type notMatcher struct {
+ wrapped Matcher
+}
+
+func (m *notMatcher) Matches(c interface{}) (err error) {
+ err = m.wrapped.Matches(c)
+
+ // Did the wrapped matcher say yes?
+ if err == nil {
+ return errors.New("")
+ }
+
+ // Did the wrapped matcher return a fatal error?
+ if _, isFatal := err.(*FatalError); isFatal {
+ return err
+ }
+
+ // The wrapped matcher returned a non-fatal error.
+ return nil
+}
+
+func (m *notMatcher) Description() string {
+ return fmt.Sprintf("not(%s)", m.wrapped.Description())
+}
diff --git a/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go b/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go
new file mode 100644
index 0000000..8ea2807
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go
@@ -0,0 +1,36 @@
+// Copyright 2011 Aaron Jacobs. All Rights Reserved.
+// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package oglematchers
+
+// transformDescription returns a matcher that is equivalent to the supplied
+// one, except that it has the supplied description instead of the one attached
+// to the existing matcher.
+func transformDescription(m Matcher, newDesc string) Matcher {
+ return &transformDescriptionMatcher{newDesc, m}
+}
+
+type transformDescriptionMatcher struct {
+ desc string
+ wrappedMatcher Matcher
+}
+
+func (m *transformDescriptionMatcher) Description() string {
+ return m.desc
+}
+
+func (m *transformDescriptionMatcher) Matches(c interface{}) error {
+ return m.wrappedMatcher.Matches(c)
+}
diff --git a/vendor/github.com/smartystreets/assertions/messages.go b/vendor/github.com/smartystreets/assertions/messages.go
new file mode 100644
index 0000000..6ed7dc2
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/messages.go
@@ -0,0 +1,97 @@
+package assertions
+
+const ( // equality
+ shouldHaveBeenEqual = "Expected: '%v'\nActual: '%v'\n(Should be equal)"
+ shouldNotHaveBeenEqual = "Expected '%v'\nto NOT equal '%v'\n(but it did)!"
+ shouldHaveBeenEqualTypeMismatch = "Expected: '%v' (%T)\nActual: '%v' (%T)\n(Should be equal, type mismatch)"
+ shouldHaveBeenAlmostEqual = "Expected '%v' to almost equal '%v' (but it didn't)!"
+ shouldHaveNotBeenAlmostEqual = "Expected '%v' to NOT almost equal '%v' (but it did)!"
+ shouldHaveResembled = "Expected: '%s'\nActual: '%s'\n(Should resemble)!"
+ shouldNotHaveResembled = "Expected '%#v'\nto NOT resemble '%#v'\n(but it did)!"
+ shouldBePointers = "Both arguments should be pointers "
+ shouldHaveBeenNonNilPointer = shouldBePointers + "(the %s was %s)!"
+ shouldHavePointedTo = "Expected '%+v' (address: '%v') and '%+v' (address: '%v') to be the same address (but their weren't)!"
+ shouldNotHavePointedTo = "Expected '%+v' and '%+v' to be different references (but they matched: '%v')!"
+ shouldHaveBeenNil = "Expected: nil\nActual: '%v'"
+ shouldNotHaveBeenNil = "Expected '%+v' to NOT be nil (but it was)!"
+ shouldHaveBeenTrue = "Expected: true\nActual: %v"
+ shouldHaveBeenFalse = "Expected: false\nActual: %v"
+ shouldHaveBeenZeroValue = "'%+v' should have been the zero value" //"Expected: (zero value)\nActual: %v"
+ shouldNotHaveBeenZeroValue = "'%+v' should NOT have been the zero value"
+)
+
+const ( // quantity comparisons
+ shouldHaveBeenGreater = "Expected '%v' to be greater than '%v' (but it wasn't)!"
+ shouldHaveBeenGreaterOrEqual = "Expected '%v' to be greater than or equal to '%v' (but it wasn't)!"
+ shouldHaveBeenLess = "Expected '%v' to be less than '%v' (but it wasn't)!"
+ shouldHaveBeenLessOrEqual = "Expected '%v' to be less than or equal to '%v' (but it wasn't)!"
+ shouldHaveBeenBetween = "Expected '%v' to be between '%v' and '%v' (but it wasn't)!"
+ shouldNotHaveBeenBetween = "Expected '%v' NOT to be between '%v' and '%v' (but it was)!"
+ shouldHaveDifferentUpperAndLower = "The lower and upper bounds must be different values (they were both '%v')."
+ shouldHaveBeenBetweenOrEqual = "Expected '%v' to be between '%v' and '%v' or equal to one of them (but it wasn't)!"
+ shouldNotHaveBeenBetweenOrEqual = "Expected '%v' NOT to be between '%v' and '%v' or equal to one of them (but it was)!"
+)
+
+const ( // collections
+ shouldHaveContained = "Expected the container (%v) to contain: '%v' (but it didn't)!"
+ shouldNotHaveContained = "Expected the container (%v) NOT to contain: '%v' (but it did)!"
+ shouldHaveContainedKey = "Expected the %v to contain the key: %v (but it didn't)!"
+ shouldNotHaveContainedKey = "Expected the %v NOT to contain the key: %v (but it did)!"
+ shouldHaveBeenIn = "Expected '%v' to be in the container (%v), but it wasn't!"
+ shouldNotHaveBeenIn = "Expected '%v' NOT to be in the container (%v), but it was!"
+ shouldHaveBeenAValidCollection = "You must provide a valid container (was %v)!"
+ shouldHaveBeenAValidMap = "You must provide a valid map type (was %v)!"
+ shouldHaveBeenEmpty = "Expected %+v to be empty (but it wasn't)!"
+ shouldNotHaveBeenEmpty = "Expected %+v to NOT be empty (but it was)!"
+ shouldHaveBeenAValidInteger = "You must provide a valid integer (was %v)!"
+ shouldHaveBeenAValidLength = "You must provide a valid positive integer (was %v)!"
+ shouldHaveHadLength = "Expected collection to have length equal to [%v], but it's length was [%v] instead! contents: %+v"
+)
+
+const ( // strings
+ shouldHaveStartedWith = "Expected '%v'\nto start with '%v'\n(but it didn't)!"
+ shouldNotHaveStartedWith = "Expected '%v'\nNOT to start with '%v'\n(but it did)!"
+ shouldHaveEndedWith = "Expected '%v'\nto end with '%v'\n(but it didn't)!"
+ shouldNotHaveEndedWith = "Expected '%v'\nNOT to end with '%v'\n(but it did)!"
+ shouldAllBeStrings = "All arguments to this assertion must be strings (you provided: %v)."
+ shouldBothBeStrings = "Both arguments to this assertion must be strings (you provided %v and %v)."
+ shouldBeString = "The argument to this assertion must be a string (you provided %v)."
+ shouldHaveContainedSubstring = "Expected '%s' to contain substring '%s' (but it didn't)!"
+ shouldNotHaveContainedSubstring = "Expected '%s' NOT to contain substring '%s' (but it did)!"
+ shouldHaveBeenBlank = "Expected '%s' to be blank (but it wasn't)!"
+ shouldNotHaveBeenBlank = "Expected value to NOT be blank (but it was)!"
+)
+
+const ( // panics
+ shouldUseVoidNiladicFunction = "You must provide a void, niladic function as the first argument!"
+ shouldHavePanickedWith = "Expected func() to panic with '%v' (but it panicked with '%v')!"
+ shouldHavePanicked = "Expected func() to panic (but it didn't)!"
+ shouldNotHavePanicked = "Expected func() NOT to panic (error: '%+v')!"
+ shouldNotHavePanickedWith = "Expected func() NOT to panic with '%v' (but it did)!"
+)
+
+const ( // type checking
+ shouldHaveBeenA = "Expected '%v' to be: '%v' (but was: '%v')!"
+ shouldNotHaveBeenA = "Expected '%v' to NOT be: '%v' (but it was)!"
+
+ shouldHaveImplemented = "Expected: '%v interface support'\nActual: '%v' does not implement the interface!"
+ shouldNotHaveImplemented = "Expected '%v'\nto NOT implement '%v'\n(but it did)!"
+ shouldCompareWithInterfacePointer = "The expected value must be a pointer to an interface type (eg. *fmt.Stringer)"
+ shouldNotBeNilActual = "The actual value was 'nil' and should be a value or a pointer to a value!"
+
+ shouldBeError = "Expected an error value (but was '%v' instead)!"
+ shouldBeErrorInvalidComparisonValue = "The final argument to this assertion must be a string or an error value (you provided: '%v')."
+)
+
+const ( // time comparisons
+ shouldUseTimes = "You must provide time instances as arguments to this assertion."
+ shouldUseTimeSlice = "You must provide a slice of time instances as the first argument to this assertion."
+ shouldUseDurationAndTime = "You must provide a duration and a time as arguments to this assertion."
+ shouldHaveHappenedBefore = "Expected '%v' to happen before '%v' (it happened '%v' after)!"
+ shouldHaveHappenedAfter = "Expected '%v' to happen after '%v' (it happened '%v' before)!"
+ shouldHaveHappenedBetween = "Expected '%v' to happen between '%v' and '%v' (it happened '%v' outside threshold)!"
+ shouldNotHaveHappenedOnOrBetween = "Expected '%v' to NOT happen on or between '%v' and '%v' (but it did)!"
+
+ // format params: incorrect-index, previous-index, previous-time, incorrect-index, incorrect-time
+ shouldHaveBeenChronological = "The 'Time' at index [%d] should have happened after the previous one (but it didn't!):\n [%d]: %s\n [%d]: %s (see, it happened before!)"
+)
diff --git a/vendor/github.com/smartystreets/assertions/panic.go b/vendor/github.com/smartystreets/assertions/panic.go
new file mode 100644
index 0000000..7e75db1
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/panic.go
@@ -0,0 +1,115 @@
+package assertions
+
+import "fmt"
+
+// ShouldPanic receives a void, niladic function and expects to recover a panic.
+func ShouldPanic(actual interface{}, expected ...interface{}) (message string) {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+
+ action, _ := actual.(func())
+
+ if action == nil {
+ message = shouldUseVoidNiladicFunction
+ return
+ }
+
+ defer func() {
+ recovered := recover()
+ if recovered == nil {
+ message = shouldHavePanicked
+ } else {
+ message = success
+ }
+ }()
+ action()
+
+ return
+}
+
+// ShouldNotPanic receives a void, niladic function and expects to execute the function without any panic.
+func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string) {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+
+ action, _ := actual.(func())
+
+ if action == nil {
+ message = shouldUseVoidNiladicFunction
+ return
+ }
+
+ defer func() {
+ recovered := recover()
+ if recovered != nil {
+ message = fmt.Sprintf(shouldNotHavePanicked, recovered)
+ } else {
+ message = success
+ }
+ }()
+ action()
+
+ return
+}
+
+// ShouldPanicWith receives a void, niladic function and expects to recover a panic with the second argument as the content.
+func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string) {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ action, _ := actual.(func())
+
+ if action == nil {
+ message = shouldUseVoidNiladicFunction
+ return
+ }
+
+ defer func() {
+ recovered := recover()
+ if recovered == nil {
+ message = shouldHavePanicked
+ } else {
+ if equal := ShouldEqual(recovered, expected[0]); equal != success {
+ message = serializer.serialize(expected[0], recovered, fmt.Sprintf(shouldHavePanickedWith, expected[0], recovered))
+ } else {
+ message = success
+ }
+ }
+ }()
+ action()
+
+ return
+}
+
+// ShouldNotPanicWith receives a void, niladic function and expects to recover a panic whose content differs from the second argument.
+func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string) {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ action, _ := actual.(func())
+
+ if action == nil {
+ message = shouldUseVoidNiladicFunction
+ return
+ }
+
+ defer func() {
+ recovered := recover()
+ if recovered == nil {
+ message = success
+ } else {
+ if equal := ShouldEqual(recovered, expected[0]); equal == success {
+ message = fmt.Sprintf(shouldNotHavePanickedWith, expected[0])
+ } else {
+ message = success
+ }
+ }
+ }()
+ action()
+
+ return
+}
diff --git a/vendor/github.com/smartystreets/assertions/quantity.go b/vendor/github.com/smartystreets/assertions/quantity.go
new file mode 100644
index 0000000..f28b0a0
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/quantity.go
@@ -0,0 +1,141 @@
+package assertions
+
+import (
+ "fmt"
+
+ "github.com/smartystreets/assertions/internal/oglematchers"
+)
+
+// ShouldBeGreaterThan receives exactly two parameters and ensures that the first is greater than the second.
+func ShouldBeGreaterThan(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ if matchError := oglematchers.GreaterThan(expected[0]).Matches(actual); matchError != nil {
+ return fmt.Sprintf(shouldHaveBeenGreater, actual, expected[0])
+ }
+ return success
+}
+
+// ShouldBeGreaterThanOrEqualTo receives exactly two parameters and ensures that the first is greater than or equal to the second.
+func ShouldBeGreaterThanOrEqualTo(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ } else if matchError := oglematchers.GreaterOrEqual(expected[0]).Matches(actual); matchError != nil {
+ return fmt.Sprintf(shouldHaveBeenGreaterOrEqual, actual, expected[0])
+ }
+ return success
+}
+
+// ShouldBeLessThan receives exactly two parameters and ensures that the first is less than the second.
+func ShouldBeLessThan(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ } else if matchError := oglematchers.LessThan(expected[0]).Matches(actual); matchError != nil {
+ return fmt.Sprintf(shouldHaveBeenLess, actual, expected[0])
+ }
+ return success
+}
+
+// ShouldBeLessThan receives exactly two parameters and ensures that the first is less than or equal to the second.
+func ShouldBeLessThanOrEqualTo(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ } else if matchError := oglematchers.LessOrEqual(expected[0]).Matches(actual); matchError != nil {
+ return fmt.Sprintf(shouldHaveBeenLessOrEqual, actual, expected[0])
+ }
+ return success
+}
+
+// ShouldBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound.
+// It ensures that the actual value is between both bounds (but not equal to either of them).
+func ShouldBeBetween(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ lower, upper, fail := deriveBounds(expected)
+
+ if fail != success {
+ return fail
+ } else if !isBetween(actual, lower, upper) {
+ return fmt.Sprintf(shouldHaveBeenBetween, actual, lower, upper)
+ }
+ return success
+}
+
+// ShouldNotBeBetween receives exactly three parameters: an actual value, a lower bound, and an upper bound.
+// It ensures that the actual value is NOT between both bounds.
+func ShouldNotBeBetween(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ lower, upper, fail := deriveBounds(expected)
+
+ if fail != success {
+ return fail
+ } else if isBetween(actual, lower, upper) {
+ return fmt.Sprintf(shouldNotHaveBeenBetween, actual, lower, upper)
+ }
+ return success
+}
+func deriveBounds(values []interface{}) (lower interface{}, upper interface{}, fail string) {
+ lower = values[0]
+ upper = values[1]
+
+ if ShouldNotEqual(lower, upper) != success {
+ return nil, nil, fmt.Sprintf(shouldHaveDifferentUpperAndLower, lower)
+ } else if ShouldBeLessThan(lower, upper) != success {
+ lower, upper = upper, lower
+ }
+ return lower, upper, success
+}
+func isBetween(value, lower, upper interface{}) bool {
+ if ShouldBeGreaterThan(value, lower) != success {
+ return false
+ } else if ShouldBeLessThan(value, upper) != success {
+ return false
+ }
+ return true
+}
+
+// ShouldBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound.
+// It ensures that the actual value is between both bounds or equal to one of them.
+func ShouldBeBetweenOrEqual(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ lower, upper, fail := deriveBounds(expected)
+
+ if fail != success {
+ return fail
+ } else if !isBetweenOrEqual(actual, lower, upper) {
+ return fmt.Sprintf(shouldHaveBeenBetweenOrEqual, actual, lower, upper)
+ }
+ return success
+}
+
+// ShouldNotBeBetweenOrEqual receives exactly three parameters: an actual value, a lower bound, and an upper bound.
+// It ensures that the actual value is nopt between the bounds nor equal to either of them.
+func ShouldNotBeBetweenOrEqual(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ lower, upper, fail := deriveBounds(expected)
+
+ if fail != success {
+ return fail
+ } else if isBetweenOrEqual(actual, lower, upper) {
+ return fmt.Sprintf(shouldNotHaveBeenBetweenOrEqual, actual, lower, upper)
+ }
+ return success
+}
+
+func isBetweenOrEqual(value, lower, upper interface{}) bool {
+ if ShouldBeGreaterThanOrEqualTo(value, lower) != success {
+ return false
+ } else if ShouldBeLessThanOrEqualTo(value, upper) != success {
+ return false
+ }
+ return true
+}
diff --git a/vendor/github.com/smartystreets/assertions/serializer.go b/vendor/github.com/smartystreets/assertions/serializer.go
new file mode 100644
index 0000000..fa32f94
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/serializer.go
@@ -0,0 +1,63 @@
+package assertions
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/smartystreets/assertions/internal/go-render/render"
+)
+
+type Serializer interface {
+ serialize(expected, actual interface{}, message string) string
+ serializeDetailed(expected, actual interface{}, message string) string
+}
+
+type failureSerializer struct{}
+
+func (self *failureSerializer) serializeDetailed(expected, actual interface{}, message string) string {
+ view := FailureView{
+ Message: message,
+ Expected: render.Render(expected),
+ Actual: render.Render(actual),
+ }
+ serialized, _ := json.Marshal(view)
+ return string(serialized)
+}
+
+func (self *failureSerializer) serialize(expected, actual interface{}, message string) string {
+ view := FailureView{
+ Message: message,
+ Expected: fmt.Sprintf("%+v", expected),
+ Actual: fmt.Sprintf("%+v", actual),
+ }
+ serialized, _ := json.Marshal(view)
+ return string(serialized)
+}
+
+func newSerializer() *failureSerializer {
+ return &failureSerializer{}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// This struct is also declared in github.com/smartystreets/goconvey/convey/reporting.
+// The json struct tags should be equal in both declarations.
+type FailureView struct {
+ Message string `json:"Message"`
+ Expected string `json:"Expected"`
+ Actual string `json:"Actual"`
+}
+
+///////////////////////////////////////////////////////
+
+// noopSerializer just gives back the original message. This is useful when we are using
+// the assertions from a context other than the GoConvey Web UI, that requires the JSON
+// structure provided by the failureSerializer.
+type noopSerializer struct{}
+
+func (self *noopSerializer) serialize(expected, actual interface{}, message string) string {
+ return message
+}
+func (self *noopSerializer) serializeDetailed(expected, actual interface{}, message string) string {
+ return message
+}
diff --git a/vendor/github.com/smartystreets/assertions/strings.go b/vendor/github.com/smartystreets/assertions/strings.go
new file mode 100644
index 0000000..dbc3f04
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/strings.go
@@ -0,0 +1,227 @@
+package assertions
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// ShouldStartWith receives exactly 2 string parameters and ensures that the first starts with the second.
+func ShouldStartWith(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ value, valueIsString := actual.(string)
+ prefix, prefixIsString := expected[0].(string)
+
+ if !valueIsString || !prefixIsString {
+ return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ return shouldStartWith(value, prefix)
+}
+func shouldStartWith(value, prefix string) string {
+ if !strings.HasPrefix(value, prefix) {
+ shortval := value
+ if len(shortval) > len(prefix) {
+ shortval = shortval[:len(prefix)] + "..."
+ }
+ return serializer.serialize(prefix, shortval, fmt.Sprintf(shouldHaveStartedWith, value, prefix))
+ }
+ return success
+}
+
+// ShouldNotStartWith receives exactly 2 string parameters and ensures that the first does not start with the second.
+func ShouldNotStartWith(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ value, valueIsString := actual.(string)
+ prefix, prefixIsString := expected[0].(string)
+
+ if !valueIsString || !prefixIsString {
+ return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ return shouldNotStartWith(value, prefix)
+}
+func shouldNotStartWith(value, prefix string) string {
+ if strings.HasPrefix(value, prefix) {
+ if value == "" {
+ value = "<empty>"
+ }
+ if prefix == "" {
+ prefix = "<empty>"
+ }
+ return fmt.Sprintf(shouldNotHaveStartedWith, value, prefix)
+ }
+ return success
+}
+
+// ShouldEndWith receives exactly 2 string parameters and ensures that the first ends with the second.
+func ShouldEndWith(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ value, valueIsString := actual.(string)
+ suffix, suffixIsString := expected[0].(string)
+
+ if !valueIsString || !suffixIsString {
+ return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ return shouldEndWith(value, suffix)
+}
+func shouldEndWith(value, suffix string) string {
+ if !strings.HasSuffix(value, suffix) {
+ shortval := value
+ if len(shortval) > len(suffix) {
+ shortval = "..." + shortval[len(shortval)-len(suffix):]
+ }
+ return serializer.serialize(suffix, shortval, fmt.Sprintf(shouldHaveEndedWith, value, suffix))
+ }
+ return success
+}
+
+// ShouldEndWith receives exactly 2 string parameters and ensures that the first does not end with the second.
+func ShouldNotEndWith(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ value, valueIsString := actual.(string)
+ suffix, suffixIsString := expected[0].(string)
+
+ if !valueIsString || !suffixIsString {
+ return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ return shouldNotEndWith(value, suffix)
+}
+func shouldNotEndWith(value, suffix string) string {
+ if strings.HasSuffix(value, suffix) {
+ if value == "" {
+ value = "<empty>"
+ }
+ if suffix == "" {
+ suffix = "<empty>"
+ }
+ return fmt.Sprintf(shouldNotHaveEndedWith, value, suffix)
+ }
+ return success
+}
+
+// ShouldContainSubstring receives exactly 2 string parameters and ensures that the first contains the second as a substring.
+func ShouldContainSubstring(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ long, longOk := actual.(string)
+ short, shortOk := expected[0].(string)
+
+ if !longOk || !shortOk {
+ return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ if !strings.Contains(long, short) {
+ return serializer.serialize(expected[0], actual, fmt.Sprintf(shouldHaveContainedSubstring, long, short))
+ }
+ return success
+}
+
+// ShouldNotContainSubstring receives exactly 2 string parameters and ensures that the first does NOT contain the second as a substring.
+func ShouldNotContainSubstring(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ long, longOk := actual.(string)
+ short, shortOk := expected[0].(string)
+
+ if !longOk || !shortOk {
+ return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ if strings.Contains(long, short) {
+ return fmt.Sprintf(shouldNotHaveContainedSubstring, long, short)
+ }
+ return success
+}
+
+// ShouldBeBlank receives exactly 1 string parameter and ensures that it is equal to "".
+func ShouldBeBlank(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+ value, ok := actual.(string)
+ if !ok {
+ return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual))
+ }
+ if value != "" {
+ return serializer.serialize("", value, fmt.Sprintf(shouldHaveBeenBlank, value))
+ }
+ return success
+}
+
+// ShouldNotBeBlank receives exactly 1 string parameter and ensures that it is equal to "".
+func ShouldNotBeBlank(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+ value, ok := actual.(string)
+ if !ok {
+ return fmt.Sprintf(shouldBeString, reflect.TypeOf(actual))
+ }
+ if value == "" {
+ return shouldNotHaveBeenBlank
+ }
+ return success
+}
+
+// ShouldEqualWithout receives exactly 3 string parameters and ensures that the first is equal to the second
+// after removing all instances of the third from the first using strings.Replace(first, third, "", -1).
+func ShouldEqualWithout(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ actualString, ok1 := actual.(string)
+ expectedString, ok2 := expected[0].(string)
+ replace, ok3 := expected[1].(string)
+
+ if !ok1 || !ok2 || !ok3 {
+ return fmt.Sprintf(shouldAllBeStrings, []reflect.Type{
+ reflect.TypeOf(actual),
+ reflect.TypeOf(expected[0]),
+ reflect.TypeOf(expected[1]),
+ })
+ }
+
+ replaced := strings.Replace(actualString, replace, "", -1)
+ if replaced == expectedString {
+ return ""
+ }
+
+ return fmt.Sprintf("Expected '%s' to equal '%s' but without any '%s' (but it didn't).", actualString, expectedString, replace)
+}
+
+// ShouldEqualTrimSpace receives exactly 2 string parameters and ensures that the first is equal to the second
+// after removing all leading and trailing whitespace using strings.TrimSpace(first).
+func ShouldEqualTrimSpace(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ actualString, valueIsString := actual.(string)
+ _, value2IsString := expected[0].(string)
+
+ if !valueIsString || !value2IsString {
+ return fmt.Sprintf(shouldBothBeStrings, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ actualString = strings.TrimSpace(actualString)
+ return ShouldEqual(actualString, expected[0])
+}
diff --git a/vendor/github.com/smartystreets/assertions/time.go b/vendor/github.com/smartystreets/assertions/time.go
new file mode 100644
index 0000000..7e05026
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/time.go
@@ -0,0 +1,202 @@
+package assertions
+
+import (
+ "fmt"
+ "time"
+)
+
+// ShouldHappenBefore receives exactly 2 time.Time arguments and asserts that the first happens before the second.
+func ShouldHappenBefore(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ expectedTime, secondOk := expected[0].(time.Time)
+
+ if !firstOk || !secondOk {
+ return shouldUseTimes
+ }
+
+ if !actualTime.Before(expectedTime) {
+ return fmt.Sprintf(shouldHaveHappenedBefore, actualTime, expectedTime, actualTime.Sub(expectedTime))
+ }
+
+ return success
+}
+
+// ShouldHappenOnOrBefore receives exactly 2 time.Time arguments and asserts that the first happens on or before the second.
+func ShouldHappenOnOrBefore(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ expectedTime, secondOk := expected[0].(time.Time)
+
+ if !firstOk || !secondOk {
+ return shouldUseTimes
+ }
+
+ if actualTime.Equal(expectedTime) {
+ return success
+ }
+ return ShouldHappenBefore(actualTime, expectedTime)
+}
+
+// ShouldHappenAfter receives exactly 2 time.Time arguments and asserts that the first happens after the second.
+func ShouldHappenAfter(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ expectedTime, secondOk := expected[0].(time.Time)
+
+ if !firstOk || !secondOk {
+ return shouldUseTimes
+ }
+ if !actualTime.After(expectedTime) {
+ return fmt.Sprintf(shouldHaveHappenedAfter, actualTime, expectedTime, expectedTime.Sub(actualTime))
+ }
+ return success
+}
+
+// ShouldHappenOnOrAfter receives exactly 2 time.Time arguments and asserts that the first happens on or after the second.
+func ShouldHappenOnOrAfter(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ expectedTime, secondOk := expected[0].(time.Time)
+
+ if !firstOk || !secondOk {
+ return shouldUseTimes
+ }
+ if actualTime.Equal(expectedTime) {
+ return success
+ }
+ return ShouldHappenAfter(actualTime, expectedTime)
+}
+
+// ShouldHappenBetween receives exactly 3 time.Time arguments and asserts that the first happens between (not on) the second and third.
+func ShouldHappenBetween(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ min, secondOk := expected[0].(time.Time)
+ max, thirdOk := expected[1].(time.Time)
+
+ if !firstOk || !secondOk || !thirdOk {
+ return shouldUseTimes
+ }
+
+ if !actualTime.After(min) {
+ return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, min.Sub(actualTime))
+ }
+ if !actualTime.Before(max) {
+ return fmt.Sprintf(shouldHaveHappenedBetween, actualTime, min, max, actualTime.Sub(max))
+ }
+ return success
+}
+
+// ShouldHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first happens between or on the second and third.
+func ShouldHappenOnOrBetween(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ min, secondOk := expected[0].(time.Time)
+ max, thirdOk := expected[1].(time.Time)
+
+ if !firstOk || !secondOk || !thirdOk {
+ return shouldUseTimes
+ }
+ if actualTime.Equal(min) || actualTime.Equal(max) {
+ return success
+ }
+ return ShouldHappenBetween(actualTime, min, max)
+}
+
+// ShouldNotHappenOnOrBetween receives exactly 3 time.Time arguments and asserts that the first
+// does NOT happen between or on the second or third.
+func ShouldNotHappenOnOrBetween(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ min, secondOk := expected[0].(time.Time)
+ max, thirdOk := expected[1].(time.Time)
+
+ if !firstOk || !secondOk || !thirdOk {
+ return shouldUseTimes
+ }
+ if actualTime.Equal(min) || actualTime.Equal(max) {
+ return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max)
+ }
+ if actualTime.After(min) && actualTime.Before(max) {
+ return fmt.Sprintf(shouldNotHaveHappenedOnOrBetween, actualTime, min, max)
+ }
+ return success
+}
+
+// ShouldHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments)
+// and asserts that the first time.Time happens within or on the duration specified relative to
+// the other time.Time.
+func ShouldHappenWithin(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ tolerance, secondOk := expected[0].(time.Duration)
+ threshold, thirdOk := expected[1].(time.Time)
+
+ if !firstOk || !secondOk || !thirdOk {
+ return shouldUseDurationAndTime
+ }
+
+ min := threshold.Add(-tolerance)
+ max := threshold.Add(tolerance)
+ return ShouldHappenOnOrBetween(actualTime, min, max)
+}
+
+// ShouldNotHappenWithin receives a time.Time, a time.Duration, and a time.Time (3 arguments)
+// and asserts that the first time.Time does NOT happen within or on the duration specified relative to
+// the other time.Time.
+func ShouldNotHappenWithin(actual interface{}, expected ...interface{}) string {
+ if fail := need(2, expected); fail != success {
+ return fail
+ }
+ actualTime, firstOk := actual.(time.Time)
+ tolerance, secondOk := expected[0].(time.Duration)
+ threshold, thirdOk := expected[1].(time.Time)
+
+ if !firstOk || !secondOk || !thirdOk {
+ return shouldUseDurationAndTime
+ }
+
+ min := threshold.Add(-tolerance)
+ max := threshold.Add(tolerance)
+ return ShouldNotHappenOnOrBetween(actualTime, min, max)
+}
+
+// ShouldBeChronological receives a []time.Time slice and asserts that the are
+// in chronological order starting with the first time.Time as the earliest.
+func ShouldBeChronological(actual interface{}, expected ...interface{}) string {
+ if fail := need(0, expected); fail != success {
+ return fail
+ }
+
+ times, ok := actual.([]time.Time)
+ if !ok {
+ return shouldUseTimeSlice
+ }
+
+ var previous time.Time
+ for i, current := range times {
+ if i > 0 && current.Before(previous) {
+ return fmt.Sprintf(shouldHaveBeenChronological,
+ i, i-1, previous.String(), i, current.String())
+ }
+ previous = current
+ }
+ return ""
+}
diff --git a/vendor/github.com/smartystreets/assertions/type.go b/vendor/github.com/smartystreets/assertions/type.go
new file mode 100644
index 0000000..d2d1dc8
--- /dev/null
+++ b/vendor/github.com/smartystreets/assertions/type.go
@@ -0,0 +1,134 @@
+package assertions
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// ShouldHaveSameTypeAs receives exactly two parameters and compares their underlying types for equality.
+func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ first := reflect.TypeOf(actual)
+ second := reflect.TypeOf(expected[0])
+
+ if first != second {
+ return serializer.serialize(second, first, fmt.Sprintf(shouldHaveBeenA, actual, second, first))
+ }
+
+ return success
+}
+
+// ShouldNotHaveSameTypeAs receives exactly two parameters and compares their underlying types for inequality.
+func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ first := reflect.TypeOf(actual)
+ second := reflect.TypeOf(expected[0])
+
+ if (actual == nil && expected[0] == nil) || first == second {
+ return fmt.Sprintf(shouldNotHaveBeenA, actual, second)
+ }
+ return success
+}
+
+// ShouldImplement receives exactly two parameters and ensures
+// that the first implements the interface type of the second.
+func ShouldImplement(actual interface{}, expectedList ...interface{}) string {
+ if fail := need(1, expectedList); fail != success {
+ return fail
+ }
+
+ expected := expectedList[0]
+ if fail := ShouldBeNil(expected); fail != success {
+ return shouldCompareWithInterfacePointer
+ }
+
+ if fail := ShouldNotBeNil(actual); fail != success {
+ return shouldNotBeNilActual
+ }
+
+ var actualType reflect.Type
+ if reflect.TypeOf(actual).Kind() != reflect.Ptr {
+ actualType = reflect.PtrTo(reflect.TypeOf(actual))
+ } else {
+ actualType = reflect.TypeOf(actual)
+ }
+
+ expectedType := reflect.TypeOf(expected)
+ if fail := ShouldNotBeNil(expectedType); fail != success {
+ return shouldCompareWithInterfacePointer
+ }
+
+ expectedInterface := expectedType.Elem()
+
+ if !actualType.Implements(expectedInterface) {
+ return fmt.Sprintf(shouldHaveImplemented, expectedInterface, actualType)
+ }
+ return success
+}
+
+// ShouldNotImplement receives exactly two parameters and ensures
+// that the first does NOT implement the interface type of the second.
+func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string {
+ if fail := need(1, expectedList); fail != success {
+ return fail
+ }
+
+ expected := expectedList[0]
+ if fail := ShouldBeNil(expected); fail != success {
+ return shouldCompareWithInterfacePointer
+ }
+
+ if fail := ShouldNotBeNil(actual); fail != success {
+ return shouldNotBeNilActual
+ }
+
+ var actualType reflect.Type
+ if reflect.TypeOf(actual).Kind() != reflect.Ptr {
+ actualType = reflect.PtrTo(reflect.TypeOf(actual))
+ } else {
+ actualType = reflect.TypeOf(actual)
+ }
+
+ expectedType := reflect.TypeOf(expected)
+ if fail := ShouldNotBeNil(expectedType); fail != success {
+ return shouldCompareWithInterfacePointer
+ }
+
+ expectedInterface := expectedType.Elem()
+
+ if actualType.Implements(expectedInterface) {
+ return fmt.Sprintf(shouldNotHaveImplemented, actualType, expectedInterface)
+ }
+ return success
+}
+
+// ShouldBeError asserts that the first argument implements the error interface.
+// It also compares the first argument against the second argument if provided
+// (which must be an error message string or another error value).
+func ShouldBeError(actual interface{}, expected ...interface{}) string {
+ if fail := atMost(1, expected); fail != success {
+ return fail
+ }
+
+ if !isError(actual) {
+ return fmt.Sprintf(shouldBeError, reflect.TypeOf(actual))
+ }
+
+ if len(expected) == 0 {
+ return success
+ }
+
+ if expected := expected[0]; !isString(expected) && !isError(expected) {
+ return fmt.Sprintf(shouldBeErrorInvalidComparisonValue, reflect.TypeOf(expected))
+ }
+ return ShouldEqual(fmt.Sprint(actual), fmt.Sprint(expected[0]))
+}
+
+func isString(value interface{}) bool { _, ok := value.(string); return ok }
+func isError(value interface{}) bool { _, ok := value.(error); return ok }
diff --git a/vendor/github.com/smartystreets/goconvey/LICENSE.md b/vendor/github.com/smartystreets/goconvey/LICENSE.md
new file mode 100644
index 0000000..3f87a40
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/LICENSE.md
@@ -0,0 +1,23 @@
+Copyright (c) 2016 SmartyStreets, LLC
+
+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.
+
+NOTE: Various optional and subordinate components carry their own licensing
+requirements and restrictions. Use of those components is subject to the terms
+and conditions outlined the respective license of each component.
diff --git a/vendor/github.com/smartystreets/goconvey/convey/assertions.go b/vendor/github.com/smartystreets/goconvey/convey/assertions.go
new file mode 100644
index 0000000..97e3bec
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/assertions.go
@@ -0,0 +1,71 @@
+package convey
+
+import "github.com/smartystreets/assertions"
+
+var (
+ ShouldEqual = assertions.ShouldEqual
+ ShouldNotEqual = assertions.ShouldNotEqual
+ ShouldAlmostEqual = assertions.ShouldAlmostEqual
+ ShouldNotAlmostEqual = assertions.ShouldNotAlmostEqual
+ ShouldResemble = assertions.ShouldResemble
+ ShouldNotResemble = assertions.ShouldNotResemble
+ ShouldPointTo = assertions.ShouldPointTo
+ ShouldNotPointTo = assertions.ShouldNotPointTo
+ ShouldBeNil = assertions.ShouldBeNil
+ ShouldNotBeNil = assertions.ShouldNotBeNil
+ ShouldBeTrue = assertions.ShouldBeTrue
+ ShouldBeFalse = assertions.ShouldBeFalse
+ ShouldBeZeroValue = assertions.ShouldBeZeroValue
+ ShouldNotBeZeroValue = assertions.ShouldNotBeZeroValue
+
+ ShouldBeGreaterThan = assertions.ShouldBeGreaterThan
+ ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo
+ ShouldBeLessThan = assertions.ShouldBeLessThan
+ ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo
+ ShouldBeBetween = assertions.ShouldBeBetween
+ ShouldNotBeBetween = assertions.ShouldNotBeBetween
+ ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual
+ ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual
+
+ ShouldContain = assertions.ShouldContain
+ ShouldNotContain = assertions.ShouldNotContain
+ ShouldContainKey = assertions.ShouldContainKey
+ ShouldNotContainKey = assertions.ShouldNotContainKey
+ ShouldBeIn = assertions.ShouldBeIn
+ ShouldNotBeIn = assertions.ShouldNotBeIn
+ ShouldBeEmpty = assertions.ShouldBeEmpty
+ ShouldNotBeEmpty = assertions.ShouldNotBeEmpty
+ ShouldHaveLength = assertions.ShouldHaveLength
+
+ ShouldStartWith = assertions.ShouldStartWith
+ ShouldNotStartWith = assertions.ShouldNotStartWith
+ ShouldEndWith = assertions.ShouldEndWith
+ ShouldNotEndWith = assertions.ShouldNotEndWith
+ ShouldBeBlank = assertions.ShouldBeBlank
+ ShouldNotBeBlank = assertions.ShouldNotBeBlank
+ ShouldContainSubstring = assertions.ShouldContainSubstring
+ ShouldNotContainSubstring = assertions.ShouldNotContainSubstring
+
+ ShouldPanic = assertions.ShouldPanic
+ ShouldNotPanic = assertions.ShouldNotPanic
+ ShouldPanicWith = assertions.ShouldPanicWith
+ ShouldNotPanicWith = assertions.ShouldNotPanicWith
+
+ ShouldHaveSameTypeAs = assertions.ShouldHaveSameTypeAs
+ ShouldNotHaveSameTypeAs = assertions.ShouldNotHaveSameTypeAs
+ ShouldImplement = assertions.ShouldImplement
+ ShouldNotImplement = assertions.ShouldNotImplement
+
+ ShouldHappenBefore = assertions.ShouldHappenBefore
+ ShouldHappenOnOrBefore = assertions.ShouldHappenOnOrBefore
+ ShouldHappenAfter = assertions.ShouldHappenAfter
+ ShouldHappenOnOrAfter = assertions.ShouldHappenOnOrAfter
+ ShouldHappenBetween = assertions.ShouldHappenBetween
+ ShouldHappenOnOrBetween = assertions.ShouldHappenOnOrBetween
+ ShouldNotHappenOnOrBetween = assertions.ShouldNotHappenOnOrBetween
+ ShouldHappenWithin = assertions.ShouldHappenWithin
+ ShouldNotHappenWithin = assertions.ShouldNotHappenWithin
+ ShouldBeChronological = assertions.ShouldBeChronological
+
+ ShouldBeError = assertions.ShouldBeError
+)
diff --git a/vendor/github.com/smartystreets/goconvey/convey/context.go b/vendor/github.com/smartystreets/goconvey/convey/context.go
new file mode 100644
index 0000000..2c75c2d
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/context.go
@@ -0,0 +1,272 @@
+package convey
+
+import (
+ "fmt"
+
+ "github.com/jtolds/gls"
+ "github.com/smartystreets/goconvey/convey/reporting"
+)
+
+type conveyErr struct {
+ fmt string
+ params []interface{}
+}
+
+func (e *conveyErr) Error() string {
+ return fmt.Sprintf(e.fmt, e.params...)
+}
+
+func conveyPanic(fmt string, params ...interface{}) {
+ panic(&conveyErr{fmt, params})
+}
+
+const (
+ missingGoTest = `Top-level calls to Convey(...) need a reference to the *testing.T.
+ Hint: Convey("description here", t, func() { /* notice that the second argument was the *testing.T (t)! */ }) `
+ extraGoTest = `Only the top-level call to Convey(...) needs a reference to the *testing.T.`
+ noStackContext = "Convey operation made without context on goroutine stack.\n" +
+ "Hint: Perhaps you meant to use `Convey(..., func(c C){...})` ?"
+ differentConveySituations = "Different set of Convey statements on subsequent pass!\nDid not expect %#v."
+ multipleIdenticalConvey = "Multiple convey suites with identical names: %#v"
+)
+
+const (
+ failureHalt = "___FAILURE_HALT___"
+
+ nodeKey = "node"
+)
+
+///////////////////////////////// Stack Context /////////////////////////////////
+
+func getCurrentContext() *context {
+ ctx, ok := ctxMgr.GetValue(nodeKey)
+ if ok {
+ return ctx.(*context)
+ }
+ return nil
+}
+
+func mustGetCurrentContext() *context {
+ ctx := getCurrentContext()
+ if ctx == nil {
+ conveyPanic(noStackContext)
+ }
+ return ctx
+}
+
+//////////////////////////////////// Context ////////////////////////////////////
+
+// context magically handles all coordination of Convey's and So assertions.
+//
+// It is tracked on the stack as goroutine-local-storage with the gls package,
+// or explicitly if the user decides to call convey like:
+//
+// Convey(..., func(c C) {
+// c.So(...)
+// })
+//
+// This implements the `C` interface.
+type context struct {
+ reporter reporting.Reporter
+
+ children map[string]*context
+
+ resets []func()
+
+ executedOnce bool
+ expectChildRun *bool
+ complete bool
+
+ focus bool
+ failureMode FailureMode
+}
+
+// rootConvey is the main entry point to a test suite. This is called when
+// there's no context in the stack already, and items must contain a `t` object,
+// or this panics.
+func rootConvey(items ...interface{}) {
+ entry := discover(items)
+
+ if entry.Test == nil {
+ conveyPanic(missingGoTest)
+ }
+
+ expectChildRun := true
+ ctx := &context{
+ reporter: buildReporter(),
+
+ children: make(map[string]*context),
+
+ expectChildRun: &expectChildRun,
+
+ focus: entry.Focus,
+ failureMode: defaultFailureMode.combine(entry.FailMode),
+ }
+ ctxMgr.SetValues(gls.Values{nodeKey: ctx}, func() {
+ ctx.reporter.BeginStory(reporting.NewStoryReport(entry.Test))
+ defer ctx.reporter.EndStory()
+
+ for ctx.shouldVisit() {
+ ctx.conveyInner(entry.Situation, entry.Func)
+ expectChildRun = true
+ }
+ })
+}
+
+//////////////////////////////////// Methods ////////////////////////////////////
+
+func (ctx *context) SkipConvey(items ...interface{}) {
+ ctx.Convey(items, skipConvey)
+}
+
+func (ctx *context) FocusConvey(items ...interface{}) {
+ ctx.Convey(items, focusConvey)
+}
+
+func (ctx *context) Convey(items ...interface{}) {
+ entry := discover(items)
+
+ // we're a branch, or leaf (on the wind)
+ if entry.Test != nil {
+ conveyPanic(extraGoTest)
+ }
+ if ctx.focus && !entry.Focus {
+ return
+ }
+
+ var inner_ctx *context
+ if ctx.executedOnce {
+ var ok bool
+ inner_ctx, ok = ctx.children[entry.Situation]
+ if !ok {
+ conveyPanic(differentConveySituations, entry.Situation)
+ }
+ } else {
+ if _, ok := ctx.children[entry.Situation]; ok {
+ conveyPanic(multipleIdenticalConvey, entry.Situation)
+ }
+ inner_ctx = &context{
+ reporter: ctx.reporter,
+
+ children: make(map[string]*context),
+
+ expectChildRun: ctx.expectChildRun,
+
+ focus: entry.Focus,
+ failureMode: ctx.failureMode.combine(entry.FailMode),
+ }
+ ctx.children[entry.Situation] = inner_ctx
+ }
+
+ if inner_ctx.shouldVisit() {
+ ctxMgr.SetValues(gls.Values{nodeKey: inner_ctx}, func() {
+ inner_ctx.conveyInner(entry.Situation, entry.Func)
+ })
+ }
+}
+
+func (ctx *context) SkipSo(stuff ...interface{}) {
+ ctx.assertionReport(reporting.NewSkipReport())
+}
+
+func (ctx *context) So(actual interface{}, assert assertion, expected ...interface{}) {
+ if result := assert(actual, expected...); result == assertionSuccess {
+ ctx.assertionReport(reporting.NewSuccessReport())
+ } else {
+ ctx.assertionReport(reporting.NewFailureReport(result))
+ }
+}
+
+func (ctx *context) Reset(action func()) {
+ /* TODO: Failure mode configuration */
+ ctx.resets = append(ctx.resets, action)
+}
+
+func (ctx *context) Print(items ...interface{}) (int, error) {
+ fmt.Fprint(ctx.reporter, items...)
+ return fmt.Print(items...)
+}
+
+func (ctx *context) Println(items ...interface{}) (int, error) {
+ fmt.Fprintln(ctx.reporter, items...)
+ return fmt.Println(items...)
+}
+
+func (ctx *context) Printf(format string, items ...interface{}) (int, error) {
+ fmt.Fprintf(ctx.reporter, format, items...)
+ return fmt.Printf(format, items...)
+}
+
+//////////////////////////////////// Private ////////////////////////////////////
+
+// shouldVisit returns true iff we should traverse down into a Convey. Note
+// that just because we don't traverse a Convey this time, doesn't mean that
+// we may not traverse it on a subsequent pass.
+func (c *context) shouldVisit() bool {
+ return !c.complete && *c.expectChildRun
+}
+
+// conveyInner is the function which actually executes the user's anonymous test
+// function body. At this point, Convey or RootConvey has decided that this
+// function should actually run.
+func (ctx *context) conveyInner(situation string, f func(C)) {
+ // Record/Reset state for next time.
+ defer func() {
+ ctx.executedOnce = true
+
+ // This is only needed at the leaves, but there's no harm in also setting it
+ // when returning from branch Convey's
+ *ctx.expectChildRun = false
+ }()
+
+ // Set up+tear down our scope for the reporter
+ ctx.reporter.Enter(reporting.NewScopeReport(situation))
+ defer ctx.reporter.Exit()
+
+ // Recover from any panics in f, and assign the `complete` status for this
+ // node of the tree.
+ defer func() {
+ ctx.complete = true
+ if problem := recover(); problem != nil {
+ if problem, ok := problem.(*conveyErr); ok {
+ panic(problem)
+ }
+ if problem != failureHalt {
+ ctx.reporter.Report(reporting.NewErrorReport(problem))
+ }
+ } else {
+ for _, child := range ctx.children {
+ if !child.complete {
+ ctx.complete = false
+ return
+ }
+ }
+ }
+ }()
+
+ // Resets are registered as the `f` function executes, so nil them here.
+ // All resets are run in registration order (FIFO).
+ ctx.resets = []func(){}
+ defer func() {
+ for _, r := range ctx.resets {
+ // panics handled by the previous defer
+ r()
+ }
+ }()
+
+ if f == nil {
+ // if f is nil, this was either a Convey(..., nil), or a SkipConvey
+ ctx.reporter.Report(reporting.NewSkipReport())
+ } else {
+ f(ctx)
+ }
+}
+
+// assertionReport is a helper for So and SkipSo which makes the report and
+// then possibly panics, depending on the current context's failureMode.
+func (ctx *context) assertionReport(r *reporting.AssertionResult) {
+ ctx.reporter.Report(r)
+ if r.Failure != "" && ctx.failureMode == FailureHalts {
+ panic(failureHalt)
+ }
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/convey.goconvey b/vendor/github.com/smartystreets/goconvey/convey/convey.goconvey
new file mode 100644
index 0000000..a2d9327
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/convey.goconvey
@@ -0,0 +1,4 @@
+#ignore
+-timeout=1s
+#-covermode=count
+#-coverpkg=github.com/smartystreets/goconvey/convey,github.com/smartystreets/goconvey/convey/gotest,github.com/smartystreets/goconvey/convey/reporting \ No newline at end of file
diff --git a/vendor/github.com/smartystreets/goconvey/convey/discovery.go b/vendor/github.com/smartystreets/goconvey/convey/discovery.go
new file mode 100644
index 0000000..eb8d4cb
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/discovery.go
@@ -0,0 +1,103 @@
+package convey
+
+type actionSpecifier uint8
+
+const (
+ noSpecifier actionSpecifier = iota
+ skipConvey
+ focusConvey
+)
+
+type suite struct {
+ Situation string
+ Test t
+ Focus bool
+ Func func(C) // nil means skipped
+ FailMode FailureMode
+}
+
+func newSuite(situation string, failureMode FailureMode, f func(C), test t, specifier actionSpecifier) *suite {
+ ret := &suite{
+ Situation: situation,
+ Test: test,
+ Func: f,
+ FailMode: failureMode,
+ }
+ switch specifier {
+ case skipConvey:
+ ret.Func = nil
+ case focusConvey:
+ ret.Focus = true
+ }
+ return ret
+}
+
+func discover(items []interface{}) *suite {
+ name, items := parseName(items)
+ test, items := parseGoTest(items)
+ failure, items := parseFailureMode(items)
+ action, items := parseAction(items)
+ specifier, items := parseSpecifier(items)
+
+ if len(items) != 0 {
+ conveyPanic(parseError)
+ }
+
+ return newSuite(name, failure, action, test, specifier)
+}
+func item(items []interface{}) interface{} {
+ if len(items) == 0 {
+ conveyPanic(parseError)
+ }
+ return items[0]
+}
+func parseName(items []interface{}) (string, []interface{}) {
+ if name, parsed := item(items).(string); parsed {
+ return name, items[1:]
+ }
+ conveyPanic(parseError)
+ panic("never get here")
+}
+func parseGoTest(items []interface{}) (t, []interface{}) {
+ if test, parsed := item(items).(t); parsed {
+ return test, items[1:]
+ }
+ return nil, items
+}
+func parseFailureMode(items []interface{}) (FailureMode, []interface{}) {
+ if mode, parsed := item(items).(FailureMode); parsed {
+ return mode, items[1:]
+ }
+ return FailureInherits, items
+}
+func parseAction(items []interface{}) (func(C), []interface{}) {
+ switch x := item(items).(type) {
+ case nil:
+ return nil, items[1:]
+ case func(C):
+ return x, items[1:]
+ case func():
+ return func(C) { x() }, items[1:]
+ }
+ conveyPanic(parseError)
+ panic("never get here")
+}
+func parseSpecifier(items []interface{}) (actionSpecifier, []interface{}) {
+ if len(items) == 0 {
+ return noSpecifier, items
+ }
+ if spec, ok := items[0].(actionSpecifier); ok {
+ return spec, items[1:]
+ }
+ conveyPanic(parseError)
+ panic("never get here")
+}
+
+// This interface allows us to pass the *testing.T struct
+// throughout the internals of this package without ever
+// having to import the "testing" package.
+type t interface {
+ Fail()
+}
+
+const parseError = "You must provide a name (string), then a *testing.T (if in outermost scope), an optional FailureMode, and then an action (func())."
diff --git a/vendor/github.com/smartystreets/goconvey/convey/doc.go b/vendor/github.com/smartystreets/goconvey/convey/doc.go
new file mode 100644
index 0000000..e4f7b51
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/doc.go
@@ -0,0 +1,218 @@
+// Package convey contains all of the public-facing entry points to this project.
+// This means that it should never be required of the user to import any other
+// packages from this project as they serve internal purposes.
+package convey
+
+import "github.com/smartystreets/goconvey/convey/reporting"
+
+////////////////////////////////// suite //////////////////////////////////
+
+// C is the Convey context which you can optionally obtain in your action
+// by calling Convey like:
+//
+// Convey(..., func(c C) {
+// ...
+// })
+//
+// See the documentation on Convey for more details.
+//
+// All methods in this context behave identically to the global functions of the
+// same name in this package.
+type C interface {
+ Convey(items ...interface{})
+ SkipConvey(items ...interface{})
+ FocusConvey(items ...interface{})
+
+ So(actual interface{}, assert assertion, expected ...interface{})
+ SkipSo(stuff ...interface{})
+
+ Reset(action func())
+
+ Println(items ...interface{}) (int, error)
+ Print(items ...interface{}) (int, error)
+ Printf(format string, items ...interface{}) (int, error)
+}
+
+// Convey is the method intended for use when declaring the scopes of
+// a specification. Each scope has a description and a func() which may contain
+// other calls to Convey(), Reset() or Should-style assertions. Convey calls can
+// be nested as far as you see fit.
+//
+// IMPORTANT NOTE: The top-level Convey() within a Test method
+// must conform to the following signature:
+//
+// Convey(description string, t *testing.T, action func())
+//
+// All other calls should look like this (no need to pass in *testing.T):
+//
+// Convey(description string, action func())
+//
+// Don't worry, goconvey will panic if you get it wrong so you can fix it.
+//
+// Additionally, you may explicitly obtain access to the Convey context by doing:
+//
+// Convey(description string, action func(c C))
+//
+// You may need to do this if you want to pass the context through to a
+// goroutine, or to close over the context in a handler to a library which
+// calls your handler in a goroutine (httptest comes to mind).
+//
+// All Convey()-blocks also accept an optional parameter of FailureMode which sets
+// how goconvey should treat failures for So()-assertions in the block and
+// nested blocks. See the constants in this file for the available options.
+//
+// By default it will inherit from its parent block and the top-level blocks
+// default to the FailureHalts setting.
+//
+// This parameter is inserted before the block itself:
+//
+// Convey(description string, t *testing.T, mode FailureMode, action func())
+// Convey(description string, mode FailureMode, action func())
+//
+// See the examples package for, well, examples.
+func Convey(items ...interface{}) {
+ if ctx := getCurrentContext(); ctx == nil {
+ rootConvey(items...)
+ } else {
+ ctx.Convey(items...)
+ }
+}
+
+// SkipConvey is analogous to Convey except that the scope is not executed
+// (which means that child scopes defined within this scope are not run either).
+// The reporter will be notified that this step was skipped.
+func SkipConvey(items ...interface{}) {
+ Convey(append(items, skipConvey)...)
+}
+
+// FocusConvey is has the inverse effect of SkipConvey. If the top-level
+// Convey is changed to `FocusConvey`, only nested scopes that are defined
+// with FocusConvey will be run. The rest will be ignored completely. This
+// is handy when debugging a large suite that runs a misbehaving function
+// repeatedly as you can disable all but one of that function
+// without swaths of `SkipConvey` calls, just a targeted chain of calls
+// to FocusConvey.
+func FocusConvey(items ...interface{}) {
+ Convey(append(items, focusConvey)...)
+}
+
+// Reset registers a cleanup function to be run after each Convey()
+// in the same scope. See the examples package for a simple use case.
+func Reset(action func()) {
+ mustGetCurrentContext().Reset(action)
+}
+
+/////////////////////////////////// Assertions ///////////////////////////////////
+
+// assertion is an alias for a function with a signature that the convey.So()
+// method can handle. Any future or custom assertions should conform to this
+// method signature. The return value should be an empty string if the assertion
+// passes and a well-formed failure message if not.
+type assertion func(actual interface{}, expected ...interface{}) string
+
+const assertionSuccess = ""
+
+// So is the means by which assertions are made against the system under test.
+// The majority of exported names in the assertions package begin with the word
+// 'Should' and describe how the first argument (actual) should compare with any
+// of the final (expected) arguments. How many final arguments are accepted
+// depends on the particular assertion that is passed in as the assert argument.
+// See the examples package for use cases and the assertions package for
+// documentation on specific assertion methods. A failing assertion will
+// cause t.Fail() to be invoked--you should never call this method (or other
+// failure-inducing methods) in your test code. Leave that to GoConvey.
+func So(actual interface{}, assert assertion, expected ...interface{}) {
+ mustGetCurrentContext().So(actual, assert, expected...)
+}
+
+// SkipSo is analogous to So except that the assertion that would have been passed
+// to So is not executed and the reporter is notified that the assertion was skipped.
+func SkipSo(stuff ...interface{}) {
+ mustGetCurrentContext().SkipSo()
+}
+
+// FailureMode is a type which determines how the So() blocks should fail
+// if their assertion fails. See constants further down for acceptable values
+type FailureMode string
+
+const (
+
+ // FailureContinues is a failure mode which prevents failing
+ // So()-assertions from halting Convey-block execution, instead
+ // allowing the test to continue past failing So()-assertions.
+ FailureContinues FailureMode = "continue"
+
+ // FailureHalts is the default setting for a top-level Convey()-block
+ // and will cause all failing So()-assertions to halt further execution
+ // in that test-arm and continue on to the next arm.
+ FailureHalts FailureMode = "halt"
+
+ // FailureInherits is the default setting for failure-mode, it will
+ // default to the failure-mode of the parent block. You should never
+ // need to specify this mode in your tests..
+ FailureInherits FailureMode = "inherits"
+)
+
+func (f FailureMode) combine(other FailureMode) FailureMode {
+ if other == FailureInherits {
+ return f
+ }
+ return other
+}
+
+var defaultFailureMode FailureMode = FailureHalts
+
+// SetDefaultFailureMode allows you to specify the default failure mode
+// for all Convey blocks. It is meant to be used in an init function to
+// allow the default mode to be changdd across all tests for an entire packgae
+// but it can be used anywhere.
+func SetDefaultFailureMode(mode FailureMode) {
+ if mode == FailureContinues || mode == FailureHalts {
+ defaultFailureMode = mode
+ } else {
+ panic("You may only use the constants named 'FailureContinues' and 'FailureHalts' as default failure modes.")
+ }
+}
+
+//////////////////////////////////// Print functions ////////////////////////////////////
+
+// Print is analogous to fmt.Print (and it even calls fmt.Print). It ensures that
+// output is aligned with the corresponding scopes in the web UI.
+func Print(items ...interface{}) (written int, err error) {
+ return mustGetCurrentContext().Print(items...)
+}
+
+// Print is analogous to fmt.Println (and it even calls fmt.Println). It ensures that
+// output is aligned with the corresponding scopes in the web UI.
+func Println(items ...interface{}) (written int, err error) {
+ return mustGetCurrentContext().Println(items...)
+}
+
+// Print is analogous to fmt.Printf (and it even calls fmt.Printf). It ensures that
+// output is aligned with the corresponding scopes in the web UI.
+func Printf(format string, items ...interface{}) (written int, err error) {
+ return mustGetCurrentContext().Printf(format, items...)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// SuppressConsoleStatistics prevents automatic printing of console statistics.
+// Calling PrintConsoleStatistics explicitly will force printing of statistics.
+func SuppressConsoleStatistics() {
+ reporting.SuppressConsoleStatistics()
+}
+
+// PrintConsoleStatistics may be called at any time to print assertion statistics.
+// Generally, the best place to do this would be in a TestMain function,
+// after all tests have been run. Something like this:
+//
+// func TestMain(m *testing.M) {
+// convey.SuppressConsoleStatistics()
+// result := m.Run()
+// convey.PrintConsoleStatistics()
+// os.Exit(result)
+// }
+//
+func PrintConsoleStatistics() {
+ reporting.PrintConsoleStatistics()
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go b/vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go
new file mode 100644
index 0000000..167c8fb
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go
@@ -0,0 +1,28 @@
+// Package gotest contains internal functionality. Although this package
+// contains one or more exported names it is not intended for public
+// consumption. See the examples package for how to use this project.
+package gotest
+
+import (
+ "runtime"
+ "strings"
+)
+
+func ResolveExternalCaller() (file string, line int, name string) {
+ var caller_id uintptr
+ callers := runtime.Callers(0, callStack)
+
+ for x := 0; x < callers; x++ {
+ caller_id, file, line, _ = runtime.Caller(x)
+ if strings.HasSuffix(file, "_test.go") || strings.HasSuffix(file, "_tests.go") {
+ name = runtime.FuncForPC(caller_id).Name()
+ return
+ }
+ }
+ file, line, name = "<unknown file>", -1, "<unknown name>"
+ return // panic?
+}
+
+const maxStackDepth = 100 // This had better be enough...
+
+var callStack []uintptr = make([]uintptr, maxStackDepth, maxStackDepth)
diff --git a/vendor/github.com/smartystreets/goconvey/convey/init.go b/vendor/github.com/smartystreets/goconvey/convey/init.go
new file mode 100644
index 0000000..cb930a0
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/init.go
@@ -0,0 +1,81 @@
+package convey
+
+import (
+ "flag"
+ "os"
+
+ "github.com/jtolds/gls"
+ "github.com/smartystreets/assertions"
+ "github.com/smartystreets/goconvey/convey/reporting"
+)
+
+func init() {
+ assertions.GoConveyMode(true)
+
+ declareFlags()
+
+ ctxMgr = gls.NewContextManager()
+}
+
+func declareFlags() {
+ flag.BoolVar(&json, "convey-json", false, "When true, emits results in JSON blocks. Default: 'false'")
+ flag.BoolVar(&silent, "convey-silent", false, "When true, all output from GoConvey is suppressed.")
+ flag.BoolVar(&story, "convey-story", false, "When true, emits story output, otherwise emits dot output. When not provided, this flag mirrors the value of the '-test.v' flag")
+
+ if noStoryFlagProvided() {
+ story = verboseEnabled
+ }
+
+ // FYI: flag.Parse() is called from the testing package.
+}
+
+func noStoryFlagProvided() bool {
+ return !story && !storyDisabled
+}
+
+func buildReporter() reporting.Reporter {
+ selectReporter := os.Getenv("GOCONVEY_REPORTER")
+
+ switch {
+ case testReporter != nil:
+ return testReporter
+ case json || selectReporter == "json":
+ return reporting.BuildJsonReporter()
+ case silent || selectReporter == "silent":
+ return reporting.BuildSilentReporter()
+ case selectReporter == "dot":
+ // Story is turned on when verbose is set, so we need to check for dot reporter first.
+ return reporting.BuildDotReporter()
+ case story || selectReporter == "story":
+ return reporting.BuildStoryReporter()
+ default:
+ return reporting.BuildDotReporter()
+ }
+}
+
+var (
+ ctxMgr *gls.ContextManager
+
+ // only set by internal tests
+ testReporter reporting.Reporter
+)
+
+var (
+ json bool
+ silent bool
+ story bool
+
+ verboseEnabled = flagFound("-test.v=true")
+ storyDisabled = flagFound("-story=false")
+)
+
+// flagFound parses the command line args manually for flags defined in other
+// packages. Like the '-v' flag from the "testing" package, for instance.
+func flagFound(flagValue string) bool {
+ for _, arg := range os.Args {
+ if arg == flagValue {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/nilReporter.go b/vendor/github.com/smartystreets/goconvey/convey/nilReporter.go
new file mode 100644
index 0000000..777b2a5
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/nilReporter.go
@@ -0,0 +1,15 @@
+package convey
+
+import (
+ "github.com/smartystreets/goconvey/convey/reporting"
+)
+
+type nilReporter struct{}
+
+func (self *nilReporter) BeginStory(story *reporting.StoryReport) {}
+func (self *nilReporter) Enter(scope *reporting.ScopeReport) {}
+func (self *nilReporter) Report(report *reporting.AssertionResult) {}
+func (self *nilReporter) Exit() {}
+func (self *nilReporter) EndStory() {}
+func (self *nilReporter) Write(p []byte) (int, error) { return len(p), nil }
+func newNilReporter() *nilReporter { return &nilReporter{} }
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/console.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/console.go
new file mode 100644
index 0000000..7bf67db
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/console.go
@@ -0,0 +1,16 @@
+package reporting
+
+import (
+ "fmt"
+ "io"
+)
+
+type console struct{}
+
+func (self *console) Write(p []byte) (n int, err error) {
+ return fmt.Print(string(p))
+}
+
+func NewConsole() io.Writer {
+ return new(console)
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go
new file mode 100644
index 0000000..a37d001
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go
@@ -0,0 +1,5 @@
+// Package reporting contains internal functionality related
+// to console reporting and output. Although this package has
+// exported names is not intended for public consumption. See the
+// examples package for how to use this project.
+package reporting
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go
new file mode 100644
index 0000000..47d57c6
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go
@@ -0,0 +1,40 @@
+package reporting
+
+import "fmt"
+
+type dot struct{ out *Printer }
+
+func (self *dot) BeginStory(story *StoryReport) {}
+
+func (self *dot) Enter(scope *ScopeReport) {}
+
+func (self *dot) Report(report *AssertionResult) {
+ if report.Error != nil {
+ fmt.Print(redColor)
+ self.out.Insert(dotError)
+ } else if report.Failure != "" {
+ fmt.Print(yellowColor)
+ self.out.Insert(dotFailure)
+ } else if report.Skipped {
+ fmt.Print(yellowColor)
+ self.out.Insert(dotSkip)
+ } else {
+ fmt.Print(greenColor)
+ self.out.Insert(dotSuccess)
+ }
+ fmt.Print(resetColor)
+}
+
+func (self *dot) Exit() {}
+
+func (self *dot) EndStory() {}
+
+func (self *dot) Write(content []byte) (written int, err error) {
+ return len(content), nil // no-op
+}
+
+func NewDotReporter(out *Printer) *dot {
+ self := new(dot)
+ self.out = out
+ return self
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go
new file mode 100644
index 0000000..c396e16
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go
@@ -0,0 +1,33 @@
+package reporting
+
+type gotestReporter struct{ test T }
+
+func (self *gotestReporter) BeginStory(story *StoryReport) {
+ self.test = story.Test
+}
+
+func (self *gotestReporter) Enter(scope *ScopeReport) {}
+
+func (self *gotestReporter) Report(r *AssertionResult) {
+ if !passed(r) {
+ self.test.Fail()
+ }
+}
+
+func (self *gotestReporter) Exit() {}
+
+func (self *gotestReporter) EndStory() {
+ self.test = nil
+}
+
+func (self *gotestReporter) Write(content []byte) (written int, err error) {
+ return len(content), nil // no-op
+}
+
+func NewGoTestReporter() *gotestReporter {
+ return new(gotestReporter)
+}
+
+func passed(r *AssertionResult) bool {
+ return r.Error == nil && r.Failure == ""
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/init.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/init.go
new file mode 100644
index 0000000..99c3bd6
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/init.go
@@ -0,0 +1,94 @@
+package reporting
+
+import (
+ "os"
+ "runtime"
+ "strings"
+)
+
+func init() {
+ if !isColorableTerminal() {
+ monochrome()
+ }
+
+ if runtime.GOOS == "windows" {
+ success, failure, error_ = dotSuccess, dotFailure, dotError
+ }
+}
+
+func BuildJsonReporter() Reporter {
+ out := NewPrinter(NewConsole())
+ return NewReporters(
+ NewGoTestReporter(),
+ NewJsonReporter(out))
+}
+func BuildDotReporter() Reporter {
+ out := NewPrinter(NewConsole())
+ return NewReporters(
+ NewGoTestReporter(),
+ NewDotReporter(out),
+ NewProblemReporter(out),
+ consoleStatistics)
+}
+func BuildStoryReporter() Reporter {
+ out := NewPrinter(NewConsole())
+ return NewReporters(
+ NewGoTestReporter(),
+ NewStoryReporter(out),
+ NewProblemReporter(out),
+ consoleStatistics)
+}
+func BuildSilentReporter() Reporter {
+ out := NewPrinter(NewConsole())
+ return NewReporters(
+ NewGoTestReporter(),
+ NewSilentProblemReporter(out))
+}
+
+var (
+ newline = "\n"
+ success = "✔"
+ failure = "✘"
+ error_ = "🔥"
+ skip = "⚠"
+ dotSuccess = "."
+ dotFailure = "x"
+ dotError = "E"
+ dotSkip = "S"
+ errorTemplate = "* %s \nLine %d: - %v \n%s\n"
+ failureTemplate = "* %s \nLine %d:\n%s\n%s\n"
+)
+
+var (
+ greenColor = "\033[32m"
+ yellowColor = "\033[33m"
+ redColor = "\033[31m"
+ resetColor = "\033[0m"
+)
+
+var consoleStatistics = NewStatisticsReporter(NewPrinter(NewConsole()))
+
+func SuppressConsoleStatistics() { consoleStatistics.Suppress() }
+func PrintConsoleStatistics() { consoleStatistics.PrintSummary() }
+
+// QuietMode disables all console output symbols. This is only meant to be used
+// for tests that are internal to goconvey where the output is distracting or
+// otherwise not needed in the test output.
+func QuietMode() {
+ success, failure, error_, skip, dotSuccess, dotFailure, dotError, dotSkip = "", "", "", "", "", "", "", ""
+}
+
+func monochrome() {
+ greenColor, yellowColor, redColor, resetColor = "", "", "", ""
+}
+
+func isColorableTerminal() bool {
+ return strings.Contains(os.Getenv("TERM"), "color")
+}
+
+// This interface allows us to pass the *testing.T struct
+// throughout the internals of this tool without ever
+// having to import the "testing" package.
+type T interface {
+ Fail()
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/json.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/json.go
new file mode 100644
index 0000000..f852697
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/json.go
@@ -0,0 +1,88 @@
+// TODO: under unit test
+
+package reporting
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "strings"
+)
+
+type JsonReporter struct {
+ out *Printer
+ currentKey []string
+ current *ScopeResult
+ index map[string]*ScopeResult
+ scopes []*ScopeResult
+}
+
+func (self *JsonReporter) depth() int { return len(self.currentKey) }
+
+func (self *JsonReporter) BeginStory(story *StoryReport) {}
+
+func (self *JsonReporter) Enter(scope *ScopeReport) {
+ self.currentKey = append(self.currentKey, scope.Title)
+ ID := strings.Join(self.currentKey, "|")
+ if _, found := self.index[ID]; !found {
+ next := newScopeResult(scope.Title, self.depth(), scope.File, scope.Line)
+ self.scopes = append(self.scopes, next)
+ self.index[ID] = next
+ }
+ self.current = self.index[ID]
+}
+
+func (self *JsonReporter) Report(report *AssertionResult) {
+ self.current.Assertions = append(self.current.Assertions, report)
+}
+
+func (self *JsonReporter) Exit() {
+ self.currentKey = self.currentKey[:len(self.currentKey)-1]
+}
+
+func (self *JsonReporter) EndStory() {
+ self.report()
+ self.reset()
+}
+func (self *JsonReporter) report() {
+ scopes := []string{}
+ for _, scope := range self.scopes {
+ serialized, err := json.Marshal(scope)
+ if err != nil {
+ self.out.Println(jsonMarshalFailure)
+ panic(err)
+ }
+ var buffer bytes.Buffer
+ json.Indent(&buffer, serialized, "", " ")
+ scopes = append(scopes, buffer.String())
+ }
+ self.out.Print(fmt.Sprintf("%s\n%s,\n%s\n", OpenJson, strings.Join(scopes, ","), CloseJson))
+}
+func (self *JsonReporter) reset() {
+ self.scopes = []*ScopeResult{}
+ self.index = map[string]*ScopeResult{}
+ self.currentKey = nil
+}
+
+func (self *JsonReporter) Write(content []byte) (written int, err error) {
+ self.current.Output += string(content)
+ return len(content), nil
+}
+
+func NewJsonReporter(out *Printer) *JsonReporter {
+ self := new(JsonReporter)
+ self.out = out
+ self.reset()
+ return self
+}
+
+const OpenJson = ">->->OPEN-JSON->->->" // "⌦"
+const CloseJson = "<-<-<-CLOSE-JSON<-<-<" // "⌫"
+const jsonMarshalFailure = `
+
+GOCONVEY_JSON_MARSHALL_FAILURE: There was an error when attempting to convert test results to JSON.
+Please file a bug report and reference the code that caused this failure if possible.
+
+Here's the panic:
+
+`
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go
new file mode 100644
index 0000000..3dac0d4
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go
@@ -0,0 +1,60 @@
+package reporting
+
+import (
+ "fmt"
+ "io"
+ "strings"
+)
+
+type Printer struct {
+ out io.Writer
+ prefix string
+}
+
+func (self *Printer) Println(message string, values ...interface{}) {
+ formatted := self.format(message, values...) + newline
+ self.out.Write([]byte(formatted))
+}
+
+func (self *Printer) Print(message string, values ...interface{}) {
+ formatted := self.format(message, values...)
+ self.out.Write([]byte(formatted))
+}
+
+func (self *Printer) Insert(text string) {
+ self.out.Write([]byte(text))
+}
+
+func (self *Printer) format(message string, values ...interface{}) string {
+ var formatted string
+ if len(values) == 0 {
+ formatted = self.prefix + message
+ } else {
+ formatted = self.prefix + fmt_Sprintf(message, values...)
+ }
+ indented := strings.Replace(formatted, newline, newline+self.prefix, -1)
+ return strings.TrimRight(indented, space)
+}
+
+// Extracting fmt.Sprintf to a separate variable circumvents go vet, which, as of go 1.10 is run with go test.
+var fmt_Sprintf = fmt.Sprintf
+
+func (self *Printer) Indent() {
+ self.prefix += pad
+}
+
+func (self *Printer) Dedent() {
+ if len(self.prefix) >= padLength {
+ self.prefix = self.prefix[:len(self.prefix)-padLength]
+ }
+}
+
+func NewPrinter(out io.Writer) *Printer {
+ self := new(Printer)
+ self.out = out
+ return self
+}
+
+const space = " "
+const pad = space + space
+const padLength = len(pad)
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go
new file mode 100644
index 0000000..33d5e14
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go
@@ -0,0 +1,80 @@
+package reporting
+
+import "fmt"
+
+type problem struct {
+ silent bool
+ out *Printer
+ errors []*AssertionResult
+ failures []*AssertionResult
+}
+
+func (self *problem) BeginStory(story *StoryReport) {}
+
+func (self *problem) Enter(scope *ScopeReport) {}
+
+func (self *problem) Report(report *AssertionResult) {
+ if report.Error != nil {
+ self.errors = append(self.errors, report)
+ } else if report.Failure != "" {
+ self.failures = append(self.failures, report)
+ }
+}
+
+func (self *problem) Exit() {}
+
+func (self *problem) EndStory() {
+ self.show(self.showErrors, redColor)
+ self.show(self.showFailures, yellowColor)
+ self.prepareForNextStory()
+}
+func (self *problem) show(display func(), color string) {
+ if !self.silent {
+ fmt.Print(color)
+ }
+ display()
+ if !self.silent {
+ fmt.Print(resetColor)
+ }
+ self.out.Dedent()
+}
+func (self *problem) showErrors() {
+ for i, e := range self.errors {
+ if i == 0 {
+ self.out.Println("\nErrors:\n")
+ self.out.Indent()
+ }
+ self.out.Println(errorTemplate, e.File, e.Line, e.Error, e.StackTrace)
+ }
+}
+func (self *problem) showFailures() {
+ for i, f := range self.failures {
+ if i == 0 {
+ self.out.Println("\nFailures:\n")
+ self.out.Indent()
+ }
+ self.out.Println(failureTemplate, f.File, f.Line, f.Failure, f.StackTrace)
+ }
+}
+
+func (self *problem) Write(content []byte) (written int, err error) {
+ return len(content), nil // no-op
+}
+
+func NewProblemReporter(out *Printer) *problem {
+ self := new(problem)
+ self.out = out
+ self.prepareForNextStory()
+ return self
+}
+
+func NewSilentProblemReporter(out *Printer) *problem {
+ self := NewProblemReporter(out)
+ self.silent = true
+ return self
+}
+
+func (self *problem) prepareForNextStory() {
+ self.errors = []*AssertionResult{}
+ self.failures = []*AssertionResult{}
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go
new file mode 100644
index 0000000..cce6c5e
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go
@@ -0,0 +1,39 @@
+package reporting
+
+import "io"
+
+type Reporter interface {
+ BeginStory(story *StoryReport)
+ Enter(scope *ScopeReport)
+ Report(r *AssertionResult)
+ Exit()
+ EndStory()
+ io.Writer
+}
+
+type reporters struct{ collection []Reporter }
+
+func (self *reporters) BeginStory(s *StoryReport) { self.foreach(func(r Reporter) { r.BeginStory(s) }) }
+func (self *reporters) Enter(s *ScopeReport) { self.foreach(func(r Reporter) { r.Enter(s) }) }
+func (self *reporters) Report(a *AssertionResult) { self.foreach(func(r Reporter) { r.Report(a) }) }
+func (self *reporters) Exit() { self.foreach(func(r Reporter) { r.Exit() }) }
+func (self *reporters) EndStory() { self.foreach(func(r Reporter) { r.EndStory() }) }
+
+func (self *reporters) Write(contents []byte) (written int, err error) {
+ self.foreach(func(r Reporter) {
+ written, err = r.Write(contents)
+ })
+ return written, err
+}
+
+func (self *reporters) foreach(action func(Reporter)) {
+ for _, r := range self.collection {
+ action(r)
+ }
+}
+
+func NewReporters(collection ...Reporter) *reporters {
+ self := new(reporters)
+ self.collection = collection
+ return self
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey
new file mode 100644
index 0000000..7998285
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey
@@ -0,0 +1,2 @@
+#ignore
+-timeout=1s
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go
new file mode 100644
index 0000000..712e6ad
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go
@@ -0,0 +1,179 @@
+package reporting
+
+import (
+ "encoding/json"
+ "fmt"
+ "runtime"
+ "strings"
+
+ "github.com/smartystreets/goconvey/convey/gotest"
+)
+
+////////////////// ScopeReport ////////////////////
+
+type ScopeReport struct {
+ Title string
+ File string
+ Line int
+}
+
+func NewScopeReport(title string) *ScopeReport {
+ file, line, _ := gotest.ResolveExternalCaller()
+ self := new(ScopeReport)
+ self.Title = title
+ self.File = file
+ self.Line = line
+ return self
+}
+
+////////////////// ScopeResult ////////////////////
+
+type ScopeResult struct {
+ Title string
+ File string
+ Line int
+ Depth int
+ Assertions []*AssertionResult
+ Output string
+}
+
+func newScopeResult(title string, depth int, file string, line int) *ScopeResult {
+ self := new(ScopeResult)
+ self.Title = title
+ self.Depth = depth
+ self.File = file
+ self.Line = line
+ self.Assertions = []*AssertionResult{}
+ return self
+}
+
+/////////////////// StoryReport /////////////////////
+
+type StoryReport struct {
+ Test T
+ Name string
+ File string
+ Line int
+}
+
+func NewStoryReport(test T) *StoryReport {
+ file, line, name := gotest.ResolveExternalCaller()
+ name = removePackagePath(name)
+ self := new(StoryReport)
+ self.Test = test
+ self.Name = name
+ self.File = file
+ self.Line = line
+ return self
+}
+
+// name comes in looking like "github.com/smartystreets/goconvey/examples.TestName".
+// We only want the stuff after the last '.', which is the name of the test function.
+func removePackagePath(name string) string {
+ parts := strings.Split(name, ".")
+ return parts[len(parts)-1]
+}
+
+/////////////////// FailureView ////////////////////////
+
+// This struct is also declared in github.com/smartystreets/assertions.
+// The json struct tags should be equal in both declarations.
+type FailureView struct {
+ Message string `json:"Message"`
+ Expected string `json:"Expected"`
+ Actual string `json:"Actual"`
+}
+
+////////////////////AssertionResult //////////////////////
+
+type AssertionResult struct {
+ File string
+ Line int
+ Expected string
+ Actual string
+ Failure string
+ Error interface{}
+ StackTrace string
+ Skipped bool
+}
+
+func NewFailureReport(failure string) *AssertionResult {
+ report := new(AssertionResult)
+ report.File, report.Line = caller()
+ report.StackTrace = stackTrace()
+ parseFailure(failure, report)
+ return report
+}
+func parseFailure(failure string, report *AssertionResult) {
+ view := new(FailureView)
+ err := json.Unmarshal([]byte(failure), view)
+ if err == nil {
+ report.Failure = view.Message
+ report.Expected = view.Expected
+ report.Actual = view.Actual
+ } else {
+ report.Failure = failure
+ }
+}
+func NewErrorReport(err interface{}) *AssertionResult {
+ report := new(AssertionResult)
+ report.File, report.Line = caller()
+ report.StackTrace = fullStackTrace()
+ report.Error = fmt.Sprintf("%v", err)
+ return report
+}
+func NewSuccessReport() *AssertionResult {
+ return new(AssertionResult)
+}
+func NewSkipReport() *AssertionResult {
+ report := new(AssertionResult)
+ report.File, report.Line = caller()
+ report.StackTrace = fullStackTrace()
+ report.Skipped = true
+ return report
+}
+
+func caller() (file string, line int) {
+ file, line, _ = gotest.ResolveExternalCaller()
+ return
+}
+
+func stackTrace() string {
+ buffer := make([]byte, 1024*64)
+ n := runtime.Stack(buffer, false)
+ return removeInternalEntries(string(buffer[:n]))
+}
+func fullStackTrace() string {
+ buffer := make([]byte, 1024*64)
+ n := runtime.Stack(buffer, true)
+ return removeInternalEntries(string(buffer[:n]))
+}
+func removeInternalEntries(stack string) string {
+ lines := strings.Split(stack, newline)
+ filtered := []string{}
+ for _, line := range lines {
+ if !isExternal(line) {
+ filtered = append(filtered, line)
+ }
+ }
+ return strings.Join(filtered, newline)
+}
+func isExternal(line string) bool {
+ for _, p := range internalPackages {
+ if strings.Contains(line, p) {
+ return true
+ }
+ }
+ return false
+}
+
+// NOTE: any new packages that host goconvey packages will need to be added here!
+// An alternative is to scan the goconvey directory and then exclude stuff like
+// the examples package but that's nasty too.
+var internalPackages = []string{
+ "goconvey/assertions",
+ "goconvey/convey",
+ "goconvey/execution",
+ "goconvey/gotest",
+ "goconvey/reporting",
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go
new file mode 100644
index 0000000..c3ccd05
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go
@@ -0,0 +1,108 @@
+package reporting
+
+import (
+ "fmt"
+ "sync"
+)
+
+func (self *statistics) BeginStory(story *StoryReport) {}
+
+func (self *statistics) Enter(scope *ScopeReport) {}
+
+func (self *statistics) Report(report *AssertionResult) {
+ self.Lock()
+ defer self.Unlock()
+
+ if !self.failing && report.Failure != "" {
+ self.failing = true
+ }
+ if !self.erroring && report.Error != nil {
+ self.erroring = true
+ }
+ if report.Skipped {
+ self.skipped += 1
+ } else {
+ self.total++
+ }
+}
+
+func (self *statistics) Exit() {}
+
+func (self *statistics) EndStory() {
+ self.Lock()
+ defer self.Unlock()
+
+ if !self.suppressed {
+ self.printSummaryLocked()
+ }
+}
+
+func (self *statistics) Suppress() {
+ self.Lock()
+ defer self.Unlock()
+ self.suppressed = true
+}
+
+func (self *statistics) PrintSummary() {
+ self.Lock()
+ defer self.Unlock()
+ self.printSummaryLocked()
+}
+
+func (self *statistics) printSummaryLocked() {
+ self.reportAssertionsLocked()
+ self.reportSkippedSectionsLocked()
+ self.completeReportLocked()
+}
+func (self *statistics) reportAssertionsLocked() {
+ self.decideColorLocked()
+ self.out.Print("\n%d total %s", self.total, plural("assertion", self.total))
+}
+func (self *statistics) decideColorLocked() {
+ if self.failing && !self.erroring {
+ fmt.Print(yellowColor)
+ } else if self.erroring {
+ fmt.Print(redColor)
+ } else {
+ fmt.Print(greenColor)
+ }
+}
+func (self *statistics) reportSkippedSectionsLocked() {
+ if self.skipped > 0 {
+ fmt.Print(yellowColor)
+ self.out.Print(" (one or more sections skipped)")
+ }
+}
+func (self *statistics) completeReportLocked() {
+ fmt.Print(resetColor)
+ self.out.Print("\n")
+ self.out.Print("\n")
+}
+
+func (self *statistics) Write(content []byte) (written int, err error) {
+ return len(content), nil // no-op
+}
+
+func NewStatisticsReporter(out *Printer) *statistics {
+ self := statistics{}
+ self.out = out
+ return &self
+}
+
+type statistics struct {
+ sync.Mutex
+
+ out *Printer
+ total int
+ failing bool
+ erroring bool
+ skipped int
+ suppressed bool
+}
+
+func plural(word string, count int) string {
+ if count == 1 {
+ return word
+ }
+ return word + "s"
+}
diff --git a/vendor/github.com/smartystreets/goconvey/convey/reporting/story.go b/vendor/github.com/smartystreets/goconvey/convey/reporting/story.go
new file mode 100644
index 0000000..9e73c97
--- /dev/null
+++ b/vendor/github.com/smartystreets/goconvey/convey/reporting/story.go
@@ -0,0 +1,73 @@
+// TODO: in order for this reporter to be completely honest
+// we need to retrofit to be more like the json reporter such that:
+// 1. it maintains ScopeResult collections, which count assertions
+// 2. it reports only after EndStory(), so that all tick marks
+// are placed near the appropriate title.
+// 3. Under unit test
+
+package reporting
+
+import (
+ "fmt"
+ "strings"
+)
+
+type story struct {
+ out *Printer
+ titlesById map[string]string
+ currentKey []string
+}
+
+func (self *story) BeginStory(story *StoryReport) {}
+
+func (self *story) Enter(scope *ScopeReport) {
+ self.out.Indent()
+
+ self.currentKey = append(self.currentKey, scope.Title)
+ ID := strings.Join(self.currentKey, "|")
+
+ if _, found := self.titlesById[ID]; !found {
+ self.out.Println("")
+ self.out.Print(scope.Title)
+ self.out.Insert(" ")
+ self.titlesById[ID] = scope.Title
+ }
+}
+
+func (self *story) Report(report *AssertionResult) {
+ if report.Error != nil {
+ fmt.Print(redColor)
+ self.out.Insert(error_)
+ } else if report.Failure != "" {
+ fmt.Print(yellowColor)
+ self.out.Insert(failure)
+ } else if report.Skipped {
+ fmt.Print(yellowColor)
+ self.out.Insert(skip)
+ } else {
+ fmt.Print(greenColor)
+ self.out.Insert(success)
+ }
+ fmt.Print(resetColor)
+}
+
+func (self *story) Exit() {
+ self.out.Dedent()
+ self.currentKey = self.currentKey[:len(self.currentKey)-1]
+}
+
+func (self *story) EndStory() {
+ self.titlesById = make(map[string]string)
+ self.out.Println("\n")
+}
+
+func (self *story) Write(content []byte) (written int, err error) {
+ return len(content), nil // no-op
+}
+
+func NewStoryReporter(out *Printer) *story {
+ self := new(story)
+ self.out = out
+ self.titlesById = make(map[string]string)
+ return self
+}
diff --git a/vendor/github.com/templexxx/cpu/.gitignore b/vendor/github.com/templexxx/cpu/.gitignore
new file mode 100644
index 0000000..f1c181e
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/.gitignore
@@ -0,0 +1,12 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
diff --git a/vendor/github.com/templexxx/cpu/LICENSE b/vendor/github.com/templexxx/cpu/LICENSE
new file mode 100644
index 0000000..dfa8f7b
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/LICENSE
@@ -0,0 +1,32 @@
+BSD 3-Clause License
+
+Copyright (c) 2018 Temple3x (temple3x@gmail.com)
+Copyright 2017 The Go Authors
+Copyright (c) 2015 Klaus Post
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* 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.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+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.
diff --git a/vendor/github.com/templexxx/cpu/README.md b/vendor/github.com/templexxx/cpu/README.md
new file mode 100644
index 0000000..50ccb9f
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/README.md
@@ -0,0 +1,23 @@
+# cpu
+internal/cpu(in Go standard lib) with these detections:
+
+>- AVX512
+>
+>- Cache Size
+>
+>- Invariant TSC
+>
+
+It also provides:
+
+>- False sharing range, see `X86FalseSharingRange` for X86 platform.
+>
+>- TSC frequency
+>
+>- Name
+>
+>- Family & Model
+
+# Acknowledgement
+
+[klauspost/cpuid](https://github.com/klauspost/cpuid) \ No newline at end of file
diff --git a/vendor/github.com/templexxx/cpu/cpu.go b/vendor/github.com/templexxx/cpu/cpu.go
new file mode 100644
index 0000000..92295d9
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu.go
@@ -0,0 +1,234 @@
+// Copyright 2017 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.
+
+// Package cpu implements processor feature detection
+// used by the Go standard library.
+package cpu
+
+// debugOptions is set to true by the runtime if go was compiled with GOEXPERIMENT=debugcpu
+// and GOOS is Linux or Darwin. This variable is linknamed in runtime/proc.go.
+var debugOptions bool
+
+var X86 x86
+
+// "Loads data or instructions from memory to the second-level cache.
+// To use the streamer, organize the data or instructions in blocks of 128 bytes,
+// aligned on 128 bytes."
+// From <Intel® 64 and IA-32 architectures optimization reference manual>,
+// in section 3.7.3 "Hardware Prefetching for Second-Level Cache"
+//
+// In practice, I have found use 128bytes can gain better performance than 64bytes (one cache line).
+const X86FalseSharingRange = 128
+
+// The booleans in x86 contain the correspondingly named cpuid feature bit.
+// HasAVX and HasAVX2 are only set if the OS does support XMM and YMM registers
+// in addition to the cpuid feature bit being set.
+// The struct is padded to avoid false sharing.
+type x86 struct {
+ _ [X86FalseSharingRange]byte
+ HasAES bool
+ HasADX bool
+ HasAVX bool
+ HasAVX2 bool
+ HasAVX512F bool
+ HasAVX512DQ bool
+ HasAVX512BW bool
+ HasAVX512VL bool
+ HasBMI1 bool
+ HasBMI2 bool
+ HasERMS bool
+ HasFMA bool
+ HasOSXSAVE bool
+ HasPCLMULQDQ bool
+ HasPOPCNT bool
+ HasSSE2 bool
+ HasSSE3 bool
+ HasSSSE3 bool
+ HasSSE41 bool
+ HasSSE42 bool
+ // The invariant TSC will run at a constant rate in all ACPI P-, C-, and T-states.
+ // This is the architectural behavior moving forward. On processors with
+ // invariant TSC support, the OS may use the TSC for wall clock timer services (instead of ACPI or HPET timers).
+ HasInvariantTSC bool
+
+ Cache Cache
+
+ // TSCFrequency only meaningful when HasInvariantTSC == true.
+ // Unit: Hz.
+ //
+ // Warn:
+ // 1. If it's 0, means can't get it. Don't use it.
+ // 2. Don't use it if you want "100%" precise timestamp.
+ TSCFrequency uint64
+
+ Name string
+ Signature string // DisplayFamily_DisplayModel.
+ Family uint32 // CPU family number.
+ Model uint32 // CPU model number.
+
+ _ [X86FalseSharingRange]byte
+}
+
+// CPU Cache Size.
+// -1 if undetected.
+type Cache struct {
+ L1I int
+ L1D int
+ L2 int
+ L3 int
+}
+
+var PPC64 ppc64
+
+// For ppc64x, it is safe to check only for ISA level starting on ISA v3.00,
+// since there are no optional categories. There are some exceptions that also
+// require kernel support to work (darn, scv), so there are feature bits for
+// those as well. The minimum processor requirement is POWER8 (ISA 2.07), so we
+// maintain some of the old feature checks for optional categories for
+// safety.
+// The struct is padded to avoid false sharing.
+type ppc64 struct {
+ _ [CacheLineSize]byte
+ HasVMX bool // Vector unit (Altivec)
+ HasDFP bool // Decimal Floating Point unit
+ HasVSX bool // Vector-scalar unit
+ HasHTM bool // Hardware Transactional Memory
+ HasISEL bool // Integer select
+ HasVCRYPTO bool // Vector cryptography
+ HasHTMNOSC bool // HTM: kernel-aborted transaction in syscalls
+ HasDARN bool // Hardware random number generator (requires kernel enablement)
+ HasSCV bool // Syscall vectored (requires kernel enablement)
+ IsPOWER8 bool // ISA v2.07 (POWER8)
+ IsPOWER9 bool // ISA v3.00 (POWER9)
+ _ [CacheLineSize]byte
+}
+
+var ARM64 arm64
+
+// The booleans in arm64 contain the correspondingly named cpu feature bit.
+// The struct is padded to avoid false sharing.
+type arm64 struct {
+ _ [CacheLineSize]byte
+ HasFP bool
+ HasASIMD bool
+ HasEVTSTRM bool
+ HasAES bool
+ HasPMULL bool
+ HasSHA1 bool
+ HasSHA2 bool
+ HasCRC32 bool
+ HasATOMICS bool
+ HasFPHP bool
+ HasASIMDHP bool
+ HasCPUID bool
+ HasASIMDRDM bool
+ HasJSCVT bool
+ HasFCMA bool
+ HasLRCPC bool
+ HasDCPOP bool
+ HasSHA3 bool
+ HasSM3 bool
+ HasSM4 bool
+ HasASIMDDP bool
+ HasSHA512 bool
+ HasSVE bool
+ HasASIMDFHM bool
+ _ [CacheLineSize]byte
+}
+
+var S390X s390x
+
+type s390x struct {
+ _ [CacheLineSize]byte
+ HasZArch bool // z architecture mode is active [mandatory]
+ HasSTFLE bool // store facility list extended [mandatory]
+ HasLDisp bool // long (20-bit) displacements [mandatory]
+ HasEImm bool // 32-bit immediates [mandatory]
+ HasDFP bool // decimal floating point
+ HasETF3Enhanced bool // ETF-3 enhanced
+ HasMSA bool // message security assist (CPACF)
+ HasAES bool // KM-AES{128,192,256} functions
+ HasAESCBC bool // KMC-AES{128,192,256} functions
+ HasAESCTR bool // KMCTR-AES{128,192,256} functions
+ HasAESGCM bool // KMA-GCM-AES{128,192,256} functions
+ HasGHASH bool // KIMD-GHASH function
+ HasSHA1 bool // K{I,L}MD-SHA-1 functions
+ HasSHA256 bool // K{I,L}MD-SHA-256 functions
+ HasSHA512 bool // K{I,L}MD-SHA-512 functions
+ HasVX bool // vector facility. Note: the runtime sets this when it processes auxv records.
+ _ [CacheLineSize]byte
+}
+
+// initialize examines the processor and sets the relevant variables above.
+// This is called by the runtime package early in program initialization,
+// before normal init functions are run. env is set by runtime on Linux and Darwin
+// if go was compiled with GOEXPERIMENT=debugcpu.
+func init() {
+ doinit()
+ processOptions("")
+}
+
+// options contains the cpu debug options that can be used in GODEBUGCPU.
+// Options are arch dependent and are added by the arch specific doinit functions.
+// Features that are mandatory for the specific GOARCH should not be added to options
+// (e.g. SSE2 on amd64).
+var options []option
+
+// Option names should be lower case. e.g. avx instead of AVX.
+type option struct {
+ Name string
+ Feature *bool
+}
+
+// processOptions disables CPU feature values based on the parsed env string.
+// The env string is expected to be of the form feature1=0,feature2=0...
+// where feature names is one of the architecture specifc list stored in the
+// cpu packages options variable. If env contains all=0 then all capabilities
+// referenced through the options variable are disabled. Other feature
+// names and values other than 0 are silently ignored.
+func processOptions(env string) {
+field:
+ for env != "" {
+ field := ""
+ i := indexByte(env, ',')
+ if i < 0 {
+ field, env = env, ""
+ } else {
+ field, env = env[:i], env[i+1:]
+ }
+ i = indexByte(field, '=')
+ if i < 0 {
+ continue
+ }
+ key, value := field[:i], field[i+1:]
+
+ // Only allow turning off CPU features by specifying '0'.
+ if value == "0" {
+ if key == "all" {
+ for _, v := range options {
+ *v.Feature = false
+ }
+ return
+ } else {
+ for _, v := range options {
+ if v.Name == key {
+ *v.Feature = false
+ continue field
+ }
+ }
+ }
+ }
+ }
+}
+
+// indexByte returns the index of the first instance of c in s,
+// or -1 if c is not present in s.
+func indexByte(s string, c byte) int {
+ for i := 0; i < len(s); i++ {
+ if s[i] == c {
+ return i
+ }
+ }
+ return -1
+}
diff --git a/vendor/github.com/templexxx/cpu/cpu_386.go b/vendor/github.com/templexxx/cpu/cpu_386.go
new file mode 100644
index 0000000..561c81f
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_386.go
@@ -0,0 +1,7 @@
+// Copyright 2018 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.
+
+package cpu
+
+const GOARCH = "386"
diff --git a/vendor/github.com/templexxx/cpu/cpu_amd64.go b/vendor/github.com/templexxx/cpu/cpu_amd64.go
new file mode 100644
index 0000000..9b00153
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_amd64.go
@@ -0,0 +1,7 @@
+// Copyright 2018 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.
+
+package cpu
+
+const GOARCH = "amd64"
diff --git a/vendor/github.com/templexxx/cpu/cpu_amd64p32.go b/vendor/github.com/templexxx/cpu/cpu_amd64p32.go
new file mode 100644
index 0000000..177b14e
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_amd64p32.go
@@ -0,0 +1,7 @@
+// Copyright 2018 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.
+
+package cpu
+
+const GOARCH = "amd64p32"
diff --git a/vendor/github.com/templexxx/cpu/cpu_arm.go b/vendor/github.com/templexxx/cpu/cpu_arm.go
new file mode 100644
index 0000000..078a6c3
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_arm.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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.
+
+package cpu
+
+const CacheLineSize = 32
diff --git a/vendor/github.com/templexxx/cpu/cpu_arm64.go b/vendor/github.com/templexxx/cpu/cpu_arm64.go
new file mode 100644
index 0000000..487ccf8
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_arm64.go
@@ -0,0 +1,102 @@
+// Copyright 2017 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.
+
+package cpu
+
+const CacheLineSize = 64
+
+// arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
+// These are linknamed in runtime/os_linux_arm64.go and are initialized by
+// archauxv().
+var hwcap uint
+var hwcap2 uint
+
+// HWCAP/HWCAP2 bits. These are exposed by Linux.
+const (
+ hwcap_FP = (1 << 0)
+ hwcap_ASIMD = (1 << 1)
+ hwcap_EVTSTRM = (1 << 2)
+ hwcap_AES = (1 << 3)
+ hwcap_PMULL = (1 << 4)
+ hwcap_SHA1 = (1 << 5)
+ hwcap_SHA2 = (1 << 6)
+ hwcap_CRC32 = (1 << 7)
+ hwcap_ATOMICS = (1 << 8)
+ hwcap_FPHP = (1 << 9)
+ hwcap_ASIMDHP = (1 << 10)
+ hwcap_CPUID = (1 << 11)
+ hwcap_ASIMDRDM = (1 << 12)
+ hwcap_JSCVT = (1 << 13)
+ hwcap_FCMA = (1 << 14)
+ hwcap_LRCPC = (1 << 15)
+ hwcap_DCPOP = (1 << 16)
+ hwcap_SHA3 = (1 << 17)
+ hwcap_SM3 = (1 << 18)
+ hwcap_SM4 = (1 << 19)
+ hwcap_ASIMDDP = (1 << 20)
+ hwcap_SHA512 = (1 << 21)
+ hwcap_SVE = (1 << 22)
+ hwcap_ASIMDFHM = (1 << 23)
+)
+
+func doinit() {
+ options = []option{
+ {"evtstrm", &ARM64.HasEVTSTRM},
+ {"aes", &ARM64.HasAES},
+ {"pmull", &ARM64.HasPMULL},
+ {"sha1", &ARM64.HasSHA1},
+ {"sha2", &ARM64.HasSHA2},
+ {"crc32", &ARM64.HasCRC32},
+ {"atomics", &ARM64.HasATOMICS},
+ {"fphp", &ARM64.HasFPHP},
+ {"asimdhp", &ARM64.HasASIMDHP},
+ {"cpuid", &ARM64.HasCPUID},
+ {"asimdrdm", &ARM64.HasASIMDRDM},
+ {"jscvt", &ARM64.HasJSCVT},
+ {"fcma", &ARM64.HasFCMA},
+ {"lrcpc", &ARM64.HasLRCPC},
+ {"dcpop", &ARM64.HasDCPOP},
+ {"sha3", &ARM64.HasSHA3},
+ {"sm3", &ARM64.HasSM3},
+ {"sm4", &ARM64.HasSM4},
+ {"asimddp", &ARM64.HasASIMDDP},
+ {"sha512", &ARM64.HasSHA512},
+ {"sve", &ARM64.HasSVE},
+ {"asimdfhm", &ARM64.HasASIMDFHM},
+
+ // These capabilities should always be enabled on arm64:
+ // {"fp", &ARM64.HasFP},
+ // {"asimd", &ARM64.HasASIMD},
+ }
+
+ // HWCAP feature bits
+ ARM64.HasFP = isSet(hwcap, hwcap_FP)
+ ARM64.HasASIMD = isSet(hwcap, hwcap_ASIMD)
+ ARM64.HasEVTSTRM = isSet(hwcap, hwcap_EVTSTRM)
+ ARM64.HasAES = isSet(hwcap, hwcap_AES)
+ ARM64.HasPMULL = isSet(hwcap, hwcap_PMULL)
+ ARM64.HasSHA1 = isSet(hwcap, hwcap_SHA1)
+ ARM64.HasSHA2 = isSet(hwcap, hwcap_SHA2)
+ ARM64.HasCRC32 = isSet(hwcap, hwcap_CRC32)
+ ARM64.HasATOMICS = isSet(hwcap, hwcap_ATOMICS)
+ ARM64.HasFPHP = isSet(hwcap, hwcap_FPHP)
+ ARM64.HasASIMDHP = isSet(hwcap, hwcap_ASIMDHP)
+ ARM64.HasCPUID = isSet(hwcap, hwcap_CPUID)
+ ARM64.HasASIMDRDM = isSet(hwcap, hwcap_ASIMDRDM)
+ ARM64.HasJSCVT = isSet(hwcap, hwcap_JSCVT)
+ ARM64.HasFCMA = isSet(hwcap, hwcap_FCMA)
+ ARM64.HasLRCPC = isSet(hwcap, hwcap_LRCPC)
+ ARM64.HasDCPOP = isSet(hwcap, hwcap_DCPOP)
+ ARM64.HasSHA3 = isSet(hwcap, hwcap_SHA3)
+ ARM64.HasSM3 = isSet(hwcap, hwcap_SM3)
+ ARM64.HasSM4 = isSet(hwcap, hwcap_SM4)
+ ARM64.HasASIMDDP = isSet(hwcap, hwcap_ASIMDDP)
+ ARM64.HasSHA512 = isSet(hwcap, hwcap_SHA512)
+ ARM64.HasSVE = isSet(hwcap, hwcap_SVE)
+ ARM64.HasASIMDFHM = isSet(hwcap, hwcap_ASIMDFHM)
+}
+
+func isSet(hwc uint, value uint) bool {
+ return hwc&value != 0
+}
diff --git a/vendor/github.com/templexxx/cpu/cpu_mips.go b/vendor/github.com/templexxx/cpu/cpu_mips.go
new file mode 100644
index 0000000..078a6c3
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_mips.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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.
+
+package cpu
+
+const CacheLineSize = 32
diff --git a/vendor/github.com/templexxx/cpu/cpu_mips64.go b/vendor/github.com/templexxx/cpu/cpu_mips64.go
new file mode 100644
index 0000000..078a6c3
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_mips64.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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.
+
+package cpu
+
+const CacheLineSize = 32
diff --git a/vendor/github.com/templexxx/cpu/cpu_mips64le.go b/vendor/github.com/templexxx/cpu/cpu_mips64le.go
new file mode 100644
index 0000000..078a6c3
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_mips64le.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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.
+
+package cpu
+
+const CacheLineSize = 32
diff --git a/vendor/github.com/templexxx/cpu/cpu_mipsle.go b/vendor/github.com/templexxx/cpu/cpu_mipsle.go
new file mode 100644
index 0000000..078a6c3
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_mipsle.go
@@ -0,0 +1,7 @@
+// Copyright 2017 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.
+
+package cpu
+
+const CacheLineSize = 32
diff --git a/vendor/github.com/templexxx/cpu/cpu_no_init.go b/vendor/github.com/templexxx/cpu/cpu_no_init.go
new file mode 100644
index 0000000..1be4f29
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_no_init.go
@@ -0,0 +1,16 @@
+// Copyright 2018 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 !386
+// +build !amd64
+// +build !amd64p32
+// +build !arm64
+// +build !ppc64
+// +build !ppc64le
+// +build !s390x
+
+package cpu
+
+func doinit() {
+}
diff --git a/vendor/github.com/templexxx/cpu/cpu_ppc64x.go b/vendor/github.com/templexxx/cpu/cpu_ppc64x.go
new file mode 100644
index 0000000..995cf02
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_ppc64x.go
@@ -0,0 +1,68 @@
+// Copyright 2017 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 ppc64 ppc64le
+
+package cpu
+
+const CacheLineSize = 128
+
+// ppc64x doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
+// These are linknamed in runtime/os_linux_ppc64x.go and are initialized by
+// archauxv().
+var hwcap uint
+var hwcap2 uint
+
+// HWCAP/HWCAP2 bits. These are exposed by the kernel.
+const (
+ // ISA Level
+ _PPC_FEATURE2_ARCH_2_07 = 0x80000000
+ _PPC_FEATURE2_ARCH_3_00 = 0x00800000
+
+ // CPU features
+ _PPC_FEATURE_HAS_ALTIVEC = 0x10000000
+ _PPC_FEATURE_HAS_DFP = 0x00000400
+ _PPC_FEATURE_HAS_VSX = 0x00000080
+ _PPC_FEATURE2_HAS_HTM = 0x40000000
+ _PPC_FEATURE2_HAS_ISEL = 0x08000000
+ _PPC_FEATURE2_HAS_VEC_CRYPTO = 0x02000000
+ _PPC_FEATURE2_HTM_NOSC = 0x01000000
+ _PPC_FEATURE2_DARN = 0x00200000
+ _PPC_FEATURE2_SCV = 0x00100000
+)
+
+func doinit() {
+ options = []option{
+ {"htm", &PPC64.HasHTM},
+ {"htmnosc", &PPC64.HasHTMNOSC},
+ {"darn", &PPC64.HasDARN},
+ {"scv", &PPC64.HasSCV},
+
+ // These capabilities should always be enabled on ppc64 and ppc64le:
+ // {"vmx", &PPC64.HasVMX},
+ // {"dfp", &PPC64.HasDFP},
+ // {"vsx", &PPC64.HasVSX},
+ // {"isel", &PPC64.HasISEL},
+ // {"vcrypto", &PPC64.HasVCRYPTO},
+ }
+
+ // HWCAP feature bits
+ PPC64.HasVMX = isSet(hwcap, _PPC_FEATURE_HAS_ALTIVEC)
+ PPC64.HasDFP = isSet(hwcap, _PPC_FEATURE_HAS_DFP)
+ PPC64.HasVSX = isSet(hwcap, _PPC_FEATURE_HAS_VSX)
+
+ // HWCAP2 feature bits
+ PPC64.IsPOWER8 = isSet(hwcap2, _PPC_FEATURE2_ARCH_2_07)
+ PPC64.HasHTM = isSet(hwcap2, _PPC_FEATURE2_HAS_HTM)
+ PPC64.HasISEL = isSet(hwcap2, _PPC_FEATURE2_HAS_ISEL)
+ PPC64.HasVCRYPTO = isSet(hwcap2, _PPC_FEATURE2_HAS_VEC_CRYPTO)
+ PPC64.HasHTMNOSC = isSet(hwcap2, _PPC_FEATURE2_HTM_NOSC)
+ PPC64.IsPOWER9 = isSet(hwcap2, _PPC_FEATURE2_ARCH_3_00)
+ PPC64.HasDARN = isSet(hwcap2, _PPC_FEATURE2_DARN)
+ PPC64.HasSCV = isSet(hwcap2, _PPC_FEATURE2_SCV)
+}
+
+func isSet(hwc uint, value uint) bool {
+ return hwc&value != 0
+}
diff --git a/vendor/github.com/templexxx/cpu/cpu_s390x.go b/vendor/github.com/templexxx/cpu/cpu_s390x.go
new file mode 100644
index 0000000..389a058
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_s390x.go
@@ -0,0 +1,153 @@
+// Copyright 2017 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.
+
+package cpu
+
+const CacheLineSize = 256
+
+// bitIsSet reports whether the bit at index is set. The bit index
+// is in big endian order, so bit index 0 is the leftmost bit.
+func bitIsSet(bits []uint64, index uint) bool {
+ return bits[index/64]&((1<<63)>>(index%64)) != 0
+}
+
+// function is the function code for the named function.
+type function uint8
+
+const (
+ // KM{,A,C,CTR} function codes
+ aes128 function = 18 // AES-128
+ aes192 = 19 // AES-192
+ aes256 = 20 // AES-256
+
+ // K{I,L}MD function codes
+ sha1 = 1 // SHA-1
+ sha256 = 2 // SHA-256
+ sha512 = 3 // SHA-512
+
+ // KLMD function codes
+ ghash = 65 // GHASH
+)
+
+// queryResult contains the result of a Query function
+// call. Bits are numbered in big endian order so the
+// leftmost bit (the MSB) is at index 0.
+type queryResult struct {
+ bits [2]uint64
+}
+
+// Has reports whether the given functions are present.
+func (q *queryResult) Has(fns ...function) bool {
+ if len(fns) == 0 {
+ panic("no function codes provided")
+ }
+ for _, f := range fns {
+ if !bitIsSet(q.bits[:], uint(f)) {
+ return false
+ }
+ }
+ return true
+}
+
+// facility is a bit index for the named facility.
+type facility uint8
+
+const (
+ // mandatory facilities
+ zarch facility = 1 // z architecture mode is active
+ stflef = 7 // store-facility-list-extended
+ ldisp = 18 // long-displacement
+ eimm = 21 // extended-immediate
+
+ // miscellaneous facilities
+ dfp = 42 // decimal-floating-point
+ etf3eh = 30 // extended-translation 3 enhancement
+
+ // cryptography facilities
+ msa = 17 // message-security-assist
+ msa3 = 76 // message-security-assist extension 3
+ msa4 = 77 // message-security-assist extension 4
+ msa5 = 57 // message-security-assist extension 5
+ msa8 = 146 // message-security-assist extension 8
+
+ // Note: vx and highgprs are excluded because they require
+ // kernel support and so must be fetched from HWCAP.
+)
+
+// facilityList contains the result of an STFLE call.
+// Bits are numbered in big endian order so the
+// leftmost bit (the MSB) is at index 0.
+type facilityList struct {
+ bits [4]uint64
+}
+
+// Has reports whether the given facilities are present.
+func (s *facilityList) Has(fs ...facility) bool {
+ if len(fs) == 0 {
+ panic("no facility bits provided")
+ }
+ for _, f := range fs {
+ if !bitIsSet(s.bits[:], uint(f)) {
+ return false
+ }
+ }
+ return true
+}
+
+// The following feature detection functions are defined in cpu_s390x.s.
+// They are likely to be expensive to call so the results should be cached.
+func stfle() facilityList
+func kmQuery() queryResult
+func kmcQuery() queryResult
+func kmctrQuery() queryResult
+func kmaQuery() queryResult
+func kimdQuery() queryResult
+func klmdQuery() queryResult
+
+func doinit() {
+ options = []option{
+ {"zarch", &S390X.HasZArch},
+ {"stfle", &S390X.HasSTFLE},
+ {"ldisp", &S390X.HasLDisp},
+ {"msa", &S390X.HasMSA},
+ {"eimm", &S390X.HasEImm},
+ {"dfp", &S390X.HasDFP},
+ {"etf3eh", &S390X.HasETF3Enhanced},
+ {"vx", &S390X.HasVX},
+ }
+
+ aes := []function{aes128, aes192, aes256}
+ facilities := stfle()
+
+ S390X.HasZArch = facilities.Has(zarch)
+ S390X.HasSTFLE = facilities.Has(stflef)
+ S390X.HasLDisp = facilities.Has(ldisp)
+ S390X.HasEImm = facilities.Has(eimm)
+ S390X.HasDFP = facilities.Has(dfp)
+ S390X.HasETF3Enhanced = facilities.Has(etf3eh)
+ S390X.HasMSA = facilities.Has(msa)
+
+ if S390X.HasMSA {
+ // cipher message
+ km, kmc := kmQuery(), kmcQuery()
+ S390X.HasAES = km.Has(aes...)
+ S390X.HasAESCBC = kmc.Has(aes...)
+ if facilities.Has(msa4) {
+ kmctr := kmctrQuery()
+ S390X.HasAESCTR = kmctr.Has(aes...)
+ }
+ if facilities.Has(msa8) {
+ kma := kmaQuery()
+ S390X.HasAESGCM = kma.Has(aes...)
+ }
+
+ // compute message digest
+ kimd := kimdQuery() // intermediate (no padding)
+ klmd := klmdQuery() // last (padding)
+ S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1)
+ S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256)
+ S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512)
+ S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist
+ }
+}
diff --git a/vendor/github.com/templexxx/cpu/cpu_s390x.s b/vendor/github.com/templexxx/cpu/cpu_s390x.s
new file mode 100644
index 0000000..9678035
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_s390x.s
@@ -0,0 +1,55 @@
+// Copyright 2018 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.
+
+#include "textflag.h"
+
+// func stfle() facilityList
+TEXT ·stfle(SB), NOSPLIT|NOFRAME, $0-32
+ MOVD $ret+0(FP), R1
+ MOVD $3, R0 // last doubleword index to store
+ XC $32, (R1), (R1) // clear 4 doublewords (32 bytes)
+ WORD $0xb2b01000 // store facility list extended (STFLE)
+ RET
+
+// func kmQuery() queryResult
+TEXT ·kmQuery(SB), NOSPLIT|NOFRAME, $0-16
+ MOVD $0, R0 // set function code to 0 (KM-Query)
+ MOVD $ret+0(FP), R1 // address of 16-byte return value
+ WORD $0xB92E0024 // cipher message (KM)
+ RET
+
+// func kmcQuery() queryResult
+TEXT ·kmcQuery(SB), NOSPLIT|NOFRAME, $0-16
+ MOVD $0, R0 // set function code to 0 (KMC-Query)
+ MOVD $ret+0(FP), R1 // address of 16-byte return value
+ WORD $0xB92F0024 // cipher message with chaining (KMC)
+ RET
+
+// func kmctrQuery() queryResult
+TEXT ·kmctrQuery(SB), NOSPLIT|NOFRAME, $0-16
+ MOVD $0, R0 // set function code to 0 (KMCTR-Query)
+ MOVD $ret+0(FP), R1 // address of 16-byte return value
+ WORD $0xB92D4024 // cipher message with counter (KMCTR)
+ RET
+
+// func kmaQuery() queryResult
+TEXT ·kmaQuery(SB), NOSPLIT|NOFRAME, $0-16
+ MOVD $0, R0 // set function code to 0 (KMA-Query)
+ MOVD $ret+0(FP), R1 // address of 16-byte return value
+ WORD $0xb9296024 // cipher message with authentication (KMA)
+ RET
+
+// func kimdQuery() queryResult
+TEXT ·kimdQuery(SB), NOSPLIT|NOFRAME, $0-16
+ MOVD $0, R0 // set function code to 0 (KIMD-Query)
+ MOVD $ret+0(FP), R1 // address of 16-byte return value
+ WORD $0xB93E0024 // compute intermediate message digest (KIMD)
+ RET
+
+// func klmdQuery() queryResult
+TEXT ·klmdQuery(SB), NOSPLIT|NOFRAME, $0-16
+ MOVD $0, R0 // set function code to 0 (KLMD-Query)
+ MOVD $ret+0(FP), R1 // address of 16-byte return value
+ WORD $0xB93F0024 // compute last message digest (KLMD)
+ RET
diff --git a/vendor/github.com/templexxx/cpu/cpu_wasm.go b/vendor/github.com/templexxx/cpu/cpu_wasm.go
new file mode 100644
index 0000000..1107a7a
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_wasm.go
@@ -0,0 +1,7 @@
+// Copyright 2018 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.
+
+package cpu
+
+const CacheLineSize = 64
diff --git a/vendor/github.com/templexxx/cpu/cpu_x86.go b/vendor/github.com/templexxx/cpu/cpu_x86.go
new file mode 100644
index 0000000..313a29a
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_x86.go
@@ -0,0 +1,425 @@
+// Copyright 2017 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 386 amd64 amd64p32
+
+package cpu
+
+import (
+ "fmt"
+ "strings"
+)
+
+const CacheLineSize = 64
+
+// cpuid is implemented in cpu_x86.s.
+func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
+
+// xgetbv with ecx = 0 is implemented in cpu_x86.s.
+func xgetbv() (eax, edx uint32)
+
+const (
+ // edx bits
+ cpuid_SSE2 = 1 << 26
+
+ // ecx bits
+ cpuid_SSE3 = 1 << 0
+ cpuid_PCLMULQDQ = 1 << 1
+ cpuid_SSSE3 = 1 << 9
+ cpuid_FMA = 1 << 12
+ cpuid_SSE41 = 1 << 19
+ cpuid_SSE42 = 1 << 20
+ cpuid_POPCNT = 1 << 23
+ cpuid_AES = 1 << 25
+ cpuid_OSXSAVE = 1 << 27
+ cpuid_AVX = 1 << 28
+
+ // ebx bits
+ cpuid_BMI1 = 1 << 3
+ cpuid_AVX2 = 1 << 5
+ cpuid_BMI2 = 1 << 8
+ cpuid_ERMS = 1 << 9
+ cpuid_ADX = 1 << 19
+ cpuid_AVX512F = 1 << 16
+ cpuid_AVX512DQ = 1 << 17
+ cpuid_AVX512BW = 1 << 30
+ cpuid_AVX512VL = 1 << 31
+
+ // edx bits
+ cpuid_Invariant_TSC = 1 << 8
+)
+
+func doinit() {
+ options = []option{
+ {"adx", &X86.HasADX},
+ {"aes", &X86.HasAES},
+ {"avx", &X86.HasAVX},
+ {"avx2", &X86.HasAVX2},
+ {"bmi1", &X86.HasBMI1},
+ {"bmi2", &X86.HasBMI2},
+ {"erms", &X86.HasERMS},
+ {"fma", &X86.HasFMA},
+ {"pclmulqdq", &X86.HasPCLMULQDQ},
+ {"popcnt", &X86.HasPOPCNT},
+ {"sse3", &X86.HasSSE3},
+ {"sse41", &X86.HasSSE41},
+ {"sse42", &X86.HasSSE42},
+ {"ssse3", &X86.HasSSSE3},
+ {"avx512f", &X86.HasAVX512F},
+ {"avx512dq", &X86.HasAVX512DQ},
+ {"avx512bw", &X86.HasAVX512BW},
+ {"avx512vl", &X86.HasAVX512VL},
+ {"invariant_tsc", &X86.HasInvariantTSC},
+
+ // sse2 set as last element so it can easily be removed again. See code below.
+ {"sse2", &X86.HasSSE2},
+ }
+
+ // Remove sse2 from options on amd64(p32) because SSE2 is a mandatory feature for these GOARCHs.
+ if GOARCH == "amd64" || GOARCH == "amd64p32" {
+ options = options[:len(options)-1]
+ }
+
+ maxID, _, _, _ := cpuid(0, 0)
+
+ if maxID < 1 {
+ return
+ }
+
+ _, _, ecx1, edx1 := cpuid(1, 0)
+ X86.HasSSE2 = isSet(edx1, cpuid_SSE2)
+
+ X86.HasSSE3 = isSet(ecx1, cpuid_SSE3)
+ X86.HasPCLMULQDQ = isSet(ecx1, cpuid_PCLMULQDQ)
+ X86.HasSSSE3 = isSet(ecx1, cpuid_SSSE3)
+ X86.HasFMA = isSet(ecx1, cpuid_FMA)
+ X86.HasSSE41 = isSet(ecx1, cpuid_SSE41)
+ X86.HasSSE42 = isSet(ecx1, cpuid_SSE42)
+ X86.HasPOPCNT = isSet(ecx1, cpuid_POPCNT)
+ X86.HasAES = isSet(ecx1, cpuid_AES)
+ X86.HasOSXSAVE = isSet(ecx1, cpuid_OSXSAVE)
+
+ osSupportsAVX := false
+ osSupportsAVX512 := false
+ // For XGETBV, OSXSAVE bit is required and sufficient.
+ if X86.HasOSXSAVE {
+ eax, _ := xgetbv()
+ // Check if XMM and YMM registers have OS support.
+ osSupportsAVX = isSet(eax, 1<<1) && isSet(eax, 1<<2)
+ // Check is ZMM registers have OS support.
+ osSupportsAVX512 = isSet(eax>>5, 7) && isSet(eax>>1, 3)
+ }
+
+ X86.HasAVX = isSet(ecx1, cpuid_AVX) && osSupportsAVX
+
+ if maxID < 7 {
+ return
+ }
+
+ _, ebx7, _, _ := cpuid(7, 0)
+ X86.HasBMI1 = isSet(ebx7, cpuid_BMI1)
+ X86.HasAVX2 = isSet(ebx7, cpuid_AVX2) && osSupportsAVX
+ X86.HasAVX512F = isSet(ebx7, cpuid_AVX512F) && osSupportsAVX512
+ X86.HasAVX512DQ = isSet(ebx7, cpuid_AVX512DQ) && osSupportsAVX512
+ X86.HasAVX512BW = isSet(ebx7, cpuid_AVX512BW) && osSupportsAVX512
+ X86.HasAVX512VL = isSet(ebx7, cpuid_AVX512VL) && osSupportsAVX512
+ X86.HasBMI2 = isSet(ebx7, cpuid_BMI2)
+ X86.HasERMS = isSet(ebx7, cpuid_ERMS)
+ X86.HasADX = isSet(ebx7, cpuid_ADX)
+
+ X86.Cache = getCacheSize()
+
+ X86.HasInvariantTSC = hasInvariantTSC()
+
+ X86.Family, X86.Model = getFamilyModel()
+
+ X86.Signature = makeSignature(X86.Family, X86.Model)
+
+ X86.Name = getName()
+
+ X86.TSCFrequency = getNativeTSCFrequency(X86.Name, X86.Signature)
+}
+
+func isSet(hwc uint32, value uint32) bool {
+ return hwc&value != 0
+}
+
+func hasInvariantTSC() bool {
+ if maxExtendedFunction() < 0x80000007 {
+ return false
+ }
+ _, _, _, edx := cpuid(0x80000007, 0)
+ return isSet(edx, cpuid_Invariant_TSC)
+}
+
+func getName() string {
+ if maxExtendedFunction() >= 0x80000004 {
+ v := make([]uint32, 0, 48)
+ for i := uint32(0); i < 3; i++ {
+ a, b, c, d := cpuid(0x80000002+i, 0)
+ v = append(v, a, b, c, d)
+ }
+ return strings.Trim(string(valAsString(v...)), " ")
+ }
+ return "unknown"
+}
+
+// getNativeTSCFrequency gets TSC frequency from CPUID,
+// only supports Intel (Skylake or later microarchitecture) & key information is from Intel manual & kernel codes
+// (especially this commit: https://github.com/torvalds/linux/commit/604dc9170f2435d27da5039a3efd757dceadc684).
+func getNativeTSCFrequency(name, sign string) uint64 {
+
+ if vendorID() != Intel {
+ return 0
+ }
+
+ if maxFunctionID() < 0x15 {
+ return 0
+ }
+
+ // ApolloLake, GeminiLake, CannonLake (and presumably all new chipsets
+ // from this point) report the crystal frequency directly via CPUID.0x15.
+ // That's definitive data that we can rely upon.
+ eax, ebx, ecx, _ := cpuid(0x15, 0)
+
+ // If ebx is 0, the TSC/”core crystal clock” ratio is not enumerated.
+ // We won't provide TSC frequency detection in this situation.
+ if eax == 0 || ebx == 0 {
+ return 0
+ }
+
+ // Skylake, Kabylake and all variants of those two chipsets report a
+ // crystal frequency of zero.
+ if ecx == 0 { // Crystal clock frequency is not enumerated.
+ ecx = getCrystalClockFrequency(sign)
+ }
+
+ // TSC frequency = “core crystal clock frequency” * EBX/EAX.
+ return uint64(ecx) * (uint64(ebx) / uint64(eax))
+}
+
+// Copied from: CPUID Signature values of DisplayFamily and DisplayModel,
+// in Intel® 64 and IA-32 Architectures Software Developer’s Manual
+// Volume 4: Model-Specific Registers
+// & https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/intel-family.h
+const (
+ IntelFam6SkylakeL = "06_4EH"
+ IntelFam6Skylake = "06_5EH"
+ IntelFam6SkylakeX = "06_55H"
+ IntelFam6KabylakeL = "06_8EH"
+ IntelFam6Kabylake = "06_9EH"
+)
+
+// getCrystalClockFrequency gets crystal clock frequency
+// for Intel processors in which CPUID.15H.EBX[31:0] ÷ CPUID.0x15.EAX[31:0] is enumerated
+// but CPUID.15H.ECX is not enumerated using this function to get nominal core crystal clock frequency.
+//
+// Actually these crystal clock frequencies provided by Intel hardcoded tables are not so accurate in some cases,
+// e.g. SkyLake server CPU may have issue (All SKX subject the crystal to an EMI reduction circuit that
+//reduces its actual frequency by (approximately) -0.25%):
+// see https://lore.kernel.org/lkml/ff6dcea166e8ff8f2f6a03c17beab2cb436aa779.1513920414.git.len.brown@intel.com/
+// for more details.
+// With this report, I set a coefficient (0.9975) for IntelFam6SkyLakeX.
+//
+// Unlike the kernel way (mentioned in https://github.com/torvalds/linux/commit/604dc9170f2435d27da5039a3efd757dceadc684),
+// I prefer the Intel hardcoded tables,
+// because after some testing (comparing with wall clock, see https://github.com/templexxx/tsc/tsc_test.go for more details),
+// I found hardcoded tables are more accurate.
+func getCrystalClockFrequency(sign string) uint32 {
+
+ if maxFunctionID() < 0x16 {
+ return 0
+ }
+
+ switch sign {
+ case IntelFam6SkylakeL:
+ return 24 * 1000 * 1000
+ case IntelFam6Skylake:
+ return 24 * 1000 * 1000
+ case IntelFam6SkylakeX:
+ return 25 * 1000 * 1000 * 0.9975
+ case IntelFam6KabylakeL:
+ return 24 * 1000 * 1000
+ case IntelFam6Kabylake:
+ return 24 * 1000 * 1000
+ }
+
+ return 0
+}
+
+func getFamilyModel() (uint32, uint32) {
+ if maxFunctionID() < 0x1 {
+ return 0, 0
+ }
+ eax, _, _, _ := cpuid(1, 0)
+ family := (eax >> 8) & 0xf
+ displayFamily := family
+ if family == 0xf {
+ displayFamily = ((eax >> 20) & 0xff) + family
+ }
+ model := (eax >> 4) & 0xf
+ displayModel := model
+ if family == 0x6 || family == 0xf {
+ displayModel = ((eax >> 12) & 0xf0) + model
+ }
+ return displayFamily, displayModel
+}
+
+// signature format: XX_XXH
+func makeSignature(family, model uint32) string {
+ signature := strings.ToUpper(fmt.Sprintf("0%x_0%xH", family, model))
+ ss := strings.Split(signature, "_")
+ for i, s := range ss {
+ // Maybe insert too more `0`, drop it.
+ if len(s) > 2 {
+ s = s[1:]
+ ss[i] = s
+ }
+ }
+ return strings.Join(ss, "_")
+}
+
+// getCacheSize is from
+// https://github.com/klauspost/cpuid/blob/5a626f7029c910cc8329dae5405ee4f65034bce5/cpuid.go#L723
+func getCacheSize() Cache {
+ c := Cache{
+ L1I: -1,
+ L1D: -1,
+ L2: -1,
+ L3: -1,
+ }
+
+ vendor := vendorID()
+ switch vendor {
+ case Intel:
+ if maxFunctionID() < 4 {
+ return c
+ }
+ for i := uint32(0); ; i++ {
+ eax, ebx, ecx, _ := cpuid(4, i)
+ cacheType := eax & 15
+ if cacheType == 0 {
+ break
+ }
+ cacheLevel := (eax >> 5) & 7
+ coherency := int(ebx&0xfff) + 1
+ partitions := int((ebx>>12)&0x3ff) + 1
+ associativity := int((ebx>>22)&0x3ff) + 1
+ sets := int(ecx) + 1
+ size := associativity * partitions * coherency * sets
+ switch cacheLevel {
+ case 1:
+ if cacheType == 1 {
+ // 1 = Data Cache
+ c.L1D = size
+ } else if cacheType == 2 {
+ // 2 = Instruction Cache
+ c.L1I = size
+ } else {
+ if c.L1D < 0 {
+ c.L1I = size
+ }
+ if c.L1I < 0 {
+ c.L1I = size
+ }
+ }
+ case 2:
+ c.L2 = size
+ case 3:
+ c.L3 = size
+ }
+ }
+ case AMD, Hygon:
+ // Untested.
+ if maxExtendedFunction() < 0x80000005 {
+ return c
+ }
+ _, _, ecx, edx := cpuid(0x80000005, 0)
+ c.L1D = int(((ecx >> 24) & 0xFF) * 1024)
+ c.L1I = int(((edx >> 24) & 0xFF) * 1024)
+
+ if maxExtendedFunction() < 0x80000006 {
+ return c
+ }
+ _, _, ecx, _ = cpuid(0x80000006, 0)
+ c.L2 = int(((ecx >> 16) & 0xFFFF) * 1024)
+ }
+
+ return c
+}
+
+func maxFunctionID() uint32 {
+ a, _, _, _ := cpuid(0, 0)
+ return a
+}
+
+func maxExtendedFunction() uint32 {
+ eax, _, _, _ := cpuid(0x80000000, 0)
+ return eax
+}
+
+const (
+ Other = iota
+ Intel
+ AMD
+ VIA
+ Transmeta
+ NSC
+ KVM // Kernel-based Virtual Machine
+ MSVM // Microsoft Hyper-V or Windows Virtual PC
+ VMware
+ XenHVM
+ Bhyve
+ Hygon
+)
+
+// Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID
+var vendorMapping = map[string]int{
+ "AMDisbetter!": AMD,
+ "AuthenticAMD": AMD,
+ "CentaurHauls": VIA,
+ "GenuineIntel": Intel,
+ "TransmetaCPU": Transmeta,
+ "GenuineTMx86": Transmeta,
+ "Geode by NSC": NSC,
+ "VIA VIA VIA ": VIA,
+ "KVMKVMKVMKVM": KVM,
+ "Microsoft Hv": MSVM,
+ "VMwareVMware": VMware,
+ "XenVMMXenVMM": XenHVM,
+ "bhyve bhyve ": Bhyve,
+ "HygonGenuine": Hygon,
+}
+
+func vendorID() int {
+ _, b, c, d := cpuid(0, 0)
+ v := valAsString(b, d, c)
+ vend, ok := vendorMapping[string(v)]
+ if !ok {
+ return Other
+ }
+ return vend
+}
+
+func valAsString(values ...uint32) []byte {
+ r := make([]byte, 4*len(values))
+ for i, v := range values {
+ dst := r[i*4:]
+ dst[0] = byte(v & 0xff)
+ dst[1] = byte((v >> 8) & 0xff)
+ dst[2] = byte((v >> 16) & 0xff)
+ dst[3] = byte((v >> 24) & 0xff)
+ switch {
+ case dst[0] == 0:
+ return r[:i*4]
+ case dst[1] == 0:
+ return r[:i*4+1]
+ case dst[2] == 0:
+ return r[:i*4+2]
+ case dst[3] == 0:
+ return r[:i*4+3]
+ }
+ }
+ return r
+}
diff --git a/vendor/github.com/templexxx/cpu/cpu_x86.s b/vendor/github.com/templexxx/cpu/cpu_x86.s
new file mode 100644
index 0000000..228fbcf
--- /dev/null
+++ b/vendor/github.com/templexxx/cpu/cpu_x86.s
@@ -0,0 +1,32 @@
+// Copyright 2017 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 386 amd64 amd64p32
+
+#include "textflag.h"
+
+// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
+TEXT ·cpuid(SB), NOSPLIT, $0-24
+ MOVL eaxArg+0(FP), AX
+ MOVL ecxArg+4(FP), CX
+ CPUID
+ MOVL AX, eax+8(FP)
+ MOVL BX, ebx+12(FP)
+ MOVL CX, ecx+16(FP)
+ MOVL DX, edx+20(FP)
+ RET
+
+// func xgetbv() (eax, edx uint32)
+TEXT ·xgetbv(SB),NOSPLIT,$0-8
+#ifdef GOOS_nacl
+ // nacl does not support XGETBV.
+ MOVL $0, eax+0(FP)
+ MOVL $0, edx+4(FP)
+#else
+ MOVL $0, CX
+ XGETBV
+ MOVL AX, eax+0(FP)
+ MOVL DX, edx+4(FP)
+#endif
+ RET
diff --git a/vendor/github.com/templexxx/xorsimd/.gitattributes b/vendor/github.com/templexxx/xorsimd/.gitattributes
new file mode 100644
index 0000000..68f7d04
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/.gitattributes
@@ -0,0 +1 @@
+*.s linguist-language=go:x
diff --git a/vendor/github.com/templexxx/xorsimd/.gitignore b/vendor/github.com/templexxx/xorsimd/.gitignore
new file mode 100644
index 0000000..43309f8
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/.gitignore
@@ -0,0 +1,13 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+.idea
diff --git a/vendor/github.com/templexxx/xorsimd/LICENSE b/vendor/github.com/templexxx/xorsimd/LICENSE
new file mode 100644
index 0000000..08ee714
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Temple3x (temple3x@gmail.com)
+
+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.
diff --git a/vendor/github.com/templexxx/xorsimd/README.md b/vendor/github.com/templexxx/xorsimd/README.md
new file mode 100644
index 0000000..9dce5c9
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/README.md
@@ -0,0 +1,46 @@
+# XOR SIMD
+
+[![GoDoc][1]][2] [![MIT licensed][3]][4] [![Build Status][5]][6] [![Go Report Card][7]][8] [![Sourcegraph][9]][10]
+
+[1]: https://godoc.org/github.com/templexxx/xorsimd?status.svg
+[2]: https://godoc.org/github.com/templexxx/xorsimd
+[3]: https://img.shields.io/badge/license-MIT-blue.svg
+[4]: LICENSE
+[5]: https://github.com/templexxx/xorsimd/workflows/unit-test/badge.svg
+[6]: https://github.com/templexxx/xorsimd
+[7]: https://goreportcard.com/badge/github.com/templexxx/xorsimd
+[8]: https://goreportcard.com/report/github.com/templexxx/xorsimd
+[9]: https://sourcegraph.com/github.com/templexxx/xorsimd/-/badge.svg
+[10]: https://sourcegraph.com/github.com/templexxx/xorsimd?badge
+
+## Introduction:
+
+>- XOR code engine in pure Go.
+>
+>- [High Performance](https://github.com/templexxx/xorsimd#performance):
+More than 270GB/s per physics core.
+
+## Performance
+
+Performance depends mainly on:
+
+>- CPU instruction extension.
+>
+>- Number of source row vectors.
+
+**Platform:**
+
+*AWS c5d.xlarge (Intel(R) Xeon(R) Platinum 8124M CPU @ 3.00GHz)*
+
+**All test run on a single Core.**
+
+`I/O = (src_num + 1) * vector_size / cost`
+
+| Src Num | Vector size | AVX512 I/O (MB/S) | AVX2 I/O (MB/S) |SSE2 I/O (MB/S) |
+|-------|-------------|-------------|---------------|---------------|
+|5|4KB| 270403.73 | 142825.25 | 74443.91 |
+|5|1MB| 26948.34 | 26887.37 | 26950.65 |
+|5|8MB| 17881.32 | 17212.56 | 16402.97 |
+|10|4KB| 190445.30 | 102953.59 | 53244.04 |
+|10|1MB| 26424.44 | 26618.65 | 26094.39 |
+|10|8MB| 15471.31 | 14866.72 | 13565.80 |
diff --git a/vendor/github.com/templexxx/xorsimd/go.mod b/vendor/github.com/templexxx/xorsimd/go.mod
new file mode 100644
index 0000000..ac5f57f
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/go.mod
@@ -0,0 +1,5 @@
+module github.com/templexxx/xorsimd
+
+require github.com/templexxx/cpu v0.0.1
+
+go 1.13
diff --git a/vendor/github.com/templexxx/xorsimd/go.sum b/vendor/github.com/templexxx/xorsimd/go.sum
new file mode 100644
index 0000000..04d04de
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/go.sum
@@ -0,0 +1,2 @@
+github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
+github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
diff --git a/vendor/github.com/templexxx/xorsimd/xor.go b/vendor/github.com/templexxx/xorsimd/xor.go
new file mode 100644
index 0000000..ae88911
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/xor.go
@@ -0,0 +1,89 @@
+// Copyright (c) 2019. Temple3x (temple3x@gmail.com)
+//
+// Use of this source code is governed by the MIT License
+// that can be found in the LICENSE file.
+
+package xorsimd
+
+import "github.com/templexxx/cpu"
+
+// EnableAVX512 may slow down CPU Clock (maybe not).
+// TODO need more research:
+// https://lemire.me/blog/2018/04/19/by-how-much-does-avx-512-slow-down-your-cpu-a-first-experiment/
+var EnableAVX512 = true
+
+// cpuFeature indicates which instruction set will be used.
+var cpuFeature = getCPUFeature()
+
+const (
+ avx512 = iota
+ avx2
+ sse2
+ generic
+)
+
+// TODO: Add ARM feature...
+func getCPUFeature() int {
+ if hasAVX512() && EnableAVX512 {
+ return avx512
+ } else if cpu.X86.HasAVX2 {
+ return avx2
+ } else {
+ return sse2 // amd64 must has sse2
+ }
+}
+
+func hasAVX512() (ok bool) {
+
+ return cpu.X86.HasAVX512VL &&
+ cpu.X86.HasAVX512BW &&
+ cpu.X86.HasAVX512F &&
+ cpu.X86.HasAVX512DQ
+}
+
+// Encode encodes elements from source slice into a
+// destination slice. The source and destination may overlap.
+// Encode returns the number of bytes encoded, which will be the minimum of
+// len(src[i]) and len(dst).
+func Encode(dst []byte, src [][]byte) (n int) {
+ n = checkLen(dst, src)
+ if n == 0 {
+ return
+ }
+
+ dst = dst[:n]
+ for i := range src {
+ src[i] = src[i][:n]
+ }
+
+ if len(src) == 1 {
+ copy(dst, src[0])
+ return
+ }
+
+ encode(dst, src)
+ return
+}
+
+func checkLen(dst []byte, src [][]byte) int {
+ n := len(dst)
+ for i := range src {
+ if len(src[i]) < n {
+ n = len(src[i])
+ }
+ }
+
+ if n <= 0 {
+ return 0
+ }
+ return n
+}
+
+// Bytes XORs the bytes in a and b into a
+// destination slice. The source and destination may overlap.
+//
+// Bytes returns the number of bytes encoded, which will be the minimum of
+// len(dst), len(a), len(b).
+func Bytes(dst, a, b []byte) int {
+ return Encode(dst, [][]byte{a, b})
+}
diff --git a/vendor/github.com/templexxx/xorsimd/xor_amd64.go b/vendor/github.com/templexxx/xorsimd/xor_amd64.go
new file mode 100644
index 0000000..5d46df3
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/xor_amd64.go
@@ -0,0 +1,95 @@
+// Copyright (c) 2019. Temple3x (temple3x@gmail.com)
+//
+// Use of this source code is governed by the MIT License
+// that can be found in the LICENSE file.
+
+package xorsimd
+
+func encode(dst []byte, src [][]byte) {
+
+ switch cpuFeature {
+ case avx512:
+ encodeAVX512(dst, src)
+ case avx2:
+ encodeAVX2(dst, src)
+ default:
+ encodeSSE2(dst, src)
+ }
+ return
+}
+
+// Bytes8 XORs of 8 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 8,
+// if not, Bytes8 will panic.
+func Bytes8(dst, a, b []byte) {
+
+ bytes8(&dst[0], &a[0], &b[0])
+}
+
+// Bytes16 XORs of packed 16 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 16,
+// if not, Bytes16 will panic.
+func Bytes16(dst, a, b []byte) {
+
+ bytes16(&dst[0], &a[0], &b[0])
+}
+
+// Bytes8Align XORs of 8 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 8,
+// if not, Bytes8 will panic.
+func Bytes8Align(dst, a, b []byte) {
+
+ bytes8(&dst[0], &a[0], &b[0])
+}
+
+// Bytes16Align XORs of packed 16 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 16,
+// if not, Bytes16 will panic.
+func Bytes16Align(dst, a, b []byte) {
+
+ bytes16(&dst[0], &a[0], &b[0])
+}
+
+// BytesA XORs the len(a) bytes in a and b into a
+// destination slice.
+// The destination should have enough space.
+//
+// It's used for encoding small bytes slices (< dozens bytes),
+// and the slices may not be aligned to 8 bytes or 16 bytes.
+// If the length is big, it's better to use 'func Bytes(dst, a, b []byte)' instead
+// for gain better performance.
+func BytesA(dst, a, b []byte) {
+
+ bytesN(&dst[0], &a[0], &b[0], len(a))
+}
+
+// BytesB XORs the len(b) bytes in a and b into a
+// destination slice.
+// The destination should have enough space.
+//
+// It's used for encoding small bytes slices (< dozens bytes),
+// and the slices may not be aligned to 8 bytes or 16 bytes.
+// If the length is big, it's better to use 'func Bytes(dst, a, b []byte)' instead
+// for gain better performance.
+func BytesB(dst, a, b []byte) {
+
+ bytesN(&dst[0], &a[0], &b[0], len(b))
+}
+
+//go:noescape
+func encodeAVX512(dst []byte, src [][]byte)
+
+//go:noescape
+func encodeAVX2(dst []byte, src [][]byte)
+
+//go:noescape
+func encodeSSE2(dst []byte, src [][]byte)
+
+//go:noescape
+func bytesN(dst, a, b *byte, n int)
+
+//go:noescape
+func bytes8(dst, a, b *byte)
+
+//go:noescape
+func bytes16(dst, a, b *byte)
diff --git a/vendor/github.com/templexxx/xorsimd/xor_generic.go b/vendor/github.com/templexxx/xorsimd/xor_generic.go
new file mode 100644
index 0000000..b12908f
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/xor_generic.go
@@ -0,0 +1,205 @@
+// Copyright (c) 2019. Temple3x (temple3x@gmail.com)
+//
+// Use of this source code is governed by the MIT License
+// that can be found in the LICENSE file.
+//
+// Copyright 2013 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 !amd64
+
+package xorsimd
+
+import (
+ "runtime"
+ "unsafe"
+)
+
+const wordSize = int(unsafe.Sizeof(uintptr(0)))
+const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
+
+func encode(dst []byte, src [][]byte) {
+ if supportsUnaligned {
+ fastEncode(dst, src, len(dst))
+ } else {
+ // TODO(hanwen): if (dst, a, b) have common alignment
+ // we could still try fastEncode. It is not clear
+ // how often this happens, and it's only worth it if
+ // the block encryption itself is hardware
+ // accelerated.
+ safeEncode(dst, src, len(dst))
+ }
+
+}
+
+// fastEncode xor in bulk. It only works on architectures that
+// support unaligned read/writes.
+func fastEncode(dst []byte, src [][]byte, n int) {
+ w := n / wordSize
+ if w > 0 {
+ wordBytes := w * wordSize
+
+ wordAlignSrc := make([][]byte, len(src))
+ for i := range src {
+ wordAlignSrc[i] = src[i][:wordBytes]
+ }
+ fastEnc(dst[:wordBytes], wordAlignSrc)
+ }
+
+ for i := n - n%wordSize; i < n; i++ {
+ s := src[0][i]
+ for j := 1; j < len(src); j++ {
+ s ^= src[j][i]
+ }
+ dst[i] = s
+ }
+}
+
+func fastEnc(dst []byte, src [][]byte) {
+ dw := *(*[]uintptr)(unsafe.Pointer(&dst))
+ sw := make([][]uintptr, len(src))
+ for i := range src {
+ sw[i] = *(*[]uintptr)(unsafe.Pointer(&src[i]))
+ }
+
+ n := len(dst) / wordSize
+ for i := 0; i < n; i++ {
+ s := sw[0][i]
+ for j := 1; j < len(sw); j++ {
+ s ^= sw[j][i]
+ }
+ dw[i] = s
+ }
+}
+
+func safeEncode(dst []byte, src [][]byte, n int) {
+ for i := 0; i < n; i++ {
+ s := src[0][i]
+ for j := 1; j < len(src); j++ {
+ s ^= src[j][i]
+ }
+ dst[i] = s
+ }
+}
+
+// Bytes8 XORs of word 8 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 8,
+// if not, Bytes8 will panic.
+func Bytes8(dst, a, b []byte) {
+
+ bytesWords(dst[:8], a[:8], b[:8])
+}
+
+// Bytes16 XORs of packed doubleword 16 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 16,
+// if not, Bytes16 will panic.
+func Bytes16(dst, a, b []byte) {
+
+ bytesWords(dst[:16], a[:16], b[:16])
+}
+
+// bytesWords XORs multiples of 4 or 8 bytes (depending on architecture.)
+// The slice arguments a and b are assumed to be of equal length.
+func bytesWords(dst, a, b []byte) {
+ if supportsUnaligned {
+ dw := *(*[]uintptr)(unsafe.Pointer(&dst))
+ aw := *(*[]uintptr)(unsafe.Pointer(&a))
+ bw := *(*[]uintptr)(unsafe.Pointer(&b))
+ n := len(b) / wordSize
+ for i := 0; i < n; i++ {
+ dw[i] = aw[i] ^ bw[i]
+ }
+ } else {
+ n := len(b)
+ for i := 0; i < n; i++ {
+ dst[i] = a[i] ^ b[i]
+ }
+ }
+}
+
+// Bytes8Align XORs of 8 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 8,
+// if not, Bytes8 will panic.
+//
+// All the byte slices must be aligned to wordsize.
+func Bytes8Align(dst, a, b []byte) {
+
+ bytesWordsAlign(dst[:8], a[:8], b[:8])
+}
+
+// Bytes16Align XORs of packed 16 Bytes.
+// The slice arguments a, b, dst's lengths are assumed to be at least 16,
+// if not, Bytes16 will panic.
+//
+// All the byte slices must be aligned to wordsize.
+func Bytes16Align(dst, a, b []byte) {
+
+ bytesWordsAlign(dst[:16], a[:16], b[:16])
+}
+
+// bytesWordsAlign XORs multiples of 4 or 8 bytes (depending on architecture.)
+// The slice arguments a and b are assumed to be of equal length.
+//
+// All the byte slices must be aligned to wordsize.
+func bytesWordsAlign(dst, a, b []byte) {
+ dw := *(*[]uintptr)(unsafe.Pointer(&dst))
+ aw := *(*[]uintptr)(unsafe.Pointer(&a))
+ bw := *(*[]uintptr)(unsafe.Pointer(&b))
+ n := len(b) / wordSize
+ for i := 0; i < n; i++ {
+ dw[i] = aw[i] ^ bw[i]
+ }
+}
+
+// BytesA XORs the len(a) bytes in a and b into a
+// destination slice.
+// The destination should have enough space.
+//
+// It's used for encoding small bytes slices (< dozens bytes),
+// and the slices may not be aligned to 8 bytes or 16 bytes.
+// If the length is big, it's better to use 'func Bytes(dst, a, b []byte)' instead
+// for gain better performance.
+func BytesA(dst, a, b []byte) {
+
+ n := len(a)
+ bytesN(dst[:n], a[:n], b[:n], n)
+}
+
+// BytesB XORs the len(b) bytes in a and b into a
+// destination slice.
+// The destination should have enough space.
+//
+// It's used for encoding small bytes slices (< dozens bytes),
+// and the slices may not be aligned to 8 bytes or 16 bytes.
+// If the length is big, it's better to use 'func Bytes(dst, a, b []byte)' instead
+// for gain better performance.
+func BytesB(dst, a, b []byte) {
+
+ n := len(b)
+ bytesN(dst[:n], a[:n], b[:n], n)
+}
+
+func bytesN(dst, a, b []byte, n int) {
+
+ switch {
+ case supportsUnaligned:
+ w := n / wordSize
+ if w > 0 {
+ dw := *(*[]uintptr)(unsafe.Pointer(&dst))
+ aw := *(*[]uintptr)(unsafe.Pointer(&a))
+ bw := *(*[]uintptr)(unsafe.Pointer(&b))
+ for i := 0; i < w; i++ {
+ dw[i] = aw[i] ^ bw[i]
+ }
+ }
+
+ for i := (n - n%wordSize); i < n; i++ {
+ dst[i] = a[i] ^ b[i]
+ }
+ default:
+ for i := 0; i < n; i++ {
+ dst[i] = a[i] ^ b[i]
+ }
+ }
+}
diff --git a/vendor/github.com/templexxx/xorsimd/xoravx2_amd64.s b/vendor/github.com/templexxx/xorsimd/xoravx2_amd64.s
new file mode 100644
index 0000000..23cf924
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/xoravx2_amd64.s
@@ -0,0 +1,124 @@
+// Copyright (c) 2019. Temple3x (temple3x@gmail.com)
+//
+// Use of this source code is governed by the MIT License
+// that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+#define dst BX // parity's address
+#define d2src SI // two-dimension src_slice's address
+#define csrc CX // cnt of src
+#define len DX // len of vect
+#define pos R8 // job position in vect
+
+#define csrc_tmp R9
+#define d2src_off R10
+#define src_tmp R11
+#define not_aligned_len R12
+#define src_val0 R13
+#define src_val1 R14
+
+// func encodeAVX2(dst []byte, src [][]byte)
+TEXT ·encodeAVX2(SB), NOSPLIT, $0
+ MOVQ d+0(FP), dst
+ MOVQ s+24(FP), d2src
+ MOVQ c+32(FP), csrc
+ MOVQ l+8(FP), len
+ TESTQ $127, len
+ JNZ not_aligned
+
+aligned:
+ MOVQ $0, pos
+
+loop128b:
+ MOVQ csrc, csrc_tmp // store src_cnt -> csrc_tmp
+ SUBQ $2, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp // get first src_vect's addr -> src_tmp
+ VMOVDQU (src_tmp)(pos*1), Y0
+ VMOVDQU 32(src_tmp)(pos*1), Y1
+ VMOVDQU 64(src_tmp)(pos*1), Y2
+ VMOVDQU 96(src_tmp)(pos*1), Y3
+
+next_vect:
+ ADDQ $24, d2src_off // len(slice) = 24
+ MOVQ (d2src)(d2src_off*1), src_tmp // next data_vect
+ VMOVDQU (src_tmp)(pos*1), Y4
+ VMOVDQU 32(src_tmp)(pos*1), Y5
+ VMOVDQU 64(src_tmp)(pos*1), Y6
+ VMOVDQU 96(src_tmp)(pos*1), Y7
+ VPXOR Y4, Y0, Y0
+ VPXOR Y5, Y1, Y1
+ VPXOR Y6, Y2, Y2
+ VPXOR Y7, Y3, Y3
+ SUBQ $1, csrc_tmp
+ JGE next_vect
+
+ VMOVDQU Y0, (dst)(pos*1)
+ VMOVDQU Y1, 32(dst)(pos*1)
+ VMOVDQU Y2, 64(dst)(pos*1)
+ VMOVDQU Y3, 96(dst)(pos*1)
+
+ ADDQ $128, pos
+ CMPQ len, pos
+ JNE loop128b
+ VZEROUPPER
+ RET
+
+loop_1b:
+ MOVQ csrc, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ SUBQ $2, csrc_tmp
+ MOVB -1(src_tmp)(len*1), src_val0 // encode from the end of src
+
+next_vect_1b:
+ ADDQ $24, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVB -1(src_tmp)(len*1), src_val1
+ XORB src_val1, src_val0
+ SUBQ $1, csrc_tmp
+ JGE next_vect_1b
+
+ MOVB src_val0, -1(dst)(len*1)
+ SUBQ $1, len
+ TESTQ $7, len
+ JNZ loop_1b
+
+ CMPQ len, $0
+ JE ret
+ TESTQ $127, len
+ JZ aligned
+
+not_aligned:
+ TESTQ $7, len
+ JNE loop_1b
+ MOVQ len, not_aligned_len
+ ANDQ $127, not_aligned_len
+
+loop_8b:
+ MOVQ csrc, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ SUBQ $2, csrc_tmp
+ MOVQ -8(src_tmp)(len*1), src_val0
+
+next_vect_8b:
+ ADDQ $24, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVQ -8(src_tmp)(len*1), src_val1
+ XORQ src_val1, src_val0
+ SUBQ $1, csrc_tmp
+ JGE next_vect_8b
+
+ MOVQ src_val0, -8(dst)(len*1)
+ SUBQ $8, len
+ SUBQ $8, not_aligned_len
+ JG loop_8b
+
+ CMPQ len, $128
+ JGE aligned
+ RET
+
+ret:
+ RET
diff --git a/vendor/github.com/templexxx/xorsimd/xoravx512_amd64.s b/vendor/github.com/templexxx/xorsimd/xoravx512_amd64.s
new file mode 100644
index 0000000..2ba6b75
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/xoravx512_amd64.s
@@ -0,0 +1,124 @@
+// Copyright (c) 2019. Temple3x (temple3x@gmail.com)
+//
+// Use of this source code is governed by the MIT License
+// that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+#define dst BX // parity's address
+#define d2src SI // two-dimension src_slice's address
+#define csrc CX // cnt of src
+#define len DX // len of vect
+#define pos R8 // job position in vect
+
+#define csrc_tmp R9
+#define d2src_off R10
+#define src_tmp R11
+#define not_aligned_len R12
+#define src_val0 R13
+#define src_val1 R14
+
+// func encodeAVX512(dst []byte, src [][]byte)
+TEXT ·encodeAVX512(SB), NOSPLIT, $0
+ MOVQ d+0(FP), dst
+ MOVQ src+24(FP), d2src
+ MOVQ c+32(FP), csrc
+ MOVQ l+8(FP), len
+ TESTQ $255, len
+ JNZ not_aligned
+
+aligned:
+ MOVQ $0, pos
+
+loop256b:
+ MOVQ csrc, csrc_tmp // store src_cnt -> csrc_tmp
+ SUBQ $2, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp // get first src_vect's addr -> src_tmp
+ VMOVDQU8 (src_tmp)(pos*1), Z0
+ VMOVDQU8 64(src_tmp)(pos*1), Z1
+ VMOVDQU8 128(src_tmp)(pos*1), Z2
+ VMOVDQU8 192(src_tmp)(pos*1), Z3
+
+next_vect:
+ ADDQ $24, d2src_off // len(slice) = 24
+ MOVQ (d2src)(d2src_off*1), src_tmp // next data_vect
+ VMOVDQU8 (src_tmp)(pos*1), Z4
+ VMOVDQU8 64(src_tmp)(pos*1), Z5
+ VMOVDQU8 128(src_tmp)(pos*1), Z6
+ VMOVDQU8 192(src_tmp)(pos*1), Z7
+ VPXORQ Z4, Z0, Z0
+ VPXORQ Z5, Z1, Z1
+ VPXORQ Z6, Z2, Z2
+ VPXORQ Z7, Z3, Z3
+ SUBQ $1, csrc_tmp
+ JGE next_vect
+
+ VMOVDQU8 Z0, (dst)(pos*1)
+ VMOVDQU8 Z1, 64(dst)(pos*1)
+ VMOVDQU8 Z2, 128(dst)(pos*1)
+ VMOVDQU8 Z3, 192(dst)(pos*1)
+
+ ADDQ $256, pos
+ CMPQ len, pos
+ JNE loop256b
+ VZEROUPPER
+ RET
+
+loop_1b:
+ MOVQ csrc, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ SUBQ $2, csrc_tmp
+ MOVB -1(src_tmp)(len*1), src_val0 // encode from the end of src
+
+next_vect_1b:
+ ADDQ $24, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVB -1(src_tmp)(len*1), src_val1
+ XORB src_val1, src_val0
+ SUBQ $1, csrc_tmp
+ JGE next_vect_1b
+
+ MOVB src_val0, -1(dst)(len*1)
+ SUBQ $1, len
+ TESTQ $7, len
+ JNZ loop_1b
+
+ CMPQ len, $0
+ JE ret
+ TESTQ $255, len
+ JZ aligned
+
+not_aligned:
+ TESTQ $7, len
+ JNE loop_1b
+ MOVQ len, not_aligned_len
+ ANDQ $255, not_aligned_len
+
+loop_8b:
+ MOVQ csrc, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ SUBQ $2, csrc_tmp
+ MOVQ -8(src_tmp)(len*1), src_val0
+
+next_vect_8b:
+ ADDQ $24, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVQ -8(src_tmp)(len*1), src_val1
+ XORQ src_val1, src_val0
+ SUBQ $1, csrc_tmp
+ JGE next_vect_8b
+
+ MOVQ src_val0, -8(dst)(len*1)
+ SUBQ $8, len
+ SUBQ $8, not_aligned_len
+ JG loop_8b
+
+ CMPQ len, $256
+ JGE aligned
+ RET
+
+ret:
+ RET
diff --git a/vendor/github.com/templexxx/xorsimd/xorbytes_amd64.s b/vendor/github.com/templexxx/xorsimd/xorbytes_amd64.s
new file mode 100644
index 0000000..8f67edd
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/xorbytes_amd64.s
@@ -0,0 +1,72 @@
+#include "textflag.h"
+
+// func bytesN(dst, a, b *byte, n int)
+TEXT ·bytesN(SB), NOSPLIT, $0
+ MOVQ d+0(FP), BX
+ MOVQ a+8(FP), SI
+ MOVQ b+16(FP), CX
+ MOVQ n+24(FP), DX
+ TESTQ $15, DX // AND 15 & len, if not zero jump to not_aligned.
+ JNZ not_aligned
+
+aligned:
+ MOVQ $0, AX // position in slices
+
+loop16b:
+ MOVOU (SI)(AX*1), X0 // XOR 16byte forwards.
+ MOVOU (CX)(AX*1), X1
+ PXOR X1, X0
+ MOVOU X0, (BX)(AX*1)
+ ADDQ $16, AX
+ CMPQ DX, AX
+ JNE loop16b
+ RET
+
+loop_1b:
+ SUBQ $1, DX // XOR 1byte backwards.
+ MOVB (SI)(DX*1), DI
+ MOVB (CX)(DX*1), AX
+ XORB AX, DI
+ MOVB DI, (BX)(DX*1)
+ TESTQ $7, DX // AND 7 & len, if not zero jump to loop_1b.
+ JNZ loop_1b
+ CMPQ DX, $0 // if len is 0, ret.
+ JE ret
+ TESTQ $15, DX // AND 15 & len, if zero jump to aligned.
+ JZ aligned
+
+not_aligned:
+ TESTQ $7, DX // AND $7 & len, if not zero jump to loop_1b.
+ JNE loop_1b
+ SUBQ $8, DX // XOR 8bytes backwards.
+ MOVQ (SI)(DX*1), DI
+ MOVQ (CX)(DX*1), AX
+ XORQ AX, DI
+ MOVQ DI, (BX)(DX*1)
+ CMPQ DX, $16 // if len is greater or equal 16 here, it must be aligned.
+ JGE aligned
+
+ret:
+ RET
+
+// func bytes8(dst, a, b *byte)
+TEXT ·bytes8(SB), NOSPLIT, $0
+ MOVQ d+0(FP), BX
+ MOVQ a+8(FP), SI
+ MOVQ b+16(FP), CX
+ MOVQ (SI), DI
+ MOVQ (CX), AX
+ XORQ AX, DI
+ MOVQ DI, (BX)
+ RET
+
+// func bytes16(dst, a, b *byte)
+TEXT ·bytes16(SB), NOSPLIT, $0
+ MOVQ d+0(FP), BX
+ MOVQ a+8(FP), SI
+ MOVQ b+16(FP), CX
+ MOVOU (SI), X0
+ MOVOU (CX), X1
+ PXOR X1, X0
+ MOVOU X0, (BX)
+ RET
diff --git a/vendor/github.com/templexxx/xorsimd/xorsse2_amd64.s b/vendor/github.com/templexxx/xorsimd/xorsse2_amd64.s
new file mode 100644
index 0000000..38df948
--- /dev/null
+++ b/vendor/github.com/templexxx/xorsimd/xorsse2_amd64.s
@@ -0,0 +1,123 @@
+// Copyright (c) 2019. Temple3x (temple3x@gmail.com)
+//
+// Use of this source code is governed by the MIT License
+// that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+#define dst BX // parity's address
+#define d2src SI // two-dimension src_slice's address
+#define csrc CX // cnt of src
+#define len DX // len of vect
+#define pos R8 // job position in vect
+
+#define csrc_tmp R9
+#define d2src_off R10
+#define src_tmp R11
+#define not_aligned_len R12
+#define src_val0 R13
+#define src_val1 R14
+
+// func encodeSSE2(dst []byte, src [][]byte)
+TEXT ·encodeSSE2(SB), NOSPLIT, $0
+ MOVQ d+0(FP), dst
+ MOVQ src+24(FP), d2src
+ MOVQ c+32(FP), csrc
+ MOVQ l+8(FP), len
+ TESTQ $63, len
+ JNZ not_aligned
+
+aligned:
+ MOVQ $0, pos
+
+loop64b:
+ MOVQ csrc, csrc_tmp
+ SUBQ $2, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVOU (src_tmp)(pos*1), X0
+ MOVOU 16(src_tmp)(pos*1), X1
+ MOVOU 32(src_tmp)(pos*1), X2
+ MOVOU 48(src_tmp)(pos*1), X3
+
+next_vect:
+ ADDQ $24, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVOU (src_tmp)(pos*1), X4
+ MOVOU 16(src_tmp)(pos*1), X5
+ MOVOU 32(src_tmp)(pos*1), X6
+ MOVOU 48(src_tmp)(pos*1), X7
+ PXOR X4, X0
+ PXOR X5, X1
+ PXOR X6, X2
+ PXOR X7, X3
+ SUBQ $1, csrc_tmp
+ JGE next_vect
+
+ MOVOU X0, (dst)(pos*1)
+ MOVOU X1, 16(dst)(pos*1)
+ MOVOU X2, 32(dst)(pos*1)
+ MOVOU X3, 48(dst)(pos*1)
+
+ ADDQ $64, pos
+ CMPQ len, pos
+ JNE loop64b
+ RET
+
+loop_1b:
+ MOVQ csrc, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ SUBQ $2, csrc_tmp
+ MOVB -1(src_tmp)(len*1), src_val0
+
+next_vect_1b:
+ ADDQ $24, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVB -1(src_tmp)(len*1), src_val1
+ XORB src_val1, src_val0
+ SUBQ $1, csrc_tmp
+ JGE next_vect_1b
+
+ MOVB src_val0, -1(dst)(len*1)
+ SUBQ $1, len
+ TESTQ $7, len
+ JNZ loop_1b
+
+ CMPQ len, $0
+ JE ret
+ TESTQ $63, len
+ JZ aligned
+
+not_aligned:
+ TESTQ $7, len
+ JNE loop_1b
+ MOVQ len, not_aligned_len
+ ANDQ $63, not_aligned_len
+
+loop_8b:
+ MOVQ csrc, csrc_tmp
+ MOVQ $0, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ SUBQ $2, csrc_tmp
+ MOVQ -8(src_tmp)(len*1), src_val0
+
+next_vect_8b:
+ ADDQ $24, d2src_off
+ MOVQ (d2src)(d2src_off*1), src_tmp
+ MOVQ -8(src_tmp)(len*1), src_val1
+ XORQ src_val1, src_val0
+ SUBQ $1, csrc_tmp
+ JGE next_vect_8b
+
+ MOVQ src_val0, -8(dst)(len*1)
+ SUBQ $8, len
+ SUBQ $8, not_aligned_len
+ JG loop_8b
+
+ CMPQ len, $64
+ JGE aligned
+ RET
+
+ret:
+ RET
diff --git a/vendor/github.com/tjfoc/gmsm/LICENSE b/vendor/github.com/tjfoc/gmsm/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/vendor/github.com/tjfoc/gmsm/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vendor/github.com/tjfoc/gmsm/sm4/sm4.go b/vendor/github.com/tjfoc/gmsm/sm4/sm4.go
new file mode 100644
index 0000000..d97f35c
--- /dev/null
+++ b/vendor/github.com/tjfoc/gmsm/sm4/sm4.go
@@ -0,0 +1,350 @@
+/*
+Copyright Suzhou Tongji Fintech Research Institute 2017 All Rights Reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+sm4 acceration
+modified by Jack, 2017 Oct
+*/
+
+package sm4
+
+import (
+ "crypto/cipher"
+ "crypto/rand"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "io/ioutil"
+ "os"
+ "strconv"
+)
+
+const BlockSize = 16
+
+type SM4Key []byte
+
+type KeySizeError int
+
+// Cipher is an instance of SM4 encryption.
+type Sm4Cipher struct {
+ subkeys []uint32
+ block1 []uint32
+ block2 []byte
+}
+
+// sm4密钥参量
+var fk = [4]uint32{
+ 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc,
+}
+
+// sm4密钥参量
+var ck = [32]uint32{
+ 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
+ 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
+ 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
+ 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
+ 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
+ 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
+ 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
+ 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279,
+}
+
+// sm4密钥参量
+var sbox = [256]uint8{
+ 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
+ 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
+ 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
+ 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
+ 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
+ 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
+ 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
+ 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
+ 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
+ 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
+ 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
+ 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
+ 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
+ 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
+ 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
+ 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
+}
+
+var sbox0 = [256]uint32{
+ 0xd55b5b8e, 0x924242d0, 0xeaa7a74d, 0xfdfbfb06, 0xcf3333fc, 0xe2878765, 0x3df4f4c9, 0xb5dede6b, 0x1658584e, 0xb4dada6e, 0x14505044, 0xc10b0bca, 0x28a0a088, 0xf8efef17, 0x2cb0b09c, 0x05141411,
+ 0x2bacac87, 0x669d9dfb, 0x986a6af2, 0x77d9d9ae, 0x2aa8a882, 0xbcfafa46, 0x04101014, 0xc00f0fcf, 0xa8aaaa02, 0x45111154, 0x134c4c5f, 0x269898be, 0x4825256d, 0x841a1a9e, 0x0618181e, 0x9b6666fd,
+ 0x9e7272ec, 0x4309094a, 0x51414110, 0xf7d3d324, 0x934646d5, 0xecbfbf53, 0x9a6262f8, 0x7be9e992, 0x33ccccff, 0x55515104, 0x0b2c2c27, 0x420d0d4f, 0xeeb7b759, 0xcc3f3ff3, 0xaeb2b21c, 0x638989ea,
+ 0xe7939374, 0xb1cece7f, 0x1c70706c, 0xaba6a60d, 0xca2727ed, 0x08202028, 0xeba3a348, 0x975656c1, 0x82020280, 0xdc7f7fa3, 0x965252c4, 0xf9ebeb12, 0x74d5d5a1, 0x8d3e3eb3, 0x3ffcfcc3, 0xa49a9a3e,
+ 0x461d1d5b, 0x071c1c1b, 0xa59e9e3b, 0xfff3f30c, 0xf0cfcf3f, 0x72cdcdbf, 0x175c5c4b, 0xb8eaea52, 0x810e0e8f, 0x5865653d, 0x3cf0f0cc, 0x1964647d, 0xe59b9b7e, 0x87161691, 0x4e3d3d73, 0xaaa2a208,
+ 0x69a1a1c8, 0x6aadadc7, 0x83060685, 0xb0caca7a, 0x70c5c5b5, 0x659191f4, 0xd96b6bb2, 0x892e2ea7, 0xfbe3e318, 0xe8afaf47, 0x0f3c3c33, 0x4a2d2d67, 0x71c1c1b0, 0x5759590e, 0x9f7676e9, 0x35d4d4e1,
+ 0x1e787866, 0x249090b4, 0x0e383836, 0x5f797926, 0x628d8def, 0x59616138, 0xd2474795, 0xa08a8a2a, 0x259494b1, 0x228888aa, 0x7df1f18c, 0x3bececd7, 0x01040405, 0x218484a5, 0x79e1e198, 0x851e1e9b,
+ 0xd7535384, 0x00000000, 0x4719195e, 0x565d5d0b, 0x9d7e7ee3, 0xd04f4f9f, 0x279c9cbb, 0x5349491a, 0x4d31317c, 0x36d8d8ee, 0x0208080a, 0xe49f9f7b, 0xa2828220, 0xc71313d4, 0xcb2323e8, 0x9c7a7ae6,
+ 0xe9abab42, 0xbdfefe43, 0x882a2aa2, 0xd14b4b9a, 0x41010140, 0xc41f1fdb, 0x38e0e0d8, 0xb7d6d661, 0xa18e8e2f, 0xf4dfdf2b, 0xf1cbcb3a, 0xcd3b3bf6, 0xfae7e71d, 0x608585e5, 0x15545441, 0xa3868625,
+ 0xe3838360, 0xacbaba16, 0x5c757529, 0xa6929234, 0x996e6ef7, 0x34d0d0e4, 0x1a686872, 0x54555501, 0xafb6b619, 0x914e4edf, 0x32c8c8fa, 0x30c0c0f0, 0xf6d7d721, 0x8e3232bc, 0xb3c6c675, 0xe08f8f6f,
+ 0x1d747469, 0xf5dbdb2e, 0xe18b8b6a, 0x2eb8b896, 0x800a0a8a, 0x679999fe, 0xc92b2be2, 0x618181e0, 0xc30303c0, 0x29a4a48d, 0x238c8caf, 0xa9aeae07, 0x0d343439, 0x524d4d1f, 0x4f393976, 0x6ebdbdd3,
+ 0xd6575781, 0xd86f6fb7, 0x37dcdceb, 0x44151551, 0xdd7b7ba6, 0xfef7f709, 0x8c3a3ab6, 0x2fbcbc93, 0x030c0c0f, 0xfcffff03, 0x6ba9a9c2, 0x73c9c9ba, 0x6cb5b5d9, 0x6db1b1dc, 0x5a6d6d37, 0x50454515,
+ 0x8f3636b9, 0x1b6c6c77, 0xadbebe13, 0x904a4ada, 0xb9eeee57, 0xde7777a9, 0xbef2f24c, 0x7efdfd83, 0x11444455, 0xda6767bd, 0x5d71712c, 0x40050545, 0x1f7c7c63, 0x10404050, 0x5b696932, 0xdb6363b8,
+ 0x0a282822, 0xc20707c5, 0x31c4c4f5, 0x8a2222a8, 0xa7969631, 0xce3737f9, 0x7aeded97, 0xbff6f649, 0x2db4b499, 0x75d1d1a4, 0xd3434390, 0x1248485a, 0xbae2e258, 0xe6979771, 0xb6d2d264, 0xb2c2c270,
+ 0x8b2626ad, 0x68a5a5cd, 0x955e5ecb, 0x4b292962, 0x0c30303c, 0x945a5ace, 0x76ddddab, 0x7ff9f986, 0x649595f1, 0xbbe6e65d, 0xf2c7c735, 0x0924242d, 0xc61717d1, 0x6fb9b9d6, 0xc51b1bde, 0x86121294,
+ 0x18606078, 0xf3c3c330, 0x7cf5f589, 0xefb3b35c, 0x3ae8e8d2, 0xdf7373ac, 0x4c353579, 0x208080a0, 0x78e5e59d, 0xedbbbb56, 0x5e7d7d23, 0x3ef8f8c6, 0xd45f5f8b, 0xc82f2fe7, 0x39e4e4dd, 0x49212168,
+}
+
+var sbox1 = [256]uint32{
+ 0x5b5b8ed5, 0x4242d092, 0xa7a74dea, 0xfbfb06fd, 0x3333fccf, 0x878765e2, 0xf4f4c93d, 0xdede6bb5, 0x58584e16, 0xdada6eb4, 0x50504414, 0x0b0bcac1, 0xa0a08828, 0xefef17f8, 0xb0b09c2c, 0x14141105,
+ 0xacac872b, 0x9d9dfb66, 0x6a6af298, 0xd9d9ae77, 0xa8a8822a, 0xfafa46bc, 0x10101404, 0x0f0fcfc0, 0xaaaa02a8, 0x11115445, 0x4c4c5f13, 0x9898be26, 0x25256d48, 0x1a1a9e84, 0x18181e06, 0x6666fd9b,
+ 0x7272ec9e, 0x09094a43, 0x41411051, 0xd3d324f7, 0x4646d593, 0xbfbf53ec, 0x6262f89a, 0xe9e9927b, 0xccccff33, 0x51510455, 0x2c2c270b, 0x0d0d4f42, 0xb7b759ee, 0x3f3ff3cc, 0xb2b21cae, 0x8989ea63,
+ 0x939374e7, 0xcece7fb1, 0x70706c1c, 0xa6a60dab, 0x2727edca, 0x20202808, 0xa3a348eb, 0x5656c197, 0x02028082, 0x7f7fa3dc, 0x5252c496, 0xebeb12f9, 0xd5d5a174, 0x3e3eb38d, 0xfcfcc33f, 0x9a9a3ea4,
+ 0x1d1d5b46, 0x1c1c1b07, 0x9e9e3ba5, 0xf3f30cff, 0xcfcf3ff0, 0xcdcdbf72, 0x5c5c4b17, 0xeaea52b8, 0x0e0e8f81, 0x65653d58, 0xf0f0cc3c, 0x64647d19, 0x9b9b7ee5, 0x16169187, 0x3d3d734e, 0xa2a208aa,
+ 0xa1a1c869, 0xadadc76a, 0x06068583, 0xcaca7ab0, 0xc5c5b570, 0x9191f465, 0x6b6bb2d9, 0x2e2ea789, 0xe3e318fb, 0xafaf47e8, 0x3c3c330f, 0x2d2d674a, 0xc1c1b071, 0x59590e57, 0x7676e99f, 0xd4d4e135,
+ 0x7878661e, 0x9090b424, 0x3838360e, 0x7979265f, 0x8d8def62, 0x61613859, 0x474795d2, 0x8a8a2aa0, 0x9494b125, 0x8888aa22, 0xf1f18c7d, 0xececd73b, 0x04040501, 0x8484a521, 0xe1e19879, 0x1e1e9b85,
+ 0x535384d7, 0x00000000, 0x19195e47, 0x5d5d0b56, 0x7e7ee39d, 0x4f4f9fd0, 0x9c9cbb27, 0x49491a53, 0x31317c4d, 0xd8d8ee36, 0x08080a02, 0x9f9f7be4, 0x828220a2, 0x1313d4c7, 0x2323e8cb, 0x7a7ae69c,
+ 0xabab42e9, 0xfefe43bd, 0x2a2aa288, 0x4b4b9ad1, 0x01014041, 0x1f1fdbc4, 0xe0e0d838, 0xd6d661b7, 0x8e8e2fa1, 0xdfdf2bf4, 0xcbcb3af1, 0x3b3bf6cd, 0xe7e71dfa, 0x8585e560, 0x54544115, 0x868625a3,
+ 0x838360e3, 0xbaba16ac, 0x7575295c, 0x929234a6, 0x6e6ef799, 0xd0d0e434, 0x6868721a, 0x55550154, 0xb6b619af, 0x4e4edf91, 0xc8c8fa32, 0xc0c0f030, 0xd7d721f6, 0x3232bc8e, 0xc6c675b3, 0x8f8f6fe0,
+ 0x7474691d, 0xdbdb2ef5, 0x8b8b6ae1, 0xb8b8962e, 0x0a0a8a80, 0x9999fe67, 0x2b2be2c9, 0x8181e061, 0x0303c0c3, 0xa4a48d29, 0x8c8caf23, 0xaeae07a9, 0x3434390d, 0x4d4d1f52, 0x3939764f, 0xbdbdd36e,
+ 0x575781d6, 0x6f6fb7d8, 0xdcdceb37, 0x15155144, 0x7b7ba6dd, 0xf7f709fe, 0x3a3ab68c, 0xbcbc932f, 0x0c0c0f03, 0xffff03fc, 0xa9a9c26b, 0xc9c9ba73, 0xb5b5d96c, 0xb1b1dc6d, 0x6d6d375a, 0x45451550,
+ 0x3636b98f, 0x6c6c771b, 0xbebe13ad, 0x4a4ada90, 0xeeee57b9, 0x7777a9de, 0xf2f24cbe, 0xfdfd837e, 0x44445511, 0x6767bdda, 0x71712c5d, 0x05054540, 0x7c7c631f, 0x40405010, 0x6969325b, 0x6363b8db,
+ 0x2828220a, 0x0707c5c2, 0xc4c4f531, 0x2222a88a, 0x969631a7, 0x3737f9ce, 0xeded977a, 0xf6f649bf, 0xb4b4992d, 0xd1d1a475, 0x434390d3, 0x48485a12, 0xe2e258ba, 0x979771e6, 0xd2d264b6, 0xc2c270b2,
+ 0x2626ad8b, 0xa5a5cd68, 0x5e5ecb95, 0x2929624b, 0x30303c0c, 0x5a5ace94, 0xddddab76, 0xf9f9867f, 0x9595f164, 0xe6e65dbb, 0xc7c735f2, 0x24242d09, 0x1717d1c6, 0xb9b9d66f, 0x1b1bdec5, 0x12129486,
+ 0x60607818, 0xc3c330f3, 0xf5f5897c, 0xb3b35cef, 0xe8e8d23a, 0x7373acdf, 0x3535794c, 0x8080a020, 0xe5e59d78, 0xbbbb56ed, 0x7d7d235e, 0xf8f8c63e, 0x5f5f8bd4, 0x2f2fe7c8, 0xe4e4dd39, 0x21216849,
+}
+
+var sbox2 = [256]uint32{
+ 0x5b8ed55b, 0x42d09242, 0xa74deaa7, 0xfb06fdfb, 0x33fccf33, 0x8765e287, 0xf4c93df4, 0xde6bb5de, 0x584e1658, 0xda6eb4da, 0x50441450, 0x0bcac10b, 0xa08828a0, 0xef17f8ef, 0xb09c2cb0, 0x14110514,
+ 0xac872bac, 0x9dfb669d, 0x6af2986a, 0xd9ae77d9, 0xa8822aa8, 0xfa46bcfa, 0x10140410, 0x0fcfc00f, 0xaa02a8aa, 0x11544511, 0x4c5f134c, 0x98be2698, 0x256d4825, 0x1a9e841a, 0x181e0618, 0x66fd9b66,
+ 0x72ec9e72, 0x094a4309, 0x41105141, 0xd324f7d3, 0x46d59346, 0xbf53ecbf, 0x62f89a62, 0xe9927be9, 0xccff33cc, 0x51045551, 0x2c270b2c, 0x0d4f420d, 0xb759eeb7, 0x3ff3cc3f, 0xb21caeb2, 0x89ea6389,
+ 0x9374e793, 0xce7fb1ce, 0x706c1c70, 0xa60daba6, 0x27edca27, 0x20280820, 0xa348eba3, 0x56c19756, 0x02808202, 0x7fa3dc7f, 0x52c49652, 0xeb12f9eb, 0xd5a174d5, 0x3eb38d3e, 0xfcc33ffc, 0x9a3ea49a,
+ 0x1d5b461d, 0x1c1b071c, 0x9e3ba59e, 0xf30cfff3, 0xcf3ff0cf, 0xcdbf72cd, 0x5c4b175c, 0xea52b8ea, 0x0e8f810e, 0x653d5865, 0xf0cc3cf0, 0x647d1964, 0x9b7ee59b, 0x16918716, 0x3d734e3d, 0xa208aaa2,
+ 0xa1c869a1, 0xadc76aad, 0x06858306, 0xca7ab0ca, 0xc5b570c5, 0x91f46591, 0x6bb2d96b, 0x2ea7892e, 0xe318fbe3, 0xaf47e8af, 0x3c330f3c, 0x2d674a2d, 0xc1b071c1, 0x590e5759, 0x76e99f76, 0xd4e135d4,
+ 0x78661e78, 0x90b42490, 0x38360e38, 0x79265f79, 0x8def628d, 0x61385961, 0x4795d247, 0x8a2aa08a, 0x94b12594, 0x88aa2288, 0xf18c7df1, 0xecd73bec, 0x04050104, 0x84a52184, 0xe19879e1, 0x1e9b851e,
+ 0x5384d753, 0x00000000, 0x195e4719, 0x5d0b565d, 0x7ee39d7e, 0x4f9fd04f, 0x9cbb279c, 0x491a5349, 0x317c4d31, 0xd8ee36d8, 0x080a0208, 0x9f7be49f, 0x8220a282, 0x13d4c713, 0x23e8cb23, 0x7ae69c7a,
+ 0xab42e9ab, 0xfe43bdfe, 0x2aa2882a, 0x4b9ad14b, 0x01404101, 0x1fdbc41f, 0xe0d838e0, 0xd661b7d6, 0x8e2fa18e, 0xdf2bf4df, 0xcb3af1cb, 0x3bf6cd3b, 0xe71dfae7, 0x85e56085, 0x54411554, 0x8625a386,
+ 0x8360e383, 0xba16acba, 0x75295c75, 0x9234a692, 0x6ef7996e, 0xd0e434d0, 0x68721a68, 0x55015455, 0xb619afb6, 0x4edf914e, 0xc8fa32c8, 0xc0f030c0, 0xd721f6d7, 0x32bc8e32, 0xc675b3c6, 0x8f6fe08f,
+ 0x74691d74, 0xdb2ef5db, 0x8b6ae18b, 0xb8962eb8, 0x0a8a800a, 0x99fe6799, 0x2be2c92b, 0x81e06181, 0x03c0c303, 0xa48d29a4, 0x8caf238c, 0xae07a9ae, 0x34390d34, 0x4d1f524d, 0x39764f39, 0xbdd36ebd,
+ 0x5781d657, 0x6fb7d86f, 0xdceb37dc, 0x15514415, 0x7ba6dd7b, 0xf709fef7, 0x3ab68c3a, 0xbc932fbc, 0x0c0f030c, 0xff03fcff, 0xa9c26ba9, 0xc9ba73c9, 0xb5d96cb5, 0xb1dc6db1, 0x6d375a6d, 0x45155045,
+ 0x36b98f36, 0x6c771b6c, 0xbe13adbe, 0x4ada904a, 0xee57b9ee, 0x77a9de77, 0xf24cbef2, 0xfd837efd, 0x44551144, 0x67bdda67, 0x712c5d71, 0x05454005, 0x7c631f7c, 0x40501040, 0x69325b69, 0x63b8db63,
+ 0x28220a28, 0x07c5c207, 0xc4f531c4, 0x22a88a22, 0x9631a796, 0x37f9ce37, 0xed977aed, 0xf649bff6, 0xb4992db4, 0xd1a475d1, 0x4390d343, 0x485a1248, 0xe258bae2, 0x9771e697, 0xd264b6d2, 0xc270b2c2,
+ 0x26ad8b26, 0xa5cd68a5, 0x5ecb955e, 0x29624b29, 0x303c0c30, 0x5ace945a, 0xddab76dd, 0xf9867ff9, 0x95f16495, 0xe65dbbe6, 0xc735f2c7, 0x242d0924, 0x17d1c617, 0xb9d66fb9, 0x1bdec51b, 0x12948612,
+ 0x60781860, 0xc330f3c3, 0xf5897cf5, 0xb35cefb3, 0xe8d23ae8, 0x73acdf73, 0x35794c35, 0x80a02080, 0xe59d78e5, 0xbb56edbb, 0x7d235e7d, 0xf8c63ef8, 0x5f8bd45f, 0x2fe7c82f, 0xe4dd39e4, 0x21684921,
+}
+
+var sbox3 = [256]uint32{
+ 0x8ed55b5b, 0xd0924242, 0x4deaa7a7, 0x06fdfbfb, 0xfccf3333, 0x65e28787, 0xc93df4f4, 0x6bb5dede, 0x4e165858, 0x6eb4dada, 0x44145050, 0xcac10b0b, 0x8828a0a0, 0x17f8efef, 0x9c2cb0b0, 0x11051414,
+ 0x872bacac, 0xfb669d9d, 0xf2986a6a, 0xae77d9d9, 0x822aa8a8, 0x46bcfafa, 0x14041010, 0xcfc00f0f, 0x02a8aaaa, 0x54451111, 0x5f134c4c, 0xbe269898, 0x6d482525, 0x9e841a1a, 0x1e061818, 0xfd9b6666,
+ 0xec9e7272, 0x4a430909, 0x10514141, 0x24f7d3d3, 0xd5934646, 0x53ecbfbf, 0xf89a6262, 0x927be9e9, 0xff33cccc, 0x04555151, 0x270b2c2c, 0x4f420d0d, 0x59eeb7b7, 0xf3cc3f3f, 0x1caeb2b2, 0xea638989,
+ 0x74e79393, 0x7fb1cece, 0x6c1c7070, 0x0daba6a6, 0xedca2727, 0x28082020, 0x48eba3a3, 0xc1975656, 0x80820202, 0xa3dc7f7f, 0xc4965252, 0x12f9ebeb, 0xa174d5d5, 0xb38d3e3e, 0xc33ffcfc, 0x3ea49a9a,
+ 0x5b461d1d, 0x1b071c1c, 0x3ba59e9e, 0x0cfff3f3, 0x3ff0cfcf, 0xbf72cdcd, 0x4b175c5c, 0x52b8eaea, 0x8f810e0e, 0x3d586565, 0xcc3cf0f0, 0x7d196464, 0x7ee59b9b, 0x91871616, 0x734e3d3d, 0x08aaa2a2,
+ 0xc869a1a1, 0xc76aadad, 0x85830606, 0x7ab0caca, 0xb570c5c5, 0xf4659191, 0xb2d96b6b, 0xa7892e2e, 0x18fbe3e3, 0x47e8afaf, 0x330f3c3c, 0x674a2d2d, 0xb071c1c1, 0x0e575959, 0xe99f7676, 0xe135d4d4,
+ 0x661e7878, 0xb4249090, 0x360e3838, 0x265f7979, 0xef628d8d, 0x38596161, 0x95d24747, 0x2aa08a8a, 0xb1259494, 0xaa228888, 0x8c7df1f1, 0xd73becec, 0x05010404, 0xa5218484, 0x9879e1e1, 0x9b851e1e,
+ 0x84d75353, 0x00000000, 0x5e471919, 0x0b565d5d, 0xe39d7e7e, 0x9fd04f4f, 0xbb279c9c, 0x1a534949, 0x7c4d3131, 0xee36d8d8, 0x0a020808, 0x7be49f9f, 0x20a28282, 0xd4c71313, 0xe8cb2323, 0xe69c7a7a,
+ 0x42e9abab, 0x43bdfefe, 0xa2882a2a, 0x9ad14b4b, 0x40410101, 0xdbc41f1f, 0xd838e0e0, 0x61b7d6d6, 0x2fa18e8e, 0x2bf4dfdf, 0x3af1cbcb, 0xf6cd3b3b, 0x1dfae7e7, 0xe5608585, 0x41155454, 0x25a38686,
+ 0x60e38383, 0x16acbaba, 0x295c7575, 0x34a69292, 0xf7996e6e, 0xe434d0d0, 0x721a6868, 0x01545555, 0x19afb6b6, 0xdf914e4e, 0xfa32c8c8, 0xf030c0c0, 0x21f6d7d7, 0xbc8e3232, 0x75b3c6c6, 0x6fe08f8f,
+ 0x691d7474, 0x2ef5dbdb, 0x6ae18b8b, 0x962eb8b8, 0x8a800a0a, 0xfe679999, 0xe2c92b2b, 0xe0618181, 0xc0c30303, 0x8d29a4a4, 0xaf238c8c, 0x07a9aeae, 0x390d3434, 0x1f524d4d, 0x764f3939, 0xd36ebdbd,
+ 0x81d65757, 0xb7d86f6f, 0xeb37dcdc, 0x51441515, 0xa6dd7b7b, 0x09fef7f7, 0xb68c3a3a, 0x932fbcbc, 0x0f030c0c, 0x03fcffff, 0xc26ba9a9, 0xba73c9c9, 0xd96cb5b5, 0xdc6db1b1, 0x375a6d6d, 0x15504545,
+ 0xb98f3636, 0x771b6c6c, 0x13adbebe, 0xda904a4a, 0x57b9eeee, 0xa9de7777, 0x4cbef2f2, 0x837efdfd, 0x55114444, 0xbdda6767, 0x2c5d7171, 0x45400505, 0x631f7c7c, 0x50104040, 0x325b6969, 0xb8db6363,
+ 0x220a2828, 0xc5c20707, 0xf531c4c4, 0xa88a2222, 0x31a79696, 0xf9ce3737, 0x977aeded, 0x49bff6f6, 0x992db4b4, 0xa475d1d1, 0x90d34343, 0x5a124848, 0x58bae2e2, 0x71e69797, 0x64b6d2d2, 0x70b2c2c2,
+ 0xad8b2626, 0xcd68a5a5, 0xcb955e5e, 0x624b2929, 0x3c0c3030, 0xce945a5a, 0xab76dddd, 0x867ff9f9, 0xf1649595, 0x5dbbe6e6, 0x35f2c7c7, 0x2d092424, 0xd1c61717, 0xd66fb9b9, 0xdec51b1b, 0x94861212,
+ 0x78186060, 0x30f3c3c3, 0x897cf5f5, 0x5cefb3b3, 0xd23ae8e8, 0xacdf7373, 0x794c3535, 0xa0208080, 0x9d78e5e5, 0x56edbbbb, 0x235e7d7d, 0xc63ef8f8, 0x8bd45f5f, 0xe7c82f2f, 0xdd39e4e4, 0x68492121,
+}
+
+func rl(x uint32, i uint8) uint32 { return (x << (i % 32)) | (x >> (32 - (i % 32))) }
+
+func l0(b uint32) uint32 { return b ^ rl(b, 13) ^ rl(b, 23) }
+
+func feistel0(x0, x1, x2, x3, rk uint32) uint32 { return x0 ^ l0(p(x1^x2^x3^rk)) }
+
+//非线性变换τ(.)
+func p(a uint32) uint32 {
+ return (uint32(sbox[a>>24]) << 24) ^ (uint32(sbox[(a>>16)&0xff]) << 16) ^ (uint32(sbox[(a>>8)&0xff]) << 8) ^ uint32(sbox[(a)&0xff])
+}
+
+func permuteInitialBlock(b []uint32, block []byte) {
+ for i := 0; i < 4; i++ {
+ b[i] = (uint32(block[i*4]) << 24) | (uint32(block[i*4+1]) << 16) |
+ (uint32(block[i*4+2]) << 8) | (uint32(block[i*4+3]))
+ }
+}
+
+func permuteFinalBlock(b []byte, block []uint32) {
+ for i := 0; i < 4; i++ {
+ b[i*4] = uint8(block[i] >> 24)
+ b[i*4+1] = uint8(block[i] >> 16)
+ b[i*4+2] = uint8(block[i] >> 8)
+ b[i*4+3] = uint8(block[i])
+ }
+}
+
+//修改后的加密核心函数
+func cryptBlock(subkeys []uint32, b []uint32, r []byte, dst, src []byte, decrypt bool) {
+ permuteInitialBlock(b, src)
+
+ // bounds check elimination in major encryption loop
+ // https://go101.org/article/bounds-check-elimination.html
+ _ = b[3]
+ if decrypt {
+ for i := 0; i < 8; i++ {
+ s := subkeys[31-4*i-3 : 31-4*i-3+4]
+ x := b[1] ^ b[2] ^ b[3] ^ s[3]
+ b[0] = b[0] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ x = b[0] ^ b[2] ^ b[3] ^ s[2]
+ b[1] = b[1] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ x = b[0] ^ b[1] ^ b[3] ^ s[1]
+ b[2] = b[2] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ x = b[1] ^ b[2] ^ b[0] ^ s[0]
+ b[3] = b[3] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ }
+ } else {
+ for i := 0; i < 8; i++ {
+ s := subkeys[4*i : 4*i+4]
+ x := b[1] ^ b[2] ^ b[3] ^ s[0]
+ b[0] = b[0] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ x = b[0] ^ b[2] ^ b[3] ^ s[1]
+ b[1] = b[1] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ x = b[0] ^ b[1] ^ b[3] ^ s[2]
+ b[2] = b[2] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ x = b[1] ^ b[2] ^ b[0] ^ s[3]
+ b[3] = b[3] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
+ }
+ }
+ b[0], b[1], b[2], b[3] = b[3], b[2], b[1], b[0]
+ permuteFinalBlock(r, b)
+ copy(dst, r)
+}
+
+func generateSubKeys(key []byte) []uint32 {
+ subkeys := make([]uint32, 32)
+ b := make([]uint32, 4)
+ permuteInitialBlock(b, key)
+ b[0] ^= fk[0]
+ b[1] ^= fk[1]
+ b[2] ^= fk[2]
+ b[3] ^= fk[3]
+ for i := 0; i < 32; i++ {
+ subkeys[i] = feistel0(b[0], b[1], b[2], b[3], ck[i])
+ b[0], b[1], b[2], b[3] = b[1], b[2], b[3], subkeys[i]
+ }
+ return subkeys
+}
+
+func EncryptBlock(key SM4Key, dst, src []byte) {
+ subkeys := generateSubKeys(key)
+ cryptBlock(subkeys, make([]uint32, 4), make([]byte, 16), dst, src, false)
+}
+
+func DecryptBlock(key SM4Key, dst, src []byte) {
+ subkeys := generateSubKeys(key)
+ cryptBlock(subkeys, make([]uint32, 4), make([]byte, 16), dst, src, true)
+}
+
+func ReadKeyFromMem(data []byte, pwd []byte) (SM4Key, error) {
+ block, _ := pem.Decode(data)
+ if block == nil {
+ return nil, errors.New("SM4: pem decode failed")
+ }
+ if x509.IsEncryptedPEMBlock(block) {
+ if block.Type != "SM4 ENCRYPTED KEY" {
+ return nil, errors.New("SM4: unknown type")
+ }
+ if pwd == nil {
+ return nil, errors.New("SM4: need passwd")
+ }
+ data, err := x509.DecryptPEMBlock(block, pwd)
+ if err != nil {
+ return nil, err
+ }
+ return data, nil
+ }
+ if block.Type != "SM4 KEY" {
+ return nil, errors.New("SM4: unknown type")
+ }
+ return block.Bytes, nil
+}
+
+func ReadKeyFromPem(FileName string, pwd []byte) (SM4Key, error) {
+ data, err := ioutil.ReadFile(FileName)
+ if err != nil {
+ return nil, err
+ }
+ return ReadKeyFromMem(data, pwd)
+}
+
+func WriteKeytoMem(key SM4Key, pwd []byte) ([]byte, error) {
+ if pwd != nil {
+ block, err := x509.EncryptPEMBlock(rand.Reader,
+ "SM4 ENCRYPTED KEY", key, pwd, x509.PEMCipherAES256)
+ if err != nil {
+ return nil, err
+ }
+ return pem.EncodeToMemory(block), nil
+ } else {
+ block := &pem.Block{
+ Type: "SM4 KEY",
+ Bytes: key,
+ }
+ return pem.EncodeToMemory(block), nil
+ }
+}
+
+func WriteKeyToPem(FileName string, key SM4Key, pwd []byte) (bool, error) {
+ var block *pem.Block
+
+ if pwd != nil {
+ var err error
+ block, err = x509.EncryptPEMBlock(rand.Reader,
+ "SM4 ENCRYPTED KEY", key, pwd, x509.PEMCipherAES256)
+ if err != nil {
+ return false, err
+ }
+ } else {
+ block = &pem.Block{
+ Type: "SM4 KEY",
+ Bytes: key,
+ }
+ }
+ file, err := os.Create(FileName)
+ if err != nil {
+ return false, err
+ }
+ defer file.Close()
+ err = pem.Encode(file, block)
+ if err != nil {
+ return false, nil
+ }
+ return true, nil
+}
+
+func (k KeySizeError) Error() string {
+ return "SM4: invalid key size " + strconv.Itoa(int(k))
+}
+
+// NewCipher creates and returns a new cipher.Block.
+func NewCipher(key []byte) (cipher.Block, error) {
+ if len(key) != BlockSize {
+ return nil, KeySizeError(len(key))
+ }
+ c := new(Sm4Cipher)
+ c.subkeys = generateSubKeys(key)
+ c.block1 = make([]uint32, 4)
+ c.block2 = make([]byte, 16)
+ return c, nil
+}
+
+func (c *Sm4Cipher) BlockSize() int {
+ return BlockSize
+}
+
+func (c *Sm4Cipher) Encrypt(dst, src []byte) {
+ cryptBlock(c.subkeys, c.block1, c.block2, dst, src, false)
+}
+
+func (c *Sm4Cipher) Decrypt(dst, src []byte) {
+ cryptBlock(c.subkeys, c.block1, c.block2, dst, src, true)
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/.gitignore b/vendor/github.com/xtaci/kcp-go/v5/.gitignore
new file mode 100644
index 0000000..2f4178c
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+/vendor/
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/xtaci/kcp-go/v5/.travis.yml b/vendor/github.com/xtaci/kcp-go/v5/.travis.yml
new file mode 100644
index 0000000..0e7c4ac
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/.travis.yml
@@ -0,0 +1,20 @@
+language: go
+go:
+ - 1.11.x
+ - 1.12.x
+ - 1.13.x
+
+env:
+ - GO111MODULE=on
+
+before_install:
+ - go get -t -v ./...
+
+install:
+ - go get github.com/xtaci/kcp-go
+
+script:
+ - go test -coverprofile=coverage.txt -covermode=atomic -bench . -timeout 10m
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/github.com/xtaci/kcp-go/v5/LICENSE b/vendor/github.com/xtaci/kcp-go/v5/LICENSE
new file mode 100644
index 0000000..8294d13
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Daniel Fu
+
+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.
+
diff --git a/vendor/github.com/xtaci/kcp-go/v5/README.md b/vendor/github.com/xtaci/kcp-go/v5/README.md
new file mode 100644
index 0000000..d2273f8
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/README.md
@@ -0,0 +1,285 @@
+<img src="kcp-go.png" alt="kcp-go" height="50px" />
+
+
+[![GoDoc][1]][2] [![Powered][9]][10] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Coverage Statusd][7]][8] [![Sourcegraph][13]][14]
+
+[1]: https://godoc.org/github.com/xtaci/kcp-go?status.svg
+[2]: https://pkg.go.dev/github.com/xtaci/kcp-go
+[3]: https://travis-ci.org/xtaci/kcp-go.svg?branch=master
+[4]: https://travis-ci.org/xtaci/kcp-go
+[5]: https://goreportcard.com/badge/github.com/xtaci/kcp-go
+[6]: https://goreportcard.com/report/github.com/xtaci/kcp-go
+[7]: https://codecov.io/gh/xtaci/kcp-go/branch/master/graph/badge.svg
+[8]: https://codecov.io/gh/xtaci/kcp-go
+[9]: https://img.shields.io/badge/KCP-Powered-blue.svg
+[10]: https://github.com/skywind3000/kcp
+[11]: https://img.shields.io/badge/license-MIT-blue.svg
+[12]: LICENSE
+[13]: https://sourcegraph.com/github.com/xtaci/kcp-go/-/badge.svg
+[14]: https://sourcegraph.com/github.com/xtaci/kcp-go?badge
+
+## Introduction
+
+**kcp-go** is a **Production-Grade Reliable-UDP** library for [golang](https://golang.org/).
+
+This library intents to provide a **smooth, resilient, ordered, error-checked and anonymous** delivery of streams over **UDP** packets, it has been battle-tested with opensource project [kcptun](https://github.com/xtaci/kcptun). Millions of devices(from low-end MIPS routers to high-end servers) have deployed **kcp-go** powered program in a variety of forms like **online games, live broadcasting, file synchronization and network acceleration**.
+
+[Lastest Release](https://github.com/xtaci/kcp-go/releases)
+
+## Features
+
+1. Designed for **Latency-sensitive** scenarios.
+1. **Cache friendly** and **Memory optimized** design, offers extremely **High Performance** core.
+1. Handles **>5K concurrent connections** on a single commodity server.
+1. Compatible with [net.Conn](https://golang.org/pkg/net/#Conn) and [net.Listener](https://golang.org/pkg/net/#Listener), a drop-in replacement for [net.TCPConn](https://golang.org/pkg/net/#TCPConn).
+1. [FEC(Forward Error Correction)](https://en.wikipedia.org/wiki/Forward_error_correction) Support with [Reed-Solomon Codes](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction)
+1. Packet level encryption support with [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard), [TEA](https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm), [3DES](https://en.wikipedia.org/wiki/Triple_DES), [Blowfish](https://en.wikipedia.org/wiki/Blowfish_(cipher)), [Cast5](https://en.wikipedia.org/wiki/CAST-128), [Salsa20]( https://en.wikipedia.org/wiki/Salsa20), etc. in [CFB](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Feedback_.28CFB.29) mode, which generates completely anonymous packet.
+1. Only **A fixed number of goroutines** will be created for the entire server application, costs in **context switch** between goroutines have been taken into consideration.
+1. Compatible with [skywind3000's](https://github.com/skywind3000) C version with various improvements.
+1. Platform-dependent optimizations: [sendmmsg](http://man7.org/linux/man-pages/man2/sendmmsg.2.html) and [recvmmsg](http://man7.org/linux/man-pages/man2/recvmmsg.2.html) were expoloited for linux.
+
+## Documentation
+
+For complete documentation, see the associated [Godoc](https://godoc.org/github.com/xtaci/kcp-go).
+
+## Specification
+
+<img src="frame.png" alt="Frame Format" height="109px" />
+
+```
+NONCE:
+ 16bytes cryptographically secure random number, nonce changes for every packet.
+
+CRC32:
+ CRC-32 checksum of data using the IEEE polynomial
+
+FEC TYPE:
+ typeData = 0xF1
+ typeParity = 0xF2
+
+FEC SEQID:
+ monotonically increasing in range: [0, (0xffffffff/shardSize) * shardSize - 1]
+
+SIZE:
+ The size of KCP frame plus 2
+```
+
+```
++-----------------+
+| SESSION |
++-----------------+
+| KCP(ARQ) |
++-----------------+
+| FEC(OPTIONAL) |
++-----------------+
+| CRYPTO(OPTIONAL)|
++-----------------+
+| UDP(PACKET) |
++-----------------+
+| IP |
++-----------------+
+| LINK |
++-----------------+
+| PHY |
++-----------------+
+(LAYER MODEL OF KCP-GO)
+```
+
+
+## Examples
+
+1. [simple examples](https://github.com/xtaci/kcp-go/tree/master/examples)
+2. [kcptun client](https://github.com/xtaci/kcptun/blob/master/client/main.go)
+3. [kcptun server](https://github.com/xtaci/kcptun/blob/master/server/main.go)
+
+## Benchmark
+```
+===
+Model Name: MacBook Pro
+Model Identifier: MacBookPro14,1
+Processor Name: Intel Core i5
+Processor Speed: 3.1 GHz
+Number of Processors: 1
+Total Number of Cores: 2
+L2 Cache (per Core): 256 KB
+L3 Cache: 4 MB
+Memory: 8 GB
+===
+
+$ go test -v -run=^$ -bench .
+beginning tests, encryption:salsa20, fec:10/3
+goos: darwin
+goarch: amd64
+pkg: github.com/xtaci/kcp-go
+BenchmarkSM4-4 50000 32180 ns/op 93.23 MB/s 0 B/op 0 allocs/op
+BenchmarkAES128-4 500000 3285 ns/op 913.21 MB/s 0 B/op 0 allocs/op
+BenchmarkAES192-4 300000 3623 ns/op 827.85 MB/s 0 B/op 0 allocs/op
+BenchmarkAES256-4 300000 3874 ns/op 774.20 MB/s 0 B/op 0 allocs/op
+BenchmarkTEA-4 100000 15384 ns/op 195.00 MB/s 0 B/op 0 allocs/op
+BenchmarkXOR-4 20000000 89.9 ns/op 33372.00 MB/s 0 B/op 0 allocs/op
+BenchmarkBlowfish-4 50000 26927 ns/op 111.41 MB/s 0 B/op 0 allocs/op
+BenchmarkNone-4 30000000 45.7 ns/op 65597.94 MB/s 0 B/op 0 allocs/op
+BenchmarkCast5-4 50000 34258 ns/op 87.57 MB/s 0 B/op 0 allocs/op
+Benchmark3DES-4 10000 117149 ns/op 25.61 MB/s 0 B/op 0 allocs/op
+BenchmarkTwofish-4 50000 33538 ns/op 89.45 MB/s 0 B/op 0 allocs/op
+BenchmarkXTEA-4 30000 45666 ns/op 65.69 MB/s 0 B/op 0 allocs/op
+BenchmarkSalsa20-4 500000 3308 ns/op 906.76 MB/s 0 B/op 0 allocs/op
+BenchmarkCRC32-4 20000000 65.2 ns/op 15712.43 MB/s
+BenchmarkCsprngSystem-4 1000000 1150 ns/op 13.91 MB/s
+BenchmarkCsprngMD5-4 10000000 145 ns/op 110.26 MB/s
+BenchmarkCsprngSHA1-4 10000000 158 ns/op 126.54 MB/s
+BenchmarkCsprngNonceMD5-4 10000000 153 ns/op 104.22 MB/s
+BenchmarkCsprngNonceAES128-4 100000000 19.1 ns/op 837.81 MB/s
+BenchmarkFECDecode-4 1000000 1119 ns/op 1339.61 MB/s 1606 B/op 2 allocs/op
+BenchmarkFECEncode-4 2000000 832 ns/op 1801.83 MB/s 17 B/op 0 allocs/op
+BenchmarkFlush-4 5000000 272 ns/op 0 B/op 0 allocs/op
+BenchmarkEchoSpeed4K-4 5000 259617 ns/op 15.78 MB/s 5451 B/op 149 allocs/op
+BenchmarkEchoSpeed64K-4 1000 1706084 ns/op 38.41 MB/s 56002 B/op 1604 allocs/op
+BenchmarkEchoSpeed512K-4 100 14345505 ns/op 36.55 MB/s 482597 B/op 13045 allocs/op
+BenchmarkEchoSpeed1M-4 30 34859104 ns/op 30.08 MB/s 1143773 B/op 27186 allocs/op
+BenchmarkSinkSpeed4K-4 50000 31369 ns/op 130.57 MB/s 1566 B/op 30 allocs/op
+BenchmarkSinkSpeed64K-4 5000 329065 ns/op 199.16 MB/s 21529 B/op 453 allocs/op
+BenchmarkSinkSpeed256K-4 500 2373354 ns/op 220.91 MB/s 166332 B/op 3554 allocs/op
+BenchmarkSinkSpeed1M-4 300 5117927 ns/op 204.88 MB/s 310378 B/op 6988 allocs/op
+PASS
+ok github.com/xtaci/kcp-go 50.349s
+```
+
+```
+=== Raspberry Pi 4 ===
+
+➜ kcp-go git:(master) cat /proc/cpuinfo
+processor : 0
+model name : ARMv7 Processor rev 3 (v7l)
+BogoMIPS : 108.00
+Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
+CPU implementer : 0x41
+CPU architecture: 7
+CPU variant : 0x0
+CPU part : 0xd08
+CPU revision : 3
+
+➜ kcp-go git:(master) go test -run=^$ -bench .
+2020/01/05 19:25:13 beginning tests, encryption:salsa20, fec:10/3
+goos: linux
+goarch: arm
+pkg: github.com/xtaci/kcp-go/v5
+BenchmarkSM4-4 20000 86475 ns/op 34.69 MB/s 0 B/op 0 allocs/op
+BenchmarkAES128-4 20000 62254 ns/op 48.19 MB/s 0 B/op 0 allocs/op
+BenchmarkAES192-4 20000 71802 ns/op 41.78 MB/s 0 B/op 0 allocs/op
+BenchmarkAES256-4 20000 80570 ns/op 37.23 MB/s 0 B/op 0 allocs/op
+BenchmarkTEA-4 50000 37343 ns/op 80.34 MB/s 0 B/op 0 allocs/op
+BenchmarkXOR-4 100000 22266 ns/op 134.73 MB/s 0 B/op 0 allocs/op
+BenchmarkBlowfish-4 20000 66123 ns/op 45.37 MB/s 0 B/op 0 allocs/op
+BenchmarkNone-4 3000000 518 ns/op 5786.77 MB/s 0 B/op 0 allocs/op
+BenchmarkCast5-4 20000 76705 ns/op 39.11 MB/s 0 B/op 0 allocs/op
+Benchmark3DES-4 5000 418868 ns/op 7.16 MB/s 0 B/op 0 allocs/op
+BenchmarkTwofish-4 5000 326896 ns/op 9.18 MB/s 0 B/op 0 allocs/op
+BenchmarkXTEA-4 10000 114418 ns/op 26.22 MB/s 0 B/op 0 allocs/op
+BenchmarkSalsa20-4 50000 36736 ns/op 81.66 MB/s 0 B/op 0 allocs/op
+BenchmarkCRC32-4 1000000 1735 ns/op 589.98 MB/s
+BenchmarkCsprngSystem-4 1000000 2179 ns/op 7.34 MB/s
+BenchmarkCsprngMD5-4 2000000 811 ns/op 19.71 MB/s
+BenchmarkCsprngSHA1-4 2000000 862 ns/op 23.19 MB/s
+BenchmarkCsprngNonceMD5-4 2000000 878 ns/op 18.22 MB/s
+BenchmarkCsprngNonceAES128-4 5000000 326 ns/op 48.97 MB/s
+BenchmarkFECDecode-4 200000 9081 ns/op 165.16 MB/s 140 B/op 1 allocs/op
+BenchmarkFECEncode-4 100000 12039 ns/op 124.59 MB/s 11 B/op 0 allocs/op
+BenchmarkFlush-4 100000 21704 ns/op 0 B/op 0 allocs/op
+BenchmarkEchoSpeed4K-4 2000 981182 ns/op 4.17 MB/s 12384 B/op 424 allocs/op
+BenchmarkEchoSpeed64K-4 100 10503324 ns/op 6.24 MB/s 123616 B/op 3779 allocs/op
+BenchmarkEchoSpeed512K-4 20 138633802 ns/op 3.78 MB/s 1606584 B/op 29233 allocs/op
+BenchmarkEchoSpeed1M-4 5 372903568 ns/op 2.81 MB/s 4080504 B/op 63600 allocs/op
+BenchmarkSinkSpeed4K-4 10000 121239 ns/op 33.78 MB/s 4647 B/op 104 allocs/op
+BenchmarkSinkSpeed64K-4 1000 1587906 ns/op 41.27 MB/s 50914 B/op 1115 allocs/op
+BenchmarkSinkSpeed256K-4 100 16277830 ns/op 32.21 MB/s 453027 B/op 9296 allocs/op
+BenchmarkSinkSpeed1M-4 100 31040703 ns/op 33.78 MB/s 898097 B/op 18932 allocs/op
+PASS
+ok github.com/xtaci/kcp-go/v5 64.151s
+```
+
+
+## Typical Flame Graph
+![Flame Graph in kcptun](flame.png)
+
+## Key Design Considerations
+
+1. slice vs. container/list
+
+`kcp.flush()` loops through the send queue for retransmission checking for every 20ms(interval).
+
+I've wrote a benchmark for comparing sequential loop through *slice* and *container/list* here:
+
+https://github.com/xtaci/notes/blob/master/golang/benchmark2/cachemiss_test.go
+
+```
+BenchmarkLoopSlice-4 2000000000 0.39 ns/op
+BenchmarkLoopList-4 100000000 54.6 ns/op
+```
+
+List structure introduces **heavy cache misses** compared to slice which owns better **locality**, 5000 connections with 32 window size and 20ms interval will cost 6us/0.03%(cpu) using slice, and 8.7ms/43.5%(cpu) for list for each `kcp.flush()`.
+
+2. Timing accuracy vs. syscall clock_gettime
+
+Timing is **critical** to **RTT estimator**, inaccurate timing leads to false retransmissions in KCP, but calling `time.Now()` costs 42 cycles(10.5ns on 4GHz CPU, 15.6ns on my MacBook Pro 2.7GHz).
+
+The benchmark for time.Now() lies here:
+
+https://github.com/xtaci/notes/blob/master/golang/benchmark2/syscall_test.go
+
+```
+BenchmarkNow-4 100000000 15.6 ns/op
+```
+
+In kcp-go, after each `kcp.output()` function call, current clock time will be updated upon return, and for a single `kcp.flush()` operation, current time will be queried from system once. For most of the time, 5000 connections costs 5000 * 15.6ns = 78us(a fixed cost while no packet needs to be sent), as for 10MB/s data transfering with 1400 MTU, `kcp.output()` will be called around 7500 times and costs 117us for `time.Now()` in **every second**.
+
+3. Memory management
+
+Primary memory allocation are done from a global buffer pool xmit.Buf, in kcp-go, when we need to allocate some bytes, we can get from that pool, and a fixed-capacity 1500 bytes(mtuLimit) will be returned, the rx queue, tx queue and fec queue all receive bytes from there, and they will return the bytes to the pool after using to prevent unnecessary zer0ing of bytes. The pool mechanism maintained a high watermark for slice objects, these in-flight objects from the pool will survive from the perodical garbage collection, meanwhile the pool kept the ability to return the memory to runtime if in idle.
+
+4. Information security
+
+kcp-go is shipped with builtin packet encryption powered by various block encryption algorithms and works in [Cipher Feedback Mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Feedback_(CFB)), for each packet to be sent, the encryption process will start from encrypting a [nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce) from the [system entropy](https://en.wikipedia.org/wiki//dev/random), so encryption to same plaintexts never leads to a same ciphertexts thereafter.
+
+The contents of the packets are completely anonymous with encryption, including the headers(FEC,KCP), checksums and contents. Note that, no matter which encryption method you choose on you upper layer, if you disable encryption, the transmit will be insecure somehow, since the header is ***PLAINTEXT*** to everyone it would be susceptible to header tampering, such as jamming the *sliding window size*, *round-trip time*, *FEC property* and *checksums*. ```AES-128``` is suggested for minimal encryption since modern CPUs are shipped with [AES-NI](https://en.wikipedia.org/wiki/AES_instruction_set) instructions and performs even better than `salsa20`(check the table above).
+
+Other possible attacks to kcp-go includes: a) [traffic analysis](https://en.wikipedia.org/wiki/Traffic_analysis), dataflow on specific websites may have pattern while interchanging data, but this type of eavesdropping has been mitigated by adapting [smux](https://github.com/xtaci/smux) to mix data streams so as to introduce noises, perfect solution to this has not appeared yet, theroretically by shuffling/mixing messages on larger scale network may mitigate this problem. b) [replay attack](https://en.wikipedia.org/wiki/Replay_attack), since the asymmetrical encryption has not been introduced into kcp-go for some reason, capturing the packets and replay them on a different machine is possible, (notice: hijacking the session and decrypting the contents is still *impossible*), so upper layers should contain a asymmetrical encryption system to guarantee the authenticity of each message(to process message exactly once), such as HTTPS/OpenSSL/LibreSSL, only by signing the requests with private keys can eliminate this type of attack.
+
+## Connection Termination
+
+Control messages like **SYN/FIN/RST** in TCP **are not defined** in KCP, you need some **keepalive/heartbeat mechanism** in the application-level. A real world example is to use some **multiplexing** protocol over session, such as [smux](https://github.com/xtaci/smux)(with embedded keepalive mechanism), see [kcptun](https://github.com/xtaci/kcptun) for example.
+
+## FAQ
+
+Q: I'm handling >5K connections on my server, the CPU utilization is so high.
+
+A: A standalone `agent` or `gate` server for running kcp-go is suggested, not only for CPU utilization, but also important to the **precision** of RTT measurements(timing) which indirectly affects retransmission. By increasing update `interval` with `SetNoDelay` like `conn.SetNoDelay(1, 40, 1, 1)` will dramatically reduce system load, but lower the performance.
+
+Q: When should I enable FEC?
+
+A: Forward error correction is critical to long-distance transmission, because a packet loss will lead to a huge penalty in time. And for the complicated packet routing network in modern world, round-trip time based loss check will not always be efficient, the big deviation of RTT samples in the long way usually leads to a larger RTO value in typical rtt estimator, which in other words, slows down the transmission.
+
+Q: Should I enable encryption?
+
+A: Yes, for the safety of protocol, even if the upper layer has encrypted.
+
+## Who is using this?
+
+1. https://github.com/xtaci/kcptun -- A Secure Tunnel Based On KCP over UDP.
+2. https://github.com/getlantern/lantern -- Lantern delivers fast access to the open Internet.
+3. https://github.com/smallnest/rpcx -- A RPC service framework based on net/rpc like alibaba Dubbo and weibo Motan.
+4. https://github.com/gonet2/agent -- A gateway for games with stream multiplexing.
+5. https://github.com/syncthing/syncthing -- Open Source Continuous File Synchronization.
+
+## Links
+
+1. https://github.com/xtaci/smux/ -- A Stream Multiplexing Library for golang with least memory
+1. https://github.com/xtaci/libkcp -- FEC enhanced KCP session library for iOS/Android in C++
+1. https://github.com/skywind3000/kcp -- A Fast and Reliable ARQ Protocol
+1. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go
+
+## Consulting
+
+WeChat(付费技术咨询)
+
+<img src="wechat_donate.jpg" alt="kcptun" height="120px" />
diff --git a/vendor/github.com/xtaci/kcp-go/v5/autotune.go b/vendor/github.com/xtaci/kcp-go/v5/autotune.go
new file mode 100644
index 0000000..1f85be3
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/autotune.go
@@ -0,0 +1,64 @@
+package kcp
+
+const maxAutoTuneSamples = 258
+
+// pulse represents a 0/1 signal with time sequence
+type pulse struct {
+ bit bool // 0 or 1
+ seq uint32 // sequence of the signal
+}
+
+// autoTune object
+type autoTune struct {
+ pulses [maxAutoTuneSamples]pulse
+}
+
+// Sample adds a signal sample to the pulse buffer
+func (tune *autoTune) Sample(bit bool, seq uint32) {
+ tune.pulses[seq%maxAutoTuneSamples] = pulse{bit, seq}
+}
+
+// Find a period for a given signal
+// returns -1 if not found
+//
+// --- ------
+// | |
+// |______________|
+// Period
+// Falling Edge Rising Edge
+func (tune *autoTune) FindPeriod(bit bool) int {
+ // last pulse and initial index setup
+ lastPulse := tune.pulses[0]
+ idx := 1
+
+ // left edge
+ var leftEdge int
+ for ; idx < len(tune.pulses); idx++ {
+ if lastPulse.bit != bit && tune.pulses[idx].bit == bit { // edge found
+ if lastPulse.seq+1 == tune.pulses[idx].seq { // ensure edge continuity
+ leftEdge = idx
+ break
+ }
+ }
+ lastPulse = tune.pulses[idx]
+ }
+
+ // right edge
+ var rightEdge int
+ lastPulse = tune.pulses[leftEdge]
+ idx = leftEdge + 1
+
+ for ; idx < len(tune.pulses); idx++ {
+ if lastPulse.seq+1 == tune.pulses[idx].seq { // ensure pulses in this level monotonic
+ if lastPulse.bit == bit && tune.pulses[idx].bit != bit { // edge found
+ rightEdge = idx
+ break
+ }
+ } else {
+ return -1
+ }
+ lastPulse = tune.pulses[idx]
+ }
+
+ return rightEdge - leftEdge
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/batchconn.go b/vendor/github.com/xtaci/kcp-go/v5/batchconn.go
new file mode 100644
index 0000000..6c30701
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/batchconn.go
@@ -0,0 +1,12 @@
+package kcp
+
+import "golang.org/x/net/ipv4"
+
+const (
+ batchSize = 16
+)
+
+type batchConn interface {
+ WriteBatch(ms []ipv4.Message, flags int) (int, error)
+ ReadBatch(ms []ipv4.Message, flags int) (int, error)
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/crypt.go b/vendor/github.com/xtaci/kcp-go/v5/crypt.go
new file mode 100644
index 0000000..d882852
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/crypt.go
@@ -0,0 +1,618 @@
+package kcp
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/sha1"
+ "unsafe"
+
+ xor "github.com/templexxx/xorsimd"
+ "github.com/tjfoc/gmsm/sm4"
+
+ "golang.org/x/crypto/blowfish"
+ "golang.org/x/crypto/cast5"
+ "golang.org/x/crypto/pbkdf2"
+ "golang.org/x/crypto/salsa20"
+ "golang.org/x/crypto/tea"
+ "golang.org/x/crypto/twofish"
+ "golang.org/x/crypto/xtea"
+)
+
+var (
+ initialVector = []byte{167, 115, 79, 156, 18, 172, 27, 1, 164, 21, 242, 193, 252, 120, 230, 107}
+ saltxor = `sH3CIVoF#rWLtJo6`
+)
+
+// BlockCrypt defines encryption/decryption methods for a given byte slice.
+// Notes on implementing: the data to be encrypted contains a builtin
+// nonce at the first 16 bytes
+type BlockCrypt interface {
+ // Encrypt encrypts the whole block in src into dst.
+ // Dst and src may point at the same memory.
+ Encrypt(dst, src []byte)
+
+ // Decrypt decrypts the whole block in src into dst.
+ // Dst and src may point at the same memory.
+ Decrypt(dst, src []byte)
+}
+
+type salsa20BlockCrypt struct {
+ key [32]byte
+}
+
+// NewSalsa20BlockCrypt https://en.wikipedia.org/wiki/Salsa20
+func NewSalsa20BlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(salsa20BlockCrypt)
+ copy(c.key[:], key)
+ return c, nil
+}
+
+func (c *salsa20BlockCrypt) Encrypt(dst, src []byte) {
+ salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
+ copy(dst[:8], src[:8])
+}
+func (c *salsa20BlockCrypt) Decrypt(dst, src []byte) {
+ salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
+ copy(dst[:8], src[:8])
+}
+
+type sm4BlockCrypt struct {
+ encbuf [sm4.BlockSize]byte // 64bit alignment enc/dec buffer
+ decbuf [2 * sm4.BlockSize]byte
+ block cipher.Block
+}
+
+// NewSM4BlockCrypt https://github.com/tjfoc/gmsm/tree/master/sm4
+func NewSM4BlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(sm4BlockCrypt)
+ block, err := sm4.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *sm4BlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *sm4BlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type twofishBlockCrypt struct {
+ encbuf [twofish.BlockSize]byte
+ decbuf [2 * twofish.BlockSize]byte
+ block cipher.Block
+}
+
+// NewTwofishBlockCrypt https://en.wikipedia.org/wiki/Twofish
+func NewTwofishBlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(twofishBlockCrypt)
+ block, err := twofish.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *twofishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *twofishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type tripleDESBlockCrypt struct {
+ encbuf [des.BlockSize]byte
+ decbuf [2 * des.BlockSize]byte
+ block cipher.Block
+}
+
+// NewTripleDESBlockCrypt https://en.wikipedia.org/wiki/Triple_DES
+func NewTripleDESBlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(tripleDESBlockCrypt)
+ block, err := des.NewTripleDESCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *tripleDESBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *tripleDESBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type cast5BlockCrypt struct {
+ encbuf [cast5.BlockSize]byte
+ decbuf [2 * cast5.BlockSize]byte
+ block cipher.Block
+}
+
+// NewCast5BlockCrypt https://en.wikipedia.org/wiki/CAST-128
+func NewCast5BlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(cast5BlockCrypt)
+ block, err := cast5.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *cast5BlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *cast5BlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type blowfishBlockCrypt struct {
+ encbuf [blowfish.BlockSize]byte
+ decbuf [2 * blowfish.BlockSize]byte
+ block cipher.Block
+}
+
+// NewBlowfishBlockCrypt https://en.wikipedia.org/wiki/Blowfish_(cipher)
+func NewBlowfishBlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(blowfishBlockCrypt)
+ block, err := blowfish.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *blowfishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *blowfishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type aesBlockCrypt struct {
+ encbuf [aes.BlockSize]byte
+ decbuf [2 * aes.BlockSize]byte
+ block cipher.Block
+}
+
+// NewAESBlockCrypt https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+func NewAESBlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(aesBlockCrypt)
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *aesBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *aesBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type teaBlockCrypt struct {
+ encbuf [tea.BlockSize]byte
+ decbuf [2 * tea.BlockSize]byte
+ block cipher.Block
+}
+
+// NewTEABlockCrypt https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
+func NewTEABlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(teaBlockCrypt)
+ block, err := tea.NewCipherWithRounds(key, 16)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *teaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *teaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type xteaBlockCrypt struct {
+ encbuf [xtea.BlockSize]byte
+ decbuf [2 * xtea.BlockSize]byte
+ block cipher.Block
+}
+
+// NewXTEABlockCrypt https://en.wikipedia.org/wiki/XTEA
+func NewXTEABlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(xteaBlockCrypt)
+ block, err := xtea.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+ c.block = block
+ return c, nil
+}
+
+func (c *xteaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
+func (c *xteaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
+
+type simpleXORBlockCrypt struct {
+ xortbl []byte
+}
+
+// NewSimpleXORBlockCrypt simple xor with key expanding
+func NewSimpleXORBlockCrypt(key []byte) (BlockCrypt, error) {
+ c := new(simpleXORBlockCrypt)
+ c.xortbl = pbkdf2.Key(key, []byte(saltxor), 32, mtuLimit, sha1.New)
+ return c, nil
+}
+
+func (c *simpleXORBlockCrypt) Encrypt(dst, src []byte) { xor.Bytes(dst, src, c.xortbl) }
+func (c *simpleXORBlockCrypt) Decrypt(dst, src []byte) { xor.Bytes(dst, src, c.xortbl) }
+
+type noneBlockCrypt struct{}
+
+// NewNoneBlockCrypt does nothing but copying
+func NewNoneBlockCrypt(key []byte) (BlockCrypt, error) {
+ return new(noneBlockCrypt), nil
+}
+
+func (c *noneBlockCrypt) Encrypt(dst, src []byte) { copy(dst, src) }
+func (c *noneBlockCrypt) Decrypt(dst, src []byte) { copy(dst, src) }
+
+// packet encryption with local CFB mode
+func encrypt(block cipher.Block, dst, src, buf []byte) {
+ switch block.BlockSize() {
+ case 8:
+ encrypt8(block, dst, src, buf)
+ case 16:
+ encrypt16(block, dst, src, buf)
+ default:
+ panic("unsupported cipher block size")
+ }
+}
+
+// optimized encryption for the ciphers which works in 8-bytes
+func encrypt8(block cipher.Block, dst, src, buf []byte) {
+ tbl := buf[:8]
+ block.Encrypt(tbl, initialVector)
+ n := len(src) / 8
+ base := 0
+ repeat := n / 8
+ left := n % 8
+ ptr_tbl := (*uint64)(unsafe.Pointer(&tbl[0]))
+
+ for i := 0; i < repeat; i++ {
+ s := src[base:][0:64]
+ d := dst[base:][0:64]
+ // 1
+ *(*uint64)(unsafe.Pointer(&d[0])) = *(*uint64)(unsafe.Pointer(&s[0])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[0:8])
+ // 2
+ *(*uint64)(unsafe.Pointer(&d[8])) = *(*uint64)(unsafe.Pointer(&s[8])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[8:16])
+ // 3
+ *(*uint64)(unsafe.Pointer(&d[16])) = *(*uint64)(unsafe.Pointer(&s[16])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[16:24])
+ // 4
+ *(*uint64)(unsafe.Pointer(&d[24])) = *(*uint64)(unsafe.Pointer(&s[24])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[24:32])
+ // 5
+ *(*uint64)(unsafe.Pointer(&d[32])) = *(*uint64)(unsafe.Pointer(&s[32])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[32:40])
+ // 6
+ *(*uint64)(unsafe.Pointer(&d[40])) = *(*uint64)(unsafe.Pointer(&s[40])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[40:48])
+ // 7
+ *(*uint64)(unsafe.Pointer(&d[48])) = *(*uint64)(unsafe.Pointer(&s[48])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[48:56])
+ // 8
+ *(*uint64)(unsafe.Pointer(&d[56])) = *(*uint64)(unsafe.Pointer(&s[56])) ^ *ptr_tbl
+ block.Encrypt(tbl, d[56:64])
+ base += 64
+ }
+
+ switch left {
+ case 7:
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
+ block.Encrypt(tbl, dst[base:])
+ base += 8
+ fallthrough
+ case 6:
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
+ block.Encrypt(tbl, dst[base:])
+ base += 8
+ fallthrough
+ case 5:
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
+ block.Encrypt(tbl, dst[base:])
+ base += 8
+ fallthrough
+ case 4:
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
+ block.Encrypt(tbl, dst[base:])
+ base += 8
+ fallthrough
+ case 3:
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
+ block.Encrypt(tbl, dst[base:])
+ base += 8
+ fallthrough
+ case 2:
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
+ block.Encrypt(tbl, dst[base:])
+ base += 8
+ fallthrough
+ case 1:
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
+ block.Encrypt(tbl, dst[base:])
+ base += 8
+ fallthrough
+ case 0:
+ xorBytes(dst[base:], src[base:], tbl)
+ }
+}
+
+// optimized encryption for the ciphers which works in 16-bytes
+func encrypt16(block cipher.Block, dst, src, buf []byte) {
+ tbl := buf[:16]
+ block.Encrypt(tbl, initialVector)
+ n := len(src) / 16
+ base := 0
+ repeat := n / 8
+ left := n % 8
+ for i := 0; i < repeat; i++ {
+ s := src[base:][0:128]
+ d := dst[base:][0:128]
+ // 1
+ xor.Bytes16Align(d[0:16], s[0:16], tbl)
+ block.Encrypt(tbl, d[0:16])
+ // 2
+ xor.Bytes16Align(d[16:32], s[16:32], tbl)
+ block.Encrypt(tbl, d[16:32])
+ // 3
+ xor.Bytes16Align(d[32:48], s[32:48], tbl)
+ block.Encrypt(tbl, d[32:48])
+ // 4
+ xor.Bytes16Align(d[48:64], s[48:64], tbl)
+ block.Encrypt(tbl, d[48:64])
+ // 5
+ xor.Bytes16Align(d[64:80], s[64:80], tbl)
+ block.Encrypt(tbl, d[64:80])
+ // 6
+ xor.Bytes16Align(d[80:96], s[80:96], tbl)
+ block.Encrypt(tbl, d[80:96])
+ // 7
+ xor.Bytes16Align(d[96:112], s[96:112], tbl)
+ block.Encrypt(tbl, d[96:112])
+ // 8
+ xor.Bytes16Align(d[112:128], s[112:128], tbl)
+ block.Encrypt(tbl, d[112:128])
+ base += 128
+ }
+
+ switch left {
+ case 7:
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ block.Encrypt(tbl, dst[base:])
+ base += 16
+ fallthrough
+ case 6:
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ block.Encrypt(tbl, dst[base:])
+ base += 16
+ fallthrough
+ case 5:
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ block.Encrypt(tbl, dst[base:])
+ base += 16
+ fallthrough
+ case 4:
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ block.Encrypt(tbl, dst[base:])
+ base += 16
+ fallthrough
+ case 3:
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ block.Encrypt(tbl, dst[base:])
+ base += 16
+ fallthrough
+ case 2:
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ block.Encrypt(tbl, dst[base:])
+ base += 16
+ fallthrough
+ case 1:
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ block.Encrypt(tbl, dst[base:])
+ base += 16
+ fallthrough
+ case 0:
+ xorBytes(dst[base:], src[base:], tbl)
+ }
+}
+
+// decryption
+func decrypt(block cipher.Block, dst, src, buf []byte) {
+ switch block.BlockSize() {
+ case 8:
+ decrypt8(block, dst, src, buf)
+ case 16:
+ decrypt16(block, dst, src, buf)
+ default:
+ panic("unsupported cipher block size")
+ }
+}
+
+// decrypt 8 bytes block, all byte slices are supposed to be 64bit aligned
+func decrypt8(block cipher.Block, dst, src, buf []byte) {
+ tbl := buf[0:8]
+ next := buf[8:16]
+ block.Encrypt(tbl, initialVector)
+ n := len(src) / 8
+ base := 0
+ repeat := n / 8
+ left := n % 8
+ ptr_tbl := (*uint64)(unsafe.Pointer(&tbl[0]))
+ ptr_next := (*uint64)(unsafe.Pointer(&next[0]))
+
+ for i := 0; i < repeat; i++ {
+ s := src[base:][0:64]
+ d := dst[base:][0:64]
+ // 1
+ block.Encrypt(next, s[0:8])
+ *(*uint64)(unsafe.Pointer(&d[0])) = *(*uint64)(unsafe.Pointer(&s[0])) ^ *ptr_tbl
+ // 2
+ block.Encrypt(tbl, s[8:16])
+ *(*uint64)(unsafe.Pointer(&d[8])) = *(*uint64)(unsafe.Pointer(&s[8])) ^ *ptr_next
+ // 3
+ block.Encrypt(next, s[16:24])
+ *(*uint64)(unsafe.Pointer(&d[16])) = *(*uint64)(unsafe.Pointer(&s[16])) ^ *ptr_tbl
+ // 4
+ block.Encrypt(tbl, s[24:32])
+ *(*uint64)(unsafe.Pointer(&d[24])) = *(*uint64)(unsafe.Pointer(&s[24])) ^ *ptr_next
+ // 5
+ block.Encrypt(next, s[32:40])
+ *(*uint64)(unsafe.Pointer(&d[32])) = *(*uint64)(unsafe.Pointer(&s[32])) ^ *ptr_tbl
+ // 6
+ block.Encrypt(tbl, s[40:48])
+ *(*uint64)(unsafe.Pointer(&d[40])) = *(*uint64)(unsafe.Pointer(&s[40])) ^ *ptr_next
+ // 7
+ block.Encrypt(next, s[48:56])
+ *(*uint64)(unsafe.Pointer(&d[48])) = *(*uint64)(unsafe.Pointer(&s[48])) ^ *ptr_tbl
+ // 8
+ block.Encrypt(tbl, s[56:64])
+ *(*uint64)(unsafe.Pointer(&d[56])) = *(*uint64)(unsafe.Pointer(&s[56])) ^ *ptr_next
+ base += 64
+ }
+
+ switch left {
+ case 7:
+ block.Encrypt(next, src[base:])
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
+ tbl, next = next, tbl
+ base += 8
+ fallthrough
+ case 6:
+ block.Encrypt(next, src[base:])
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
+ tbl, next = next, tbl
+ base += 8
+ fallthrough
+ case 5:
+ block.Encrypt(next, src[base:])
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
+ tbl, next = next, tbl
+ base += 8
+ fallthrough
+ case 4:
+ block.Encrypt(next, src[base:])
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
+ tbl, next = next, tbl
+ base += 8
+ fallthrough
+ case 3:
+ block.Encrypt(next, src[base:])
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
+ tbl, next = next, tbl
+ base += 8
+ fallthrough
+ case 2:
+ block.Encrypt(next, src[base:])
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
+ tbl, next = next, tbl
+ base += 8
+ fallthrough
+ case 1:
+ block.Encrypt(next, src[base:])
+ *(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
+ tbl, next = next, tbl
+ base += 8
+ fallthrough
+ case 0:
+ xorBytes(dst[base:], src[base:], tbl)
+ }
+}
+
+func decrypt16(block cipher.Block, dst, src, buf []byte) {
+ tbl := buf[0:16]
+ next := buf[16:32]
+ block.Encrypt(tbl, initialVector)
+ n := len(src) / 16
+ base := 0
+ repeat := n / 8
+ left := n % 8
+ for i := 0; i < repeat; i++ {
+ s := src[base:][0:128]
+ d := dst[base:][0:128]
+ // 1
+ block.Encrypt(next, s[0:16])
+ xor.Bytes16Align(d[0:16], s[0:16], tbl)
+ // 2
+ block.Encrypt(tbl, s[16:32])
+ xor.Bytes16Align(d[16:32], s[16:32], next)
+ // 3
+ block.Encrypt(next, s[32:48])
+ xor.Bytes16Align(d[32:48], s[32:48], tbl)
+ // 4
+ block.Encrypt(tbl, s[48:64])
+ xor.Bytes16Align(d[48:64], s[48:64], next)
+ // 5
+ block.Encrypt(next, s[64:80])
+ xor.Bytes16Align(d[64:80], s[64:80], tbl)
+ // 6
+ block.Encrypt(tbl, s[80:96])
+ xor.Bytes16Align(d[80:96], s[80:96], next)
+ // 7
+ block.Encrypt(next, s[96:112])
+ xor.Bytes16Align(d[96:112], s[96:112], tbl)
+ // 8
+ block.Encrypt(tbl, s[112:128])
+ xor.Bytes16Align(d[112:128], s[112:128], next)
+ base += 128
+ }
+
+ switch left {
+ case 7:
+ block.Encrypt(next, src[base:])
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ tbl, next = next, tbl
+ base += 16
+ fallthrough
+ case 6:
+ block.Encrypt(next, src[base:])
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ tbl, next = next, tbl
+ base += 16
+ fallthrough
+ case 5:
+ block.Encrypt(next, src[base:])
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ tbl, next = next, tbl
+ base += 16
+ fallthrough
+ case 4:
+ block.Encrypt(next, src[base:])
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ tbl, next = next, tbl
+ base += 16
+ fallthrough
+ case 3:
+ block.Encrypt(next, src[base:])
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ tbl, next = next, tbl
+ base += 16
+ fallthrough
+ case 2:
+ block.Encrypt(next, src[base:])
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ tbl, next = next, tbl
+ base += 16
+ fallthrough
+ case 1:
+ block.Encrypt(next, src[base:])
+ xor.Bytes16Align(dst[base:], src[base:], tbl)
+ tbl, next = next, tbl
+ base += 16
+ fallthrough
+ case 0:
+ xorBytes(dst[base:], src[base:], tbl)
+ }
+}
+
+// per bytes xors
+func xorBytes(dst, a, b []byte) int {
+ n := len(a)
+ if len(b) < n {
+ n = len(b)
+ }
+ if n == 0 {
+ return 0
+ }
+
+ for i := 0; i < n; i++ {
+ dst[i] = a[i] ^ b[i]
+ }
+ return n
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/donate.png b/vendor/github.com/xtaci/kcp-go/v5/donate.png
new file mode 100644
index 0000000..0f353d9
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/donate.png
Binary files differ
diff --git a/vendor/github.com/xtaci/kcp-go/v5/entropy.go b/vendor/github.com/xtaci/kcp-go/v5/entropy.go
new file mode 100644
index 0000000..156c1cd
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/entropy.go
@@ -0,0 +1,52 @@
+package kcp
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/md5"
+ "crypto/rand"
+ "io"
+)
+
+// Entropy defines a entropy source
+type Entropy interface {
+ Init()
+ Fill(nonce []byte)
+}
+
+// nonceMD5 nonce generator for packet header
+type nonceMD5 struct {
+ seed [md5.Size]byte
+}
+
+func (n *nonceMD5) Init() { /*nothing required*/ }
+
+func (n *nonceMD5) Fill(nonce []byte) {
+ if n.seed[0] == 0 { // entropy update
+ io.ReadFull(rand.Reader, n.seed[:])
+ }
+ n.seed = md5.Sum(n.seed[:])
+ copy(nonce, n.seed[:])
+}
+
+// nonceAES128 nonce generator for packet headers
+type nonceAES128 struct {
+ seed [aes.BlockSize]byte
+ block cipher.Block
+}
+
+func (n *nonceAES128) Init() {
+ var key [16]byte //aes-128
+ io.ReadFull(rand.Reader, key[:])
+ io.ReadFull(rand.Reader, n.seed[:])
+ block, _ := aes.NewCipher(key[:])
+ n.block = block
+}
+
+func (n *nonceAES128) Fill(nonce []byte) {
+ if n.seed[0] == 0 { // entropy update
+ io.ReadFull(rand.Reader, n.seed[:])
+ }
+ n.block.Encrypt(n.seed[:], n.seed[:])
+ copy(nonce, n.seed[:])
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/fec.go b/vendor/github.com/xtaci/kcp-go/v5/fec.go
new file mode 100644
index 0000000..0a203ee
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/fec.go
@@ -0,0 +1,381 @@
+package kcp
+
+import (
+ "encoding/binary"
+ "sync/atomic"
+
+ "github.com/klauspost/reedsolomon"
+)
+
+const (
+ fecHeaderSize = 6
+ fecHeaderSizePlus2 = fecHeaderSize + 2 // plus 2B data size
+ typeData = 0xf1
+ typeParity = 0xf2
+ fecExpire = 60000
+ rxFECMulti = 3 // FEC keeps rxFECMulti* (dataShard+parityShard) ordered packets in memory
+)
+
+// fecPacket is a decoded FEC packet
+type fecPacket []byte
+
+func (bts fecPacket) seqid() uint32 { return binary.LittleEndian.Uint32(bts) }
+func (bts fecPacket) flag() uint16 { return binary.LittleEndian.Uint16(bts[4:]) }
+func (bts fecPacket) data() []byte { return bts[6:] }
+
+// fecElement has auxcilliary time field
+type fecElement struct {
+ fecPacket
+ ts uint32
+}
+
+// fecDecoder for decoding incoming packets
+type fecDecoder struct {
+ rxlimit int // queue size limit
+ dataShards int
+ parityShards int
+ shardSize int
+ rx []fecElement // ordered receive queue
+
+ // caches
+ decodeCache [][]byte
+ flagCache []bool
+
+ // zeros
+ zeros []byte
+
+ // RS decoder
+ codec reedsolomon.Encoder
+
+ // auto tune fec parameter
+ autoTune autoTune
+}
+
+func newFECDecoder(dataShards, parityShards int) *fecDecoder {
+ if dataShards <= 0 || parityShards <= 0 {
+ return nil
+ }
+
+ dec := new(fecDecoder)
+ dec.dataShards = dataShards
+ dec.parityShards = parityShards
+ dec.shardSize = dataShards + parityShards
+ dec.rxlimit = rxFECMulti * dec.shardSize
+ codec, err := reedsolomon.New(dataShards, parityShards)
+ if err != nil {
+ return nil
+ }
+ dec.codec = codec
+ dec.decodeCache = make([][]byte, dec.shardSize)
+ dec.flagCache = make([]bool, dec.shardSize)
+ dec.zeros = make([]byte, mtuLimit)
+ return dec
+}
+
+// decode a fec packet
+func (dec *fecDecoder) decode(in fecPacket) (recovered [][]byte) {
+ // sample to auto FEC tuner
+ if in.flag() == typeData {
+ dec.autoTune.Sample(true, in.seqid())
+ } else {
+ dec.autoTune.Sample(false, in.seqid())
+ }
+
+ // check if FEC parameters is out of sync
+ var shouldTune bool
+ if int(in.seqid())%dec.shardSize < dec.dataShards {
+ if in.flag() != typeData { // expect typeData
+ shouldTune = true
+ }
+ } else {
+ if in.flag() != typeParity {
+ shouldTune = true
+ }
+ }
+
+ if shouldTune {
+ autoDS := dec.autoTune.FindPeriod(true)
+ autoPS := dec.autoTune.FindPeriod(false)
+
+ // edges found, we can tune parameters now
+ if autoDS > 0 && autoPS > 0 && autoDS < 256 && autoPS < 256 {
+ // and make sure it's different
+ if autoDS != dec.dataShards || autoPS != dec.parityShards {
+ dec.dataShards = autoDS
+ dec.parityShards = autoPS
+ dec.shardSize = autoDS + autoPS
+ dec.rxlimit = rxFECMulti * dec.shardSize
+ codec, err := reedsolomon.New(autoDS, autoPS)
+ if err != nil {
+ return nil
+ }
+ dec.codec = codec
+ dec.decodeCache = make([][]byte, dec.shardSize)
+ dec.flagCache = make([]bool, dec.shardSize)
+ //log.Println("autotune to :", dec.dataShards, dec.parityShards)
+ }
+ }
+ }
+
+ // insertion
+ n := len(dec.rx) - 1
+ insertIdx := 0
+ for i := n; i >= 0; i-- {
+ if in.seqid() == dec.rx[i].seqid() { // de-duplicate
+ return nil
+ } else if _itimediff(in.seqid(), dec.rx[i].seqid()) > 0 { // insertion
+ insertIdx = i + 1
+ break
+ }
+ }
+
+ // make a copy
+ pkt := fecPacket(xmitBuf.Get().([]byte)[:len(in)])
+ copy(pkt, in)
+ elem := fecElement{pkt, currentMs()}
+
+ // insert into ordered rx queue
+ if insertIdx == n+1 {
+ dec.rx = append(dec.rx, elem)
+ } else {
+ dec.rx = append(dec.rx, fecElement{})
+ copy(dec.rx[insertIdx+1:], dec.rx[insertIdx:]) // shift right
+ dec.rx[insertIdx] = elem
+ }
+
+ // shard range for current packet
+ shardBegin := pkt.seqid() - pkt.seqid()%uint32(dec.shardSize)
+ shardEnd := shardBegin + uint32(dec.shardSize) - 1
+
+ // max search range in ordered queue for current shard
+ searchBegin := insertIdx - int(pkt.seqid()%uint32(dec.shardSize))
+ if searchBegin < 0 {
+ searchBegin = 0
+ }
+ searchEnd := searchBegin + dec.shardSize - 1
+ if searchEnd >= len(dec.rx) {
+ searchEnd = len(dec.rx) - 1
+ }
+
+ // re-construct datashards
+ if searchEnd-searchBegin+1 >= dec.dataShards {
+ var numshard, numDataShard, first, maxlen int
+
+ // zero caches
+ shards := dec.decodeCache
+ shardsflag := dec.flagCache
+ for k := range dec.decodeCache {
+ shards[k] = nil
+ shardsflag[k] = false
+ }
+
+ // shard assembly
+ for i := searchBegin; i <= searchEnd; i++ {
+ seqid := dec.rx[i].seqid()
+ if _itimediff(seqid, shardEnd) > 0 {
+ break
+ } else if _itimediff(seqid, shardBegin) >= 0 {
+ shards[seqid%uint32(dec.shardSize)] = dec.rx[i].data()
+ shardsflag[seqid%uint32(dec.shardSize)] = true
+ numshard++
+ if dec.rx[i].flag() == typeData {
+ numDataShard++
+ }
+ if numshard == 1 {
+ first = i
+ }
+ if len(dec.rx[i].data()) > maxlen {
+ maxlen = len(dec.rx[i].data())
+ }
+ }
+ }
+
+ if numDataShard == dec.dataShards {
+ // case 1: no loss on data shards
+ dec.rx = dec.freeRange(first, numshard, dec.rx)
+ } else if numshard >= dec.dataShards {
+ // case 2: loss on data shards, but it's recoverable from parity shards
+ for k := range shards {
+ if shards[k] != nil {
+ dlen := len(shards[k])
+ shards[k] = shards[k][:maxlen]
+ copy(shards[k][dlen:], dec.zeros)
+ } else if k < dec.dataShards {
+ shards[k] = xmitBuf.Get().([]byte)[:0]
+ }
+ }
+ if err := dec.codec.ReconstructData(shards); err == nil {
+ for k := range shards[:dec.dataShards] {
+ if !shardsflag[k] {
+ // recovered data should be recycled
+ recovered = append(recovered, shards[k])
+ }
+ }
+ }
+ dec.rx = dec.freeRange(first, numshard, dec.rx)
+ }
+ }
+
+ // keep rxlimit
+ if len(dec.rx) > dec.rxlimit {
+ if dec.rx[0].flag() == typeData { // track the unrecoverable data
+ atomic.AddUint64(&DefaultSnmp.FECShortShards, 1)
+ }
+ dec.rx = dec.freeRange(0, 1, dec.rx)
+ }
+
+ // timeout policy
+ current := currentMs()
+ numExpired := 0
+ for k := range dec.rx {
+ if _itimediff(current, dec.rx[k].ts) > fecExpire {
+ numExpired++
+ continue
+ }
+ break
+ }
+ if numExpired > 0 {
+ dec.rx = dec.freeRange(0, numExpired, dec.rx)
+ }
+ return
+}
+
+// free a range of fecPacket
+func (dec *fecDecoder) freeRange(first, n int, q []fecElement) []fecElement {
+ for i := first; i < first+n; i++ { // recycle buffer
+ xmitBuf.Put([]byte(q[i].fecPacket))
+ }
+
+ if first == 0 && n < cap(q)/2 {
+ return q[n:]
+ }
+ copy(q[first:], q[first+n:])
+ return q[:len(q)-n]
+}
+
+// release all segments back to xmitBuf
+func (dec *fecDecoder) release() {
+ if n := len(dec.rx); n > 0 {
+ dec.rx = dec.freeRange(0, n, dec.rx)
+ }
+}
+
+type (
+ // fecEncoder for encoding outgoing packets
+ fecEncoder struct {
+ dataShards int
+ parityShards int
+ shardSize int
+ paws uint32 // Protect Against Wrapped Sequence numbers
+ next uint32 // next seqid
+
+ shardCount int // count the number of datashards collected
+ maxSize int // track maximum data length in datashard
+
+ headerOffset int // FEC header offset
+ payloadOffset int // FEC payload offset
+
+ // caches
+ shardCache [][]byte
+ encodeCache [][]byte
+
+ // zeros
+ zeros []byte
+
+ // RS encoder
+ codec reedsolomon.Encoder
+ }
+)
+
+func newFECEncoder(dataShards, parityShards, offset int) *fecEncoder {
+ if dataShards <= 0 || parityShards <= 0 {
+ return nil
+ }
+ enc := new(fecEncoder)
+ enc.dataShards = dataShards
+ enc.parityShards = parityShards
+ enc.shardSize = dataShards + parityShards
+ enc.paws = 0xffffffff / uint32(enc.shardSize) * uint32(enc.shardSize)
+ enc.headerOffset = offset
+ enc.payloadOffset = enc.headerOffset + fecHeaderSize
+
+ codec, err := reedsolomon.New(dataShards, parityShards)
+ if err != nil {
+ return nil
+ }
+ enc.codec = codec
+
+ // caches
+ enc.encodeCache = make([][]byte, enc.shardSize)
+ enc.shardCache = make([][]byte, enc.shardSize)
+ for k := range enc.shardCache {
+ enc.shardCache[k] = make([]byte, mtuLimit)
+ }
+ enc.zeros = make([]byte, mtuLimit)
+ return enc
+}
+
+// encodes the packet, outputs parity shards if we have collected quorum datashards
+// notice: the contents of 'ps' will be re-written in successive calling
+func (enc *fecEncoder) encode(b []byte) (ps [][]byte) {
+ // The header format:
+ // | FEC SEQID(4B) | FEC TYPE(2B) | SIZE (2B) | PAYLOAD(SIZE-2) |
+ // |<-headerOffset |<-payloadOffset
+ enc.markData(b[enc.headerOffset:])
+ binary.LittleEndian.PutUint16(b[enc.payloadOffset:], uint16(len(b[enc.payloadOffset:])))
+
+ // copy data from payloadOffset to fec shard cache
+ sz := len(b)
+ enc.shardCache[enc.shardCount] = enc.shardCache[enc.shardCount][:sz]
+ copy(enc.shardCache[enc.shardCount][enc.payloadOffset:], b[enc.payloadOffset:])
+ enc.shardCount++
+
+ // track max datashard length
+ if sz > enc.maxSize {
+ enc.maxSize = sz
+ }
+
+ // Generation of Reed-Solomon Erasure Code
+ if enc.shardCount == enc.dataShards {
+ // fill '0' into the tail of each datashard
+ for i := 0; i < enc.dataShards; i++ {
+ shard := enc.shardCache[i]
+ slen := len(shard)
+ copy(shard[slen:enc.maxSize], enc.zeros)
+ }
+
+ // construct equal-sized slice with stripped header
+ cache := enc.encodeCache
+ for k := range cache {
+ cache[k] = enc.shardCache[k][enc.payloadOffset:enc.maxSize]
+ }
+
+ // encoding
+ if err := enc.codec.Encode(cache); err == nil {
+ ps = enc.shardCache[enc.dataShards:]
+ for k := range ps {
+ enc.markParity(ps[k][enc.headerOffset:])
+ ps[k] = ps[k][:enc.maxSize]
+ }
+ }
+
+ // counters resetting
+ enc.shardCount = 0
+ enc.maxSize = 0
+ }
+
+ return
+}
+
+func (enc *fecEncoder) markData(data []byte) {
+ binary.LittleEndian.PutUint32(data, enc.next)
+ binary.LittleEndian.PutUint16(data[4:], typeData)
+ enc.next++
+}
+
+func (enc *fecEncoder) markParity(data []byte) {
+ binary.LittleEndian.PutUint32(data, enc.next)
+ binary.LittleEndian.PutUint16(data[4:], typeParity)
+ // sequence wrap will only happen at parity shard
+ enc.next = (enc.next + 1) % enc.paws
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/flame.png b/vendor/github.com/xtaci/kcp-go/v5/flame.png
new file mode 100644
index 0000000..672f649
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/flame.png
Binary files differ
diff --git a/vendor/github.com/xtaci/kcp-go/v5/frame.png b/vendor/github.com/xtaci/kcp-go/v5/frame.png
new file mode 100644
index 0000000..0b0aefd
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/frame.png
Binary files differ
diff --git a/vendor/github.com/xtaci/kcp-go/v5/go.mod b/vendor/github.com/xtaci/kcp-go/v5/go.mod
new file mode 100644
index 0000000..ff51020
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/go.mod
@@ -0,0 +1,20 @@
+module github.com/xtaci/kcp-go/v5
+
+require (
+ github.com/klauspost/cpuid v1.3.1 // indirect
+ github.com/klauspost/reedsolomon v1.9.9
+ github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
+ github.com/pkg/errors v0.9.1
+ github.com/stretchr/testify v1.6.1
+ github.com/templexxx/cpu v0.0.7 // indirect
+ github.com/templexxx/xorsimd v0.4.1
+ github.com/tjfoc/gmsm v1.3.2
+ github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae
+ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
+ golang.org/x/net v0.0.0-20200707034311-ab3426394381
+ golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 // indirect
+ golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123 // indirect
+ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
+)
+
+go 1.13
diff --git a/vendor/github.com/xtaci/kcp-go/v5/go.sum b/vendor/github.com/xtaci/kcp-go/v5/go.sum
new file mode 100644
index 0000000..db81c71
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/go.sum
@@ -0,0 +1,69 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+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/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
+github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
+github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
+github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
+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/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
+github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
+github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
+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/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=
+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 h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
+golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+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 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs=
+golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200425043458-8463f397d07c h1:iHhCR0b26amDCiiO+kBguKZom9aMF+NrFxh9zeKR/XU=
+golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123 h1:4JSJPND/+4555t1HfXYF4UEqDqiSKCgeV0+hbA8hMs4=
+golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/vendor/github.com/xtaci/kcp-go/v5/kcp-go.png b/vendor/github.com/xtaci/kcp-go/v5/kcp-go.png
new file mode 100644
index 0000000..151b7c4
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/kcp-go.png
Binary files differ
diff --git a/vendor/github.com/xtaci/kcp-go/v5/kcp.go b/vendor/github.com/xtaci/kcp-go/v5/kcp.go
new file mode 100644
index 0000000..0c6c304
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/kcp.go
@@ -0,0 +1,1080 @@
+package kcp
+
+import (
+ "encoding/binary"
+ "sync/atomic"
+ "time"
+)
+
+const (
+ IKCP_RTO_NDL = 30 // no delay min rto
+ IKCP_RTO_MIN = 100 // normal min rto
+ IKCP_RTO_DEF = 200
+ IKCP_RTO_MAX = 60000
+ IKCP_CMD_PUSH = 81 // cmd: push data
+ IKCP_CMD_ACK = 82 // cmd: ack
+ IKCP_CMD_WASK = 83 // cmd: window probe (ask)
+ IKCP_CMD_WINS = 84 // cmd: window size (tell)
+ IKCP_ASK_SEND = 1 // need to send IKCP_CMD_WASK
+ IKCP_ASK_TELL = 2 // need to send IKCP_CMD_WINS
+ IKCP_WND_SND = 32
+ IKCP_WND_RCV = 32
+ IKCP_MTU_DEF = 1400
+ IKCP_ACK_FAST = 3
+ IKCP_INTERVAL = 100
+ IKCP_OVERHEAD = 24
+ IKCP_DEADLINK = 20
+ IKCP_THRESH_INIT = 2
+ IKCP_THRESH_MIN = 2
+ IKCP_PROBE_INIT = 7000 // 7 secs to probe window size
+ IKCP_PROBE_LIMIT = 120000 // up to 120 secs to probe window
+ IKCP_SN_OFFSET = 12
+)
+
+// monotonic reference time point
+var refTime time.Time = time.Now()
+
+// currentMs returns current elapsed monotonic milliseconds since program startup
+func currentMs() uint32 { return uint32(time.Since(refTime) / time.Millisecond) }
+
+// output_callback is a prototype which ought capture conn and call conn.Write
+type output_callback func(buf []byte, size int)
+
+/* encode 8 bits unsigned int */
+func ikcp_encode8u(p []byte, c byte) []byte {
+ p[0] = c
+ return p[1:]
+}
+
+/* decode 8 bits unsigned int */
+func ikcp_decode8u(p []byte, c *byte) []byte {
+ *c = p[0]
+ return p[1:]
+}
+
+/* encode 16 bits unsigned int (lsb) */
+func ikcp_encode16u(p []byte, w uint16) []byte {
+ binary.LittleEndian.PutUint16(p, w)
+ return p[2:]
+}
+
+/* decode 16 bits unsigned int (lsb) */
+func ikcp_decode16u(p []byte, w *uint16) []byte {
+ *w = binary.LittleEndian.Uint16(p)
+ return p[2:]
+}
+
+/* encode 32 bits unsigned int (lsb) */
+func ikcp_encode32u(p []byte, l uint32) []byte {
+ binary.LittleEndian.PutUint32(p, l)
+ return p[4:]
+}
+
+/* decode 32 bits unsigned int (lsb) */
+func ikcp_decode32u(p []byte, l *uint32) []byte {
+ *l = binary.LittleEndian.Uint32(p)
+ return p[4:]
+}
+
+func _imin_(a, b uint32) uint32 {
+ if a <= b {
+ return a
+ }
+ return b
+}
+
+func _imax_(a, b uint32) uint32 {
+ if a >= b {
+ return a
+ }
+ return b
+}
+
+func _ibound_(lower, middle, upper uint32) uint32 {
+ return _imin_(_imax_(lower, middle), upper)
+}
+
+func _itimediff(later, earlier uint32) int32 {
+ return (int32)(later - earlier)
+}
+
+// segment defines a KCP segment
+type segment struct {
+ conv uint32
+ cmd uint8
+ frg uint8
+ wnd uint16
+ ts uint32
+ sn uint32
+ una uint32
+ rto uint32
+ xmit uint32
+ resendts uint32
+ fastack uint32
+ acked uint32 // mark if the seg has acked
+ data []byte
+}
+
+// encode a segment into buffer
+func (seg *segment) encode(ptr []byte) []byte {
+ ptr = ikcp_encode32u(ptr, seg.conv)
+ ptr = ikcp_encode8u(ptr, seg.cmd)
+ ptr = ikcp_encode8u(ptr, seg.frg)
+ ptr = ikcp_encode16u(ptr, seg.wnd)
+ ptr = ikcp_encode32u(ptr, seg.ts)
+ ptr = ikcp_encode32u(ptr, seg.sn)
+ ptr = ikcp_encode32u(ptr, seg.una)
+ ptr = ikcp_encode32u(ptr, uint32(len(seg.data)))
+ atomic.AddUint64(&DefaultSnmp.OutSegs, 1)
+ return ptr
+}
+
+// KCP defines a single KCP connection
+type KCP struct {
+ conv, mtu, mss, state uint32
+ snd_una, snd_nxt, rcv_nxt uint32
+ ssthresh uint32
+ rx_rttvar, rx_srtt int32
+ rx_rto, rx_minrto uint32
+ snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe uint32
+ interval, ts_flush uint32
+ nodelay, updated uint32
+ ts_probe, probe_wait uint32
+ dead_link, incr uint32
+
+ fastresend int32
+ nocwnd, stream int32
+
+ snd_queue []segment
+ rcv_queue []segment
+ snd_buf []segment
+ rcv_buf []segment
+
+ acklist []ackItem
+
+ buffer []byte
+ reserved int
+ output output_callback
+}
+
+type ackItem struct {
+ sn uint32
+ ts uint32
+}
+
+// NewKCP create a new kcp state machine
+//
+// 'conv' must be equal in the connection peers, or else data will be silently rejected.
+//
+// 'output' function will be called whenever these is data to be sent on wire.
+func NewKCP(conv uint32, output output_callback) *KCP {
+ kcp := new(KCP)
+ kcp.conv = conv
+ kcp.snd_wnd = IKCP_WND_SND
+ kcp.rcv_wnd = IKCP_WND_RCV
+ kcp.rmt_wnd = IKCP_WND_RCV
+ kcp.mtu = IKCP_MTU_DEF
+ kcp.mss = kcp.mtu - IKCP_OVERHEAD
+ kcp.buffer = make([]byte, kcp.mtu)
+ kcp.rx_rto = IKCP_RTO_DEF
+ kcp.rx_minrto = IKCP_RTO_MIN
+ kcp.interval = IKCP_INTERVAL
+ kcp.ts_flush = IKCP_INTERVAL
+ kcp.ssthresh = IKCP_THRESH_INIT
+ kcp.dead_link = IKCP_DEADLINK
+ kcp.output = output
+ return kcp
+}
+
+// newSegment creates a KCP segment
+func (kcp *KCP) newSegment(size int) (seg segment) {
+ seg.data = xmitBuf.Get().([]byte)[:size]
+ return
+}
+
+// delSegment recycles a KCP segment
+func (kcp *KCP) delSegment(seg *segment) {
+ if seg.data != nil {
+ xmitBuf.Put(seg.data)
+ seg.data = nil
+ }
+}
+
+// ReserveBytes keeps n bytes untouched from the beginning of the buffer,
+// the output_callback function should be aware of this.
+//
+// Return false if n >= mss
+func (kcp *KCP) ReserveBytes(n int) bool {
+ if n >= int(kcp.mtu-IKCP_OVERHEAD) || n < 0 {
+ return false
+ }
+ kcp.reserved = n
+ kcp.mss = kcp.mtu - IKCP_OVERHEAD - uint32(n)
+ return true
+}
+
+// PeekSize checks the size of next message in the recv queue
+func (kcp *KCP) PeekSize() (length int) {
+ if len(kcp.rcv_queue) == 0 {
+ return -1
+ }
+
+ seg := &kcp.rcv_queue[0]
+ if seg.frg == 0 {
+ return len(seg.data)
+ }
+
+ if len(kcp.rcv_queue) < int(seg.frg+1) {
+ return -1
+ }
+
+ for k := range kcp.rcv_queue {
+ seg := &kcp.rcv_queue[k]
+ length += len(seg.data)
+ if seg.frg == 0 {
+ break
+ }
+ }
+ return
+}
+
+// Receive data from kcp state machine
+//
+// Return number of bytes read.
+//
+// Return -1 when there is no readable data.
+//
+// Return -2 if len(buffer) is smaller than kcp.PeekSize().
+func (kcp *KCP) Recv(buffer []byte) (n int) {
+ peeksize := kcp.PeekSize()
+ if peeksize < 0 {
+ return -1
+ }
+
+ if peeksize > len(buffer) {
+ return -2
+ }
+
+ var fast_recover bool
+ if len(kcp.rcv_queue) >= int(kcp.rcv_wnd) {
+ fast_recover = true
+ }
+
+ // merge fragment
+ count := 0
+ for k := range kcp.rcv_queue {
+ seg := &kcp.rcv_queue[k]
+ copy(buffer, seg.data)
+ buffer = buffer[len(seg.data):]
+ n += len(seg.data)
+ count++
+ kcp.delSegment(seg)
+ if seg.frg == 0 {
+ break
+ }
+ }
+ if count > 0 {
+ kcp.rcv_queue = kcp.remove_front(kcp.rcv_queue, count)
+ }
+
+ // move available data from rcv_buf -> rcv_queue
+ count = 0
+ for k := range kcp.rcv_buf {
+ seg := &kcp.rcv_buf[k]
+ if seg.sn == kcp.rcv_nxt && len(kcp.rcv_queue)+count < int(kcp.rcv_wnd) {
+ kcp.rcv_nxt++
+ count++
+ } else {
+ break
+ }
+ }
+
+ if count > 0 {
+ kcp.rcv_queue = append(kcp.rcv_queue, kcp.rcv_buf[:count]...)
+ kcp.rcv_buf = kcp.remove_front(kcp.rcv_buf, count)
+ }
+
+ // fast recover
+ if len(kcp.rcv_queue) < int(kcp.rcv_wnd) && fast_recover {
+ // ready to send back IKCP_CMD_WINS in ikcp_flush
+ // tell remote my window size
+ kcp.probe |= IKCP_ASK_TELL
+ }
+ return
+}
+
+// Send is user/upper level send, returns below zero for error
+func (kcp *KCP) Send(buffer []byte) int {
+ var count int
+ if len(buffer) == 0 {
+ return -1
+ }
+
+ // append to previous segment in streaming mode (if possible)
+ if kcp.stream != 0 {
+ n := len(kcp.snd_queue)
+ if n > 0 {
+ seg := &kcp.snd_queue[n-1]
+ if len(seg.data) < int(kcp.mss) {
+ capacity := int(kcp.mss) - len(seg.data)
+ extend := capacity
+ if len(buffer) < capacity {
+ extend = len(buffer)
+ }
+
+ // grow slice, the underlying cap is guaranteed to
+ // be larger than kcp.mss
+ oldlen := len(seg.data)
+ seg.data = seg.data[:oldlen+extend]
+ copy(seg.data[oldlen:], buffer)
+ buffer = buffer[extend:]
+ }
+ }
+
+ if len(buffer) == 0 {
+ return 0
+ }
+ }
+
+ if len(buffer) <= int(kcp.mss) {
+ count = 1
+ } else {
+ count = (len(buffer) + int(kcp.mss) - 1) / int(kcp.mss)
+ }
+
+ if count > 255 {
+ return -2
+ }
+
+ if count == 0 {
+ count = 1
+ }
+
+ for i := 0; i < count; i++ {
+ var size int
+ if len(buffer) > int(kcp.mss) {
+ size = int(kcp.mss)
+ } else {
+ size = len(buffer)
+ }
+ seg := kcp.newSegment(size)
+ copy(seg.data, buffer[:size])
+ if kcp.stream == 0 { // message mode
+ seg.frg = uint8(count - i - 1)
+ } else { // stream mode
+ seg.frg = 0
+ }
+ kcp.snd_queue = append(kcp.snd_queue, seg)
+ buffer = buffer[size:]
+ }
+ return 0
+}
+
+func (kcp *KCP) update_ack(rtt int32) {
+ // https://tools.ietf.org/html/rfc6298
+ var rto uint32
+ if kcp.rx_srtt == 0 {
+ kcp.rx_srtt = rtt
+ kcp.rx_rttvar = rtt >> 1
+ } else {
+ delta := rtt - kcp.rx_srtt
+ kcp.rx_srtt += delta >> 3
+ if delta < 0 {
+ delta = -delta
+ }
+ if rtt < kcp.rx_srtt-kcp.rx_rttvar {
+ // if the new RTT sample is below the bottom of the range of
+ // what an RTT measurement is expected to be.
+ // give an 8x reduced weight versus its normal weighting
+ kcp.rx_rttvar += (delta - kcp.rx_rttvar) >> 5
+ } else {
+ kcp.rx_rttvar += (delta - kcp.rx_rttvar) >> 2
+ }
+ }
+ rto = uint32(kcp.rx_srtt) + _imax_(kcp.interval, uint32(kcp.rx_rttvar)<<2)
+ kcp.rx_rto = _ibound_(kcp.rx_minrto, rto, IKCP_RTO_MAX)
+}
+
+func (kcp *KCP) shrink_buf() {
+ if len(kcp.snd_buf) > 0 {
+ seg := &kcp.snd_buf[0]
+ kcp.snd_una = seg.sn
+ } else {
+ kcp.snd_una = kcp.snd_nxt
+ }
+}
+
+func (kcp *KCP) parse_ack(sn uint32) {
+ if _itimediff(sn, kcp.snd_una) < 0 || _itimediff(sn, kcp.snd_nxt) >= 0 {
+ return
+ }
+
+ for k := range kcp.snd_buf {
+ seg := &kcp.snd_buf[k]
+ if sn == seg.sn {
+ // mark and free space, but leave the segment here,
+ // and wait until `una` to delete this, then we don't
+ // have to shift the segments behind forward,
+ // which is an expensive operation for large window
+ seg.acked = 1
+ kcp.delSegment(seg)
+ break
+ }
+ if _itimediff(sn, seg.sn) < 0 {
+ break
+ }
+ }
+}
+
+func (kcp *KCP) parse_fastack(sn, ts uint32) {
+ if _itimediff(sn, kcp.snd_una) < 0 || _itimediff(sn, kcp.snd_nxt) >= 0 {
+ return
+ }
+
+ for k := range kcp.snd_buf {
+ seg := &kcp.snd_buf[k]
+ if _itimediff(sn, seg.sn) < 0 {
+ break
+ } else if sn != seg.sn && _itimediff(seg.ts, ts) <= 0 {
+ seg.fastack++
+ }
+ }
+}
+
+func (kcp *KCP) parse_una(una uint32) int {
+ count := 0
+ for k := range kcp.snd_buf {
+ seg := &kcp.snd_buf[k]
+ if _itimediff(una, seg.sn) > 0 {
+ kcp.delSegment(seg)
+ count++
+ } else {
+ break
+ }
+ }
+ if count > 0 {
+ kcp.snd_buf = kcp.remove_front(kcp.snd_buf, count)
+ }
+ return count
+}
+
+// ack append
+func (kcp *KCP) ack_push(sn, ts uint32) {
+ kcp.acklist = append(kcp.acklist, ackItem{sn, ts})
+}
+
+// returns true if data has repeated
+func (kcp *KCP) parse_data(newseg segment) bool {
+ sn := newseg.sn
+ if _itimediff(sn, kcp.rcv_nxt+kcp.rcv_wnd) >= 0 ||
+ _itimediff(sn, kcp.rcv_nxt) < 0 {
+ return true
+ }
+
+ n := len(kcp.rcv_buf) - 1
+ insert_idx := 0
+ repeat := false
+ for i := n; i >= 0; i-- {
+ seg := &kcp.rcv_buf[i]
+ if seg.sn == sn {
+ repeat = true
+ break
+ }
+ if _itimediff(sn, seg.sn) > 0 {
+ insert_idx = i + 1
+ break
+ }
+ }
+
+ if !repeat {
+ // replicate the content if it's new
+ dataCopy := xmitBuf.Get().([]byte)[:len(newseg.data)]
+ copy(dataCopy, newseg.data)
+ newseg.data = dataCopy
+
+ if insert_idx == n+1 {
+ kcp.rcv_buf = append(kcp.rcv_buf, newseg)
+ } else {
+ kcp.rcv_buf = append(kcp.rcv_buf, segment{})
+ copy(kcp.rcv_buf[insert_idx+1:], kcp.rcv_buf[insert_idx:])
+ kcp.rcv_buf[insert_idx] = newseg
+ }
+ }
+
+ // move available data from rcv_buf -> rcv_queue
+ count := 0
+ for k := range kcp.rcv_buf {
+ seg := &kcp.rcv_buf[k]
+ if seg.sn == kcp.rcv_nxt && len(kcp.rcv_queue)+count < int(kcp.rcv_wnd) {
+ kcp.rcv_nxt++
+ count++
+ } else {
+ break
+ }
+ }
+ if count > 0 {
+ kcp.rcv_queue = append(kcp.rcv_queue, kcp.rcv_buf[:count]...)
+ kcp.rcv_buf = kcp.remove_front(kcp.rcv_buf, count)
+ }
+
+ return repeat
+}
+
+// Input a packet into kcp state machine.
+//
+// 'regular' indicates it's a real data packet from remote, and it means it's not generated from ReedSolomon
+// codecs.
+//
+// 'ackNoDelay' will trigger immediate ACK, but surely it will not be efficient in bandwidth
+func (kcp *KCP) Input(data []byte, regular, ackNoDelay bool) int {
+ snd_una := kcp.snd_una
+ if len(data) < IKCP_OVERHEAD {
+ return -1
+ }
+
+ var latest uint32 // the latest ack packet
+ var flag int
+ var inSegs uint64
+ var windowSlides bool
+
+ for {
+ var ts, sn, length, una, conv uint32
+ var wnd uint16
+ var cmd, frg uint8
+
+ if len(data) < int(IKCP_OVERHEAD) {
+ break
+ }
+
+ data = ikcp_decode32u(data, &conv)
+ if conv != kcp.conv {
+ return -1
+ }
+
+ data = ikcp_decode8u(data, &cmd)
+ data = ikcp_decode8u(data, &frg)
+ data = ikcp_decode16u(data, &wnd)
+ data = ikcp_decode32u(data, &ts)
+ data = ikcp_decode32u(data, &sn)
+ data = ikcp_decode32u(data, &una)
+ data = ikcp_decode32u(data, &length)
+ if len(data) < int(length) {
+ return -2
+ }
+
+ if cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK &&
+ cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS {
+ return -3
+ }
+
+ // only trust window updates from regular packets. i.e: latest update
+ if regular {
+ kcp.rmt_wnd = uint32(wnd)
+ }
+ if kcp.parse_una(una) > 0 {
+ windowSlides = true
+ }
+ kcp.shrink_buf()
+
+ if cmd == IKCP_CMD_ACK {
+ kcp.parse_ack(sn)
+ kcp.parse_fastack(sn, ts)
+ flag |= 1
+ latest = ts
+ } else if cmd == IKCP_CMD_PUSH {
+ repeat := true
+ if _itimediff(sn, kcp.rcv_nxt+kcp.rcv_wnd) < 0 {
+ kcp.ack_push(sn, ts)
+ if _itimediff(sn, kcp.rcv_nxt) >= 0 {
+ var seg segment
+ seg.conv = conv
+ seg.cmd = cmd
+ seg.frg = frg
+ seg.wnd = wnd
+ seg.ts = ts
+ seg.sn = sn
+ seg.una = una
+ seg.data = data[:length] // delayed data copying
+ repeat = kcp.parse_data(seg)
+ }
+ }
+ if regular && repeat {
+ atomic.AddUint64(&DefaultSnmp.RepeatSegs, 1)
+ }
+ } else if cmd == IKCP_CMD_WASK {
+ // ready to send back IKCP_CMD_WINS in Ikcp_flush
+ // tell remote my window size
+ kcp.probe |= IKCP_ASK_TELL
+ } else if cmd == IKCP_CMD_WINS {
+ // do nothing
+ } else {
+ return -3
+ }
+
+ inSegs++
+ data = data[length:]
+ }
+ atomic.AddUint64(&DefaultSnmp.InSegs, inSegs)
+
+ // update rtt with the latest ts
+ // ignore the FEC packet
+ if flag != 0 && regular {
+ current := currentMs()
+ if _itimediff(current, latest) >= 0 {
+ kcp.update_ack(_itimediff(current, latest))
+ }
+ }
+
+ // cwnd update when packet arrived
+ if kcp.nocwnd == 0 {
+ if _itimediff(kcp.snd_una, snd_una) > 0 {
+ if kcp.cwnd < kcp.rmt_wnd {
+ mss := kcp.mss
+ if kcp.cwnd < kcp.ssthresh {
+ kcp.cwnd++
+ kcp.incr += mss
+ } else {
+ if kcp.incr < mss {
+ kcp.incr = mss
+ }
+ kcp.incr += (mss*mss)/kcp.incr + (mss / 16)
+ if (kcp.cwnd+1)*mss <= kcp.incr {
+ if mss > 0 {
+ kcp.cwnd = (kcp.incr + mss - 1) / mss
+ } else {
+ kcp.cwnd = kcp.incr + mss - 1
+ }
+ }
+ }
+ if kcp.cwnd > kcp.rmt_wnd {
+ kcp.cwnd = kcp.rmt_wnd
+ kcp.incr = kcp.rmt_wnd * mss
+ }
+ }
+ }
+ }
+
+ if windowSlides { // if window has slided, flush
+ kcp.flush(false)
+ } else if ackNoDelay && len(kcp.acklist) > 0 { // ack immediately
+ kcp.flush(true)
+ }
+ return 0
+}
+
+func (kcp *KCP) wnd_unused() uint16 {
+ if len(kcp.rcv_queue) < int(kcp.rcv_wnd) {
+ return uint16(int(kcp.rcv_wnd) - len(kcp.rcv_queue))
+ }
+ return 0
+}
+
+// flush pending data
+func (kcp *KCP) flush(ackOnly bool) uint32 {
+ var seg segment
+ seg.conv = kcp.conv
+ seg.cmd = IKCP_CMD_ACK
+ seg.wnd = kcp.wnd_unused()
+ seg.una = kcp.rcv_nxt
+
+ buffer := kcp.buffer
+ ptr := buffer[kcp.reserved:] // keep n bytes untouched
+
+ // makeSpace makes room for writing
+ makeSpace := func(space int) {
+ size := len(buffer) - len(ptr)
+ if size+space > int(kcp.mtu) {
+ kcp.output(buffer, size)
+ ptr = buffer[kcp.reserved:]
+ }
+ }
+
+ // flush bytes in buffer if there is any
+ flushBuffer := func() {
+ size := len(buffer) - len(ptr)
+ if size > kcp.reserved {
+ kcp.output(buffer, size)
+ }
+ }
+
+ // flush acknowledges
+ for i, ack := range kcp.acklist {
+ makeSpace(IKCP_OVERHEAD)
+ // filter jitters caused by bufferbloat
+ if _itimediff(ack.sn, kcp.rcv_nxt) >= 0 || len(kcp.acklist)-1 == i {
+ seg.sn, seg.ts = ack.sn, ack.ts
+ ptr = seg.encode(ptr)
+ }
+ }
+ kcp.acklist = kcp.acklist[0:0]
+
+ if ackOnly { // flash remain ack segments
+ flushBuffer()
+ return kcp.interval
+ }
+
+ // probe window size (if remote window size equals zero)
+ if kcp.rmt_wnd == 0 {
+ current := currentMs()
+ if kcp.probe_wait == 0 {
+ kcp.probe_wait = IKCP_PROBE_INIT
+ kcp.ts_probe = current + kcp.probe_wait
+ } else {
+ if _itimediff(current, kcp.ts_probe) >= 0 {
+ if kcp.probe_wait < IKCP_PROBE_INIT {
+ kcp.probe_wait = IKCP_PROBE_INIT
+ }
+ kcp.probe_wait += kcp.probe_wait / 2
+ if kcp.probe_wait > IKCP_PROBE_LIMIT {
+ kcp.probe_wait = IKCP_PROBE_LIMIT
+ }
+ kcp.ts_probe = current + kcp.probe_wait
+ kcp.probe |= IKCP_ASK_SEND
+ }
+ }
+ } else {
+ kcp.ts_probe = 0
+ kcp.probe_wait = 0
+ }
+
+ // flush window probing commands
+ if (kcp.probe & IKCP_ASK_SEND) != 0 {
+ seg.cmd = IKCP_CMD_WASK
+ makeSpace(IKCP_OVERHEAD)
+ ptr = seg.encode(ptr)
+ }
+
+ // flush window probing commands
+ if (kcp.probe & IKCP_ASK_TELL) != 0 {
+ seg.cmd = IKCP_CMD_WINS
+ makeSpace(IKCP_OVERHEAD)
+ ptr = seg.encode(ptr)
+ }
+
+ kcp.probe = 0
+
+ // calculate window size
+ cwnd := _imin_(kcp.snd_wnd, kcp.rmt_wnd)
+ if kcp.nocwnd == 0 {
+ cwnd = _imin_(kcp.cwnd, cwnd)
+ }
+
+ // sliding window, controlled by snd_nxt && sna_una+cwnd
+ newSegsCount := 0
+ for k := range kcp.snd_queue {
+ if _itimediff(kcp.snd_nxt, kcp.snd_una+cwnd) >= 0 {
+ break
+ }
+ newseg := kcp.snd_queue[k]
+ newseg.conv = kcp.conv
+ newseg.cmd = IKCP_CMD_PUSH
+ newseg.sn = kcp.snd_nxt
+ kcp.snd_buf = append(kcp.snd_buf, newseg)
+ kcp.snd_nxt++
+ newSegsCount++
+ }
+ if newSegsCount > 0 {
+ kcp.snd_queue = kcp.remove_front(kcp.snd_queue, newSegsCount)
+ }
+
+ // calculate resent
+ resent := uint32(kcp.fastresend)
+ if kcp.fastresend <= 0 {
+ resent = 0xffffffff
+ }
+
+ // check for retransmissions
+ current := currentMs()
+ var change, lostSegs, fastRetransSegs, earlyRetransSegs uint64
+ minrto := int32(kcp.interval)
+
+ ref := kcp.snd_buf[:len(kcp.snd_buf)] // for bounds check elimination
+ for k := range ref {
+ segment := &ref[k]
+ needsend := false
+ if segment.acked == 1 {
+ continue
+ }
+ if segment.xmit == 0 { // initial transmit
+ needsend = true
+ segment.rto = kcp.rx_rto
+ segment.resendts = current + segment.rto
+ } else if segment.fastack >= resent { // fast retransmit
+ needsend = true
+ segment.fastack = 0
+ segment.rto = kcp.rx_rto
+ segment.resendts = current + segment.rto
+ change++
+ fastRetransSegs++
+ } else if segment.fastack > 0 && newSegsCount == 0 { // early retransmit
+ needsend = true
+ segment.fastack = 0
+ segment.rto = kcp.rx_rto
+ segment.resendts = current + segment.rto
+ change++
+ earlyRetransSegs++
+ } else if _itimediff(current, segment.resendts) >= 0 { // RTO
+ needsend = true
+ if kcp.nodelay == 0 {
+ segment.rto += kcp.rx_rto
+ } else {
+ segment.rto += kcp.rx_rto / 2
+ }
+ segment.fastack = 0
+ segment.resendts = current + segment.rto
+ lostSegs++
+ }
+
+ if needsend {
+ current = currentMs()
+ segment.xmit++
+ segment.ts = current
+ segment.wnd = seg.wnd
+ segment.una = seg.una
+
+ need := IKCP_OVERHEAD + len(segment.data)
+ makeSpace(need)
+ ptr = segment.encode(ptr)
+ copy(ptr, segment.data)
+ ptr = ptr[len(segment.data):]
+
+ if segment.xmit >= kcp.dead_link {
+ kcp.state = 0xFFFFFFFF
+ }
+ }
+
+ // get the nearest rto
+ if rto := _itimediff(segment.resendts, current); rto > 0 && rto < minrto {
+ minrto = rto
+ }
+ }
+
+ // flash remain segments
+ flushBuffer()
+
+ // counter updates
+ sum := lostSegs
+ if lostSegs > 0 {
+ atomic.AddUint64(&DefaultSnmp.LostSegs, lostSegs)
+ }
+ if fastRetransSegs > 0 {
+ atomic.AddUint64(&DefaultSnmp.FastRetransSegs, fastRetransSegs)
+ sum += fastRetransSegs
+ }
+ if earlyRetransSegs > 0 {
+ atomic.AddUint64(&DefaultSnmp.EarlyRetransSegs, earlyRetransSegs)
+ sum += earlyRetransSegs
+ }
+ if sum > 0 {
+ atomic.AddUint64(&DefaultSnmp.RetransSegs, sum)
+ }
+
+ // cwnd update
+ if kcp.nocwnd == 0 {
+ // update ssthresh
+ // rate halving, https://tools.ietf.org/html/rfc6937
+ if change > 0 {
+ inflight := kcp.snd_nxt - kcp.snd_una
+ kcp.ssthresh = inflight / 2
+ if kcp.ssthresh < IKCP_THRESH_MIN {
+ kcp.ssthresh = IKCP_THRESH_MIN
+ }
+ kcp.cwnd = kcp.ssthresh + resent
+ kcp.incr = kcp.cwnd * kcp.mss
+ }
+
+ // congestion control, https://tools.ietf.org/html/rfc5681
+ if lostSegs > 0 {
+ kcp.ssthresh = cwnd / 2
+ if kcp.ssthresh < IKCP_THRESH_MIN {
+ kcp.ssthresh = IKCP_THRESH_MIN
+ }
+ kcp.cwnd = 1
+ kcp.incr = kcp.mss
+ }
+
+ if kcp.cwnd < 1 {
+ kcp.cwnd = 1
+ kcp.incr = kcp.mss
+ }
+ }
+
+ return uint32(minrto)
+}
+
+// (deprecated)
+//
+// Update updates state (call it repeatedly, every 10ms-100ms), or you can ask
+// ikcp_check when to call it again (without ikcp_input/_send calling).
+// 'current' - current timestamp in millisec.
+func (kcp *KCP) Update() {
+ var slap int32
+
+ current := currentMs()
+ if kcp.updated == 0 {
+ kcp.updated = 1
+ kcp.ts_flush = current
+ }
+
+ slap = _itimediff(current, kcp.ts_flush)
+
+ if slap >= 10000 || slap < -10000 {
+ kcp.ts_flush = current
+ slap = 0
+ }
+
+ if slap >= 0 {
+ kcp.ts_flush += kcp.interval
+ if _itimediff(current, kcp.ts_flush) >= 0 {
+ kcp.ts_flush = current + kcp.interval
+ }
+ kcp.flush(false)
+ }
+}
+
+// (deprecated)
+//
+// Check determines when should you invoke ikcp_update:
+// returns when you should invoke ikcp_update in millisec, if there
+// is no ikcp_input/_send calling. you can call ikcp_update in that
+// time, instead of call update repeatly.
+// Important to reduce unnacessary ikcp_update invoking. use it to
+// schedule ikcp_update (eg. implementing an epoll-like mechanism,
+// or optimize ikcp_update when handling massive kcp connections)
+func (kcp *KCP) Check() uint32 {
+ current := currentMs()
+ ts_flush := kcp.ts_flush
+ tm_flush := int32(0x7fffffff)
+ tm_packet := int32(0x7fffffff)
+ minimal := uint32(0)
+ if kcp.updated == 0 {
+ return current
+ }
+
+ if _itimediff(current, ts_flush) >= 10000 ||
+ _itimediff(current, ts_flush) < -10000 {
+ ts_flush = current
+ }
+
+ if _itimediff(current, ts_flush) >= 0 {
+ return current
+ }
+
+ tm_flush = _itimediff(ts_flush, current)
+
+ for k := range kcp.snd_buf {
+ seg := &kcp.snd_buf[k]
+ diff := _itimediff(seg.resendts, current)
+ if diff <= 0 {
+ return current
+ }
+ if diff < tm_packet {
+ tm_packet = diff
+ }
+ }
+
+ minimal = uint32(tm_packet)
+ if tm_packet >= tm_flush {
+ minimal = uint32(tm_flush)
+ }
+ if minimal >= kcp.interval {
+ minimal = kcp.interval
+ }
+
+ return current + minimal
+}
+
+// SetMtu changes MTU size, default is 1400
+func (kcp *KCP) SetMtu(mtu int) int {
+ if mtu < 50 || mtu < IKCP_OVERHEAD {
+ return -1
+ }
+ if kcp.reserved >= int(kcp.mtu-IKCP_OVERHEAD) || kcp.reserved < 0 {
+ return -1
+ }
+
+ buffer := make([]byte, mtu)
+ if buffer == nil {
+ return -2
+ }
+ kcp.mtu = uint32(mtu)
+ kcp.mss = kcp.mtu - IKCP_OVERHEAD - uint32(kcp.reserved)
+ kcp.buffer = buffer
+ return 0
+}
+
+// NoDelay options
+// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
+// nodelay: 0:disable(default), 1:enable
+// interval: internal update timer interval in millisec, default is 100ms
+// resend: 0:disable fast resend(default), 1:enable fast resend
+// nc: 0:normal congestion control(default), 1:disable congestion control
+func (kcp *KCP) NoDelay(nodelay, interval, resend, nc int) int {
+ if nodelay >= 0 {
+ kcp.nodelay = uint32(nodelay)
+ if nodelay != 0 {
+ kcp.rx_minrto = IKCP_RTO_NDL
+ } else {
+ kcp.rx_minrto = IKCP_RTO_MIN
+ }
+ }
+ if interval >= 0 {
+ if interval > 5000 {
+ interval = 5000
+ } else if interval < 10 {
+ interval = 10
+ }
+ kcp.interval = uint32(interval)
+ }
+ if resend >= 0 {
+ kcp.fastresend = int32(resend)
+ }
+ if nc >= 0 {
+ kcp.nocwnd = int32(nc)
+ }
+ return 0
+}
+
+// WndSize sets maximum window size: sndwnd=32, rcvwnd=32 by default
+func (kcp *KCP) WndSize(sndwnd, rcvwnd int) int {
+ if sndwnd > 0 {
+ kcp.snd_wnd = uint32(sndwnd)
+ }
+ if rcvwnd > 0 {
+ kcp.rcv_wnd = uint32(rcvwnd)
+ }
+ return 0
+}
+
+// WaitSnd gets how many packet is waiting to be sent
+func (kcp *KCP) WaitSnd() int {
+ return len(kcp.snd_buf) + len(kcp.snd_queue)
+}
+
+// remove front n elements from queue
+// if the number of elements to remove is more than half of the size.
+// just shift the rear elements to front, otherwise just reslice q to q[n:]
+// then the cost of runtime.growslice can always be less than n/2
+func (kcp *KCP) remove_front(q []segment, n int) []segment {
+ if n > cap(q)/2 {
+ newn := copy(q, q[n:])
+ return q[:newn]
+ }
+ return q[n:]
+}
+
+// Release all cached outgoing segments
+func (kcp *KCP) ReleaseTX() {
+ for k := range kcp.snd_queue {
+ if kcp.snd_queue[k].data != nil {
+ xmitBuf.Put(kcp.snd_queue[k].data)
+ }
+ }
+ for k := range kcp.snd_buf {
+ if kcp.snd_buf[k].data != nil {
+ xmitBuf.Put(kcp.snd_buf[k].data)
+ }
+ }
+ kcp.snd_queue = nil
+ kcp.snd_buf = nil
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/readloop.go b/vendor/github.com/xtaci/kcp-go/v5/readloop.go
new file mode 100644
index 0000000..697395a
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/readloop.go
@@ -0,0 +1,39 @@
+package kcp
+
+import (
+ "sync/atomic"
+
+ "github.com/pkg/errors"
+)
+
+func (s *UDPSession) defaultReadLoop() {
+ buf := make([]byte, mtuLimit)
+ var src string
+ for {
+ if n, addr, err := s.conn.ReadFrom(buf); err == nil {
+ // make sure the packet is from the same source
+ if src == "" { // set source address
+ src = addr.String()
+ } else if addr.String() != src {
+ atomic.AddUint64(&DefaultSnmp.InErrs, 1)
+ continue
+ }
+ s.packetInput(buf[:n])
+ } else {
+ s.notifyReadError(errors.WithStack(err))
+ return
+ }
+ }
+}
+
+func (l *Listener) defaultMonitor() {
+ buf := make([]byte, mtuLimit)
+ for {
+ if n, from, err := l.conn.ReadFrom(buf); err == nil {
+ l.packetInput(buf[:n], from)
+ } else {
+ l.notifyReadError(errors.WithStack(err))
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/readloop_generic.go b/vendor/github.com/xtaci/kcp-go/v5/readloop_generic.go
new file mode 100644
index 0000000..5dbe4f4
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/readloop_generic.go
@@ -0,0 +1,11 @@
+// +build !linux
+
+package kcp
+
+func (s *UDPSession) readLoop() {
+ s.defaultReadLoop()
+}
+
+func (l *Listener) monitor() {
+ l.defaultMonitor()
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/readloop_linux.go b/vendor/github.com/xtaci/kcp-go/v5/readloop_linux.go
new file mode 100644
index 0000000..be194af
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/readloop_linux.go
@@ -0,0 +1,111 @@
+// +build linux
+
+package kcp
+
+import (
+ "net"
+ "os"
+ "sync/atomic"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+// the read loop for a client session
+func (s *UDPSession) readLoop() {
+ // default version
+ if s.xconn == nil {
+ s.defaultReadLoop()
+ return
+ }
+
+ // x/net version
+ var src string
+ msgs := make([]ipv4.Message, batchSize)
+ for k := range msgs {
+ msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
+ }
+
+ for {
+ if count, err := s.xconn.ReadBatch(msgs, 0); err == nil {
+ for i := 0; i < count; i++ {
+ msg := &msgs[i]
+ // make sure the packet is from the same source
+ if src == "" { // set source address if nil
+ src = msg.Addr.String()
+ } else if msg.Addr.String() != src {
+ atomic.AddUint64(&DefaultSnmp.InErrs, 1)
+ continue
+ }
+
+ // source and size has validated
+ s.packetInput(msg.Buffers[0][:msg.N])
+ }
+ } else {
+ // compatibility issue:
+ // for linux kernel<=2.6.32, support for sendmmsg is not available
+ // an error of type os.SyscallError will be returned
+ if operr, ok := err.(*net.OpError); ok {
+ if se, ok := operr.Err.(*os.SyscallError); ok {
+ if se.Syscall == "recvmmsg" {
+ s.defaultReadLoop()
+ return
+ }
+ }
+ }
+ s.notifyReadError(errors.WithStack(err))
+ return
+ }
+ }
+}
+
+// monitor incoming data for all connections of server
+func (l *Listener) monitor() {
+ var xconn batchConn
+ if _, ok := l.conn.(*net.UDPConn); ok {
+ addr, err := net.ResolveUDPAddr("udp", l.conn.LocalAddr().String())
+ if err == nil {
+ if addr.IP.To4() != nil {
+ xconn = ipv4.NewPacketConn(l.conn)
+ } else {
+ xconn = ipv6.NewPacketConn(l.conn)
+ }
+ }
+ }
+
+ // default version
+ if xconn == nil {
+ l.defaultMonitor()
+ return
+ }
+
+ // x/net version
+ msgs := make([]ipv4.Message, batchSize)
+ for k := range msgs {
+ msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
+ }
+
+ for {
+ if count, err := xconn.ReadBatch(msgs, 0); err == nil {
+ for i := 0; i < count; i++ {
+ msg := &msgs[i]
+ l.packetInput(msg.Buffers[0][:msg.N], msg.Addr)
+ }
+ } else {
+ // compatibility issue:
+ // for linux kernel<=2.6.32, support for sendmmsg is not available
+ // an error of type os.SyscallError will be returned
+ if operr, ok := err.(*net.OpError); ok {
+ if se, ok := operr.Err.(*os.SyscallError); ok {
+ if se.Syscall == "recvmmsg" {
+ l.defaultMonitor()
+ return
+ }
+ }
+ }
+ l.notifyReadError(errors.WithStack(err))
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/sess.go b/vendor/github.com/xtaci/kcp-go/v5/sess.go
new file mode 100644
index 0000000..2dedd74
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/sess.go
@@ -0,0 +1,1075 @@
+// Package kcp-go is a Reliable-UDP library for golang.
+//
+// This library intents to provide a smooth, resilient, ordered,
+// error-checked and anonymous delivery of streams over UDP packets.
+//
+// The interfaces of this package aims to be compatible with
+// net.Conn in standard library, but offers powerful features for advanced users.
+package kcp
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "hash/crc32"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/net/ipv6"
+)
+
+const (
+ // 16-bytes nonce for each packet
+ nonceSize = 16
+
+ // 4-bytes packet checksum
+ crcSize = 4
+
+ // overall crypto header size
+ cryptHeaderSize = nonceSize + crcSize
+
+ // maximum packet size
+ mtuLimit = 1500
+
+ // accept backlog
+ acceptBacklog = 128
+)
+
+var (
+ errInvalidOperation = errors.New("invalid operation")
+ errTimeout = errors.New("timeout")
+)
+
+var (
+ // a system-wide packet buffer shared among sending, receiving and FEC
+ // to mitigate high-frequency memory allocation for packets, bytes from xmitBuf
+ // is aligned to 64bit
+ xmitBuf sync.Pool
+)
+
+func init() {
+ xmitBuf.New = func() interface{} {
+ return make([]byte, mtuLimit)
+ }
+}
+
+type (
+ // UDPSession defines a KCP session implemented by UDP
+ UDPSession struct {
+ conn net.PacketConn // the underlying packet connection
+ ownConn bool // true if we created conn internally, false if provided by caller
+ kcp *KCP // KCP ARQ protocol
+ l *Listener // pointing to the Listener object if it's been accepted by a Listener
+ block BlockCrypt // block encryption object
+
+ // kcp receiving is based on packets
+ // recvbuf turns packets into stream
+ recvbuf []byte
+ bufptr []byte
+
+ // FEC codec
+ fecDecoder *fecDecoder
+ fecEncoder *fecEncoder
+
+ // settings
+ remote net.Addr // remote peer address
+ rd time.Time // read deadline
+ wd time.Time // write deadline
+ headerSize int // the header size additional to a KCP frame
+ ackNoDelay bool // send ack immediately for each incoming packet(testing purpose)
+ writeDelay bool // delay kcp.flush() for Write() for bulk transfer
+ dup int // duplicate udp packets(testing purpose)
+
+ // notifications
+ die chan struct{} // notify current session has Closed
+ dieOnce sync.Once
+ chReadEvent chan struct{} // notify Read() can be called without blocking
+ chWriteEvent chan struct{} // notify Write() can be called without blocking
+
+ // socket error handling
+ socketReadError atomic.Value
+ socketWriteError atomic.Value
+ chSocketReadError chan struct{}
+ chSocketWriteError chan struct{}
+ socketReadErrorOnce sync.Once
+ socketWriteErrorOnce sync.Once
+
+ // nonce generator
+ nonce Entropy
+
+ // packets waiting to be sent on wire
+ txqueue []ipv4.Message
+ xconn batchConn // for x/net
+ xconnWriteError error
+
+ mu sync.Mutex
+ }
+
+ setReadBuffer interface {
+ SetReadBuffer(bytes int) error
+ }
+
+ setWriteBuffer interface {
+ SetWriteBuffer(bytes int) error
+ }
+
+ setDSCP interface {
+ SetDSCP(int) error
+ }
+)
+
+// newUDPSession create a new udp session for client or server
+func newUDPSession(conv uint32, dataShards, parityShards int, l *Listener, conn net.PacketConn, ownConn bool, remote net.Addr, block BlockCrypt) *UDPSession {
+ sess := new(UDPSession)
+ sess.die = make(chan struct{})
+ sess.nonce = new(nonceAES128)
+ sess.nonce.Init()
+ sess.chReadEvent = make(chan struct{}, 1)
+ sess.chWriteEvent = make(chan struct{}, 1)
+ sess.chSocketReadError = make(chan struct{})
+ sess.chSocketWriteError = make(chan struct{})
+ sess.remote = remote
+ sess.conn = conn
+ sess.ownConn = ownConn
+ sess.l = l
+ sess.block = block
+ sess.recvbuf = make([]byte, mtuLimit)
+
+ // cast to writebatch conn
+ if _, ok := conn.(*net.UDPConn); ok {
+ addr, err := net.ResolveUDPAddr("udp", conn.LocalAddr().String())
+ if err == nil {
+ if addr.IP.To4() != nil {
+ sess.xconn = ipv4.NewPacketConn(conn)
+ } else {
+ sess.xconn = ipv6.NewPacketConn(conn)
+ }
+ }
+ }
+
+ // FEC codec initialization
+ sess.fecDecoder = newFECDecoder(dataShards, parityShards)
+ if sess.block != nil {
+ sess.fecEncoder = newFECEncoder(dataShards, parityShards, cryptHeaderSize)
+ } else {
+ sess.fecEncoder = newFECEncoder(dataShards, parityShards, 0)
+ }
+
+ // calculate additional header size introduced by FEC and encryption
+ if sess.block != nil {
+ sess.headerSize += cryptHeaderSize
+ }
+ if sess.fecEncoder != nil {
+ sess.headerSize += fecHeaderSizePlus2
+ }
+
+ sess.kcp = NewKCP(conv, func(buf []byte, size int) {
+ if size >= IKCP_OVERHEAD+sess.headerSize {
+ sess.output(buf[:size])
+ }
+ })
+ sess.kcp.ReserveBytes(sess.headerSize)
+
+ if sess.l == nil { // it's a client connection
+ go sess.readLoop()
+ atomic.AddUint64(&DefaultSnmp.ActiveOpens, 1)
+ } else {
+ atomic.AddUint64(&DefaultSnmp.PassiveOpens, 1)
+ }
+
+ // start per-session updater
+ SystemTimedSched.Put(sess.update, time.Now())
+
+ currestab := atomic.AddUint64(&DefaultSnmp.CurrEstab, 1)
+ maxconn := atomic.LoadUint64(&DefaultSnmp.MaxConn)
+ if currestab > maxconn {
+ atomic.CompareAndSwapUint64(&DefaultSnmp.MaxConn, maxconn, currestab)
+ }
+
+ return sess
+}
+
+// Read implements net.Conn
+func (s *UDPSession) Read(b []byte) (n int, err error) {
+ for {
+ s.mu.Lock()
+ if len(s.bufptr) > 0 { // copy from buffer into b
+ n = copy(b, s.bufptr)
+ s.bufptr = s.bufptr[n:]
+ s.mu.Unlock()
+ atomic.AddUint64(&DefaultSnmp.BytesReceived, uint64(n))
+ return n, nil
+ }
+
+ if size := s.kcp.PeekSize(); size > 0 { // peek data size from kcp
+ if len(b) >= size { // receive data into 'b' directly
+ s.kcp.Recv(b)
+ s.mu.Unlock()
+ atomic.AddUint64(&DefaultSnmp.BytesReceived, uint64(size))
+ return size, nil
+ }
+
+ // if necessary resize the stream buffer to guarantee a sufficient buffer space
+ if cap(s.recvbuf) < size {
+ s.recvbuf = make([]byte, size)
+ }
+
+ // resize the length of recvbuf to correspond to data size
+ s.recvbuf = s.recvbuf[:size]
+ s.kcp.Recv(s.recvbuf)
+ n = copy(b, s.recvbuf) // copy to 'b'
+ s.bufptr = s.recvbuf[n:] // pointer update
+ s.mu.Unlock()
+ atomic.AddUint64(&DefaultSnmp.BytesReceived, uint64(n))
+ return n, nil
+ }
+
+ // deadline for current reading operation
+ var timeout *time.Timer
+ var c <-chan time.Time
+ if !s.rd.IsZero() {
+ if time.Now().After(s.rd) {
+ s.mu.Unlock()
+ return 0, errors.WithStack(errTimeout)
+ }
+
+ delay := time.Until(s.rd)
+ timeout = time.NewTimer(delay)
+ c = timeout.C
+ }
+ s.mu.Unlock()
+
+ // wait for read event or timeout or error
+ select {
+ case <-s.chReadEvent:
+ if timeout != nil {
+ timeout.Stop()
+ }
+ case <-c:
+ return 0, errors.WithStack(errTimeout)
+ case <-s.chSocketReadError:
+ return 0, s.socketReadError.Load().(error)
+ case <-s.die:
+ return 0, errors.WithStack(io.ErrClosedPipe)
+ }
+ }
+}
+
+// Write implements net.Conn
+func (s *UDPSession) Write(b []byte) (n int, err error) { return s.WriteBuffers([][]byte{b}) }
+
+// WriteBuffers write a vector of byte slices to the underlying connection
+func (s *UDPSession) WriteBuffers(v [][]byte) (n int, err error) {
+ for {
+ select {
+ case <-s.chSocketWriteError:
+ return 0, s.socketWriteError.Load().(error)
+ case <-s.die:
+ return 0, errors.WithStack(io.ErrClosedPipe)
+ default:
+ }
+
+ s.mu.Lock()
+
+ // make sure write do not overflow the max sliding window on both side
+ waitsnd := s.kcp.WaitSnd()
+ if waitsnd < int(s.kcp.snd_wnd) && waitsnd < int(s.kcp.rmt_wnd) {
+ for _, b := range v {
+ n += len(b)
+ for {
+ if len(b) <= int(s.kcp.mss) {
+ s.kcp.Send(b)
+ break
+ } else {
+ s.kcp.Send(b[:s.kcp.mss])
+ b = b[s.kcp.mss:]
+ }
+ }
+ }
+
+ waitsnd = s.kcp.WaitSnd()
+ if waitsnd >= int(s.kcp.snd_wnd) || waitsnd >= int(s.kcp.rmt_wnd) || !s.writeDelay {
+ s.kcp.flush(false)
+ s.uncork()
+ }
+ s.mu.Unlock()
+ atomic.AddUint64(&DefaultSnmp.BytesSent, uint64(n))
+ return n, nil
+ }
+
+ var timeout *time.Timer
+ var c <-chan time.Time
+ if !s.wd.IsZero() {
+ if time.Now().After(s.wd) {
+ s.mu.Unlock()
+ return 0, errors.WithStack(errTimeout)
+ }
+ delay := time.Until(s.wd)
+ timeout = time.NewTimer(delay)
+ c = timeout.C
+ }
+ s.mu.Unlock()
+
+ select {
+ case <-s.chWriteEvent:
+ if timeout != nil {
+ timeout.Stop()
+ }
+ case <-c:
+ return 0, errors.WithStack(errTimeout)
+ case <-s.chSocketWriteError:
+ return 0, s.socketWriteError.Load().(error)
+ case <-s.die:
+ return 0, errors.WithStack(io.ErrClosedPipe)
+ }
+ }
+}
+
+// uncork sends data in txqueue if there is any
+func (s *UDPSession) uncork() {
+ if len(s.txqueue) > 0 {
+ s.tx(s.txqueue)
+ // recycle
+ for k := range s.txqueue {
+ xmitBuf.Put(s.txqueue[k].Buffers[0])
+ s.txqueue[k].Buffers = nil
+ }
+ s.txqueue = s.txqueue[:0]
+ }
+}
+
+// Close closes the connection.
+func (s *UDPSession) Close() error {
+ var once bool
+ s.dieOnce.Do(func() {
+ close(s.die)
+ once = true
+ })
+
+ if once {
+ atomic.AddUint64(&DefaultSnmp.CurrEstab, ^uint64(0))
+
+ // try best to send all queued messages
+ s.mu.Lock()
+ s.kcp.flush(false)
+ s.uncork()
+ // release pending segments
+ s.kcp.ReleaseTX()
+ if s.fecDecoder != nil {
+ s.fecDecoder.release()
+ }
+ s.mu.Unlock()
+
+ if s.l != nil { // belongs to listener
+ s.l.closeSession(s.remote)
+ return nil
+ } else if s.ownConn { // client socket close
+ return s.conn.Close()
+ } else {
+ return nil
+ }
+ } else {
+ return errors.WithStack(io.ErrClosedPipe)
+ }
+}
+
+// LocalAddr returns the local network address. The Addr returned is shared by all invocations of LocalAddr, so do not modify it.
+func (s *UDPSession) LocalAddr() net.Addr { return s.conn.LocalAddr() }
+
+// RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it.
+func (s *UDPSession) RemoteAddr() net.Addr { return s.remote }
+
+// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline.
+func (s *UDPSession) SetDeadline(t time.Time) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.rd = t
+ s.wd = t
+ s.notifyReadEvent()
+ s.notifyWriteEvent()
+ return nil
+}
+
+// SetReadDeadline implements the Conn SetReadDeadline method.
+func (s *UDPSession) SetReadDeadline(t time.Time) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.rd = t
+ s.notifyReadEvent()
+ return nil
+}
+
+// SetWriteDeadline implements the Conn SetWriteDeadline method.
+func (s *UDPSession) SetWriteDeadline(t time.Time) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.wd = t
+ s.notifyWriteEvent()
+ return nil
+}
+
+// SetWriteDelay delays write for bulk transfer until the next update interval
+func (s *UDPSession) SetWriteDelay(delay bool) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.writeDelay = delay
+}
+
+// SetWindowSize set maximum window size
+func (s *UDPSession) SetWindowSize(sndwnd, rcvwnd int) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.kcp.WndSize(sndwnd, rcvwnd)
+}
+
+// SetMtu sets the maximum transmission unit(not including UDP header)
+func (s *UDPSession) SetMtu(mtu int) bool {
+ if mtu > mtuLimit {
+ return false
+ }
+
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.kcp.SetMtu(mtu)
+ return true
+}
+
+// SetStreamMode toggles the stream mode on/off
+func (s *UDPSession) SetStreamMode(enable bool) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if enable {
+ s.kcp.stream = 1
+ } else {
+ s.kcp.stream = 0
+ }
+}
+
+// SetACKNoDelay changes ack flush option, set true to flush ack immediately,
+func (s *UDPSession) SetACKNoDelay(nodelay bool) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.ackNoDelay = nodelay
+}
+
+// (deprecated)
+//
+// SetDUP duplicates udp packets for kcp output.
+func (s *UDPSession) SetDUP(dup int) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.dup = dup
+}
+
+// SetNoDelay calls nodelay() of kcp
+// https://github.com/skywind3000/kcp/blob/master/README.en.md#protocol-configuration
+func (s *UDPSession) SetNoDelay(nodelay, interval, resend, nc int) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ s.kcp.NoDelay(nodelay, interval, resend, nc)
+}
+
+// SetDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header.
+//
+// if the underlying connection has implemented `func SetDSCP(int) error`, SetDSCP() will invoke
+// this function instead.
+//
+// It has no effect if it's accepted from Listener.
+func (s *UDPSession) SetDSCP(dscp int) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.l != nil {
+ return errInvalidOperation
+ }
+
+ // interface enabled
+ if ts, ok := s.conn.(setDSCP); ok {
+ return ts.SetDSCP(dscp)
+ }
+
+ if nc, ok := s.conn.(net.Conn); ok {
+ var succeed bool
+ if err := ipv4.NewConn(nc).SetTOS(dscp << 2); err == nil {
+ succeed = true
+ }
+ if err := ipv6.NewConn(nc).SetTrafficClass(dscp); err == nil {
+ succeed = true
+ }
+
+ if succeed {
+ return nil
+ }
+ }
+ return errInvalidOperation
+}
+
+// SetReadBuffer sets the socket read buffer, no effect if it's accepted from Listener
+func (s *UDPSession) SetReadBuffer(bytes int) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.l == nil {
+ if nc, ok := s.conn.(setReadBuffer); ok {
+ return nc.SetReadBuffer(bytes)
+ }
+ }
+ return errInvalidOperation
+}
+
+// SetWriteBuffer sets the socket write buffer, no effect if it's accepted from Listener
+func (s *UDPSession) SetWriteBuffer(bytes int) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.l == nil {
+ if nc, ok := s.conn.(setWriteBuffer); ok {
+ return nc.SetWriteBuffer(bytes)
+ }
+ }
+ return errInvalidOperation
+}
+
+// post-processing for sending a packet from kcp core
+// steps:
+// 1. FEC packet generation
+// 2. CRC32 integrity
+// 3. Encryption
+// 4. TxQueue
+func (s *UDPSession) output(buf []byte) {
+ var ecc [][]byte
+
+ // 1. FEC encoding
+ if s.fecEncoder != nil {
+ ecc = s.fecEncoder.encode(buf)
+ }
+
+ // 2&3. crc32 & encryption
+ if s.block != nil {
+ s.nonce.Fill(buf[:nonceSize])
+ checksum := crc32.ChecksumIEEE(buf[cryptHeaderSize:])
+ binary.LittleEndian.PutUint32(buf[nonceSize:], checksum)
+ s.block.Encrypt(buf, buf)
+
+ for k := range ecc {
+ s.nonce.Fill(ecc[k][:nonceSize])
+ checksum := crc32.ChecksumIEEE(ecc[k][cryptHeaderSize:])
+ binary.LittleEndian.PutUint32(ecc[k][nonceSize:], checksum)
+ s.block.Encrypt(ecc[k], ecc[k])
+ }
+ }
+
+ // 4. TxQueue
+ var msg ipv4.Message
+ for i := 0; i < s.dup+1; i++ {
+ bts := xmitBuf.Get().([]byte)[:len(buf)]
+ copy(bts, buf)
+ msg.Buffers = [][]byte{bts}
+ msg.Addr = s.remote
+ s.txqueue = append(s.txqueue, msg)
+ }
+
+ for k := range ecc {
+ bts := xmitBuf.Get().([]byte)[:len(ecc[k])]
+ copy(bts, ecc[k])
+ msg.Buffers = [][]byte{bts}
+ msg.Addr = s.remote
+ s.txqueue = append(s.txqueue, msg)
+ }
+}
+
+// sess update to trigger protocol
+func (s *UDPSession) update() {
+ select {
+ case <-s.die:
+ default:
+ s.mu.Lock()
+ interval := s.kcp.flush(false)
+ waitsnd := s.kcp.WaitSnd()
+ if waitsnd < int(s.kcp.snd_wnd) && waitsnd < int(s.kcp.rmt_wnd) {
+ s.notifyWriteEvent()
+ }
+ s.uncork()
+ s.mu.Unlock()
+ // self-synchronized timed scheduling
+ SystemTimedSched.Put(s.update, time.Now().Add(time.Duration(interval)*time.Millisecond))
+ }
+}
+
+// GetConv gets conversation id of a session
+func (s *UDPSession) GetConv() uint32 { return s.kcp.conv }
+
+// GetRTO gets current rto of the session
+func (s *UDPSession) GetRTO() uint32 {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.kcp.rx_rto
+}
+
+// GetSRTT gets current srtt of the session
+func (s *UDPSession) GetSRTT() int32 {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.kcp.rx_srtt
+}
+
+// GetRTTVar gets current rtt variance of the session
+func (s *UDPSession) GetSRTTVar() int32 {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.kcp.rx_rttvar
+}
+
+func (s *UDPSession) notifyReadEvent() {
+ select {
+ case s.chReadEvent <- struct{}{}:
+ default:
+ }
+}
+
+func (s *UDPSession) notifyWriteEvent() {
+ select {
+ case s.chWriteEvent <- struct{}{}:
+ default:
+ }
+}
+
+func (s *UDPSession) notifyReadError(err error) {
+ s.socketReadErrorOnce.Do(func() {
+ s.socketReadError.Store(err)
+ close(s.chSocketReadError)
+ })
+}
+
+func (s *UDPSession) notifyWriteError(err error) {
+ s.socketWriteErrorOnce.Do(func() {
+ s.socketWriteError.Store(err)
+ close(s.chSocketWriteError)
+ })
+}
+
+// packet input stage
+func (s *UDPSession) packetInput(data []byte) {
+ decrypted := false
+ if s.block != nil && len(data) >= cryptHeaderSize {
+ s.block.Decrypt(data, data)
+ data = data[nonceSize:]
+ checksum := crc32.ChecksumIEEE(data[crcSize:])
+ if checksum == binary.LittleEndian.Uint32(data) {
+ data = data[crcSize:]
+ decrypted = true
+ } else {
+ atomic.AddUint64(&DefaultSnmp.InCsumErrors, 1)
+ }
+ } else if s.block == nil {
+ decrypted = true
+ }
+
+ if decrypted && len(data) >= IKCP_OVERHEAD {
+ s.kcpInput(data)
+ }
+}
+
+func (s *UDPSession) kcpInput(data []byte) {
+ var kcpInErrors, fecErrs, fecRecovered, fecParityShards uint64
+
+ fecFlag := binary.LittleEndian.Uint16(data[4:])
+ if fecFlag == typeData || fecFlag == typeParity { // 16bit kcp cmd [81-84] and frg [0-255] will not overlap with FEC type 0x00f1 0x00f2
+ if len(data) >= fecHeaderSizePlus2 {
+ f := fecPacket(data)
+ if f.flag() == typeParity {
+ fecParityShards++
+ }
+
+ // lock
+ s.mu.Lock()
+ // if fecDecoder is not initialized, create one with default parameter
+ if s.fecDecoder == nil {
+ s.fecDecoder = newFECDecoder(1, 1)
+ }
+ recovers := s.fecDecoder.decode(f)
+ if f.flag() == typeData {
+ if ret := s.kcp.Input(data[fecHeaderSizePlus2:], true, s.ackNoDelay); ret != 0 {
+ kcpInErrors++
+ }
+ }
+
+ for _, r := range recovers {
+ if len(r) >= 2 { // must be larger than 2bytes
+ sz := binary.LittleEndian.Uint16(r)
+ if int(sz) <= len(r) && sz >= 2 {
+ if ret := s.kcp.Input(r[2:sz], false, s.ackNoDelay); ret == 0 {
+ fecRecovered++
+ } else {
+ kcpInErrors++
+ }
+ } else {
+ fecErrs++
+ }
+ } else {
+ fecErrs++
+ }
+ // recycle the recovers
+ xmitBuf.Put(r)
+ }
+
+ // to notify the readers to receive the data
+ if n := s.kcp.PeekSize(); n > 0 {
+ s.notifyReadEvent()
+ }
+ // to notify the writers
+ waitsnd := s.kcp.WaitSnd()
+ if waitsnd < int(s.kcp.snd_wnd) && waitsnd < int(s.kcp.rmt_wnd) {
+ s.notifyWriteEvent()
+ }
+
+ s.uncork()
+ s.mu.Unlock()
+ } else {
+ atomic.AddUint64(&DefaultSnmp.InErrs, 1)
+ }
+ } else {
+ s.mu.Lock()
+ if ret := s.kcp.Input(data, true, s.ackNoDelay); ret != 0 {
+ kcpInErrors++
+ }
+ if n := s.kcp.PeekSize(); n > 0 {
+ s.notifyReadEvent()
+ }
+ waitsnd := s.kcp.WaitSnd()
+ if waitsnd < int(s.kcp.snd_wnd) && waitsnd < int(s.kcp.rmt_wnd) {
+ s.notifyWriteEvent()
+ }
+ s.uncork()
+ s.mu.Unlock()
+ }
+
+ atomic.AddUint64(&DefaultSnmp.InPkts, 1)
+ atomic.AddUint64(&DefaultSnmp.InBytes, uint64(len(data)))
+ if fecParityShards > 0 {
+ atomic.AddUint64(&DefaultSnmp.FECParityShards, fecParityShards)
+ }
+ if kcpInErrors > 0 {
+ atomic.AddUint64(&DefaultSnmp.KCPInErrors, kcpInErrors)
+ }
+ if fecErrs > 0 {
+ atomic.AddUint64(&DefaultSnmp.FECErrs, fecErrs)
+ }
+ if fecRecovered > 0 {
+ atomic.AddUint64(&DefaultSnmp.FECRecovered, fecRecovered)
+ }
+
+}
+
+type (
+ // Listener defines a server which will be waiting to accept incoming connections
+ Listener struct {
+ block BlockCrypt // block encryption
+ dataShards int // FEC data shard
+ parityShards int // FEC parity shard
+ conn net.PacketConn // the underlying packet connection
+ ownConn bool // true if we created conn internally, false if provided by caller
+
+ sessions map[string]*UDPSession // all sessions accepted by this Listener
+ sessionLock sync.RWMutex
+ chAccepts chan *UDPSession // Listen() backlog
+ chSessionClosed chan net.Addr // session close queue
+
+ die chan struct{} // notify the listener has closed
+ dieOnce sync.Once
+
+ // socket error handling
+ socketReadError atomic.Value
+ chSocketReadError chan struct{}
+ socketReadErrorOnce sync.Once
+
+ rd atomic.Value // read deadline for Accept()
+ }
+)
+
+// packet input stage
+func (l *Listener) packetInput(data []byte, addr net.Addr) {
+ decrypted := false
+ if l.block != nil && len(data) >= cryptHeaderSize {
+ l.block.Decrypt(data, data)
+ data = data[nonceSize:]
+ checksum := crc32.ChecksumIEEE(data[crcSize:])
+ if checksum == binary.LittleEndian.Uint32(data) {
+ data = data[crcSize:]
+ decrypted = true
+ } else {
+ atomic.AddUint64(&DefaultSnmp.InCsumErrors, 1)
+ }
+ } else if l.block == nil {
+ decrypted = true
+ }
+
+ if decrypted && len(data) >= IKCP_OVERHEAD {
+ l.sessionLock.RLock()
+ s, ok := l.sessions[addr.String()]
+ l.sessionLock.RUnlock()
+
+ var conv, sn uint32
+ convRecovered := false
+ fecFlag := binary.LittleEndian.Uint16(data[4:])
+ if fecFlag == typeData || fecFlag == typeParity { // 16bit kcp cmd [81-84] and frg [0-255] will not overlap with FEC type 0x00f1 0x00f2
+ // packet with FEC
+ if fecFlag == typeData && len(data) >= fecHeaderSizePlus2+IKCP_OVERHEAD {
+ conv = binary.LittleEndian.Uint32(data[fecHeaderSizePlus2:])
+ sn = binary.LittleEndian.Uint32(data[fecHeaderSizePlus2+IKCP_SN_OFFSET:])
+ convRecovered = true
+ }
+ } else {
+ // packet without FEC
+ conv = binary.LittleEndian.Uint32(data)
+ sn = binary.LittleEndian.Uint32(data[IKCP_SN_OFFSET:])
+ convRecovered = true
+ }
+
+ if ok { // existing connection
+ if !convRecovered || conv == s.kcp.conv { // parity data or valid conversation
+ s.kcpInput(data)
+ } else if sn == 0 { // should replace current connection
+ s.Close()
+ s = nil
+ }
+ }
+
+ if s == nil && convRecovered { // new session
+ if len(l.chAccepts) < cap(l.chAccepts) { // do not let the new sessions overwhelm accept queue
+ s := newUDPSession(conv, l.dataShards, l.parityShards, l, l.conn, false, addr, l.block)
+ s.kcpInput(data)
+ l.sessionLock.Lock()
+ l.sessions[addr.String()] = s
+ l.sessionLock.Unlock()
+ l.chAccepts <- s
+ }
+ }
+ }
+}
+
+func (l *Listener) notifyReadError(err error) {
+ l.socketReadErrorOnce.Do(func() {
+ l.socketReadError.Store(err)
+ close(l.chSocketReadError)
+
+ // propagate read error to all sessions
+ l.sessionLock.RLock()
+ for _, s := range l.sessions {
+ s.notifyReadError(err)
+ }
+ l.sessionLock.RUnlock()
+ })
+}
+
+// SetReadBuffer sets the socket read buffer for the Listener
+func (l *Listener) SetReadBuffer(bytes int) error {
+ if nc, ok := l.conn.(setReadBuffer); ok {
+ return nc.SetReadBuffer(bytes)
+ }
+ return errInvalidOperation
+}
+
+// SetWriteBuffer sets the socket write buffer for the Listener
+func (l *Listener) SetWriteBuffer(bytes int) error {
+ if nc, ok := l.conn.(setWriteBuffer); ok {
+ return nc.SetWriteBuffer(bytes)
+ }
+ return errInvalidOperation
+}
+
+// SetDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header.
+//
+// if the underlying connection has implemented `func SetDSCP(int) error`, SetDSCP() will invoke
+// this function instead.
+func (l *Listener) SetDSCP(dscp int) error {
+ // interface enabled
+ if ts, ok := l.conn.(setDSCP); ok {
+ return ts.SetDSCP(dscp)
+ }
+
+ if nc, ok := l.conn.(net.Conn); ok {
+ var succeed bool
+ if err := ipv4.NewConn(nc).SetTOS(dscp << 2); err == nil {
+ succeed = true
+ }
+ if err := ipv6.NewConn(nc).SetTrafficClass(dscp); err == nil {
+ succeed = true
+ }
+
+ if succeed {
+ return nil
+ }
+ }
+ return errInvalidOperation
+}
+
+// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.
+func (l *Listener) Accept() (net.Conn, error) {
+ return l.AcceptKCP()
+}
+
+// AcceptKCP accepts a KCP connection
+func (l *Listener) AcceptKCP() (*UDPSession, error) {
+ var timeout <-chan time.Time
+ if tdeadline, ok := l.rd.Load().(time.Time); ok && !tdeadline.IsZero() {
+ timeout = time.After(time.Until(tdeadline))
+ }
+
+ select {
+ case <-timeout:
+ return nil, errors.WithStack(errTimeout)
+ case c := <-l.chAccepts:
+ return c, nil
+ case <-l.chSocketReadError:
+ return nil, l.socketReadError.Load().(error)
+ case <-l.die:
+ return nil, errors.WithStack(io.ErrClosedPipe)
+ }
+}
+
+// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline.
+func (l *Listener) SetDeadline(t time.Time) error {
+ l.SetReadDeadline(t)
+ l.SetWriteDeadline(t)
+ return nil
+}
+
+// SetReadDeadline implements the Conn SetReadDeadline method.
+func (l *Listener) SetReadDeadline(t time.Time) error {
+ l.rd.Store(t)
+ return nil
+}
+
+// SetWriteDeadline implements the Conn SetWriteDeadline method.
+func (l *Listener) SetWriteDeadline(t time.Time) error { return errInvalidOperation }
+
+// Close stops listening on the UDP address, and closes the socket
+func (l *Listener) Close() error {
+ var once bool
+ l.dieOnce.Do(func() {
+ close(l.die)
+ once = true
+ })
+
+ var err error
+ if once {
+ if l.ownConn {
+ err = l.conn.Close()
+ }
+ } else {
+ err = errors.WithStack(io.ErrClosedPipe)
+ }
+ return err
+}
+
+// closeSession notify the listener that a session has closed
+func (l *Listener) closeSession(remote net.Addr) (ret bool) {
+ l.sessionLock.Lock()
+ defer l.sessionLock.Unlock()
+ if _, ok := l.sessions[remote.String()]; ok {
+ delete(l.sessions, remote.String())
+ return true
+ }
+ return false
+}
+
+// Addr returns the listener's network address, The Addr returned is shared by all invocations of Addr, so do not modify it.
+func (l *Listener) Addr() net.Addr { return l.conn.LocalAddr() }
+
+// Listen listens for incoming KCP packets addressed to the local address laddr on the network "udp",
+func Listen(laddr string) (net.Listener, error) { return ListenWithOptions(laddr, nil, 0, 0) }
+
+// ListenWithOptions listens for incoming KCP packets addressed to the local address laddr on the network "udp" with packet encryption.
+//
+// 'block' is the block encryption algorithm to encrypt packets.
+//
+// 'dataShards', 'parityShards' specify how many parity packets will be generated following the data packets.
+//
+// Check https://github.com/klauspost/reedsolomon for details
+func ListenWithOptions(laddr string, block BlockCrypt, dataShards, parityShards int) (*Listener, error) {
+ udpaddr, err := net.ResolveUDPAddr("udp", laddr)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ conn, err := net.ListenUDP("udp", udpaddr)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ return serveConn(block, dataShards, parityShards, conn, true)
+}
+
+// ServeConn serves KCP protocol for a single packet connection.
+func ServeConn(block BlockCrypt, dataShards, parityShards int, conn net.PacketConn) (*Listener, error) {
+ return serveConn(block, dataShards, parityShards, conn, false)
+}
+
+func serveConn(block BlockCrypt, dataShards, parityShards int, conn net.PacketConn, ownConn bool) (*Listener, error) {
+ l := new(Listener)
+ l.conn = conn
+ l.ownConn = ownConn
+ l.sessions = make(map[string]*UDPSession)
+ l.chAccepts = make(chan *UDPSession, acceptBacklog)
+ l.chSessionClosed = make(chan net.Addr)
+ l.die = make(chan struct{})
+ l.dataShards = dataShards
+ l.parityShards = parityShards
+ l.block = block
+ l.chSocketReadError = make(chan struct{})
+ go l.monitor()
+ return l, nil
+}
+
+// Dial connects to the remote address "raddr" on the network "udp" without encryption and FEC
+func Dial(raddr string) (net.Conn, error) { return DialWithOptions(raddr, nil, 0, 0) }
+
+// DialWithOptions connects to the remote address "raddr" on the network "udp" with packet encryption
+//
+// 'block' is the block encryption algorithm to encrypt packets.
+//
+// 'dataShards', 'parityShards' specify how many parity packets will be generated following the data packets.
+//
+// Check https://github.com/klauspost/reedsolomon for details
+func DialWithOptions(raddr string, block BlockCrypt, dataShards, parityShards int) (*UDPSession, error) {
+ // network type detection
+ udpaddr, err := net.ResolveUDPAddr("udp", raddr)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ network := "udp4"
+ if udpaddr.IP.To4() == nil {
+ network = "udp"
+ }
+
+ conn, err := net.ListenUDP(network, nil)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+
+ var convid uint32
+ binary.Read(rand.Reader, binary.LittleEndian, &convid)
+ return newUDPSession(convid, dataShards, parityShards, nil, conn, true, udpaddr, block), nil
+}
+
+// NewConn3 establishes a session and talks KCP protocol over a packet connection.
+func NewConn3(convid uint32, raddr net.Addr, block BlockCrypt, dataShards, parityShards int, conn net.PacketConn) (*UDPSession, error) {
+ return newUDPSession(convid, dataShards, parityShards, nil, conn, false, raddr, block), nil
+}
+
+// NewConn2 establishes a session and talks KCP protocol over a packet connection.
+func NewConn2(raddr net.Addr, block BlockCrypt, dataShards, parityShards int, conn net.PacketConn) (*UDPSession, error) {
+ var convid uint32
+ binary.Read(rand.Reader, binary.LittleEndian, &convid)
+ return NewConn3(convid, raddr, block, dataShards, parityShards, conn)
+}
+
+// NewConn establishes a session and talks KCP protocol over a packet connection.
+func NewConn(raddr string, block BlockCrypt, dataShards, parityShards int, conn net.PacketConn) (*UDPSession, error) {
+ udpaddr, err := net.ResolveUDPAddr("udp", raddr)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ return NewConn2(udpaddr, block, dataShards, parityShards, conn)
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/snmp.go b/vendor/github.com/xtaci/kcp-go/v5/snmp.go
new file mode 100644
index 0000000..f961810
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/snmp.go
@@ -0,0 +1,164 @@
+package kcp
+
+import (
+ "fmt"
+ "sync/atomic"
+)
+
+// Snmp defines network statistics indicator
+type Snmp struct {
+ BytesSent uint64 // bytes sent from upper level
+ BytesReceived uint64 // bytes received to upper level
+ MaxConn uint64 // max number of connections ever reached
+ ActiveOpens uint64 // accumulated active open connections
+ PassiveOpens uint64 // accumulated passive open connections
+ CurrEstab uint64 // current number of established connections
+ InErrs uint64 // UDP read errors reported from net.PacketConn
+ InCsumErrors uint64 // checksum errors from CRC32
+ KCPInErrors uint64 // packet iput errors reported from KCP
+ InPkts uint64 // incoming packets count
+ OutPkts uint64 // outgoing packets count
+ InSegs uint64 // incoming KCP segments
+ OutSegs uint64 // outgoing KCP segments
+ InBytes uint64 // UDP bytes received
+ OutBytes uint64 // UDP bytes sent
+ RetransSegs uint64 // accmulated retransmited segments
+ FastRetransSegs uint64 // accmulated fast retransmitted segments
+ EarlyRetransSegs uint64 // accmulated early retransmitted segments
+ LostSegs uint64 // number of segs inferred as lost
+ RepeatSegs uint64 // number of segs duplicated
+ FECRecovered uint64 // correct packets recovered from FEC
+ FECErrs uint64 // incorrect packets recovered from FEC
+ FECParityShards uint64 // FEC segments received
+ FECShortShards uint64 // number of data shards that's not enough for recovery
+}
+
+func newSnmp() *Snmp {
+ return new(Snmp)
+}
+
+// Header returns all field names
+func (s *Snmp) Header() []string {
+ return []string{
+ "BytesSent",
+ "BytesReceived",
+ "MaxConn",
+ "ActiveOpens",
+ "PassiveOpens",
+ "CurrEstab",
+ "InErrs",
+ "InCsumErrors",
+ "KCPInErrors",
+ "InPkts",
+ "OutPkts",
+ "InSegs",
+ "OutSegs",
+ "InBytes",
+ "OutBytes",
+ "RetransSegs",
+ "FastRetransSegs",
+ "EarlyRetransSegs",
+ "LostSegs",
+ "RepeatSegs",
+ "FECParityShards",
+ "FECErrs",
+ "FECRecovered",
+ "FECShortShards",
+ }
+}
+
+// ToSlice returns current snmp info as slice
+func (s *Snmp) ToSlice() []string {
+ snmp := s.Copy()
+ return []string{
+ fmt.Sprint(snmp.BytesSent),
+ fmt.Sprint(snmp.BytesReceived),
+ fmt.Sprint(snmp.MaxConn),
+ fmt.Sprint(snmp.ActiveOpens),
+ fmt.Sprint(snmp.PassiveOpens),
+ fmt.Sprint(snmp.CurrEstab),
+ fmt.Sprint(snmp.InErrs),
+ fmt.Sprint(snmp.InCsumErrors),
+ fmt.Sprint(snmp.KCPInErrors),
+ fmt.Sprint(snmp.InPkts),
+ fmt.Sprint(snmp.OutPkts),
+ fmt.Sprint(snmp.InSegs),
+ fmt.Sprint(snmp.OutSegs),
+ fmt.Sprint(snmp.InBytes),
+ fmt.Sprint(snmp.OutBytes),
+ fmt.Sprint(snmp.RetransSegs),
+ fmt.Sprint(snmp.FastRetransSegs),
+ fmt.Sprint(snmp.EarlyRetransSegs),
+ fmt.Sprint(snmp.LostSegs),
+ fmt.Sprint(snmp.RepeatSegs),
+ fmt.Sprint(snmp.FECParityShards),
+ fmt.Sprint(snmp.FECErrs),
+ fmt.Sprint(snmp.FECRecovered),
+ fmt.Sprint(snmp.FECShortShards),
+ }
+}
+
+// Copy make a copy of current snmp snapshot
+func (s *Snmp) Copy() *Snmp {
+ d := newSnmp()
+ d.BytesSent = atomic.LoadUint64(&s.BytesSent)
+ d.BytesReceived = atomic.LoadUint64(&s.BytesReceived)
+ d.MaxConn = atomic.LoadUint64(&s.MaxConn)
+ d.ActiveOpens = atomic.LoadUint64(&s.ActiveOpens)
+ d.PassiveOpens = atomic.LoadUint64(&s.PassiveOpens)
+ d.CurrEstab = atomic.LoadUint64(&s.CurrEstab)
+ d.InErrs = atomic.LoadUint64(&s.InErrs)
+ d.InCsumErrors = atomic.LoadUint64(&s.InCsumErrors)
+ d.KCPInErrors = atomic.LoadUint64(&s.KCPInErrors)
+ d.InPkts = atomic.LoadUint64(&s.InPkts)
+ d.OutPkts = atomic.LoadUint64(&s.OutPkts)
+ d.InSegs = atomic.LoadUint64(&s.InSegs)
+ d.OutSegs = atomic.LoadUint64(&s.OutSegs)
+ d.InBytes = atomic.LoadUint64(&s.InBytes)
+ d.OutBytes = atomic.LoadUint64(&s.OutBytes)
+ d.RetransSegs = atomic.LoadUint64(&s.RetransSegs)
+ d.FastRetransSegs = atomic.LoadUint64(&s.FastRetransSegs)
+ d.EarlyRetransSegs = atomic.LoadUint64(&s.EarlyRetransSegs)
+ d.LostSegs = atomic.LoadUint64(&s.LostSegs)
+ d.RepeatSegs = atomic.LoadUint64(&s.RepeatSegs)
+ d.FECParityShards = atomic.LoadUint64(&s.FECParityShards)
+ d.FECErrs = atomic.LoadUint64(&s.FECErrs)
+ d.FECRecovered = atomic.LoadUint64(&s.FECRecovered)
+ d.FECShortShards = atomic.LoadUint64(&s.FECShortShards)
+ return d
+}
+
+// Reset values to zero
+func (s *Snmp) Reset() {
+ atomic.StoreUint64(&s.BytesSent, 0)
+ atomic.StoreUint64(&s.BytesReceived, 0)
+ atomic.StoreUint64(&s.MaxConn, 0)
+ atomic.StoreUint64(&s.ActiveOpens, 0)
+ atomic.StoreUint64(&s.PassiveOpens, 0)
+ atomic.StoreUint64(&s.CurrEstab, 0)
+ atomic.StoreUint64(&s.InErrs, 0)
+ atomic.StoreUint64(&s.InCsumErrors, 0)
+ atomic.StoreUint64(&s.KCPInErrors, 0)
+ atomic.StoreUint64(&s.InPkts, 0)
+ atomic.StoreUint64(&s.OutPkts, 0)
+ atomic.StoreUint64(&s.InSegs, 0)
+ atomic.StoreUint64(&s.OutSegs, 0)
+ atomic.StoreUint64(&s.InBytes, 0)
+ atomic.StoreUint64(&s.OutBytes, 0)
+ atomic.StoreUint64(&s.RetransSegs, 0)
+ atomic.StoreUint64(&s.FastRetransSegs, 0)
+ atomic.StoreUint64(&s.EarlyRetransSegs, 0)
+ atomic.StoreUint64(&s.LostSegs, 0)
+ atomic.StoreUint64(&s.RepeatSegs, 0)
+ atomic.StoreUint64(&s.FECParityShards, 0)
+ atomic.StoreUint64(&s.FECErrs, 0)
+ atomic.StoreUint64(&s.FECRecovered, 0)
+ atomic.StoreUint64(&s.FECShortShards, 0)
+}
+
+// DefaultSnmp is the global KCP connection statistics collector
+var DefaultSnmp *Snmp
+
+func init() {
+ DefaultSnmp = newSnmp()
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/timedsched.go b/vendor/github.com/xtaci/kcp-go/v5/timedsched.go
new file mode 100644
index 0000000..2db7c20
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/timedsched.go
@@ -0,0 +1,146 @@
+package kcp
+
+import (
+ "container/heap"
+ "runtime"
+ "sync"
+ "time"
+)
+
+// SystemTimedSched is the library level timed-scheduler
+var SystemTimedSched *TimedSched = NewTimedSched(runtime.NumCPU())
+
+type timedFunc struct {
+ execute func()
+ ts time.Time
+}
+
+// a heap for sorted timed function
+type timedFuncHeap []timedFunc
+
+func (h timedFuncHeap) Len() int { return len(h) }
+func (h timedFuncHeap) Less(i, j int) bool { return h[i].ts.Before(h[j].ts) }
+func (h timedFuncHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+func (h *timedFuncHeap) Push(x interface{}) { *h = append(*h, x.(timedFunc)) }
+func (h *timedFuncHeap) Pop() interface{} {
+ old := *h
+ n := len(old)
+ x := old[n-1]
+ old[n-1].execute = nil // avoid memory leak
+ *h = old[0 : n-1]
+ return x
+}
+
+// TimedSched represents the control struct for timed parallel scheduler
+type TimedSched struct {
+ // prepending tasks
+ prependTasks []timedFunc
+ prependLock sync.Mutex
+ chPrependNotify chan struct{}
+
+ // tasks will be distributed through chTask
+ chTask chan timedFunc
+
+ dieOnce sync.Once
+ die chan struct{}
+}
+
+// NewTimedSched creates a parallel-scheduler with given parallelization
+func NewTimedSched(parallel int) *TimedSched {
+ ts := new(TimedSched)
+ ts.chTask = make(chan timedFunc)
+ ts.die = make(chan struct{})
+ ts.chPrependNotify = make(chan struct{}, 1)
+
+ for i := 0; i < parallel; i++ {
+ go ts.sched()
+ }
+ go ts.prepend()
+ return ts
+}
+
+func (ts *TimedSched) sched() {
+ var tasks timedFuncHeap
+ timer := time.NewTimer(0)
+ drained := false
+ for {
+ select {
+ case task := <-ts.chTask:
+ now := time.Now()
+ if now.After(task.ts) {
+ // already delayed! execute immediately
+ task.execute()
+ } else {
+ heap.Push(&tasks, task)
+ // properly reset timer to trigger based on the top element
+ stopped := timer.Stop()
+ if !stopped && !drained {
+ <-timer.C
+ }
+ timer.Reset(tasks[0].ts.Sub(now))
+ drained = false
+ }
+ case now := <-timer.C:
+ drained = true
+ for tasks.Len() > 0 {
+ if now.After(tasks[0].ts) {
+ heap.Pop(&tasks).(timedFunc).execute()
+ } else {
+ timer.Reset(tasks[0].ts.Sub(now))
+ drained = false
+ break
+ }
+ }
+ case <-ts.die:
+ return
+ }
+ }
+}
+
+func (ts *TimedSched) prepend() {
+ var tasks []timedFunc
+ for {
+ select {
+ case <-ts.chPrependNotify:
+ ts.prependLock.Lock()
+ // keep cap to reuse slice
+ if cap(tasks) < cap(ts.prependTasks) {
+ tasks = make([]timedFunc, 0, cap(ts.prependTasks))
+ }
+ tasks = tasks[:len(ts.prependTasks)]
+ copy(tasks, ts.prependTasks)
+ for k := range ts.prependTasks {
+ ts.prependTasks[k].execute = nil // avoid memory leak
+ }
+ ts.prependTasks = ts.prependTasks[:0]
+ ts.prependLock.Unlock()
+
+ for k := range tasks {
+ select {
+ case ts.chTask <- tasks[k]:
+ tasks[k].execute = nil // avoid memory leak
+ case <-ts.die:
+ return
+ }
+ }
+ tasks = tasks[:0]
+ case <-ts.die:
+ return
+ }
+ }
+}
+
+// Put a function 'f' awaiting to be executed at 'deadline'
+func (ts *TimedSched) Put(f func(), deadline time.Time) {
+ ts.prependLock.Lock()
+ ts.prependTasks = append(ts.prependTasks, timedFunc{f, deadline})
+ ts.prependLock.Unlock()
+
+ select {
+ case ts.chPrependNotify <- struct{}{}:
+ default:
+ }
+}
+
+// Close terminates this scheduler
+func (ts *TimedSched) Close() { ts.dieOnce.Do(func() { close(ts.die) }) }
diff --git a/vendor/github.com/xtaci/kcp-go/v5/tx.go b/vendor/github.com/xtaci/kcp-go/v5/tx.go
new file mode 100644
index 0000000..3397b82
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/tx.go
@@ -0,0 +1,24 @@
+package kcp
+
+import (
+ "sync/atomic"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/ipv4"
+)
+
+func (s *UDPSession) defaultTx(txqueue []ipv4.Message) {
+ nbytes := 0
+ npkts := 0
+ for k := range txqueue {
+ if n, err := s.conn.WriteTo(txqueue[k].Buffers[0], txqueue[k].Addr); err == nil {
+ nbytes += n
+ npkts++
+ } else {
+ s.notifyWriteError(errors.WithStack(err))
+ break
+ }
+ }
+ atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
+ atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/tx_generic.go b/vendor/github.com/xtaci/kcp-go/v5/tx_generic.go
new file mode 100644
index 0000000..0b4f349
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/tx_generic.go
@@ -0,0 +1,11 @@
+// +build !linux
+
+package kcp
+
+import (
+ "golang.org/x/net/ipv4"
+)
+
+func (s *UDPSession) tx(txqueue []ipv4.Message) {
+ s.defaultTx(txqueue)
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/tx_linux.go b/vendor/github.com/xtaci/kcp-go/v5/tx_linux.go
new file mode 100644
index 0000000..4f19df5
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/tx_linux.go
@@ -0,0 +1,51 @@
+// +build linux
+
+package kcp
+
+import (
+ "net"
+ "os"
+ "sync/atomic"
+
+ "github.com/pkg/errors"
+ "golang.org/x/net/ipv4"
+)
+
+func (s *UDPSession) tx(txqueue []ipv4.Message) {
+ // default version
+ if s.xconn == nil || s.xconnWriteError != nil {
+ s.defaultTx(txqueue)
+ return
+ }
+
+ // x/net version
+ nbytes := 0
+ npkts := 0
+ for len(txqueue) > 0 {
+ if n, err := s.xconn.WriteBatch(txqueue, 0); err == nil {
+ for k := range txqueue[:n] {
+ nbytes += len(txqueue[k].Buffers[0])
+ }
+ npkts += n
+ txqueue = txqueue[n:]
+ } else {
+ // compatibility issue:
+ // for linux kernel<=2.6.32, support for sendmmsg is not available
+ // an error of type os.SyscallError will be returned
+ if operr, ok := err.(*net.OpError); ok {
+ if se, ok := operr.Err.(*os.SyscallError); ok {
+ if se.Syscall == "sendmmsg" {
+ s.xconnWriteError = se
+ s.defaultTx(txqueue)
+ return
+ }
+ }
+ }
+ s.notifyWriteError(errors.WithStack(err))
+ break
+ }
+ }
+
+ atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
+ atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
+}
diff --git a/vendor/github.com/xtaci/kcp-go/v5/wechat_donate.jpg b/vendor/github.com/xtaci/kcp-go/v5/wechat_donate.jpg
new file mode 100644
index 0000000..ad72505
--- /dev/null
+++ b/vendor/github.com/xtaci/kcp-go/v5/wechat_donate.jpg
Binary files differ
diff --git a/vendor/github.com/xtaci/smux/.gitignore b/vendor/github.com/xtaci/smux/.gitignore
new file mode 100644
index 0000000..daf913b
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/xtaci/smux/.travis.yml b/vendor/github.com/xtaci/smux/.travis.yml
new file mode 100644
index 0000000..e1d30fa
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/.travis.yml
@@ -0,0 +1,17 @@
+language: go
+go:
+ - 1.9.x
+ - 1.10.x
+ - 1.11.x
+
+before_install:
+ - go get -t -v ./...
+
+install:
+ - go get github.com/xtaci/smux
+
+script:
+ - go test -coverprofile=coverage.txt -covermode=atomic -bench .
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/github.com/xtaci/smux/LICENSE b/vendor/github.com/xtaci/smux/LICENSE
new file mode 100644
index 0000000..eed41ac
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016-2017 Daniel Fu
+
+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.
diff --git a/vendor/github.com/xtaci/smux/README.md b/vendor/github.com/xtaci/smux/README.md
new file mode 100644
index 0000000..a5c4680
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/README.md
@@ -0,0 +1,136 @@
+<img src="smux.png" alt="smux" height="35px" />
+
+[![GoDoc][1]][2] [![MIT licensed][3]][4] [![Build Status][5]][6] [![Go Report Card][7]][8] [![Coverage Statusd][9]][10] [![Sourcegraph][11]][12]
+
+<img src="mux.jpg" alt="smux" height="120px" />
+
+[1]: https://godoc.org/github.com/xtaci/smux?status.svg
+[2]: https://godoc.org/github.com/xtaci/smux
+[3]: https://img.shields.io/badge/license-MIT-blue.svg
+[4]: LICENSE
+[5]: https://travis-ci.org/xtaci/smux.svg?branch=master
+[6]: https://travis-ci.org/xtaci/smux
+[7]: https://goreportcard.com/badge/github.com/xtaci/smux
+[8]: https://goreportcard.com/report/github.com/xtaci/smux
+[9]: https://codecov.io/gh/xtaci/smux/branch/master/graph/badge.svg
+[10]: https://codecov.io/gh/xtaci/smux
+[11]: https://sourcegraph.com/github.com/xtaci/smux/-/badge.svg
+[12]: https://sourcegraph.com/github.com/xtaci/smux?badge
+
+## Introduction
+
+Smux ( **S**imple **MU**ltiple**X**ing) is a multiplexing library for Golang. It relies on an underlying connection to provide reliability and ordering, such as TCP or [KCP](https://github.com/xtaci/kcp-go), and provides stream-oriented multiplexing. The original intention of this library is to power the connection management for [kcp-go](https://github.com/xtaci/kcp-go).
+
+## Features
+
+1. ***Token bucket*** controlled receiving, which provides smoother bandwidth graph(see picture below).
+2. Session-wide receive buffer, shared among streams, **fully controlled** overall memory usage.
+3. Minimized header(8Bytes), maximized payload.
+4. Well-tested on millions of devices in [kcptun](https://github.com/xtaci/kcptun).
+5. Builtin fair queue traffic shaping.
+6. Per-stream sliding window to control congestion.(protocol version 2+).
+
+![smooth bandwidth curve](curve.jpg)
+
+## Documentation
+
+For complete documentation, see the associated [Godoc](https://godoc.org/github.com/xtaci/smux).
+
+## Benchmark
+```
+$ go test -v -run=^$ -bench .
+goos: darwin
+goarch: amd64
+pkg: github.com/xtaci/smux
+BenchmarkMSB-4 30000000 51.8 ns/op
+BenchmarkAcceptClose-4 50000 36783 ns/op
+BenchmarkConnSmux-4 30000 58335 ns/op 2246.88 MB/s 1208 B/op 19 allocs/op
+BenchmarkConnTCP-4 50000 25579 ns/op 5124.04 MB/s 0 B/op 0 allocs/op
+PASS
+ok github.com/xtaci/smux 7.811s
+```
+
+## Specification
+
+```
+VERSION(1B) | CMD(1B) | LENGTH(2B) | STREAMID(4B) | DATA(LENGTH)
+
+VALUES FOR LATEST VERSION:
+VERSION:
+ 1/2
+
+CMD:
+ cmdSYN(0)
+ cmdFIN(1)
+ cmdPSH(2)
+ cmdNOP(3)
+ cmdUPD(4) // only supported on version 2
+
+STREAMID:
+ client use odd numbers starts from 1
+ server use even numbers starts from 0
+
+cmdUPD:
+ | CONSUMED(4B) | WINDOW(4B) |
+```
+
+## Usage
+
+```go
+
+func client() {
+ // Get a TCP connection
+ conn, err := net.Dial(...)
+ if err != nil {
+ panic(err)
+ }
+
+ // Setup client side of smux
+ session, err := smux.Client(conn, nil)
+ if err != nil {
+ panic(err)
+ }
+
+ // Open a new stream
+ stream, err := session.OpenStream()
+ if err != nil {
+ panic(err)
+ }
+
+ // Stream implements io.ReadWriteCloser
+ stream.Write([]byte("ping"))
+ stream.Close()
+ session.Close()
+}
+
+func server() {
+ // Accept a TCP connection
+ conn, err := listener.Accept()
+ if err != nil {
+ panic(err)
+ }
+
+ // Setup server side of smux
+ session, err := smux.Server(conn, nil)
+ if err != nil {
+ panic(err)
+ }
+
+ // Accept a stream
+ stream, err := session.AcceptStream()
+ if err != nil {
+ panic(err)
+ }
+
+ // Listen for a message
+ buf := make([]byte, 4)
+ stream.Read(buf)
+ stream.Close()
+ session.Close()
+}
+
+```
+
+## Status
+
+Stable
diff --git a/vendor/github.com/xtaci/smux/alloc.go b/vendor/github.com/xtaci/smux/alloc.go
new file mode 100644
index 0000000..9e3acf3
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/alloc.go
@@ -0,0 +1,72 @@
+package smux
+
+import (
+ "errors"
+ "sync"
+)
+
+var (
+ defaultAllocator *Allocator
+ debruijinPos = [...]byte{0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}
+)
+
+func init() {
+ defaultAllocator = NewAllocator()
+}
+
+// Allocator for incoming frames, optimized to prevent overwriting after zeroing
+type Allocator struct {
+ buffers []sync.Pool
+}
+
+// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
+// the waste(memory fragmentation) of space allocation is guaranteed to be
+// no more than 50%.
+func NewAllocator() *Allocator {
+ alloc := new(Allocator)
+ alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K
+ for k := range alloc.buffers {
+ i := k
+ alloc.buffers[k].New = func() interface{} {
+ return make([]byte, 1<<uint32(i))
+ }
+ }
+ return alloc
+}
+
+// Get a []byte from pool with most appropriate cap
+func (alloc *Allocator) Get(size int) []byte {
+ if size <= 0 || size > 65536 {
+ return nil
+ }
+
+ bits := msb(size)
+ if size == 1<<bits {
+ return alloc.buffers[bits].Get().([]byte)[:size]
+ } else {
+ return alloc.buffers[bits+1].Get().([]byte)[:size]
+ }
+}
+
+// Put returns a []byte to pool for future use,
+// which the cap must be exactly 2^n
+func (alloc *Allocator) Put(buf []byte) error {
+ bits := msb(cap(buf))
+ if cap(buf) == 0 || cap(buf) > 65536 || cap(buf) != 1<<bits {
+ return errors.New("allocator Put() incorrect buffer size")
+ }
+ alloc.buffers[bits].Put(buf)
+ return nil
+}
+
+// msb return the pos of most significiant bit
+// http://supertech.csail.mit.edu/papers/debruijn.pdf
+func msb(size int) byte {
+ v := uint32(size)
+ v |= v >> 1
+ v |= v >> 2
+ v |= v >> 4
+ v |= v >> 8
+ v |= v >> 16
+ return debruijinPos[(v*0x07C4ACDD)>>27]
+}
diff --git a/vendor/github.com/xtaci/smux/curve.jpg b/vendor/github.com/xtaci/smux/curve.jpg
new file mode 100644
index 0000000..3fc4863
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/curve.jpg
Binary files differ
diff --git a/vendor/github.com/xtaci/smux/frame.go b/vendor/github.com/xtaci/smux/frame.go
new file mode 100644
index 0000000..467a058
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/frame.go
@@ -0,0 +1,81 @@
+package smux
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+const ( // cmds
+ // protocol version 1:
+ cmdSYN byte = iota // stream open
+ cmdFIN // stream close, a.k.a EOF mark
+ cmdPSH // data push
+ cmdNOP // no operation
+
+ // protocol version 2 extra commands
+ // notify bytes consumed by remote peer-end
+ cmdUPD
+)
+
+const (
+ // data size of cmdUPD, format:
+ // |4B data consumed(ACK)| 4B window size(WINDOW) |
+ szCmdUPD = 8
+)
+
+const (
+ // initial peer window guess, a slow-start
+ initialPeerWindow = 262144
+)
+
+const (
+ sizeOfVer = 1
+ sizeOfCmd = 1
+ sizeOfLength = 2
+ sizeOfSid = 4
+ headerSize = sizeOfVer + sizeOfCmd + sizeOfSid + sizeOfLength
+)
+
+// Frame defines a packet from or to be multiplexed into a single connection
+type Frame struct {
+ ver byte
+ cmd byte
+ sid uint32
+ data []byte
+}
+
+func newFrame(version byte, cmd byte, sid uint32) Frame {
+ return Frame{ver: version, cmd: cmd, sid: sid}
+}
+
+type rawHeader [headerSize]byte
+
+func (h rawHeader) Version() byte {
+ return h[0]
+}
+
+func (h rawHeader) Cmd() byte {
+ return h[1]
+}
+
+func (h rawHeader) Length() uint16 {
+ return binary.LittleEndian.Uint16(h[2:])
+}
+
+func (h rawHeader) StreamID() uint32 {
+ return binary.LittleEndian.Uint32(h[4:])
+}
+
+func (h rawHeader) String() string {
+ return fmt.Sprintf("Version:%d Cmd:%d StreamID:%d Length:%d",
+ h.Version(), h.Cmd(), h.StreamID(), h.Length())
+}
+
+type updHeader [szCmdUPD]byte
+
+func (h updHeader) Consumed() uint32 {
+ return binary.LittleEndian.Uint32(h[:])
+}
+func (h updHeader) Window() uint32 {
+ return binary.LittleEndian.Uint32(h[4:])
+}
diff --git a/vendor/github.com/xtaci/smux/go.mod b/vendor/github.com/xtaci/smux/go.mod
new file mode 100644
index 0000000..9ddead6
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/go.mod
@@ -0,0 +1,3 @@
+module github.com/xtaci/smux
+
+go 1.13
diff --git a/vendor/github.com/xtaci/smux/go.sum b/vendor/github.com/xtaci/smux/go.sum
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/go.sum
diff --git a/vendor/github.com/xtaci/smux/mux.go b/vendor/github.com/xtaci/smux/mux.go
new file mode 100644
index 0000000..c0b8ab8
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/mux.go
@@ -0,0 +1,110 @@
+// Package smux is a multiplexing library for Golang.
+//
+// It relies on an underlying connection to provide reliability and ordering, such as TCP or KCP,
+// and provides stream-oriented multiplexing over a single channel.
+package smux
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "math"
+ "time"
+)
+
+// Config is used to tune the Smux session
+type Config struct {
+ // SMUX Protocol version, support 1,2
+ Version int
+
+ // Disabled keepalive
+ KeepAliveDisabled bool
+
+ // KeepAliveInterval is how often to send a NOP command to the remote
+ KeepAliveInterval time.Duration
+
+ // KeepAliveTimeout is how long the session
+ // will be closed if no data has arrived
+ KeepAliveTimeout time.Duration
+
+ // MaxFrameSize is used to control the maximum
+ // frame size to sent to the remote
+ MaxFrameSize int
+
+ // MaxReceiveBuffer is used to control the maximum
+ // number of data in the buffer pool
+ MaxReceiveBuffer int
+
+ // MaxStreamBuffer is used to control the maximum
+ // number of data per stream
+ MaxStreamBuffer int
+}
+
+// DefaultConfig is used to return a default configuration
+func DefaultConfig() *Config {
+ return &Config{
+ Version: 1,
+ KeepAliveInterval: 10 * time.Second,
+ KeepAliveTimeout: 30 * time.Second,
+ MaxFrameSize: 32768,
+ MaxReceiveBuffer: 4194304,
+ MaxStreamBuffer: 65536,
+ }
+}
+
+// VerifyConfig is used to verify the sanity of configuration
+func VerifyConfig(config *Config) error {
+ if !(config.Version == 1 || config.Version == 2) {
+ return errors.New("unsupported protocol version")
+ }
+ if !config.KeepAliveDisabled {
+ if config.KeepAliveInterval == 0 {
+ return errors.New("keep-alive interval must be positive")
+ }
+ if config.KeepAliveTimeout < config.KeepAliveInterval {
+ return fmt.Errorf("keep-alive timeout must be larger than keep-alive interval")
+ }
+ }
+ if config.MaxFrameSize <= 0 {
+ return errors.New("max frame size must be positive")
+ }
+ if config.MaxFrameSize > 65535 {
+ return errors.New("max frame size must not be larger than 65535")
+ }
+ if config.MaxReceiveBuffer <= 0 {
+ return errors.New("max receive buffer must be positive")
+ }
+ if config.MaxStreamBuffer <= 0 {
+ return errors.New("max stream buffer must be positive")
+ }
+ if config.MaxStreamBuffer > config.MaxReceiveBuffer {
+ return errors.New("max stream buffer must not be larger than max receive buffer")
+ }
+ if config.MaxStreamBuffer > math.MaxInt32 {
+ return errors.New("max stream buffer cannot be larger than 2147483647")
+ }
+ return nil
+}
+
+// Server is used to initialize a new server-side connection.
+func Server(conn io.ReadWriteCloser, config *Config) (*Session, error) {
+ if config == nil {
+ config = DefaultConfig()
+ }
+ if err := VerifyConfig(config); err != nil {
+ return nil, err
+ }
+ return newSession(config, conn, false), nil
+}
+
+// Client is used to initialize a new client-side connection.
+func Client(conn io.ReadWriteCloser, config *Config) (*Session, error) {
+ if config == nil {
+ config = DefaultConfig()
+ }
+
+ if err := VerifyConfig(config); err != nil {
+ return nil, err
+ }
+ return newSession(config, conn, true), nil
+}
diff --git a/vendor/github.com/xtaci/smux/mux.jpg b/vendor/github.com/xtaci/smux/mux.jpg
new file mode 100644
index 0000000..dde2e11
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/mux.jpg
Binary files differ
diff --git a/vendor/github.com/xtaci/smux/session.go b/vendor/github.com/xtaci/smux/session.go
new file mode 100644
index 0000000..bc56066
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/session.go
@@ -0,0 +1,525 @@
+package smux
+
+import (
+ "container/heap"
+ "encoding/binary"
+ "errors"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+const (
+ defaultAcceptBacklog = 1024
+)
+
+var (
+ ErrInvalidProtocol = errors.New("invalid protocol")
+ ErrConsumed = errors.New("peer consumed more than sent")
+ ErrGoAway = errors.New("stream id overflows, should start a new connection")
+ ErrTimeout = errors.New("timeout")
+ ErrWouldBlock = errors.New("operation would block on IO")
+)
+
+type writeRequest struct {
+ prio uint64
+ frame Frame
+ result chan writeResult
+}
+
+type writeResult struct {
+ n int
+ err error
+}
+
+type buffersWriter interface {
+ WriteBuffers(v [][]byte) (n int, err error)
+}
+
+// Session defines a multiplexed connection for streams
+type Session struct {
+ conn io.ReadWriteCloser
+
+ config *Config
+ nextStreamID uint32 // next stream identifier
+ nextStreamIDLock sync.Mutex
+
+ bucket int32 // token bucket
+ bucketNotify chan struct{} // used for waiting for tokens
+
+ streams map[uint32]*Stream // all streams in this session
+ streamLock sync.Mutex // locks streams
+
+ die chan struct{} // flag session has died
+ dieOnce sync.Once
+
+ // socket error handling
+ socketReadError atomic.Value
+ socketWriteError atomic.Value
+ chSocketReadError chan struct{}
+ chSocketWriteError chan struct{}
+ socketReadErrorOnce sync.Once
+ socketWriteErrorOnce sync.Once
+
+ // smux protocol errors
+ protoError atomic.Value
+ chProtoError chan struct{}
+ protoErrorOnce sync.Once
+
+ chAccepts chan *Stream
+
+ dataReady int32 // flag data has arrived
+
+ goAway int32 // flag id exhausted
+
+ deadline atomic.Value
+
+ shaper chan writeRequest // a shaper for writing
+ writes chan writeRequest
+}
+
+func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
+ s := new(Session)
+ s.die = make(chan struct{})
+ s.conn = conn
+ s.config = config
+ s.streams = make(map[uint32]*Stream)
+ s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
+ s.bucket = int32(config.MaxReceiveBuffer)
+ s.bucketNotify = make(chan struct{}, 1)
+ s.shaper = make(chan writeRequest)
+ s.writes = make(chan writeRequest)
+ s.chSocketReadError = make(chan struct{})
+ s.chSocketWriteError = make(chan struct{})
+ s.chProtoError = make(chan struct{})
+
+ if client {
+ s.nextStreamID = 1
+ } else {
+ s.nextStreamID = 0
+ }
+
+ go s.shaperLoop()
+ go s.recvLoop()
+ go s.sendLoop()
+ if !config.KeepAliveDisabled {
+ go s.keepalive()
+ }
+ return s
+}
+
+// OpenStream is used to create a new stream
+func (s *Session) OpenStream() (*Stream, error) {
+ if s.IsClosed() {
+ return nil, io.ErrClosedPipe
+ }
+
+ // generate stream id
+ s.nextStreamIDLock.Lock()
+ if s.goAway > 0 {
+ s.nextStreamIDLock.Unlock()
+ return nil, ErrGoAway
+ }
+
+ s.nextStreamID += 2
+ sid := s.nextStreamID
+ if sid == sid%2 { // stream-id overflows
+ s.goAway = 1
+ s.nextStreamIDLock.Unlock()
+ return nil, ErrGoAway
+ }
+ s.nextStreamIDLock.Unlock()
+
+ stream := newStream(sid, s.config.MaxFrameSize, s)
+
+ if _, err := s.writeFrame(newFrame(byte(s.config.Version), cmdSYN, sid)); err != nil {
+ return nil, err
+ }
+
+ s.streamLock.Lock()
+ defer s.streamLock.Unlock()
+ select {
+ case <-s.chSocketReadError:
+ return nil, s.socketReadError.Load().(error)
+ case <-s.chSocketWriteError:
+ return nil, s.socketWriteError.Load().(error)
+ case <-s.die:
+ return nil, io.ErrClosedPipe
+ default:
+ s.streams[sid] = stream
+ return stream, nil
+ }
+}
+
+// Open returns a generic ReadWriteCloser
+func (s *Session) Open() (io.ReadWriteCloser, error) {
+ return s.OpenStream()
+}
+
+// AcceptStream is used to block until the next available stream
+// is ready to be accepted.
+func (s *Session) AcceptStream() (*Stream, error) {
+ var deadline <-chan time.Time
+ if d, ok := s.deadline.Load().(time.Time); ok && !d.IsZero() {
+ timer := time.NewTimer(time.Until(d))
+ defer timer.Stop()
+ deadline = timer.C
+ }
+
+ select {
+ case stream := <-s.chAccepts:
+ return stream, nil
+ case <-deadline:
+ return nil, ErrTimeout
+ case <-s.chSocketReadError:
+ return nil, s.socketReadError.Load().(error)
+ case <-s.chProtoError:
+ return nil, s.protoError.Load().(error)
+ case <-s.die:
+ return nil, io.ErrClosedPipe
+ }
+}
+
+// Accept Returns a generic ReadWriteCloser instead of smux.Stream
+func (s *Session) Accept() (io.ReadWriteCloser, error) {
+ return s.AcceptStream()
+}
+
+// Close is used to close the session and all streams.
+func (s *Session) Close() error {
+ var once bool
+ s.dieOnce.Do(func() {
+ close(s.die)
+ once = true
+ })
+
+ if once {
+ s.streamLock.Lock()
+ for k := range s.streams {
+ s.streams[k].sessionClose()
+ }
+ s.streamLock.Unlock()
+ return s.conn.Close()
+ } else {
+ return io.ErrClosedPipe
+ }
+}
+
+// notifyBucket notifies recvLoop that bucket is available
+func (s *Session) notifyBucket() {
+ select {
+ case s.bucketNotify <- struct{}{}:
+ default:
+ }
+}
+
+func (s *Session) notifyReadError(err error) {
+ s.socketReadErrorOnce.Do(func() {
+ s.socketReadError.Store(err)
+ close(s.chSocketReadError)
+ })
+}
+
+func (s *Session) notifyWriteError(err error) {
+ s.socketWriteErrorOnce.Do(func() {
+ s.socketWriteError.Store(err)
+ close(s.chSocketWriteError)
+ })
+}
+
+func (s *Session) notifyProtoError(err error) {
+ s.protoErrorOnce.Do(func() {
+ s.protoError.Store(err)
+ close(s.chProtoError)
+ })
+}
+
+// IsClosed does a safe check to see if we have shutdown
+func (s *Session) IsClosed() bool {
+ select {
+ case <-s.die:
+ return true
+ default:
+ return false
+ }
+}
+
+// NumStreams returns the number of currently open streams
+func (s *Session) NumStreams() int {
+ if s.IsClosed() {
+ return 0
+ }
+ s.streamLock.Lock()
+ defer s.streamLock.Unlock()
+ return len(s.streams)
+}
+
+// SetDeadline sets a deadline used by Accept* calls.
+// A zero time value disables the deadline.
+func (s *Session) SetDeadline(t time.Time) error {
+ s.deadline.Store(t)
+ return nil
+}
+
+// LocalAddr satisfies net.Conn interface
+func (s *Session) LocalAddr() net.Addr {
+ if ts, ok := s.conn.(interface {
+ LocalAddr() net.Addr
+ }); ok {
+ return ts.LocalAddr()
+ }
+ return nil
+}
+
+// RemoteAddr satisfies net.Conn interface
+func (s *Session) RemoteAddr() net.Addr {
+ if ts, ok := s.conn.(interface {
+ RemoteAddr() net.Addr
+ }); ok {
+ return ts.RemoteAddr()
+ }
+ return nil
+}
+
+// notify the session that a stream has closed
+func (s *Session) streamClosed(sid uint32) {
+ s.streamLock.Lock()
+ if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
+ if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
+ s.notifyBucket()
+ }
+ }
+ delete(s.streams, sid)
+ s.streamLock.Unlock()
+}
+
+// returnTokens is called by stream to return token after read
+func (s *Session) returnTokens(n int) {
+ if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
+ s.notifyBucket()
+ }
+}
+
+// recvLoop keeps on reading from underlying connection if tokens are available
+func (s *Session) recvLoop() {
+ var hdr rawHeader
+ var updHdr updHeader
+
+ for {
+ for atomic.LoadInt32(&s.bucket) <= 0 && !s.IsClosed() {
+ select {
+ case <-s.bucketNotify:
+ case <-s.die:
+ return
+ }
+ }
+
+ // read header first
+ if _, err := io.ReadFull(s.conn, hdr[:]); err == nil {
+ atomic.StoreInt32(&s.dataReady, 1)
+ if hdr.Version() != byte(s.config.Version) {
+ s.notifyProtoError(ErrInvalidProtocol)
+ return
+ }
+ sid := hdr.StreamID()
+ switch hdr.Cmd() {
+ case cmdNOP:
+ case cmdSYN:
+ s.streamLock.Lock()
+ if _, ok := s.streams[sid]; !ok {
+ stream := newStream(sid, s.config.MaxFrameSize, s)
+ s.streams[sid] = stream
+ select {
+ case s.chAccepts <- stream:
+ case <-s.die:
+ }
+ }
+ s.streamLock.Unlock()
+ case cmdFIN:
+ s.streamLock.Lock()
+ if stream, ok := s.streams[sid]; ok {
+ stream.fin()
+ stream.notifyReadEvent()
+ }
+ s.streamLock.Unlock()
+ case cmdPSH:
+ if hdr.Length() > 0 {
+ newbuf := defaultAllocator.Get(int(hdr.Length()))
+ if written, err := io.ReadFull(s.conn, newbuf); err == nil {
+ s.streamLock.Lock()
+ if stream, ok := s.streams[sid]; ok {
+ stream.pushBytes(newbuf)
+ atomic.AddInt32(&s.bucket, -int32(written))
+ stream.notifyReadEvent()
+ }
+ s.streamLock.Unlock()
+ } else {
+ s.notifyReadError(err)
+ return
+ }
+ }
+ case cmdUPD:
+ if _, err := io.ReadFull(s.conn, updHdr[:]); err == nil {
+ s.streamLock.Lock()
+ if stream, ok := s.streams[sid]; ok {
+ stream.update(updHdr.Consumed(), updHdr.Window())
+ }
+ s.streamLock.Unlock()
+ } else {
+ s.notifyReadError(err)
+ return
+ }
+ default:
+ s.notifyProtoError(ErrInvalidProtocol)
+ return
+ }
+ } else {
+ s.notifyReadError(err)
+ return
+ }
+ }
+}
+
+func (s *Session) keepalive() {
+ tickerPing := time.NewTicker(s.config.KeepAliveInterval)
+ tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
+ defer tickerPing.Stop()
+ defer tickerTimeout.Stop()
+ for {
+ select {
+ case <-tickerPing.C:
+ s.writeFrameInternal(newFrame(byte(s.config.Version), cmdNOP, 0), tickerPing.C, 0)
+ s.notifyBucket() // force a signal to the recvLoop
+ case <-tickerTimeout.C:
+ if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
+ // recvLoop may block while bucket is 0, in this case,
+ // session should not be closed.
+ if atomic.LoadInt32(&s.bucket) > 0 {
+ s.Close()
+ return
+ }
+ }
+ case <-s.die:
+ return
+ }
+ }
+}
+
+// shaper shapes the sending sequence among streams
+func (s *Session) shaperLoop() {
+ var reqs shaperHeap
+ var next writeRequest
+ var chWrite chan writeRequest
+
+ for {
+ if len(reqs) > 0 {
+ chWrite = s.writes
+ next = heap.Pop(&reqs).(writeRequest)
+ } else {
+ chWrite = nil
+ }
+
+ select {
+ case <-s.die:
+ return
+ case r := <-s.shaper:
+ if chWrite != nil { // next is valid, reshape
+ heap.Push(&reqs, next)
+ }
+ heap.Push(&reqs, r)
+ case chWrite <- next:
+ }
+ }
+}
+
+func (s *Session) sendLoop() {
+ var buf []byte
+ var n int
+ var err error
+ var vec [][]byte // vector for writeBuffers
+
+ bw, ok := s.conn.(buffersWriter)
+ if ok {
+ buf = make([]byte, headerSize)
+ vec = make([][]byte, 2)
+ } else {
+ buf = make([]byte, (1<<16)+headerSize)
+ }
+
+ for {
+ select {
+ case <-s.die:
+ return
+ case request := <-s.writes:
+ buf[0] = request.frame.ver
+ buf[1] = request.frame.cmd
+ binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
+ binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)
+
+ if len(vec) > 0 {
+ vec[0] = buf[:headerSize]
+ vec[1] = request.frame.data
+ n, err = bw.WriteBuffers(vec)
+ } else {
+ copy(buf[headerSize:], request.frame.data)
+ n, err = s.conn.Write(buf[:headerSize+len(request.frame.data)])
+ }
+
+ n -= headerSize
+ if n < 0 {
+ n = 0
+ }
+
+ result := writeResult{
+ n: n,
+ err: err,
+ }
+
+ request.result <- result
+ close(request.result)
+
+ // store conn error
+ if err != nil {
+ s.notifyWriteError(err)
+ return
+ }
+ }
+ }
+}
+
+// writeFrame writes the frame to the underlying connection
+// and returns the number of bytes written if successful
+func (s *Session) writeFrame(f Frame) (n int, err error) {
+ return s.writeFrameInternal(f, nil, 0)
+}
+
+// internal writeFrame version to support deadline used in keepalive
+func (s *Session) writeFrameInternal(f Frame, deadline <-chan time.Time, prio uint64) (int, error) {
+ req := writeRequest{
+ prio: prio,
+ frame: f,
+ result: make(chan writeResult, 1),
+ }
+ select {
+ case s.shaper <- req:
+ case <-s.die:
+ return 0, io.ErrClosedPipe
+ case <-s.chSocketWriteError:
+ return 0, s.socketWriteError.Load().(error)
+ case <-deadline:
+ return 0, ErrTimeout
+ }
+
+ select {
+ case result := <-req.result:
+ return result.n, result.err
+ case <-s.die:
+ return 0, io.ErrClosedPipe
+ case <-s.chSocketWriteError:
+ return 0, s.socketWriteError.Load().(error)
+ case <-deadline:
+ return 0, ErrTimeout
+ }
+}
diff --git a/vendor/github.com/xtaci/smux/shaper.go b/vendor/github.com/xtaci/smux/shaper.go
new file mode 100644
index 0000000..be03406
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/shaper.go
@@ -0,0 +1,16 @@
+package smux
+
+type shaperHeap []writeRequest
+
+func (h shaperHeap) Len() int { return len(h) }
+func (h shaperHeap) Less(i, j int) bool { return h[i].prio < h[j].prio }
+func (h shaperHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
+func (h *shaperHeap) Push(x interface{}) { *h = append(*h, x.(writeRequest)) }
+
+func (h *shaperHeap) Pop() interface{} {
+ old := *h
+ n := len(old)
+ x := old[n-1]
+ *h = old[0 : n-1]
+ return x
+}
diff --git a/vendor/github.com/xtaci/smux/smux.png b/vendor/github.com/xtaci/smux/smux.png
new file mode 100644
index 0000000..26aba3b
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/smux.png
Binary files differ
diff --git a/vendor/github.com/xtaci/smux/stream.go b/vendor/github.com/xtaci/smux/stream.go
new file mode 100644
index 0000000..6c9499c
--- /dev/null
+++ b/vendor/github.com/xtaci/smux/stream.go
@@ -0,0 +1,549 @@
+package smux
+
+import (
+ "encoding/binary"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// Stream implements net.Conn
+type Stream struct {
+ id uint32
+ sess *Session
+
+ buffers [][]byte
+ heads [][]byte // slice heads kept for recycle
+
+ bufferLock sync.Mutex
+ frameSize int
+
+ // notify a read event
+ chReadEvent chan struct{}
+
+ // flag the stream has closed
+ die chan struct{}
+ dieOnce sync.Once
+
+ // FIN command
+ chFinEvent chan struct{}
+ finEventOnce sync.Once
+
+ // deadlines
+ readDeadline atomic.Value
+ writeDeadline atomic.Value
+
+ // per stream sliding window control
+ numRead uint32 // number of consumed bytes
+ numWritten uint32 // count num of bytes written
+ incr uint32 // counting for sending
+
+ // UPD command
+ peerConsumed uint32 // num of bytes the peer has consumed
+ peerWindow uint32 // peer window, initialized to 256KB, updated by peer
+ chUpdate chan struct{} // notify of remote data consuming and window update
+}
+
+// newStream initiates a Stream struct
+func newStream(id uint32, frameSize int, sess *Session) *Stream {
+ s := new(Stream)
+ s.id = id
+ s.chReadEvent = make(chan struct{}, 1)
+ s.chUpdate = make(chan struct{}, 1)
+ s.frameSize = frameSize
+ s.sess = sess
+ s.die = make(chan struct{})
+ s.chFinEvent = make(chan struct{})
+ s.peerWindow = initialPeerWindow // set to initial window size
+ return s
+}
+
+// ID returns the unique stream ID.
+func (s *Stream) ID() uint32 {
+ return s.id
+}
+
+// Read implements net.Conn
+func (s *Stream) Read(b []byte) (n int, err error) {
+ for {
+ n, err = s.tryRead(b)
+ if err == ErrWouldBlock {
+ if ew := s.waitRead(); ew != nil {
+ return 0, ew
+ }
+ } else {
+ return n, err
+ }
+ }
+}
+
+// tryRead is the nonblocking version of Read
+func (s *Stream) tryRead(b []byte) (n int, err error) {
+ if s.sess.config.Version == 2 {
+ return s.tryReadv2(b)
+ }
+
+ if len(b) == 0 {
+ return 0, nil
+ }
+
+ s.bufferLock.Lock()
+ if len(s.buffers) > 0 {
+ n = copy(b, s.buffers[0])
+ s.buffers[0] = s.buffers[0][n:]
+ if len(s.buffers[0]) == 0 {
+ s.buffers[0] = nil
+ s.buffers = s.buffers[1:]
+ // full recycle
+ defaultAllocator.Put(s.heads[0])
+ s.heads = s.heads[1:]
+ }
+ }
+ s.bufferLock.Unlock()
+
+ if n > 0 {
+ s.sess.returnTokens(n)
+ return n, nil
+ }
+
+ select {
+ case <-s.die:
+ return 0, io.EOF
+ default:
+ return 0, ErrWouldBlock
+ }
+}
+
+func (s *Stream) tryReadv2(b []byte) (n int, err error) {
+ if len(b) == 0 {
+ return 0, nil
+ }
+
+ var notifyConsumed uint32
+ s.bufferLock.Lock()
+ if len(s.buffers) > 0 {
+ n = copy(b, s.buffers[0])
+ s.buffers[0] = s.buffers[0][n:]
+ if len(s.buffers[0]) == 0 {
+ s.buffers[0] = nil
+ s.buffers = s.buffers[1:]
+ // full recycle
+ defaultAllocator.Put(s.heads[0])
+ s.heads = s.heads[1:]
+ }
+ }
+
+ // in an ideal environment:
+ // if more than half of buffer has consumed, send read ack to peer
+ // based on round-trip time of ACK, continous flowing data
+ // won't slow down because of waiting for ACK, as long as the
+ // consumer keeps on reading data
+ // s.numRead == n also notify window at the first read
+ s.numRead += uint32(n)
+ s.incr += uint32(n)
+ if s.incr >= uint32(s.sess.config.MaxStreamBuffer/2) || s.numRead == uint32(n) {
+ notifyConsumed = s.numRead
+ s.incr = 0
+ }
+ s.bufferLock.Unlock()
+
+ if n > 0 {
+ s.sess.returnTokens(n)
+ if notifyConsumed > 0 {
+ err := s.sendWindowUpdate(notifyConsumed)
+ return n, err
+ } else {
+ return n, nil
+ }
+ }
+
+ select {
+ case <-s.die:
+ return 0, io.EOF
+ default:
+ return 0, ErrWouldBlock
+ }
+}
+
+// WriteTo implements io.WriteTo
+func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
+ if s.sess.config.Version == 2 {
+ return s.writeTov2(w)
+ }
+
+ for {
+ var buf []byte
+ s.bufferLock.Lock()
+ if len(s.buffers) > 0 {
+ buf = s.buffers[0]
+ s.buffers = s.buffers[1:]
+ s.heads = s.heads[1:]
+ }
+ s.bufferLock.Unlock()
+
+ if buf != nil {
+ nw, ew := w.Write(buf)
+ s.sess.returnTokens(len(buf))
+ defaultAllocator.Put(buf)
+ if nw > 0 {
+ n += int64(nw)
+ }
+
+ if ew != nil {
+ return n, ew
+ }
+ } else if ew := s.waitRead(); ew != nil {
+ return n, ew
+ }
+ }
+}
+
+func (s *Stream) writeTov2(w io.Writer) (n int64, err error) {
+ for {
+ var notifyConsumed uint32
+ var buf []byte
+ s.bufferLock.Lock()
+ if len(s.buffers) > 0 {
+ buf = s.buffers[0]
+ s.buffers = s.buffers[1:]
+ s.heads = s.heads[1:]
+ }
+ s.numRead += uint32(len(buf))
+ s.incr += uint32(len(buf))
+ if s.incr >= uint32(s.sess.config.MaxStreamBuffer/2) || s.numRead == uint32(len(buf)) {
+ notifyConsumed = s.numRead
+ s.incr = 0
+ }
+ s.bufferLock.Unlock()
+
+ if buf != nil {
+ nw, ew := w.Write(buf)
+ s.sess.returnTokens(len(buf))
+ defaultAllocator.Put(buf)
+ if nw > 0 {
+ n += int64(nw)
+ }
+
+ if ew != nil {
+ return n, ew
+ }
+
+ if notifyConsumed > 0 {
+ if err := s.sendWindowUpdate(notifyConsumed); err != nil {
+ return n, err
+ }
+ }
+ } else if ew := s.waitRead(); ew != nil {
+ return n, ew
+ }
+ }
+}
+
+func (s *Stream) sendWindowUpdate(consumed uint32) error {
+ var timer *time.Timer
+ var deadline <-chan time.Time
+ if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
+ timer = time.NewTimer(time.Until(d))
+ defer timer.Stop()
+ deadline = timer.C
+ }
+
+ frame := newFrame(byte(s.sess.config.Version), cmdUPD, s.id)
+ var hdr updHeader
+ binary.LittleEndian.PutUint32(hdr[:], consumed)
+ binary.LittleEndian.PutUint32(hdr[4:], uint32(s.sess.config.MaxStreamBuffer))
+ frame.data = hdr[:]
+ _, err := s.sess.writeFrameInternal(frame, deadline, 0)
+ return err
+}
+
+func (s *Stream) waitRead() error {
+ var timer *time.Timer
+ var deadline <-chan time.Time
+ if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
+ timer = time.NewTimer(time.Until(d))
+ defer timer.Stop()
+ deadline = timer.C
+ }
+
+ select {
+ case <-s.chReadEvent:
+ return nil
+ case <-s.chFinEvent:
+ // BUG(xtaci): Fix for https://github.com/xtaci/smux/issues/82
+ s.bufferLock.Lock()
+ defer s.bufferLock.Unlock()
+ if len(s.buffers) > 0 {
+ return nil
+ }
+ return io.EOF
+ case <-s.sess.chSocketReadError:
+ return s.sess.socketReadError.Load().(error)
+ case <-s.sess.chProtoError:
+ return s.sess.protoError.Load().(error)
+ case <-deadline:
+ return ErrTimeout
+ case <-s.die:
+ return io.ErrClosedPipe
+ }
+
+}
+
+// Write implements net.Conn
+//
+// Note that the behavior when multiple goroutines write concurrently is not deterministic,
+// frames may interleave in random way.
+func (s *Stream) Write(b []byte) (n int, err error) {
+ if s.sess.config.Version == 2 {
+ return s.writeV2(b)
+ }
+
+ var deadline <-chan time.Time
+ if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
+ timer := time.NewTimer(time.Until(d))
+ defer timer.Stop()
+ deadline = timer.C
+ }
+
+ // check if stream has closed
+ select {
+ case <-s.die:
+ return 0, io.ErrClosedPipe
+ default:
+ }
+
+ // frame split and transmit
+ sent := 0
+ frame := newFrame(byte(s.sess.config.Version), cmdPSH, s.id)
+ bts := b
+ for len(bts) > 0 {
+ sz := len(bts)
+ if sz > s.frameSize {
+ sz = s.frameSize
+ }
+ frame.data = bts[:sz]
+ bts = bts[sz:]
+ n, err := s.sess.writeFrameInternal(frame, deadline, uint64(s.numWritten))
+ s.numWritten++
+ sent += n
+ if err != nil {
+ return sent, err
+ }
+ }
+
+ return sent, nil
+}
+
+func (s *Stream) writeV2(b []byte) (n int, err error) {
+ // check empty input
+ if len(b) == 0 {
+ return 0, nil
+ }
+
+ // check if stream has closed
+ select {
+ case <-s.die:
+ return 0, io.ErrClosedPipe
+ default:
+ }
+
+ // create write deadline timer
+ var deadline <-chan time.Time
+ if d, ok := s.writeDeadline.Load().(time.Time); ok && !d.IsZero() {
+ timer := time.NewTimer(time.Until(d))
+ defer timer.Stop()
+ deadline = timer.C
+ }
+
+ // frame split and transmit process
+ sent := 0
+ frame := newFrame(byte(s.sess.config.Version), cmdPSH, s.id)
+
+ for {
+ // per stream sliding window control
+ // [.... [consumed... numWritten] ... win... ]
+ // [.... [consumed...................+rmtwnd]]
+ var bts []byte
+ // note:
+ // even if uint32 overflow, this math still works:
+ // eg1: uint32(0) - uint32(math.MaxUint32) = 1
+ // eg2: int32(uint32(0) - uint32(1)) = -1
+ // security check for misbehavior
+ inflight := int32(atomic.LoadUint32(&s.numWritten) - atomic.LoadUint32(&s.peerConsumed))
+ if inflight < 0 {
+ return 0, ErrConsumed
+ }
+
+ win := int32(atomic.LoadUint32(&s.peerWindow)) - inflight
+ if win > 0 {
+ if win > int32(len(b)) {
+ bts = b
+ b = nil
+ } else {
+ bts = b[:win]
+ b = b[win:]
+ }
+
+ for len(bts) > 0 {
+ sz := len(bts)
+ if sz > s.frameSize {
+ sz = s.frameSize
+ }
+ frame.data = bts[:sz]
+ bts = bts[sz:]
+ n, err := s.sess.writeFrameInternal(frame, deadline, uint64(atomic.LoadUint32(&s.numWritten)))
+ atomic.AddUint32(&s.numWritten, uint32(sz))
+ sent += n
+ if err != nil {
+ return sent, err
+ }
+ }
+ }
+
+ // if there is any data remaining to be sent
+ // wait until stream closes, window changes or deadline reached
+ // this blocking behavior will inform upper layer to do flow control
+ if len(b) > 0 {
+ select {
+ case <-s.chFinEvent: // if fin arrived, future window update is impossible
+ return 0, io.EOF
+ case <-s.die:
+ return sent, io.ErrClosedPipe
+ case <-deadline:
+ return sent, ErrTimeout
+ case <-s.sess.chSocketWriteError:
+ return sent, s.sess.socketWriteError.Load().(error)
+ case <-s.chUpdate:
+ continue
+ }
+ } else {
+ return sent, nil
+ }
+ }
+}
+
+// Close implements net.Conn
+func (s *Stream) Close() error {
+ var once bool
+ var err error
+ s.dieOnce.Do(func() {
+ close(s.die)
+ once = true
+ })
+
+ if once {
+ _, err = s.sess.writeFrame(newFrame(byte(s.sess.config.Version), cmdFIN, s.id))
+ s.sess.streamClosed(s.id)
+ return err
+ } else {
+ return io.ErrClosedPipe
+ }
+}
+
+// GetDieCh returns a readonly chan which can be readable
+// when the stream is to be closed.
+func (s *Stream) GetDieCh() <-chan struct{} {
+ return s.die
+}
+
+// SetReadDeadline sets the read deadline as defined by
+// net.Conn.SetReadDeadline.
+// A zero time value disables the deadline.
+func (s *Stream) SetReadDeadline(t time.Time) error {
+ s.readDeadline.Store(t)
+ s.notifyReadEvent()
+ return nil
+}
+
+// SetWriteDeadline sets the write deadline as defined by
+// net.Conn.SetWriteDeadline.
+// A zero time value disables the deadline.
+func (s *Stream) SetWriteDeadline(t time.Time) error {
+ s.writeDeadline.Store(t)
+ return nil
+}
+
+// SetDeadline sets both read and write deadlines as defined by
+// net.Conn.SetDeadline.
+// A zero time value disables the deadlines.
+func (s *Stream) SetDeadline(t time.Time) error {
+ if err := s.SetReadDeadline(t); err != nil {
+ return err
+ }
+ if err := s.SetWriteDeadline(t); err != nil {
+ return err
+ }
+ return nil
+}
+
+// session closes
+func (s *Stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }
+
+// LocalAddr satisfies net.Conn interface
+func (s *Stream) LocalAddr() net.Addr {
+ if ts, ok := s.sess.conn.(interface {
+ LocalAddr() net.Addr
+ }); ok {
+ return ts.LocalAddr()
+ }
+ return nil
+}
+
+// RemoteAddr satisfies net.Conn interface
+func (s *Stream) RemoteAddr() net.Addr {
+ if ts, ok := s.sess.conn.(interface {
+ RemoteAddr() net.Addr
+ }); ok {
+ return ts.RemoteAddr()
+ }
+ return nil
+}
+
+// pushBytes append buf to buffers
+func (s *Stream) pushBytes(buf []byte) (written int, err error) {
+ s.bufferLock.Lock()
+ s.buffers = append(s.buffers, buf)
+ s.heads = append(s.heads, buf)
+ s.bufferLock.Unlock()
+ return
+}
+
+// recycleTokens transform remaining bytes to tokens(will truncate buffer)
+func (s *Stream) recycleTokens() (n int) {
+ s.bufferLock.Lock()
+ for k := range s.buffers {
+ n += len(s.buffers[k])
+ defaultAllocator.Put(s.heads[k])
+ }
+ s.buffers = nil
+ s.heads = nil
+ s.bufferLock.Unlock()
+ return
+}
+
+// notify read event
+func (s *Stream) notifyReadEvent() {
+ select {
+ case s.chReadEvent <- struct{}{}:
+ default:
+ }
+}
+
+// update command
+func (s *Stream) update(consumed uint32, window uint32) {
+ atomic.StoreUint32(&s.peerConsumed, consumed)
+ atomic.StoreUint32(&s.peerWindow, window)
+ select {
+ case s.chUpdate <- struct{}{}:
+ default:
+ }
+}
+
+// mark this stream has been closed in protocol
+func (s *Stream) fin() {
+ s.finEventOnce.Do(func() {
+ close(s.chFinEvent)
+ })
+}