ReusableSliceBuffer.cs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. #region Copyright notice and license
  2. // Copyright 2019 The gRPC Authors
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #endregion
  16. using Grpc.Core.Utils;
  17. using System;
  18. using System.Threading;
  19. using System.Buffers;
  20. namespace Grpc.Core.Internal
  21. {
  22. internal class ReusableSliceBuffer
  23. {
  24. public const int MaxCachedSegments = 1024; // ~4MB payload for 4K slices
  25. readonly SliceSegment[] cachedSegments = new SliceSegment[MaxCachedSegments];
  26. int populatedSegmentCount;
  27. public ReadOnlySequence<byte> PopulateFrom(IBufferReader bufferReader)
  28. {
  29. populatedSegmentCount = 0;
  30. long offset = 0;
  31. SliceSegment prevSegment = null;
  32. while (bufferReader.TryGetNextSlice(out Slice slice))
  33. {
  34. // Initialize cached segment if still null or just allocate a new segment if we already reached MaxCachedSegments
  35. var current = populatedSegmentCount < cachedSegments.Length ? cachedSegments[populatedSegmentCount] : new SliceSegment();
  36. if (current == null)
  37. {
  38. current = cachedSegments[populatedSegmentCount] = new SliceSegment();
  39. }
  40. current.Reset(slice, offset);
  41. prevSegment?.SetNext(current);
  42. populatedSegmentCount ++;
  43. offset += slice.Length;
  44. prevSegment = current;
  45. }
  46. // Not necessary for ending the ReadOnlySequence, but for making sure we
  47. // don't keep more than MaxCachedSegments alive.
  48. prevSegment?.SetNext(null);
  49. if (populatedSegmentCount == 0)
  50. {
  51. return ReadOnlySequence<byte>.Empty;
  52. }
  53. var firstSegment = cachedSegments[0];
  54. var lastSegment = prevSegment;
  55. return new ReadOnlySequence<byte>(firstSegment, 0, lastSegment, lastSegment.Memory.Length);
  56. }
  57. public void Invalidate()
  58. {
  59. if (populatedSegmentCount == 0)
  60. {
  61. return;
  62. }
  63. var segment = cachedSegments[0];
  64. while (segment != null)
  65. {
  66. segment.Reset(new Slice(IntPtr.Zero, 0), 0);
  67. var nextSegment = (SliceSegment) segment.Next;
  68. segment.SetNext(null);
  69. segment = nextSegment;
  70. }
  71. populatedSegmentCount = 0;
  72. }
  73. // Represents a segment in ReadOnlySequence
  74. // Segment is backed by Slice and the instances are reusable.
  75. private class SliceSegment : ReadOnlySequenceSegment<byte>
  76. {
  77. readonly SliceMemoryManager pointerMemoryManager = new SliceMemoryManager();
  78. public void Reset(Slice slice, long runningIndex)
  79. {
  80. pointerMemoryManager.Reset(slice);
  81. Memory = pointerMemoryManager.Memory; // maybe not always necessary
  82. RunningIndex = runningIndex;
  83. }
  84. public void SetNext(ReadOnlySequenceSegment<byte> next)
  85. {
  86. Next = next;
  87. }
  88. }
  89. }
  90. }