diff options
| author | Christine Dodrill <me@christine.website> | 2018-10-05 10:06:54 -0700 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2018-10-05 14:31:22 -0700 |
| commit | dbeba1e5c5c0bc534a515eb298ee4f1d49df4d20 (patch) | |
| tree | 2bbca457936bbf32290b9a8543f9a2f156b45175 /vendor/github.com/xtaci | |
| parent | 7332b16d4a09492e5a35ad00d3ab58ac5742cad6 (diff) | |
| download | x-dbeba1e5c5c0bc534a515eb298ee4f1d49df4d20.tar.xz x-dbeba1e5c5c0bc534a515eb298ee4f1d49df4d20.zip | |
update vendor
Diffstat (limited to 'vendor/github.com/xtaci')
24 files changed, 3651 insertions, 0 deletions
diff --git a/vendor/github.com/xtaci/kcp-go/.gitignore b/vendor/github.com/xtaci/kcp-go/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/.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/kcp-go/.travis.yml b/vendor/github.com/xtaci/kcp-go/.travis.yml new file mode 100644 index 0000000..875d28d --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/.travis.yml @@ -0,0 +1,15 @@ +language: go +go: + - tip + +before_install: + - go get -t -v ./... + +install: + - go get github.com/xtaci/kcp-go + +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/kcp-go/LICENSE b/vendor/github.com/xtaci/kcp-go/LICENSE new file mode 100644 index 0000000..8294d13 --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/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/README.md b/vendor/github.com/xtaci/kcp-go/README.md new file mode 100644 index 0000000..d02eee5 --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/README.md @@ -0,0 +1,92 @@ +# kcp-go + +[![GoDoc][1]][2] [![Powered][9]][10] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Coverage Statusd][7]][8] + +[1]: https://godoc.org/github.com/xtaci/kcp-go?status.svg +[2]: https://godoc.org/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 + +[](https://en.wikipedia.org/wiki/Claude_Shannon) + +## Introduction + +kcp-go is a full-featured ***reliable-UDP*** library for golang. It provides ***reliable, ordered, and error-checked*** delivery of a stream of octets between applications running on hosts communicating over an IP network. + +## Features + +1. Optimized for ***Real-Time Strategy Game***. +1. Compatible with [skywind3000's](https://github.com/skywind3000) C version with modifications. +1. ***Cache friendly*** and ***Memory optimized*** design in golang. +1. Compatible with [net.Conn](https://golang.org/pkg/net/#Conn) and [net.Listener](https://golang.org/pkg/net/#Listener). +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. + +## Conventions + +Control messages like SYN/FIN/RST in TCP ***are not defined*** in KCP, you need some ***keepalive mechanims*** in the application-level. a real world example is to use some ***multiplexing*** protocol over session, such as [smux](https://github.com/xtaci/smux), see [kcptun](https://github.com/xtaci/kcptun) for example. + +## 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="160px" /> + +## Usage + +Client: [full demo](https://github.com/xtaci/kcptun/blob/master/client/main.go#L231) +```go +kcpconn, err := kcp.DialWithOptions("192.168.0.1:10000", nil, 10, 3) +``` +Server: [full demo](https://github.com/xtaci/kcptun/blob/master/server/main.go#L235) +```go +lis, err := kcp.ListenWithOptions(":10000", nil, 10, 3) +``` + +## Performance +``` + 型号名称: MacBook Pro + 型号标识符: MacBookPro12,1 + 处理器名称: Intel Core i5 + 处理器速度: 2.7 GHz + 处理器数目: 1 + 核总数: 2 + L2 缓存(每个核): 256 KB + L3 缓存: 3 MB + 内存: 8 GB +``` +``` +$ go test -run Speed +new client 127.0.0.1:61165 +total recv: 16777216 +time for 16MB rtt with encryption 570.41176ms +&{BytesSent:33554432 BytesReceived:33554432 MaxConn:2 ActiveOpens:1 PassiveOpens:1 CurrEstab:1 InErrs:0 InCsumErrors:0 InSegs:42577 OutSegs:42641 OutBytes:48111336 RetransSegs:92 FastRetransSegs:92 LostSegs:0 RepeatSegs:0 FECRecovered:1 FECErrs:0 FECSegs:8514} +PASS +ok github.com/xtaci/kcp-go 0.600s +``` + +## Links + +1. https://github.com/xtaci/libkcp -- Official client library for iOS/Android(C++11) +2. https://github.com/skywind3000/kcp -- A Fast and Reliable ARQ Protocol +3. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go + +## Status + +Production Ready + +## Donation + + + +All donations on this project will be used to support the development of [gonet/2](http://gonet2.github.io/). diff --git a/vendor/github.com/xtaci/kcp-go/crypt.go b/vendor/github.com/xtaci/kcp-go/crypt.go new file mode 100644 index 0000000..2e456b8 --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/crypt.go @@ -0,0 +1,263 @@ +package kcp + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/sha1" + + "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 twofishBlockCrypt struct { + encbuf []byte + decbuf []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 + c.encbuf = make([]byte, twofish.BlockSize) + c.decbuf = make([]byte, 2*twofish.BlockSize) + 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 []byte + decbuf []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 + c.encbuf = make([]byte, des.BlockSize) + c.decbuf = make([]byte, 2*des.BlockSize) + 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 []byte + decbuf []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 + c.encbuf = make([]byte, cast5.BlockSize) + c.decbuf = make([]byte, 2*cast5.BlockSize) + 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 []byte + decbuf []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 + c.encbuf = make([]byte, blowfish.BlockSize) + c.decbuf = make([]byte, 2*blowfish.BlockSize) + 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 []byte + decbuf []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 + c.encbuf = make([]byte, aes.BlockSize) + c.decbuf = make([]byte, 2*aes.BlockSize) + 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 []byte + decbuf []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 + c.encbuf = make([]byte, tea.BlockSize) + c.decbuf = make([]byte, 2*tea.BlockSize) + 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 []byte + decbuf []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 + c.encbuf = make([]byte, xtea.BlockSize) + c.decbuf = make([]byte, 2*xtea.BlockSize) + 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) { xorBytes(dst, src, c.xortbl) } +func (c *simpleXORBlockCrypt) Decrypt(dst, src []byte) { xorBytes(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) { + blocksize := block.BlockSize() + tbl := buf[:blocksize] + block.Encrypt(tbl, initialVector) + n := len(src) / blocksize + base := 0 + for i := 0; i < n; i++ { + xorWords(dst[base:], src[base:], tbl) + block.Encrypt(tbl, dst[base:]) + base += blocksize + } + xorBytes(dst[base:], src[base:], tbl) +} + +func decrypt(block cipher.Block, dst, src, buf []byte) { + blocksize := block.BlockSize() + tbl := buf[:blocksize] + next := buf[blocksize:] + block.Encrypt(tbl, initialVector) + n := len(src) / blocksize + base := 0 + for i := 0; i < n; i++ { + block.Encrypt(next, src[base:]) + xorWords(dst[base:], src[base:], tbl) + tbl, next = next, tbl + base += blocksize + } + xorBytes(dst[base:], src[base:], tbl) +} diff --git a/vendor/github.com/xtaci/kcp-go/donate.png b/vendor/github.com/xtaci/kcp-go/donate.png Binary files differnew file mode 100644 index 0000000..0f353d9 --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/donate.png diff --git a/vendor/github.com/xtaci/kcp-go/fec.go b/vendor/github.com/xtaci/kcp-go/fec.go new file mode 100644 index 0000000..fa7f0ec --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/fec.go @@ -0,0 +1,236 @@ +package kcp + +import ( + "encoding/binary" + + "github.com/klauspost/reedsolomon" +) + +const ( + fecHeaderSize = 6 + fecHeaderSizePlus2 = fecHeaderSize + 2 // plus 2B data size + typeData = 0xf1 + typeFEC = 0xf2 + fecExpire = 30000 // 30s +) + +type ( + // FEC defines forward error correction for packets + FEC struct { + rx []fecPacket // ordered receive queue + rxlimit int // queue size limit + dataShards int + parityShards int + shardSize int + next uint32 // next seqid + enc reedsolomon.Encoder + shards [][]byte + shardsflag []bool + paws uint32 // Protect Against Wrapped Sequence numbers + lastCheck uint32 + } + + fecPacket struct { + seqid uint32 + flag uint16 + data []byte + ts uint32 + } +) + +func newFEC(rxlimit, dataShards, parityShards int) *FEC { + if dataShards <= 0 || parityShards <= 0 { + return nil + } + if rxlimit < dataShards+parityShards { + return nil + } + + fec := new(FEC) + fec.rxlimit = rxlimit + fec.dataShards = dataShards + fec.parityShards = parityShards + fec.shardSize = dataShards + parityShards + fec.paws = (0xffffffff/uint32(fec.shardSize) - 1) * uint32(fec.shardSize) + enc, err := reedsolomon.New(dataShards, parityShards) + if err != nil { + return nil + } + fec.enc = enc + fec.shards = make([][]byte, fec.shardSize) + fec.shardsflag = make([]bool, fec.shardSize) + return fec +} + +// decode a fec packet +func (fec *FEC) decode(data []byte) fecPacket { + var pkt fecPacket + pkt.seqid = binary.LittleEndian.Uint32(data) + pkt.flag = binary.LittleEndian.Uint16(data[4:]) + pkt.ts = currentMs() + // allocate memory & copy + buf := xmitBuf.Get().([]byte)[:len(data)-6] + copy(buf, data[6:]) + pkt.data = buf + return pkt +} + +func (fec *FEC) markData(data []byte) { + binary.LittleEndian.PutUint32(data, fec.next) + binary.LittleEndian.PutUint16(data[4:], typeData) + fec.next++ +} + +func (fec *FEC) markFEC(data []byte) { + binary.LittleEndian.PutUint32(data, fec.next) + binary.LittleEndian.PutUint16(data[4:], typeFEC) + fec.next++ + if fec.next >= fec.paws { // paws would only occurs in markFEC + fec.next = 0 + } +} + +// input a fec packet +func (fec *FEC) input(pkt fecPacket) (recovered [][]byte) { + // expiration + now := currentMs() + if now-fec.lastCheck >= fecExpire { + var rx []fecPacket + for k := range fec.rx { + if now-fec.rx[k].ts < fecExpire { + rx = append(rx, fec.rx[k]) + } else { + xmitBuf.Put(fec.rx[k].data) + } + } + fec.rx = rx + fec.lastCheck = now + } + + // insertion + n := len(fec.rx) - 1 + insertIdx := 0 + for i := n; i >= 0; i-- { + if pkt.seqid == fec.rx[i].seqid { // de-duplicate + xmitBuf.Put(pkt.data) + return nil + } else if pkt.seqid > fec.rx[i].seqid { // insertion + insertIdx = i + 1 + break + } + } + + // insert into ordered rx queue + if insertIdx == n+1 { + fec.rx = append(fec.rx, pkt) + } else { + fec.rx = append(fec.rx, fecPacket{}) + copy(fec.rx[insertIdx+1:], fec.rx[insertIdx:]) + fec.rx[insertIdx] = pkt + } + + // shard range for current packet + shardBegin := pkt.seqid - pkt.seqid%uint32(fec.shardSize) + shardEnd := shardBegin + uint32(fec.shardSize) - 1 + + // max search range in ordered queue for current shard + searchBegin := insertIdx - int(pkt.seqid%uint32(fec.shardSize)) + if searchBegin < 0 { + searchBegin = 0 + } + searchEnd := searchBegin + fec.shardSize - 1 + if searchEnd >= len(fec.rx) { + searchEnd = len(fec.rx) - 1 + } + + if searchEnd > searchBegin && searchEnd-searchBegin+1 >= fec.dataShards { + numshard := 0 + numDataShard := 0 + first := -1 + maxlen := 0 + shards := fec.shards + shardsflag := fec.shardsflag + for k := range fec.shards { + shards[k] = nil + shardsflag[k] = false + } + + for i := searchBegin; i <= searchEnd; i++ { + seqid := fec.rx[i].seqid + if seqid > shardEnd { + break + } else if seqid >= shardBegin { + shards[seqid%uint32(fec.shardSize)] = fec.rx[i].data + shardsflag[seqid%uint32(fec.shardSize)] = true + numshard++ + if fec.rx[i].flag == typeData { + numDataShard++ + } + if numshard == 1 { + first = i + } + if len(fec.rx[i].data) > maxlen { + maxlen = len(fec.rx[i].data) + } + } + } + + if numDataShard == fec.dataShards { // no lost + for i := first; i < first+numshard; i++ { // free + xmitBuf.Put(fec.rx[i].data) + } + copy(fec.rx[first:], fec.rx[first+numshard:]) + for i := 0; i < numshard; i++ { // dereference + fec.rx[len(fec.rx)-1-i] = fecPacket{} + } + fec.rx = fec.rx[:len(fec.rx)-numshard] + } else if numshard >= fec.dataShards { // recoverable + for k := range shards { + if shards[k] != nil { + dlen := len(shards[k]) + shards[k] = shards[k][:maxlen] + xorBytes(shards[k][dlen:], shards[k][dlen:], shards[k][dlen:]) + } + } + if err := fec.enc.Reconstruct(shards); err == nil { + for k := range shards[:fec.dataShards] { + if !shardsflag[k] { + recovered = append(recovered, shards[k]) + } + } + } + + for i := first; i < first+numshard; i++ { // free + xmitBuf.Put(fec.rx[i].data) + } + copy(fec.rx[first:], fec.rx[first+numshard:]) + for i := 0; i < numshard; i++ { // dereference + fec.rx[len(fec.rx)-1-i] = fecPacket{} + } + fec.rx = fec.rx[:len(fec.rx)-numshard] + } + } + + // keep rxlimit + if len(fec.rx) > fec.rxlimit { + xmitBuf.Put(fec.rx[0].data) // free + fec.rx[0].data = nil + fec.rx = fec.rx[1:] + } + return +} + +func (fec *FEC) calcECC(data [][]byte, offset, maxlen int) (ecc [][]byte) { + if len(data) != fec.shardSize { + return nil + } + shards := make([][]byte, fec.shardSize) + for k := range shards { + shards[k] = data[k][offset:maxlen] + } + + if err := fec.enc.Encode(shards); err != nil { + return nil + } + return data[fec.dataShards:] +} diff --git a/vendor/github.com/xtaci/kcp-go/frame.png b/vendor/github.com/xtaci/kcp-go/frame.png Binary files differnew file mode 100644 index 0000000..7952e4a --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/frame.png diff --git a/vendor/github.com/xtaci/kcp-go/kcp.go b/vendor/github.com/xtaci/kcp-go/kcp.go new file mode 100644 index 0000000..eec4143 --- /dev/null +++ b/vendor/github.com/xtaci/kcp-go/kcp.go @@ -0,0 +1,978 @@ +// Package kcp - A Fast and Reliable ARQ Protocol +package kcp + +import ( + "container/heap" + "encoding/binary" + "sync/atomic" +) + +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 +) + +// Output is a closure which captures conn and calls conn.Write +type Output 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 + } else { + return b + } +} + +func _imax_(a, b uint32) uint32 { + if a >= b { + return a + } else { + 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 uint32 + frg uint32 + wnd uint32 + ts uint32 + sn uint32 + una uint32 + resendts uint32 + rto uint32 + fastack uint32 + xmit uint32 + data []byte +} + +// encode a segment into buffer +func (seg *Segment) encode(ptr []byte) []byte { + ptr = ikcp_encode32u(ptr, seg.conv) + ptr = ikcp_encode8u(ptr, uint8(seg.cmd)) + ptr = ikcp_encode8u(ptr, uint8(seg.frg)) + ptr = ikcp_encode16u(ptr, uint16(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))) + 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_rttval, rx_srtt, rx_rto, rx_minrto uint32 + snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe uint32 + current, interval, ts_flush, xmit 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 ackList + + buffer []byte + output Output +} + +type ackItem struct { + sn uint32 + ts uint32 +} + +type ackList []ackItem + +func (l ackList) Len() int { return len(l) } +func (l ackList) Less(i, j int) bool { return l[i].sn < l[j].sn } +func (l ackList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l *ackList) Push(x interface{}) { *l = append(*l, x.(ackItem)) } +func (l *ackList) Pop() interface{} { + old := *l + n := len(old) + x := old[n-1] + *l = old[0 : n-1] + return x +} + +// NewKCP create a new kcp control object, 'conv' must equal in two endpoint +// from the same connection. +func NewKCP(conv uint32, output Output) *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+IKCP_OVERHEAD)*3) + 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) *Segment { + seg := new(Segment) + seg.data = xmitBuf.Get().([]byte)[:size] + return seg +} + +// delSegment recycles a KCP segment +func (kcp *KCP) delSegment(seg *Segment) { + xmitBuf.Put(seg.data) +} + +// 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 +} + +// Recv is user/upper level recv: returns size, returns below zero for EAGAIN +func (kcp *KCP) Recv(buffer []byte) (n int) { + if len(kcp.rcv_queue) == 0 { + return -1 + } + + peeksize := kcp.PeekSize() + if peeksize < 0 { + return -2 + } + + if peeksize > len(buffer) { + return -3 + } + + 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 + } + } + kcp.rcv_queue = 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) < int(kcp.rcv_wnd) { + kcp.rcv_nxt++ + count++ + } else { + break + } |
