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