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 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)ÏûÏ¢µÄ³¤¶È£¬²»°üÀ¨Õâ2bytes - /// (------------------------------------- - /// 1byte ÏûÏ¢ÀàÐÍ£¬ÄÚÖÃÏûÏ¢0¡¢Íæ¼Ò×Ô¶¨ÒåÏûÏ¢1 - /// (2byte) (Èç¹ûÊÇÍæ¼Ò×Ô¶¨ÒåÏûÏ¢£¬Ìṩһ¸ömodÐòºÅ£¬Steam.UGC.ItemµÄidÊÇulong 8bytesÓеã´ó£¬»»³ÉÐòºÅ£¬×î¶àÖ§³Ö65535¸ömod) - /// 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]; - } - - /// - /// ´Ó³Ø×ÓÖÐÄÃÒ»¸ö»º³åÇø´óСΪBufferSizeµÄMessage - /// - /// - 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)ÏûÏ¢µÄ³¤¶È£¬²»°üÀ¨Õâ2bytes + /// (------------------------------------- + /// 1byte ÏûÏ¢ÀàÐÍ£¬ÄÚÖÃÏûÏ¢0¡¢Íæ¼Ò×Ô¶¨ÒåÏûÏ¢1 + /// (2byte) (Èç¹ûÊÇÍæ¼Ò×Ô¶¨ÒåÏûÏ¢£¬Ìṩһ¸ömodÐòºÅ£¬Steam.UGC.ItemµÄidÊÇulong 8bytesÓеã´ó£¬»»³ÉÐòºÅ£¬×î¶àÖ§³Ö65535¸ömod) + /// 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]; + } + + /// + /// ´Ó³Ø×ÓÖÐÄÃÒ»¸ö»º³åÇø´óСΪBufferSizeµÄMessage + /// + /// + 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