178 lines
5.9 KiB
C#
178 lines
5.9 KiB
C#
|
|
using System;
|
|||
|
|
using DotNetty.Buffers;
|
|||
|
|
using fec;
|
|||
|
|
|
|||
|
|
namespace fec.fec
|
|||
|
|
{
|
|||
|
|
public class FecEncode
|
|||
|
|
{
|
|||
|
|
/**消息包长度**/
|
|||
|
|
private readonly int dataShards;
|
|||
|
|
|
|||
|
|
/**冗余包长度**/
|
|||
|
|
private readonly int parityShards;
|
|||
|
|
|
|||
|
|
/** dataShards+parityShards **/
|
|||
|
|
private int shardSize;
|
|||
|
|
|
|||
|
|
//Protect Against Wrapped Sequence numbers
|
|||
|
|
private readonly long paws;
|
|||
|
|
|
|||
|
|
// next seqid
|
|||
|
|
private long next;
|
|||
|
|
|
|||
|
|
//count the number of datashards collected
|
|||
|
|
private int shardCount;
|
|||
|
|
|
|||
|
|
// record maximum data length in datashard
|
|||
|
|
private int maxSize;
|
|||
|
|
|
|||
|
|
// FEC header offset
|
|||
|
|
private readonly int headerOffset;
|
|||
|
|
|
|||
|
|
// FEC payload offset
|
|||
|
|
private readonly int payloadOffset;
|
|||
|
|
|
|||
|
|
//用完需要手动release
|
|||
|
|
private readonly IByteBuffer[] shardCache;
|
|||
|
|
private readonly IByteBuffer[] encodeCache;
|
|||
|
|
|
|||
|
|
private readonly IByteBuffer zeros;
|
|||
|
|
|
|||
|
|
private readonly ReedSolomon codec;
|
|||
|
|
|
|||
|
|
public FecEncode(int headerOffset, ReedSolomon codec, int mtu)
|
|||
|
|
{
|
|||
|
|
this.dataShards = codec.getDataShardCount();
|
|||
|
|
this.parityShards = codec.getParityShardCount();
|
|||
|
|
this.shardSize = this.dataShards + this.parityShards;
|
|||
|
|
//this.paws = (Integer.MAX_VALUE/shardSize - 1) * shardSize;
|
|||
|
|
this.paws = 0xffffffffL / shardSize * shardSize;
|
|||
|
|
this.headerOffset = headerOffset;
|
|||
|
|
this.payloadOffset = headerOffset + Fec.fecHeaderSize;
|
|||
|
|
this.codec = codec;
|
|||
|
|
this.shardCache = new IByteBuffer[shardSize];
|
|||
|
|
this.encodeCache = new IByteBuffer[parityShards];
|
|||
|
|
zeros = PooledByteBufferAllocator.Default.DirectBuffer(mtu);
|
|||
|
|
zeros.WriteBytes(new byte[mtu]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
*
|
|||
|
|
* 使用方法:
|
|||
|
|
* 1,入bytebuf后 把bytebuf发送出去,并释放bytebuf
|
|||
|
|
* 2,判断返回值是否为null,如果不为null发送出去并释放它
|
|||
|
|
*
|
|||
|
|
* headerOffset +6字节fectHead + 2字节bodylenth(lenth-headerOffset-6)
|
|||
|
|
*
|
|||
|
|
* 1,对数据写入头标记为数据类型 markData
|
|||
|
|
* 2,写入消息长度
|
|||
|
|
* 3,获得缓存数据中最大长度,其他的缓存进行扩容到同样长度
|
|||
|
|
* 4,去掉头长度,进行fec编码
|
|||
|
|
* 5,对冗余字节数组进行标记为fec makefec
|
|||
|
|
* 6,返回完整长度
|
|||
|
|
*
|
|||
|
|
* 注意: 传入的bytebuf如果需要释放在传入后手动释放。
|
|||
|
|
* 返回的bytebuf 也需要自己释放
|
|||
|
|
* @param byteBuf
|
|||
|
|
* @return
|
|||
|
|
*/
|
|||
|
|
public IByteBuffer[] encode(IByteBuffer byteBuf)
|
|||
|
|
{
|
|||
|
|
markData(byteBuf, headerOffset);
|
|||
|
|
int sz = byteBuf.WriterIndex;
|
|||
|
|
byteBuf.SetShort(payloadOffset, sz - headerOffset - Fec.fecHeaderSizePlus2);
|
|||
|
|
this.shardCache[shardCount] = byteBuf.RetainedDuplicate();
|
|||
|
|
this.shardCount++;
|
|||
|
|
if (sz > this.maxSize)
|
|||
|
|
{
|
|||
|
|
this.maxSize = sz;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (shardCount != dataShards)
|
|||
|
|
{
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//填充parityShards
|
|||
|
|
for (int i = 0; i < parityShards; i++)
|
|||
|
|
{
|
|||
|
|
IByteBuffer parityByte = PooledByteBufferAllocator.Default.DirectBuffer(this.maxSize);
|
|||
|
|
shardCache[i + dataShards] = parityByte;
|
|||
|
|
encodeCache[i] = parityByte;
|
|||
|
|
markParity(parityByte, headerOffset);
|
|||
|
|
parityByte.SetWriterIndex(this.maxSize);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//按着最大长度不足补充0
|
|||
|
|
for (var i = 0; i < this.dataShards; i++)
|
|||
|
|
{
|
|||
|
|
var shard = shardCache[i];
|
|||
|
|
var left = this.maxSize - shard.WriterIndex;
|
|||
|
|
if (left <= 0)
|
|||
|
|
continue;
|
|||
|
|
//是否需要扩容 会出现吗??
|
|||
|
|
//if(shard.capacity()<this.maxSize){
|
|||
|
|
// ByteBuf newByteBuf = ByteBufAllocator.DEFAULT.buffer(this.maxSize);
|
|||
|
|
// newByteBuf.writeBytes(shard);
|
|||
|
|
// shard.release();
|
|||
|
|
// shard = newByteBuf;
|
|||
|
|
// shardCache[i] = shard;
|
|||
|
|
//}
|
|||
|
|
shard.WriteBytes(zeros, left);
|
|||
|
|
zeros.SetReaderIndex(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
codec.encodeParity(shardCache, payloadOffset, this.maxSize - payloadOffset);
|
|||
|
|
//释放dataShards
|
|||
|
|
for (int i = 0; i < dataShards; i++)
|
|||
|
|
{
|
|||
|
|
this.shardCache[i].Release();
|
|||
|
|
this.shardCache[i] = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.shardCount = 0;
|
|||
|
|
this.maxSize = 0;
|
|||
|
|
return this.encodeCache;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public void release()
|
|||
|
|
{
|
|||
|
|
this.shardSize = 0;
|
|||
|
|
this.next = 0;
|
|||
|
|
this.shardCount = 0;
|
|||
|
|
this.maxSize = 0;
|
|||
|
|
for (int i = 0; i < dataShards; i++)
|
|||
|
|
{
|
|||
|
|
var byteBuf = this.shardCache[i];
|
|||
|
|
byteBuf?.Release();
|
|||
|
|
}
|
|||
|
|
zeros.Release();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
private void markData(IByteBuffer byteBuf, int offset)
|
|||
|
|
{
|
|||
|
|
byteBuf.SetIntLE(offset, (int)this.next);
|
|||
|
|
byteBuf.SetShortLE(offset + 4, Fec.typeData);
|
|||
|
|
this.next++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void markParity(IByteBuffer byteBuf, int offset)
|
|||
|
|
{
|
|||
|
|
byteBuf.SetIntLE(offset, (int) this.next);
|
|||
|
|
byteBuf.SetShortLE(offset + 4, Fec.typeParity);
|
|||
|
|
//if(next==this.paws){
|
|||
|
|
// next=0;
|
|||
|
|
//}else{
|
|||
|
|
// next++;
|
|||
|
|
//}
|
|||
|
|
this.next = (this.next + 1) % this.paws;
|
|||
|
|
//if(next+1>=this.paws) {
|
|||
|
|
// this.next++;
|
|||
|
|
// //this.next = (this.next + 1) % this.paws;
|
|||
|
|
//}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|