160 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using FishNet.Managing;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Runtime.CompilerServices;
 | |
| 
 | |
| namespace FishNet.Serializing
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Writer which is reused to save on garbage collection and performance.
 | |
|     /// </summary>
 | |
|     public sealed class PooledWriter : Writer, IDisposable
 | |
|     {
 | |
|         public void Dispose() => WriterPool.Recycle(this);
 | |
|         public void DisposeLength() => WriterPool.RecycleLength(this);
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Collection of PooledWriter. Stores and gets PooledWriter.
 | |
|     /// </summary>
 | |
|     public static class WriterPool
 | |
|     {
 | |
|         #region Private.
 | |
|         /// <summary>
 | |
|         /// Pool of writers where length is the minimum and increased at runtime.
 | |
|         /// </summary>
 | |
|         private static readonly Stack<PooledWriter> _pool = new Stack<PooledWriter>();
 | |
|         /// <summary>
 | |
|         /// Pool of writers where length is of minimum key and may be increased at runtime.
 | |
|         /// </summary>
 | |
|         private static readonly Dictionary<int, Stack<PooledWriter>> _lengthPool = new Dictionary<int, Stack<PooledWriter>>();
 | |
|         #endregion
 | |
| 
 | |
|         #region Const.
 | |
|         /// <summary>
 | |
|         /// Length of each bracket when using the length based writer pool.
 | |
|         /// </summary>
 | |
|         internal const int LENGTH_BRACKET = 1000;
 | |
|         #endregion
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets a writer from the pool.
 | |
|         /// </summary>
 | |
|         public static PooledWriter GetWriter(NetworkManager networkManager)
 | |
|         {
 | |
|             PooledWriter result = (_pool.Count > 0) ? _pool.Pop() : new PooledWriter();
 | |
|             result.Reset(networkManager);
 | |
|             return result;
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Gets a writer from the pool.
 | |
|         /// </summary>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         public static PooledWriter GetWriter()
 | |
|         {
 | |
|             return GetWriter(null);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Gets which index to use for length based pooled readers based on length.
 | |
|         /// </summary>
 | |
|         private static int GetDictionaryIndex(int length)
 | |
|         {
 | |
|             return (length / LENGTH_BRACKET);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Gets the next writer in the pool of minimum length.
 | |
|         /// </summary>
 | |
|         /// <param name="length">Minimum length the writer buffer must be.</param>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         public static PooledWriter GetWriter(int length)
 | |
|         {
 | |
|             return GetWriter(null, length);
 | |
|         }
 | |
|         /// <summary>
 | |
|         /// Gets the next writer in the pool of minimum length.
 | |
|         /// </summary>
 | |
|         /// <param name="length">Minimum length the writer buffer must be.</param>
 | |
|         [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | |
|         public static PooledWriter GetWriter(NetworkManager networkManager, int length)
 | |
|         {
 | |
|             //Ensure length is the minimum.
 | |
|             if (length < LENGTH_BRACKET)
 | |
|                 length = LENGTH_BRACKET;
 | |
| 
 | |
|             /* The index returned will be for writers which have
 | |
|              * length as a minimum capacity.
 | |
|              * EG: if length is 1200 / 1000 (length_bracket) result
 | |
|              * will be index 1. Index 0 will be up to 1000, while
 | |
|              * index 1 will be up to 2000. */
 | |
|             int dictIndex = GetDictionaryIndex(length);
 | |
|             Stack<PooledWriter> stack;
 | |
|             //There is already one pooled.
 | |
|             if (_lengthPool.TryGetValue(dictIndex, out stack) && stack.Count > 0)
 | |
|             {
 | |
|                 PooledWriter result = stack.Pop();
 | |
|                 result.Reset(networkManager);
 | |
|                 return result;
 | |
|             }
 | |
|             //Not pooled yet.
 | |
|             else
 | |
|             {
 | |
|                 //Get any ol' writer.
 | |
|                 PooledWriter writer = GetWriter(networkManager);
 | |
|                 /* Ensure length to fill it's bracket.
 | |
|                  * Increase index by 1 since 0 index would
 | |
|                  * just return 0 as the capacity. */
 | |
|                 int requiredCapacity = (dictIndex + 1) * LENGTH_BRACKET;
 | |
|                 writer.EnsureBufferCapacity(requiredCapacity);
 | |
|                 return writer;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns a writer to the appropriate length pool.
 | |
|         /// Writers must be a minimum of 1000 bytes in length to be sorted by length.
 | |
|         /// Writers which do not meet the minimum will be resized to 1000 bytes.
 | |
|         /// </summary>
 | |
|         public static void RecycleLength(PooledWriter writer)
 | |
|         {
 | |
|             int capacity = writer.Capacity;
 | |
|             /* If capacity is less than 1000 then the writer
 | |
|              * does not meet the minimum length bracket. This should never
 | |
|              * be the case unless the user perhaps manually calls this method. */
 | |
|             if (capacity < LENGTH_BRACKET)
 | |
|             {
 | |
|                 capacity = LENGTH_BRACKET;
 | |
|                 writer.EnsureBufferCapacity(LENGTH_BRACKET);
 | |
|             }
 | |
| 
 | |
|             /* When getting the recycle index subtract one from
 | |
|              * the dictionary index. This is because the writer being
 | |
|              * recycled must meet the minimum for that index.
 | |
|              * EG: if LENGTH_BRACKET is 1000....
 | |
|              * 1200 / 1000 = 1(after flooring).
 | |
|              * However, each incremeent in index should have a capacity
 | |
|              * of 1000, so index 1 should have a minimum capacity of 2000,
 | |
|              * which 1200 does not meet. By subtracting 1 from the index
 | |
|              * 1200 will now be placed in index 0 meeting the capacity for that index. */
 | |
|             int dictIndex = GetDictionaryIndex(capacity) - 1;
 | |
|             Stack<PooledWriter> stack;
 | |
|             if (!_lengthPool.TryGetValue(dictIndex, out stack))
 | |
|             {
 | |
|                 stack = new Stack<PooledWriter>();
 | |
|                 _lengthPool[dictIndex] = stack;
 | |
|             }
 | |
| 
 | |
|             stack.Push(writer);
 | |
|         }
 | |
| 
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Returns a writer to the pool.
 | |
|         /// </summary>
 | |
|         public static void Recycle(PooledWriter writer)
 | |
|         {
 | |
|             _pool.Push(writer);
 | |
|         }
 | |
|     }
 | |
| }
 |