1 #region Copyright notice and license 2 3 // Copyright 2018 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 System; 20 using System.Collections.Generic; 21 using System.Runtime.InteropServices; 22 using System.Threading.Tasks; 23 24 using Grpc.Core.Internal; 25 using Grpc.Core.Utils; 26 27 namespace Grpc.Core.Internal.Tests 28 { 29 // Creates instances of fake IBufferReader. All created instances will become invalid once Dispose is called. 30 internal class FakeBufferReaderManager : IDisposable 31 { 32 List<GCHandle> pinnedHandles = new List<GCHandle>(); 33 bool disposed = false; CreateSingleSegmentBufferReader(byte[] data)34 public IBufferReader CreateSingleSegmentBufferReader(byte[] data) 35 { 36 return CreateMultiSegmentBufferReader(new List<byte[]> { data }); 37 } 38 CreateMultiSegmentBufferReader(IEnumerable<byte[]> dataSegments)39 public IBufferReader CreateMultiSegmentBufferReader(IEnumerable<byte[]> dataSegments) 40 { 41 GrpcPreconditions.CheckState(!disposed); 42 GrpcPreconditions.CheckNotNull(dataSegments); 43 var segments = new List<GCHandle>(); 44 foreach (var data in dataSegments) 45 { 46 GrpcPreconditions.CheckNotNull(data); 47 segments.Add(GCHandle.Alloc(data, GCHandleType.Pinned)); 48 } 49 pinnedHandles.AddRange(segments); // all the allocated GCHandles will be freed on Dispose() 50 return new FakeBufferReader(segments); 51 } 52 CreateNullPayloadBufferReader()53 public IBufferReader CreateNullPayloadBufferReader() 54 { 55 GrpcPreconditions.CheckState(!disposed); 56 return new FakeBufferReader(null); 57 } 58 Dispose()59 public void Dispose() 60 { 61 if (!disposed) 62 { 63 disposed = true; 64 for (int i = 0; i < pinnedHandles.Count; i++) 65 { 66 pinnedHandles[i].Free(); 67 } 68 } 69 } 70 71 private class FakeBufferReader : IBufferReader 72 { 73 readonly List<GCHandle> bufferSegments; 74 readonly int? totalLength; 75 readonly IEnumerator<GCHandle> segmentEnumerator; 76 FakeBufferReader(List<GCHandle> bufferSegments)77 public FakeBufferReader(List<GCHandle> bufferSegments) 78 { 79 this.bufferSegments = bufferSegments; 80 this.totalLength = ComputeTotalLength(bufferSegments); 81 this.segmentEnumerator = bufferSegments?.GetEnumerator(); 82 } 83 84 public int? TotalLength => totalLength; 85 TryGetNextSlice(out Slice slice)86 public bool TryGetNextSlice(out Slice slice) 87 { 88 GrpcPreconditions.CheckNotNull(bufferSegments); 89 if (!segmentEnumerator.MoveNext()) 90 { 91 slice = default(Slice); 92 return false; 93 } 94 95 var segment = segmentEnumerator.Current; 96 int sliceLen = ((byte[]) segment.Target).Length; 97 slice = new Slice(segment.AddrOfPinnedObject(), sliceLen); 98 return true; 99 } 100 ComputeTotalLength(List<GCHandle> bufferSegments)101 static int? ComputeTotalLength(List<GCHandle> bufferSegments) 102 { 103 if (bufferSegments == null) 104 { 105 return null; 106 } 107 108 int sum = 0; 109 foreach (var segment in bufferSegments) 110 { 111 var data = (byte[]) segment.Target; 112 sum += data.Length; 113 } 114 return sum; 115 } 116 } 117 } 118 } 119