150 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C#
		
	
	
| using System;
 | |
| 
 | |
| namespace ProtoBuf
 | |
| {
 | |
|     internal sealed class BufferPool
 | |
|     {
 | |
|         internal static void Flush()
 | |
|         {
 | |
|             lock (Pool)
 | |
|             {
 | |
|                 for (var i = 0; i < Pool.Length; i++)
 | |
|                     Pool[i] = null;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private BufferPool() { }
 | |
|         private const int POOL_SIZE = 20;
 | |
|         internal const int BUFFER_LENGTH = 1024;
 | |
|         private static readonly CachedBuffer[] Pool = new CachedBuffer[POOL_SIZE];
 | |
| 
 | |
|         internal static byte[] GetBuffer() => GetBuffer(BUFFER_LENGTH);
 | |
| 
 | |
|         internal static byte[] GetBuffer(int minSize)
 | |
|         {
 | |
|             byte[] cachedBuff = GetCachedBuffer(minSize);
 | |
|             return cachedBuff ?? new byte[minSize];
 | |
|         }
 | |
| 
 | |
|         internal static byte[] GetCachedBuffer(int minSize)
 | |
|         {
 | |
|             lock (Pool)
 | |
|             {
 | |
|                 var bestIndex = -1;
 | |
|                 byte[] bestMatch = null;
 | |
|                 for (var i = 0; i < Pool.Length; i++)
 | |
|                 {
 | |
|                     var buffer = Pool[i];
 | |
|                     if (buffer == null || buffer.Size < minSize)
 | |
|                     {
 | |
|                         continue;
 | |
|                     }
 | |
|                     if (bestMatch != null && bestMatch.Length < buffer.Size)
 | |
|                     {
 | |
|                         continue;
 | |
|                     }
 | |
| 
 | |
|                     var tmp = buffer.Buffer;
 | |
|                     if (tmp == null)
 | |
|                     {
 | |
|                         Pool[i] = null;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         bestMatch = tmp;
 | |
|                         bestIndex = i;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 if (bestIndex >= 0)
 | |
|                 {
 | |
|                     Pool[bestIndex] = null;
 | |
|                 }
 | |
| 
 | |
|                 return bestMatch;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <remarks>
 | |
|         /// https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element
 | |
|         /// </remarks>
 | |
|         private const int MaxByteArraySize = int.MaxValue - 56;
 | |
| 
 | |
|         internal static void ResizeAndFlushLeft(ref byte[] buffer, int toFitAtLeastBytes, int copyFromIndex, int copyBytes)
 | |
|         {
 | |
|             Helpers.DebugAssert(buffer != null);
 | |
|             Helpers.DebugAssert(toFitAtLeastBytes > buffer.Length);
 | |
|             Helpers.DebugAssert(copyFromIndex >= 0);
 | |
|             Helpers.DebugAssert(copyBytes >= 0);
 | |
| 
 | |
|             int newLength = buffer.Length * 2;
 | |
|             if (newLength < 0)
 | |
|             {
 | |
|                 newLength = MaxByteArraySize;
 | |
|             }
 | |
| 
 | |
|             if (newLength < toFitAtLeastBytes) newLength = toFitAtLeastBytes;
 | |
| 
 | |
|             if (copyBytes == 0)
 | |
|             {
 | |
|                 ReleaseBufferToPool(ref buffer);
 | |
|             }
 | |
| 
 | |
|             var newBuffer = GetCachedBuffer(toFitAtLeastBytes) ?? new byte[newLength];
 | |
| 
 | |
|             if (copyBytes > 0)
 | |
|             {
 | |
|                 Buffer.BlockCopy(buffer, copyFromIndex, newBuffer, 0, copyBytes);
 | |
|                 ReleaseBufferToPool(ref buffer);
 | |
|             }
 | |
| 
 | |
|             buffer = newBuffer;
 | |
|         }
 | |
| 
 | |
|         internal static void ReleaseBufferToPool(ref byte[] buffer)
 | |
|         {
 | |
|             if (buffer == null) return;
 | |
| 
 | |
|             lock (Pool)
 | |
|             {
 | |
|                 var minIndex = 0;
 | |
|                 var minSize = int.MaxValue;
 | |
|                 for (var i = 0; i < Pool.Length; i++)
 | |
|                 {
 | |
|                     var tmp = Pool[i];
 | |
|                     if (tmp == null || !tmp.IsAlive)
 | |
|                     {
 | |
|                         minIndex = 0;
 | |
|                         break;
 | |
|                     }
 | |
|                     if (tmp.Size < minSize)
 | |
|                     {
 | |
|                         minIndex = i;
 | |
|                         minSize = tmp.Size;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 Pool[minIndex] = new CachedBuffer(buffer);
 | |
|             }
 | |
| 
 | |
|             buffer = null;
 | |
|         }
 | |
| 
 | |
|         private class CachedBuffer
 | |
|         {
 | |
|             private readonly WeakReference _reference;
 | |
| 
 | |
|             public int Size { get; }
 | |
| 
 | |
|             public bool IsAlive => _reference.IsAlive;
 | |
|             public byte[] Buffer => (byte[])_reference.Target;
 | |
| 
 | |
|             public CachedBuffer(byte[] buffer)
 | |
|             {
 | |
|                 Size = buffer.Length;
 | |
|                 _reference = new WeakReference(buffer);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |