From 852e4d13026c2a3af0bfc5d67dbca24cace16a7f Mon Sep 17 00:00:00 2001 From: chai <215380520@qq.com> Date: Sat, 14 Oct 2023 12:50:22 +0800 Subject: * move --- Projects/Message/Message/IRecyclable.cs | 29 -- Projects/Message/Message/MessageReader.cs | 530 --------------------- Projects/Message/Message/MessageWriter.cs | 341 ------------- Projects/Message/Message/MessageWriterTests.cs | 1 - .../Message/MultiplayerToolkit/IRecyclable.cs | 29 ++ .../Message/MultiplayerToolkit/MessageReader.cs | 530 +++++++++++++++++++++ .../Message/MultiplayerToolkit/MessageWriter.cs | 341 +++++++++++++ .../Message/MultiplayerToolkit/ObjectPool.cs | 108 +++++ Projects/Message/Message/ObjectPool.cs | 108 ----- 9 files changed, 1008 insertions(+), 1009 deletions(-) delete mode 100644 Projects/Message/Message/IRecyclable.cs delete mode 100644 Projects/Message/Message/MessageReader.cs delete mode 100644 Projects/Message/Message/MessageWriter.cs create mode 100644 Projects/Message/Message/MultiplayerToolkit/IRecyclable.cs create mode 100644 Projects/Message/Message/MultiplayerToolkit/MessageReader.cs create mode 100644 Projects/Message/Message/MultiplayerToolkit/MessageWriter.cs create mode 100644 Projects/Message/Message/MultiplayerToolkit/ObjectPool.cs delete mode 100644 Projects/Message/Message/ObjectPool.cs (limited to 'Projects/Message') diff --git a/Projects/Message/Message/IRecyclable.cs b/Projects/Message/Message/IRecyclable.cs deleted file mode 100644 index 17d3104..0000000 --- a/Projects/Message/Message/IRecyclable.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace MultiplayerToolkit -{ - /// - /// Interface for all items that can be returned to an object pool. - /// - /// - public interface IRecyclable - { - /// - /// Returns this object back to the object pool. - /// - /// - /// - /// Calling this when you are done with the object returns the object back to a pool in order to be reused. - /// This can reduce the amount of work the GC has to do dramatically but it is optional to call this. - /// - /// - /// Calling this indicates to Hazel that this can be reused and thus you should only call this when you are - /// completely finished with the object as the contents can be overwritten at any point after. - /// - /// - void Recycle(); - } -} diff --git a/Projects/Message/Message/MessageReader.cs b/Projects/Message/Message/MessageReader.cs deleted file mode 100644 index 32b0218..0000000 --- a/Projects/Message/Message/MessageReader.cs +++ /dev/null @@ -1,530 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; - -namespace MultiplayerToolkit -{ - /// - /// 可以处理嵌套消息的结构,和MessageWriter配对 - /// - public class MessageReader : IRecyclable - { - public static readonly ObjectPool ReaderPool = new ObjectPool(() => new MessageReader()); - - public byte[] Buffer; // 缓冲区,会被子消息共享这个缓冲区 - - // 消息长度,定值 - // * 如果是非根消息,对应的是MessageWriter里写入的2bytes包长度,即不含length的长度 - // * 如果是根消息,Length是整个有效数据在Buffer中的长度,包括Length - public int Length; - - // 有效数据偏移,定值,游标会在这个基础上偏移 - // * 如果是非根消息,Offset在Length后面的位置 - // * 如果是根消息,Offset等于0,表示有效数据在Buffer中的偏移 - public int Offset; - - public int BytesRemaining => this.Length - this.Position; - - public int TotalLength => this.Length + this.Offset; - - private MessageReader Parent; // 保存父消息索引,和父消息共享父消息的Buffer - - // 是否是根消息,根消息下面包含多个子消息,子消息可以嵌套重孙消息 - public bool IsRoot - { - get { return Parent == null; } - } - - // 子消息可读 - public bool IsReadable - { - get { return this.Parent != null; } - } - - public int Position - { - get { return this._position; } - set - { - this._position = value; - this.readHead = value + Offset; - } - } - - private int _position; // 读取游标,相对于消息内容部分的偏移,从0开始,子消息也是从0开始,不是在Buffer中的索引 - private int readHead; // 读取游标,消息头部在Buffer中的位置,参考ReadMessage() - -#region 创建 - /// - /// 创建一个容量至少为minSize的空缓冲区 - /// - /// - /// - /// - public static MessageReader GetSized(int minSize, bool exactly = false) - { - var output = ReaderPool.GetObject(); - - if (output.Buffer == null || output.Buffer.Length < minSize) - { - output.Buffer = new byte[minSize]; - } - else - { - if(exactly && output.Buffer.Length > minSize) - { - output.Buffer = new byte[minSize]; - } - Array.Clear(output.Buffer, 0, output.Buffer.Length); - } - - output.Offset = 0; - output.Length = 0; - output.Position = 0; - output.Parent = null; - return output; - } - - /// - /// 将缓冲区封装为根消息,不拷贝 - /// - /// - /// - public static MessageReader Get(byte[] buffer) - { - var output = ReaderPool.GetObject(); - - output.Buffer = buffer; - output.Offset = 0; - output.Position = 0; - output.Length = buffer.Length; - output.Parent = null; - - return output; - } - - /// - /// 将缓冲区封装为根消息,拷贝 - /// - /// - /// - public static MessageReader GetAndCopy(byte[] buffer) - { - var output = ReaderPool.GetObject(); - - byte[] _buffer = new byte[buffer.Length]; - Array.Copy(buffer, _buffer, buffer.Length); - output.Buffer = _buffer; - output.Offset = 0; - output.Position = 0; - output.Length = _buffer.Length; - output.Parent = null; - - return output; - } - - /// - /// 创建一个新的父message,保存source。用于缓存message,因为source所在的原父message可能会被销毁,如果需要延迟处理消息,需要拷贝一份新的 - /// - /// - /// - public static MessageReader CopyMessageIntoParent(MessageReader source) - { - var output = MessageReader.GetSized(source.Length + 2); //2=2bytes(length) - System.Buffer.BlockCopy(source.Buffer, source.Offset - 2, output.Buffer, 0, source.Length + 2); - - output.Offset = 0; - output.Position = 0; - output.Length = source.Length + 2; - output.Parent = null; - - return output; - } - -#if UNIT_TEST - /// - /// 用于测试,引用buffer的一部分作为child message,后续可以直接读取 - /// - /// - /// - /// - public static MessageReader GetChildMessage(byte[] buffer, int offset) - { - // 确保至少有一个length - if (offset + 2 > buffer.Length) return null; - - var output = ReaderPool.GetObject(); - - output.Buffer = buffer; - output.Offset = offset; - output.Position = 0; - - output.Length = output.ReadUInt16(); // 读到的值为length - - output.Offset += 2; - output.Position = 0; - output.Parent = null; - - return output; - } - - public static MessageReader Copy(MessageReader source) - { - var output = MessageReader.GetSized(source.Buffer.Length); - System.Buffer.BlockCopy(source.Buffer, 0, output.Buffer, 0, source.Buffer.Length); - - output.Offset = source.Offset; - - output._position = source._position; - output.readHead = source.readHead; - - output.Length = source.Length; - - return output; - } - -#endif - - #endregion - - /// - /// 返回一个可读子消息,引用了父缓冲区,**不要回收** - /// - public MessageReader ReadMessage() - { - // 至少有一个length - if (this.BytesRemaining < 2) throw new InvalidDataException($"ReadMessage header is longer than message length: 2 of {this.BytesRemaining}"); - - MessageReader output = new MessageReader(); - - output.Parent = this; - output.Buffer = this.Buffer; - output.Offset = this.readHead; - output.Position = 0; - - output.Length = output.ReadUInt16(); // length - - output.Offset += 2; // 2=length - output.Position = 0; - - // 数据不全 - if (this.BytesRemaining < output.Length + 2) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {output.Length + 2} of {this.BytesRemaining}"); - - this.Position += output.Length + 2; //跳过整个子消息 - return output; - } - -#if UNIT_TEST - /// - /// 仅用于测试,返回一个可读子消息,并且拷贝数据,可回收 - /// Offset为0,开头没有Length数据 - /// - public MessageReader ReadMessageAsNewBuffer() - { - if (this.BytesRemaining < 2) throw new InvalidDataException($"ReadMessage header is longer than message length: 2 of {this.BytesRemaining}"); - - var len = this.ReadUInt16(); // Position += 2 - - if (this.BytesRemaining < len) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {len} of {this.BytesRemaining}"); - - var output = MessageReader.GetSized(len); - - Array.Copy(this.Buffer, this.readHead, output.Buffer, 0, len); - - output.Length = len; - - this.Position += output.Length; - return output; - } -#endif - -#if UNIT_TEST - public MessageReader Duplicate() - { - var output = GetSized(this.Length); - Array.Copy(this.Buffer, this.Offset, output.Buffer, 0, this.Length); - output.Length = this.Length; - output.Offset = 0; - output.Position = 0; - - return output; - } - - public void RemoveMessage(MessageReader reader) - { - MessageReader temp = null; - try - { - var headerOffset = reader.Offset - 2; - var endOfMessage = reader.Offset + reader.Length; // 有效部分的后一个byte - var len = reader.Buffer.Length - endOfMessage; - - temp = MessageReader.GetSized(len); - Array.Copy(reader.Buffer, endOfMessage, temp.Buffer, 0, len); - Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset, len); - - this.AdjustLength(reader.Offset, reader.Length + 2); - } - finally - { - if(temp != null) - { - temp.Recycle(); - } - } - } - - /// - /// 在reader前面插入一条消息 - /// - /// 父reader - /// - public void InsertMessage(MessageReader reader, MessageWriter writer) - { - var temp = MessageReader.GetSized(reader.Buffer.Length); - try - { - var headerOffset = reader.Offset - 2; - var startOfMessage = reader.Offset; - var len = reader.Buffer.Length - headerOffset; // 疑似写错了,应该是headerOffset - int writerOffset = 0; - - //store the original buffer in temp - Array.Copy(reader.Buffer, headerOffset, temp.Buffer, 0, len); - - //put the contents of writer in at headerOffset - Array.Copy(writer.Buffer, writerOffset, this.Buffer, headerOffset, writer.Length - writerOffset); - - //put the original buffer in after that - Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset + (writer.Length - writerOffset), len - writer.Length); - - this.AdjustLength(-1 * reader.Offset, -1 * (writer.Length - writerOffset)); - } - finally - { - temp.Recycle(); - } - } - - /// - /// 递归调整length值 - /// - /// 子消息的Offset值 - /// 子消息总长度,含length - private void AdjustLength(int offset, int amount) - { - if (this.readHead > offset) - { - this.Position -= amount; - } - - if (Parent != null) - { - var lengthOffset = this.Offset - 2; - var curLen = this.Buffer[lengthOffset] - | (this.Buffer[lengthOffset + 1] << 8); - - curLen -= amount; - this.Length -= amount; - - this.Buffer[lengthOffset] = (byte)curLen; - this.Buffer[lengthOffset + 1] = (byte)(curLen >> 8); - - Parent.AdjustLength(offset, amount); - } - } - -#endif - - public void Recycle() - { - this.Parent = null; - ReaderPool.PutObject(this); - } - -#region Read Methods - public bool ReadBoolean() - { - byte val = this.FastByte(); - return val != 0; - } - - public sbyte ReadSByte() - { - return (sbyte)this.FastByte(); - } - - public byte ReadByte() - { - return this.FastByte(); - } - - public ushort ReadUInt16() - { - ushort output = - (ushort)(this.FastByte() - | this.FastByte() << 8); - return output; - } - - public short ReadInt16() - { - short output = - (short)(this.FastByte() - | this.FastByte() << 8); - return output; - } - - public uint ReadUInt32() - { - uint output = this.FastByte() - | (uint)this.FastByte() << 8 - | (uint)this.FastByte() << 16 - | (uint)this.FastByte() << 24; - - return output; - } - - public int ReadInt32() - { - int output = this.FastByte() - | this.FastByte() << 8 - | this.FastByte() << 16 - | this.FastByte() << 24; - - return output; - } - - public ulong ReadUInt64() - { - ulong output = (ulong)this.FastByte() - | (ulong)this.FastByte() << 8 - | (ulong)this.FastByte() << 16 - | (ulong)this.FastByte() << 24 - | (ulong)this.FastByte() << 32 - | (ulong)this.FastByte() << 40 - | (ulong)this.FastByte() << 48 - | (ulong)this.FastByte() << 56; - - return output; - } - - public long ReadInt64() - { - long output = (long)this.FastByte() - | (long)this.FastByte() << 8 - | (long)this.FastByte() << 16 - | (long)this.FastByte() << 24 - | (long)this.FastByte() << 32 - | (long)this.FastByte() << 40 - | (long)this.FastByte() << 48 - | (long)this.FastByte() << 56; - - return output; - } - - public unsafe float ReadSingle() - { - float output = 0; - fixed (byte* bufPtr = &this.Buffer[this.readHead]) - { - byte* outPtr = (byte*)&output; - - *outPtr = *bufPtr; - *(outPtr + 1) = *(bufPtr + 1); - *(outPtr + 2) = *(bufPtr + 2); - *(outPtr + 3) = *(bufPtr + 3); - } - - this.Position += 4; - return output; - } - - public string ReadString() - { - int len = this.ReadPackedInt32(); - if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}"); - - string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len); - - this.Position += len; - return output; - } - - public byte[] ReadBytesAndSize() - { - int len = this.ReadPackedInt32(); - if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}"); - - return this.ReadBytes(len); - } - - public byte[] ReadBytes(int length) - { - if (this.BytesRemaining < length) throw new InvalidDataException($"Read length is longer than message length: {length} of {this.BytesRemaining}"); - - byte[] output = new byte[length]; - Array.Copy(this.Buffer, this.readHead, output, 0, output.Length); - this.Position += output.Length; - return output; - } - - /// - public int ReadPackedInt32() - { - return (int)this.ReadPackedUInt32(); - } - - /// - public uint ReadPackedUInt32() - { - bool readMore = true; - int shift = 0; - uint output = 0; - - while (readMore) - { - if (this.BytesRemaining < 1) throw new InvalidDataException($"Read length is longer than message length."); - - byte b = this.ReadByte(); - if (b >= 0x80) - { - readMore = true; - b ^= 0x80; - } - else - { - readMore = false; - } - - output |= (uint)(b << shift); - shift += 7; - } - - return output; - } -#endregion - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte FastByte() - { - this._position++; - return this.Buffer[this.readHead++]; - } - - public unsafe static bool IsLittleEndian() - { - byte b; - unsafe - { - int i = 1; - byte* bp = (byte*)&i; - b = *bp; - } - - return b == 1; - } - } -} diff --git a/Projects/Message/Message/MessageWriter.cs b/Projects/Message/Message/MessageWriter.cs deleted file mode 100644 index 68a5ac3..0000000 --- a/Projects/Message/Message/MessageWriter.cs +++ /dev/null @@ -1,341 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace MultiplayerToolkit -{ - - /// - /// AmongUsùȥ˰ͷش - /// ṹ - /// ------------------------------------- - /// 2bytes (ushort)Ϣijȣ2bytes - /// (------------------------------------- - /// 1byte ϢͣϢ0ԶϢ1 - /// (2byte) (ԶϢṩһmodţSteam.UGC.Itemidulong 8bytesе󣬻ţ֧65535mod) - /// 2byte (ushort)ϢID 0-65535 - /// - /// -------------------------------------) - /// - public class MessageWriter : IRecyclable - { - public const int BYTE_COUNT_OF_LENGTH = 2; // ռ2 bytes - public static int BUFFER_SIZE = 64000; // Ϣ֧64000ֽڣ62.5KB - public static readonly ObjectPool WriterPool = new ObjectPool(() => new MessageWriter(BUFFER_SIZE)); - - public byte[] Buffer; - public int Length; // ЧbufferеijȣܰǶϢ - public int Position; // дα - - // lengthһڻĴСLengthһÿϢϢݲֵֽ - - private Stack messageStarts = new Stack(); // ¼ϢbufferеʼλãǶ׽ṹ - -#region - /// - /// һΪָСMessage - /// - /// - public MessageWriter(int bufferSize) - { - this.Buffer = new byte[bufferSize]; - } - - /// - /// ӳһСΪBufferSizeMessage - /// - /// - public static MessageWriter Get() - { - MessageWriter output = WriterPool.GetObject(); - output.Clear(); - - return output; - } -#endregion - - public byte[] ToByteArray() - { - byte[] output = new byte[this.Length]; - System.Buffer.BlockCopy(this.Buffer, 0, output, 0, this.Length); - return output; - } - - /// - /// Ƿexpectedȵ - /// - /// - /// - public bool HasBytes(int expected) - { - return this.Length >= expected; - } - - public bool HasBytesExactly(int exact) - { - return this.Length == exact; - } - - /// - /// ʼдϢд2bytesΪ - /// - public void StartMessage() - { - var messageStart = this.Position; - messageStarts.Push(messageStart); - this.Buffer[messageStart] = 0; // length - this.Buffer[messageStart + 1] = 0; // length - this.Position += 2; // ushort length - this.Length = this.Position; - } - -#if UNIT_TEST - public void StartMessage(int dummy) - { - StartMessage(); - } -#endif - - /// - /// ôϢȣ־Ϣ - /// - public void EndMessage() - { - var lastMessageStart = messageStarts.Pop(); - ushort length = (ushort)(this.Position - lastMessageStart - 2); // Minus length - this.Buffer[lastMessageStart] = (byte)length; - this.Buffer[lastMessageStart + 1] = (byte)(length >> 8); - } - - /// - /// ȡǰ༭messageصһ - /// - public void CancelMessage() - { - this.Position = this.messageStarts.Pop(); - this.Length = this.Position; - } - - public void Clear() - { - Array.Clear(this.Buffer, 0, this.Buffer.Length); - this.messageStarts.Clear(); - this.Length = 0; - this.Position = 0; - } - - /// - /// message - /// - public void Recycle() - { - this.Position = this.Length = 0; - WriterPool.PutObject(this); - } - -#region WriteMethods - - /// - /// дȡϢͨڷܵϢĹ㲥תҪStart\End - /// - /// - public void CopyFrom(MessageReader target) - { - int offset, length; - if (target.IsRoot) - { - offset = target.Offset; - length = target.Length; - } - else - { - offset = target.Offset - 2; - length = target.Length + 2; - } - - System.Buffer.BlockCopy(target.Buffer, offset, this.Buffer, this.Position, length); - this.Position += length; - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(bool value) - { - this.Buffer[this.Position++] = (byte)(value ? 1 : 0); - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(sbyte value) - { - this.Buffer[this.Position++] = (byte)value; - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(byte value) - { - this.Buffer[this.Position++] = value; - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(short value) - { - this.Buffer[this.Position++] = (byte)value; - this.Buffer[this.Position++] = (byte)(value >> 8); - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(ushort value) - { - this.Buffer[this.Position++] = (byte)value; - this.Buffer[this.Position++] = (byte)(value >> 8); - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(uint value) - { - this.Buffer[this.Position++] = (byte)value; - this.Buffer[this.Position++] = (byte)(value >> 8); - this.Buffer[this.Position++] = (byte)(value >> 16); - this.Buffer[this.Position++] = (byte)(value >> 24); - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(int value) - { - this.Buffer[this.Position++] = (byte)value; - this.Buffer[this.Position++] = (byte)(value >> 8); - this.Buffer[this.Position++] = (byte)(value >> 16); - this.Buffer[this.Position++] = (byte)(value >> 24); - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(ulong value) - { - this.Buffer[this.Position++] = (byte)value; - this.Buffer[this.Position++] = (byte)(value >> 8); - this.Buffer[this.Position++] = (byte)(value >> 16); - this.Buffer[this.Position++] = (byte)(value >> 24); - this.Buffer[this.Position++] = (byte)(value >> 32); - this.Buffer[this.Position++] = (byte)(value >> 40); - this.Buffer[this.Position++] = (byte)(value >> 48); - this.Buffer[this.Position++] = (byte)(value >> 56); - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(long value) - { - this.Buffer[this.Position++] = (byte)value; - this.Buffer[this.Position++] = (byte)(value >> 8); - this.Buffer[this.Position++] = (byte)(value >> 16); - this.Buffer[this.Position++] = (byte)(value >> 24); - this.Buffer[this.Position++] = (byte)(value >> 32); - this.Buffer[this.Position++] = (byte)(value >> 40); - this.Buffer[this.Position++] = (byte)(value >> 48); - this.Buffer[this.Position++] = (byte)(value >> 56); - if (this.Position > this.Length) this.Length = this.Position; - } - - public unsafe void Write(float value) - { - fixed (byte* ptr = &this.Buffer[this.Position]) - { - byte* valuePtr = (byte*)&value; - - *ptr = *valuePtr; - *(ptr + 1) = *(valuePtr + 1); - *(ptr + 2) = *(valuePtr + 2); - *(ptr + 3) = *(valuePtr + 3); - } - - this.Position += 4; - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(string value) - { - var bytes = UTF8Encoding.UTF8.GetBytes(value); - this.WritePacked(bytes.Length); - this.Write(bytes); - } - - public void WriteBytesAndSize(byte[] bytes) - { - this.WritePacked((uint)bytes.Length); - this.Write(bytes); - } - - public void WriteBytesAndSize(byte[] bytes, int length) - { - this.WritePacked((uint)length); - this.Write(bytes, length); - } - - public void WriteBytesAndSize(byte[] bytes, int offset, int length) - { - this.WritePacked((uint)length); - this.Write(bytes, offset, length); - } - - public void Write(byte[] bytes) - { - Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length); - this.Position += bytes.Length; - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(byte[] bytes, int offset, int length) - { - Array.Copy(bytes, offset, this.Buffer, this.Position, length); - this.Position += length; - if (this.Position > this.Length) this.Length = this.Position; - } - - public void Write(byte[] bytes, int length) - { - Array.Copy(bytes, 0, this.Buffer, this.Position, length); - this.Position += length; - if (this.Position > this.Length) this.Length = this.Position; - } - - /// - public void WritePacked(int value) - { - this.WritePacked((uint)value); - } - - /// - public void WritePacked(uint value) - { - do - { - byte b = (byte)(value & 0xFF); - if (value >= 0x80) - { - b |= 0x80; - } - - this.Write(b); - value >>= 7; - } while (value > 0); - } -#endregion - - public void Write(MessageWriter msg) - { - this.Write(msg.Buffer, 0, msg.Length); - } - - public unsafe static bool IsLittleEndian() - { - byte b; - unsafe - { - int i = 1; - byte* bp = (byte*)&i; - b = *bp; - } - - return b == 1; - } - } -} diff --git a/Projects/Message/Message/MessageWriterTests.cs b/Projects/Message/Message/MessageWriterTests.cs index 9a873a5..b6efba0 100644 --- a/Projects/Message/Message/MessageWriterTests.cs +++ b/Projects/Message/Message/MessageWriterTests.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.IO; using System.Text; - namespace MultiplayerToolkit { diff --git a/Projects/Message/Message/MultiplayerToolkit/IRecyclable.cs b/Projects/Message/Message/MultiplayerToolkit/IRecyclable.cs new file mode 100644 index 0000000..17d3104 --- /dev/null +++ b/Projects/Message/Message/MultiplayerToolkit/IRecyclable.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MultiplayerToolkit +{ + /// + /// Interface for all items that can be returned to an object pool. + /// + /// + public interface IRecyclable + { + /// + /// Returns this object back to the object pool. + /// + /// + /// + /// Calling this when you are done with the object returns the object back to a pool in order to be reused. + /// This can reduce the amount of work the GC has to do dramatically but it is optional to call this. + /// + /// + /// Calling this indicates to Hazel that this can be reused and thus you should only call this when you are + /// completely finished with the object as the contents can be overwritten at any point after. + /// + /// + void Recycle(); + } +} diff --git a/Projects/Message/Message/MultiplayerToolkit/MessageReader.cs b/Projects/Message/Message/MultiplayerToolkit/MessageReader.cs new file mode 100644 index 0000000..32b0218 --- /dev/null +++ b/Projects/Message/Message/MultiplayerToolkit/MessageReader.cs @@ -0,0 +1,530 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; + +namespace MultiplayerToolkit +{ + /// + /// 可以处理嵌套消息的结构,和MessageWriter配对 + /// + public class MessageReader : IRecyclable + { + public static readonly ObjectPool ReaderPool = new ObjectPool(() => new MessageReader()); + + public byte[] Buffer; // 缓冲区,会被子消息共享这个缓冲区 + + // 消息长度,定值 + // * 如果是非根消息,对应的是MessageWriter里写入的2bytes包长度,即不含length的长度 + // * 如果是根消息,Length是整个有效数据在Buffer中的长度,包括Length + public int Length; + + // 有效数据偏移,定值,游标会在这个基础上偏移 + // * 如果是非根消息,Offset在Length后面的位置 + // * 如果是根消息,Offset等于0,表示有效数据在Buffer中的偏移 + public int Offset; + + public int BytesRemaining => this.Length - this.Position; + + public int TotalLength => this.Length + this.Offset; + + private MessageReader Parent; // 保存父消息索引,和父消息共享父消息的Buffer + + // 是否是根消息,根消息下面包含多个子消息,子消息可以嵌套重孙消息 + public bool IsRoot + { + get { return Parent == null; } + } + + // 子消息可读 + public bool IsReadable + { + get { return this.Parent != null; } + } + + public int Position + { + get { return this._position; } + set + { + this._position = value; + this.readHead = value + Offset; + } + } + + private int _position; // 读取游标,相对于消息内容部分的偏移,从0开始,子消息也是从0开始,不是在Buffer中的索引 + private int readHead; // 读取游标,消息头部在Buffer中的位置,参考ReadMessage() + +#region 创建 + /// + /// 创建一个容量至少为minSize的空缓冲区 + /// + /// + /// + /// + public static MessageReader GetSized(int minSize, bool exactly = false) + { + var output = ReaderPool.GetObject(); + + if (output.Buffer == null || output.Buffer.Length < minSize) + { + output.Buffer = new byte[minSize]; + } + else + { + if(exactly && output.Buffer.Length > minSize) + { + output.Buffer = new byte[minSize]; + } + Array.Clear(output.Buffer, 0, output.Buffer.Length); + } + + output.Offset = 0; + output.Length = 0; + output.Position = 0; + output.Parent = null; + return output; + } + + /// + /// 将缓冲区封装为根消息,不拷贝 + /// + /// + /// + public static MessageReader Get(byte[] buffer) + { + var output = ReaderPool.GetObject(); + + output.Buffer = buffer; + output.Offset = 0; + output.Position = 0; + output.Length = buffer.Length; + output.Parent = null; + + return output; + } + + /// + /// 将缓冲区封装为根消息,拷贝 + /// + /// + /// + public static MessageReader GetAndCopy(byte[] buffer) + { + var output = ReaderPool.GetObject(); + + byte[] _buffer = new byte[buffer.Length]; + Array.Copy(buffer, _buffer, buffer.Length); + output.Buffer = _buffer; + output.Offset = 0; + output.Position = 0; + output.Length = _buffer.Length; + output.Parent = null; + + return output; + } + + /// + /// 创建一个新的父message,保存source。用于缓存message,因为source所在的原父message可能会被销毁,如果需要延迟处理消息,需要拷贝一份新的 + /// + /// + /// + public static MessageReader CopyMessageIntoParent(MessageReader source) + { + var output = MessageReader.GetSized(source.Length + 2); //2=2bytes(length) + System.Buffer.BlockCopy(source.Buffer, source.Offset - 2, output.Buffer, 0, source.Length + 2); + + output.Offset = 0; + output.Position = 0; + output.Length = source.Length + 2; + output.Parent = null; + + return output; + } + +#if UNIT_TEST + /// + /// 用于测试,引用buffer的一部分作为child message,后续可以直接读取 + /// + /// + /// + /// + public static MessageReader GetChildMessage(byte[] buffer, int offset) + { + // 确保至少有一个length + if (offset + 2 > buffer.Length) return null; + + var output = ReaderPool.GetObject(); + + output.Buffer = buffer; + output.Offset = offset; + output.Position = 0; + + output.Length = output.ReadUInt16(); // 读到的值为length + + output.Offset += 2; + output.Position = 0; + output.Parent = null; + + return output; + } + + public static MessageReader Copy(MessageReader source) + { + var output = MessageReader.GetSized(source.Buffer.Length); + System.Buffer.BlockCopy(source.Buffer, 0, output.Buffer, 0, source.Buffer.Length); + + output.Offset = source.Offset; + + output._position = source._position; + output.readHead = source.readHead; + + output.Length = source.Length; + + return output; + } + +#endif + + #endregion + + /// + /// 返回一个可读子消息,引用了父缓冲区,**不要回收** + /// + public MessageReader ReadMessage() + { + // 至少有一个length + if (this.BytesRemaining < 2) throw new InvalidDataException($"ReadMessage header is longer than message length: 2 of {this.BytesRemaining}"); + + MessageReader output = new MessageReader(); + + output.Parent = this; + output.Buffer = this.Buffer; + output.Offset = this.readHead; + output.Position = 0; + + output.Length = output.ReadUInt16(); // length + + output.Offset += 2; // 2=length + output.Position = 0; + + // 数据不全 + if (this.BytesRemaining < output.Length + 2) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {output.Length + 2} of {this.BytesRemaining}"); + + this.Position += output.Length + 2; //跳过整个子消息 + return output; + } + +#if UNIT_TEST + /// + /// 仅用于测试,返回一个可读子消息,并且拷贝数据,可回收 + /// Offset为0,开头没有Length数据 + /// + public MessageReader ReadMessageAsNewBuffer() + { + if (this.BytesRemaining < 2) throw new InvalidDataException($"ReadMessage header is longer than message length: 2 of {this.BytesRemaining}"); + + var len = this.ReadUInt16(); // Position += 2 + + if (this.BytesRemaining < len) throw new InvalidDataException($"Message Length at Position {this.readHead} is longer than message length: {len} of {this.BytesRemaining}"); + + var output = MessageReader.GetSized(len); + + Array.Copy(this.Buffer, this.readHead, output.Buffer, 0, len); + + output.Length = len; + + this.Position += output.Length; + return output; + } +#endif + +#if UNIT_TEST + public MessageReader Duplicate() + { + var output = GetSized(this.Length); + Array.Copy(this.Buffer, this.Offset, output.Buffer, 0, this.Length); + output.Length = this.Length; + output.Offset = 0; + output.Position = 0; + + return output; + } + + public void RemoveMessage(MessageReader reader) + { + MessageReader temp = null; + try + { + var headerOffset = reader.Offset - 2; + var endOfMessage = reader.Offset + reader.Length; // 有效部分的后一个byte + var len = reader.Buffer.Length - endOfMessage; + + temp = MessageReader.GetSized(len); + Array.Copy(reader.Buffer, endOfMessage, temp.Buffer, 0, len); + Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset, len); + + this.AdjustLength(reader.Offset, reader.Length + 2); + } + finally + { + if(temp != null) + { + temp.Recycle(); + } + } + } + + /// + /// 在reader前面插入一条消息 + /// + /// 父reader + /// + public void InsertMessage(MessageReader reader, MessageWriter writer) + { + var temp = MessageReader.GetSized(reader.Buffer.Length); + try + { + var headerOffset = reader.Offset - 2; + var startOfMessage = reader.Offset; + var len = reader.Buffer.Length - headerOffset; // 疑似写错了,应该是headerOffset + int writerOffset = 0; + + //store the original buffer in temp + Array.Copy(reader.Buffer, headerOffset, temp.Buffer, 0, len); + + //put the contents of writer in at headerOffset + Array.Copy(writer.Buffer, writerOffset, this.Buffer, headerOffset, writer.Length - writerOffset); + + //put the original buffer in after that + Array.Copy(temp.Buffer, 0, this.Buffer, headerOffset + (writer.Length - writerOffset), len - writer.Length); + + this.AdjustLength(-1 * reader.Offset, -1 * (writer.Length - writerOffset)); + } + finally + { + temp.Recycle(); + } + } + + /// + /// 递归调整length值 + /// + /// 子消息的Offset值 + /// 子消息总长度,含length + private void AdjustLength(int offset, int amount) + { + if (this.readHead > offset) + { + this.Position -= amount; + } + + if (Parent != null) + { + var lengthOffset = this.Offset - 2; + var curLen = this.Buffer[lengthOffset] + | (this.Buffer[lengthOffset + 1] << 8); + + curLen -= amount; + this.Length -= amount; + + this.Buffer[lengthOffset] = (byte)curLen; + this.Buffer[lengthOffset + 1] = (byte)(curLen >> 8); + + Parent.AdjustLength(offset, amount); + } + } + +#endif + + public void Recycle() + { + this.Parent = null; + ReaderPool.PutObject(this); + } + +#region Read Methods + public bool ReadBoolean() + { + byte val = this.FastByte(); + return val != 0; + } + + public sbyte ReadSByte() + { + return (sbyte)this.FastByte(); + } + + public byte ReadByte() + { + return this.FastByte(); + } + + public ushort ReadUInt16() + { + ushort output = + (ushort)(this.FastByte() + | this.FastByte() << 8); + return output; + } + + public short ReadInt16() + { + short output = + (short)(this.FastByte() + | this.FastByte() << 8); + return output; + } + + public uint ReadUInt32() + { + uint output = this.FastByte() + | (uint)this.FastByte() << 8 + | (uint)this.FastByte() << 16 + | (uint)this.FastByte() << 24; + + return output; + } + + public int ReadInt32() + { + int output = this.FastByte() + | this.FastByte() << 8 + | this.FastByte() << 16 + | this.FastByte() << 24; + + return output; + } + + public ulong ReadUInt64() + { + ulong output = (ulong)this.FastByte() + | (ulong)this.FastByte() << 8 + | (ulong)this.FastByte() << 16 + | (ulong)this.FastByte() << 24 + | (ulong)this.FastByte() << 32 + | (ulong)this.FastByte() << 40 + | (ulong)this.FastByte() << 48 + | (ulong)this.FastByte() << 56; + + return output; + } + + public long ReadInt64() + { + long output = (long)this.FastByte() + | (long)this.FastByte() << 8 + | (long)this.FastByte() << 16 + | (long)this.FastByte() << 24 + | (long)this.FastByte() << 32 + | (long)this.FastByte() << 40 + | (long)this.FastByte() << 48 + | (long)this.FastByte() << 56; + + return output; + } + + public unsafe float ReadSingle() + { + float output = 0; + fixed (byte* bufPtr = &this.Buffer[this.readHead]) + { + byte* outPtr = (byte*)&output; + + *outPtr = *bufPtr; + *(outPtr + 1) = *(bufPtr + 1); + *(outPtr + 2) = *(bufPtr + 2); + *(outPtr + 3) = *(bufPtr + 3); + } + + this.Position += 4; + return output; + } + + public string ReadString() + { + int len = this.ReadPackedInt32(); + if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}"); + + string output = UTF8Encoding.UTF8.GetString(this.Buffer, this.readHead, len); + + this.Position += len; + return output; + } + + public byte[] ReadBytesAndSize() + { + int len = this.ReadPackedInt32(); + if (this.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {this.BytesRemaining}"); + + return this.ReadBytes(len); + } + + public byte[] ReadBytes(int length) + { + if (this.BytesRemaining < length) throw new InvalidDataException($"Read length is longer than message length: {length} of {this.BytesRemaining}"); + + byte[] output = new byte[length]; + Array.Copy(this.Buffer, this.readHead, output, 0, output.Length); + this.Position += output.Length; + return output; + } + + /// + public int ReadPackedInt32() + { + return (int)this.ReadPackedUInt32(); + } + + /// + public uint ReadPackedUInt32() + { + bool readMore = true; + int shift = 0; + uint output = 0; + + while (readMore) + { + if (this.BytesRemaining < 1) throw new InvalidDataException($"Read length is longer than message length."); + + byte b = this.ReadByte(); + if (b >= 0x80) + { + readMore = true; + b ^= 0x80; + } + else + { + readMore = false; + } + + output |= (uint)(b << shift); + shift += 7; + } + + return output; + } +#endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte FastByte() + { + this._position++; + return this.Buffer[this.readHead++]; + } + + public unsafe static bool IsLittleEndian() + { + byte b; + unsafe + { + int i = 1; + byte* bp = (byte*)&i; + b = *bp; + } + + return b == 1; + } + } +} diff --git a/Projects/Message/Message/MultiplayerToolkit/MessageWriter.cs b/Projects/Message/Message/MultiplayerToolkit/MessageWriter.cs new file mode 100644 index 0000000..0b37c5d --- /dev/null +++ b/Projects/Message/Message/MultiplayerToolkit/MessageWriter.cs @@ -0,0 +1,341 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace MultiplayerToolkit +{ + + /// + /// AmongUsùȥ˰ͷش + /// ṹ + /// ------------------------------------- + /// 2bytes (ushort)Ϣijȣ2bytes + /// (------------------------------------- + /// 1byte ϢͣϢ0ԶϢ1 + /// (2byte) (ԶϢṩһmodţSteam.UGC.Itemidulong 8bytesе󣬻ţ֧65535mod) + /// 2byte (ushort)ϢID 0-65535 + /// + /// -------------------------------------) + /// + public class MessageWriter : IRecyclable + { + public const int BYTE_COUNT_OF_LENGTH = 2; // ռ2 bytes + public static int BUFFER_SIZE = 64000; // Ϣ֧64000ֽڣ62.5KB + public static readonly ObjectPool WriterPool = new ObjectPool(() => new MessageWriter(BUFFER_SIZE)); + + public byte[] Buffer; + public int Length; // ЧbufferеijȣܰǶϢ + public int Position; // дα + + // lengthһڻĴСLengthһÿϢϢݲֵֽ + + private Stack messageStarts = new Stack(); + +#region + /// + /// һΪָСMessage + /// + /// + public MessageWriter(int bufferSize) + { + this.Buffer = new byte[bufferSize]; + } + + /// + /// ӳһСΪBufferSizeMessage + /// + /// + public static MessageWriter Get() + { + MessageWriter output = WriterPool.GetObject(); + output.Clear(); + + return output; + } +#endregion + + public byte[] ToByteArray() + { + byte[] output = new byte[this.Length]; + System.Buffer.BlockCopy(this.Buffer, 0, output, 0, this.Length); + return output; + } + + /// + /// Ƿexpectedȵ + /// + /// + /// + public bool HasBytes(int expected) + { + return this.Length >= expected; + } + + public bool HasBytesExactly(int exact) + { + return this.Length == exact; + } + + /// + /// ʼдϢд2bytesΪ + /// + public void StartMessage() + { + var messageStart = this.Position; + messageStarts.Push(messageStart); + this.Buffer[messageStart] = 0; // length + this.Buffer[messageStart + 1] = 0; // length + this.Position += 2; // ushort length + this.Length = this.Position; + } + +#if UNIT_TEST + public void StartMessage(int dummy) + { + StartMessage(); + } +#endif + + /// + /// ôϢȣ־Ϣ + /// + public void EndMessage() + { + var lastMessageStart = messageStarts.Pop(); + ushort length = (ushort)(this.Position - lastMessageStart - 2); // Minus length + this.Buffer[lastMessageStart] = (byte)length; + this.Buffer[lastMessageStart + 1] = (byte)(length >> 8); + } + + /// + /// ȡǰ༭messageصһ + /// + public void CancelMessage() + { + this.Position = this.messageStarts.Pop(); + this.Length = this.Position; + } + + public void Clear() + { + Array.Clear(this.Buffer, 0, this.Buffer.Length); + this.messageStarts.Clear(); + this.Length = 0; + this.Position = 0; + } + + /// + /// message + /// + public void Recycle() + { + this.Position = this.Length = 0; + WriterPool.PutObject(this); + } + +#region WriteMethods + + /// + /// дȡϢͨڷܵϢĹ㲥תҪStart\End + /// + /// + public void CopyFrom(MessageReader target) + { + int offset, length; + if (target.IsRoot) + { + offset = target.Offset; + length = target.Length; + } + else + { + offset = target.Offset - 2; + length = target.Length + 2; + } + + System.Buffer.BlockCopy(target.Buffer, offset, this.Buffer, this.Position, length); + this.Position += length; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(bool value) + { + this.Buffer[this.Position++] = (byte)(value ? 1 : 0); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(sbyte value) + { + this.Buffer[this.Position++] = (byte)value; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(byte value) + { + this.Buffer[this.Position++] = value; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(short value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(ushort value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(uint value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + this.Buffer[this.Position++] = (byte)(value >> 16); + this.Buffer[this.Position++] = (byte)(value >> 24); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(int value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + this.Buffer[this.Position++] = (byte)(value >> 16); + this.Buffer[this.Position++] = (byte)(value >> 24); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(ulong value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + this.Buffer[this.Position++] = (byte)(value >> 16); + this.Buffer[this.Position++] = (byte)(value >> 24); + this.Buffer[this.Position++] = (byte)(value >> 32); + this.Buffer[this.Position++] = (byte)(value >> 40); + this.Buffer[this.Position++] = (byte)(value >> 48); + this.Buffer[this.Position++] = (byte)(value >> 56); + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(long value) + { + this.Buffer[this.Position++] = (byte)value; + this.Buffer[this.Position++] = (byte)(value >> 8); + this.Buffer[this.Position++] = (byte)(value >> 16); + this.Buffer[this.Position++] = (byte)(value >> 24); + this.Buffer[this.Position++] = (byte)(value >> 32); + this.Buffer[this.Position++] = (byte)(value >> 40); + this.Buffer[this.Position++] = (byte)(value >> 48); + this.Buffer[this.Position++] = (byte)(value >> 56); + if (this.Position > this.Length) this.Length = this.Position; + } + + public unsafe void Write(float value) + { + fixed (byte* ptr = &this.Buffer[this.Position]) + { + byte* valuePtr = (byte*)&value; + + *ptr = *valuePtr; + *(ptr + 1) = *(valuePtr + 1); + *(ptr + 2) = *(valuePtr + 2); + *(ptr + 3) = *(valuePtr + 3); + } + + this.Position += 4; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(string value) + { + var bytes = UTF8Encoding.UTF8.GetBytes(value); + this.WritePacked(bytes.Length); + this.Write(bytes); + } + + public void WriteBytesAndSize(byte[] bytes) + { + this.WritePacked((uint)bytes.Length); + this.Write(bytes); + } + + public void WriteBytesAndSize(byte[] bytes, int length) + { + this.WritePacked((uint)length); + this.Write(bytes, length); + } + + public void WriteBytesAndSize(byte[] bytes, int offset, int length) + { + this.WritePacked((uint)length); + this.Write(bytes, offset, length); + } + + public void Write(byte[] bytes) + { + Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length); + this.Position += bytes.Length; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(byte[] bytes, int offset, int length) + { + Array.Copy(bytes, offset, this.Buffer, this.Position, length); + this.Position += length; + if (this.Position > this.Length) this.Length = this.Position; + } + + public void Write(byte[] bytes, int length) + { + Array.Copy(bytes, 0, this.Buffer, this.Position, length); + this.Position += length; + if (this.Position > this.Length) this.Length = this.Position; + } + + /// + public void WritePacked(int value) + { + this.WritePacked((uint)value); + } + + /// + public void WritePacked(uint value) + { + do + { + byte b = (byte)(value & 0xFF); + if (value >= 0x80) + { + b |= 0x80; + } + + this.Write(b); + value >>= 7; + } while (value > 0); + } +#endregion + + public void Write(MessageWriter msg) + { + this.Write(msg.Buffer, 0, msg.Length); + } + + public unsafe static bool IsLittleEndian() + { + byte b; + unsafe + { + int i = 1; + byte* bp = (byte*)&i; + b = *bp; + } + + return b == 1; + } + } +} diff --git a/Projects/Message/Message/MultiplayerToolkit/ObjectPool.cs b/Projects/Message/Message/MultiplayerToolkit/ObjectPool.cs new file mode 100644 index 0000000..e42cdd6 --- /dev/null +++ b/Projects/Message/Message/MultiplayerToolkit/ObjectPool.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace MultiplayerToolkit +{ + /// + /// A fairly simple object pool for items that will be created a lot. + /// + /// The type that is pooled. + /// + public sealed class ObjectPool where T : IRecyclable + { + private int numberCreated; + public int NumberCreated { get { return numberCreated; } } + + public int NumberInUse { get { return this.inuse.Count; } } + public int NumberNotInUse { get { return this.pool.Count; } } + public int Size { get { return this.NumberInUse + this.NumberNotInUse; } } + +#if HAZEL_BAG + private readonly ConcurrentBag pool = new ConcurrentBag(); +#else + private readonly List pool = new List(); +#endif + + // Unavailable objects + private readonly ConcurrentDictionary inuse = new ConcurrentDictionary(); + + /// + /// The generator for creating new objects. + /// + /// + private readonly Func objectFactory; + + /// + /// Internal constructor for our ObjectPool. + /// + internal ObjectPool(Func objectFactory) + { + this.objectFactory = objectFactory; + } + + /// + /// Returns a pooled object of type T, if none are available another is created. + /// + /// An instance of T. + internal T GetObject() + { +#if HAZEL_BAG + if (!pool.TryTake(out T item)) + { + Interlocked.Increment(ref numberCreated); + item = objectFactory.Invoke(); + } +#else + T item; + lock (this.pool) + { + if (this.pool.Count > 0) + { + var idx = this.pool.Count - 1; + item = this.pool[idx]; + this.pool.RemoveAt(idx); + } + else + { + Interlocked.Increment(ref numberCreated); + item = objectFactory.Invoke(); + } + } +#endif + + if (!inuse.TryAdd(item, true)) + { + throw new Exception("Duplicate pull " + typeof(T).Name); + } + + return item; + } + + /// + /// Returns an object to the pool. + /// + /// The item to return. + internal void PutObject(T item) + { + if (inuse.TryRemove(item, out bool b)) + { +#if HAZEL_BAG + pool.Add(item); +#else + lock (this.pool) + { + pool.Add(item); + } +#endif + } + else + { +#if DEBUG + throw new Exception("Duplicate add " + typeof(T).Name); +#endif + } + } + } +} diff --git a/Projects/Message/Message/ObjectPool.cs b/Projects/Message/Message/ObjectPool.cs deleted file mode 100644 index e42cdd6..0000000 --- a/Projects/Message/Message/ObjectPool.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; - -namespace MultiplayerToolkit -{ - /// - /// A fairly simple object pool for items that will be created a lot. - /// - /// The type that is pooled. - /// - public sealed class ObjectPool where T : IRecyclable - { - private int numberCreated; - public int NumberCreated { get { return numberCreated; } } - - public int NumberInUse { get { return this.inuse.Count; } } - public int NumberNotInUse { get { return this.pool.Count; } } - public int Size { get { return this.NumberInUse + this.NumberNotInUse; } } - -#if HAZEL_BAG - private readonly ConcurrentBag pool = new ConcurrentBag(); -#else - private readonly List pool = new List(); -#endif - - // Unavailable objects - private readonly ConcurrentDictionary inuse = new ConcurrentDictionary(); - - /// - /// The generator for creating new objects. - /// - /// - private readonly Func objectFactory; - - /// - /// Internal constructor for our ObjectPool. - /// - internal ObjectPool(Func objectFactory) - { - this.objectFactory = objectFactory; - } - - /// - /// Returns a pooled object of type T, if none are available another is created. - /// - /// An instance of T. - internal T GetObject() - { -#if HAZEL_BAG - if (!pool.TryTake(out T item)) - { - Interlocked.Increment(ref numberCreated); - item = objectFactory.Invoke(); - } -#else - T item; - lock (this.pool) - { - if (this.pool.Count > 0) - { - var idx = this.pool.Count - 1; - item = this.pool[idx]; - this.pool.RemoveAt(idx); - } - else - { - Interlocked.Increment(ref numberCreated); - item = objectFactory.Invoke(); - } - } -#endif - - if (!inuse.TryAdd(item, true)) - { - throw new Exception("Duplicate pull " + typeof(T).Name); - } - - return item; - } - - /// - /// Returns an object to the pool. - /// - /// The item to return. - internal void PutObject(T item) - { - if (inuse.TryRemove(item, out bool b)) - { -#if HAZEL_BAG - pool.Add(item); -#else - lock (this.pool) - { - pool.Add(item); - } -#endif - } - else - { -#if DEBUG - throw new Exception("Duplicate add " + typeof(T).Name); -#endif - } - } - } -} -- cgit v1.1-26-g67d0