using System; using UnityEngine; namespace Mirror { /// /// NetworkReader to be used with NetworkReaderPool /// public class PooledNetworkReader : NetworkReader, IDisposable { internal PooledNetworkReader(byte[] bytes) : base(bytes) { } internal PooledNetworkReader(ArraySegment segment) : base(segment) { } public void Dispose() { NetworkReaderPool.Recycle(this); } } /// /// Pool of NetworkReaders /// Use this pool instead of NetworkReader to reduce memory allocation /// Use Capacity to change size of pool /// public static class NetworkReaderPool { static readonly ILogger logger = LogFactory.GetLogger(typeof(NetworkReaderPool), LogType.Error); /// /// Size of the pool /// If pool is too small getting readers will causes memory allocation /// Default value: 100 /// public static int Capacity { get => pool.Length; set { // resize the array Array.Resize(ref pool, value); // if capacity is smaller than before, then we need to adjust // 'next' so it doesn't point to an index out of range // -> if we set '0' then next = min(_, 0-1) => -1 // -> if we set '2' then next = min(_, 2-1) => 1 next = Mathf.Min(next, pool.Length - 1); } } /// /// Mirror usually only uses up to 4 readers in nested usings, /// 100 is a good margin for edge cases when users need a lot readers at /// the same time. /// /// keep in mind, most entries of the pool will be null in most cases /// /// /// Note: we use an Array instead of a Stack because it's significantly /// faster: https://github.com/vis2k/Mirror/issues/1614 static PooledNetworkReader[] pool = new PooledNetworkReader[100]; static int next = -1; /// /// Get the next reader in the pool /// If pool is empty, creates a new Reader /// public static PooledNetworkReader GetReader(byte[] bytes) { if (next == -1) { return new PooledNetworkReader(bytes); } PooledNetworkReader reader = pool[next]; pool[next] = null; next--; // reset buffer SetBuffer(reader, bytes); return reader; } /// /// Get the next reader in the pool /// If pool is empty, creates a new Reader /// public static PooledNetworkReader GetReader(ArraySegment segment) { if (next == -1) { return new PooledNetworkReader(segment); } PooledNetworkReader reader = pool[next]; pool[next] = null; next--; // reset buffer SetBuffer(reader, segment); return reader; } /// /// Puts reader back into pool /// When pool is full, the extra reader is left for the GC /// public static void Recycle(PooledNetworkReader reader) { if (next < pool.Length - 1) { next++; pool[next] = reader; } else { logger.LogWarning("NetworkReaderPool.Recycle, Pool was full leaving extra reader for GC"); } } // SetBuffer methods mirror constructor for ReaderPool static void SetBuffer(NetworkReader reader, byte[] bytes) { reader.buffer = new ArraySegment(bytes); reader.Position = 0; } static void SetBuffer(NetworkReader reader, ArraySegment segment) { reader.buffer = segment; reader.Position = 0; } } }