BufferPool.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. using System;
  2. namespace ProtoBuf
  3. {
  4. internal sealed class BufferPool
  5. {
  6. internal static void Flush()
  7. {
  8. lock (Pool)
  9. {
  10. for (var i = 0; i < Pool.Length; i++)
  11. Pool[i] = null;
  12. }
  13. }
  14. private BufferPool() { }
  15. private const int POOL_SIZE = 20;
  16. internal const int BUFFER_LENGTH = 1024;
  17. private static readonly CachedBuffer[] Pool = new CachedBuffer[POOL_SIZE];
  18. internal static byte[] GetBuffer() => GetBuffer(BUFFER_LENGTH);
  19. internal static byte[] GetBuffer(int minSize)
  20. {
  21. byte[] cachedBuff = GetCachedBuffer(minSize);
  22. return cachedBuff ?? new byte[minSize];
  23. }
  24. internal static byte[] GetCachedBuffer(int minSize)
  25. {
  26. lock (Pool)
  27. {
  28. var bestIndex = -1;
  29. byte[] bestMatch = null;
  30. for (var i = 0; i < Pool.Length; i++)
  31. {
  32. var buffer = Pool[i];
  33. if (buffer == null || buffer.Size < minSize)
  34. {
  35. continue;
  36. }
  37. if (bestMatch != null && bestMatch.Length < buffer.Size)
  38. {
  39. continue;
  40. }
  41. var tmp = buffer.Buffer;
  42. if (tmp == null)
  43. {
  44. Pool[i] = null;
  45. }
  46. else
  47. {
  48. bestMatch = tmp;
  49. bestIndex = i;
  50. }
  51. }
  52. if (bestIndex >= 0)
  53. {
  54. Pool[bestIndex] = null;
  55. }
  56. return bestMatch;
  57. }
  58. }
  59. /// <remarks>
  60. /// https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element
  61. /// </remarks>
  62. private const int MaxByteArraySize = int.MaxValue - 56;
  63. internal static void ResizeAndFlushLeft(ref byte[] buffer, int toFitAtLeastBytes, int copyFromIndex, int copyBytes)
  64. {
  65. Helpers.DebugAssert(buffer != null);
  66. Helpers.DebugAssert(toFitAtLeastBytes > buffer.Length);
  67. Helpers.DebugAssert(copyFromIndex >= 0);
  68. Helpers.DebugAssert(copyBytes >= 0);
  69. int newLength = buffer.Length * 2;
  70. if (newLength < 0)
  71. {
  72. newLength = MaxByteArraySize;
  73. }
  74. if (newLength < toFitAtLeastBytes) newLength = toFitAtLeastBytes;
  75. if (copyBytes == 0)
  76. {
  77. ReleaseBufferToPool(ref buffer);
  78. }
  79. var newBuffer = GetCachedBuffer(toFitAtLeastBytes) ?? new byte[newLength];
  80. if (copyBytes > 0)
  81. {
  82. Buffer.BlockCopy(buffer, copyFromIndex, newBuffer, 0, copyBytes);
  83. ReleaseBufferToPool(ref buffer);
  84. }
  85. buffer = newBuffer;
  86. }
  87. internal static void ReleaseBufferToPool(ref byte[] buffer)
  88. {
  89. if (buffer == null) return;
  90. lock (Pool)
  91. {
  92. var minIndex = 0;
  93. var minSize = int.MaxValue;
  94. for (var i = 0; i < Pool.Length; i++)
  95. {
  96. var tmp = Pool[i];
  97. if (tmp == null || !tmp.IsAlive)
  98. {
  99. minIndex = 0;
  100. break;
  101. }
  102. if (tmp.Size < minSize)
  103. {
  104. minIndex = i;
  105. minSize = tmp.Size;
  106. }
  107. }
  108. Pool[minIndex] = new CachedBuffer(buffer);
  109. }
  110. buffer = null;
  111. }
  112. private class CachedBuffer
  113. {
  114. private readonly WeakReference _reference;
  115. public int Size { get; }
  116. public bool IsAlive => _reference.IsAlive;
  117. public byte[] Buffer => (byte[])_reference.Target;
  118. public CachedBuffer(byte[] buffer)
  119. {
  120. Size = buffer.Length;
  121. _reference = new WeakReference(buffer);
  122. }
  123. }
  124. }
  125. }