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 Grpc.Core.Utils; 20 using System; 21 using System.Buffers; 22 using System.Threading; 23 24 namespace Grpc.Core.Internal 25 { 26 internal class DefaultSerializationContext : SerializationContext 27 { 28 static readonly ThreadLocal<DefaultSerializationContext> threadLocalInstance = 29 new ThreadLocal<DefaultSerializationContext>(() => new DefaultSerializationContext(), false); 30 31 bool isComplete; 32 SliceBufferSafeHandle sliceBuffer = SliceBufferSafeHandle.Create(); 33 DefaultSerializationContext()34 public DefaultSerializationContext() 35 { 36 Reset(); 37 } 38 Complete(byte[] payload)39 public override void Complete(byte[] payload) 40 { 41 GrpcPreconditions.CheckState(!isComplete); 42 this.isComplete = true; 43 44 var destSpan = sliceBuffer.GetSpan(payload.Length); 45 payload.AsSpan().CopyTo(destSpan); 46 sliceBuffer.Advance(payload.Length); 47 sliceBuffer.Complete(); 48 } 49 50 /// <summary> 51 /// Expose serializer as buffer writer 52 /// </summary> GetBufferWriter()53 public override IBufferWriter<byte> GetBufferWriter() 54 { 55 GrpcPreconditions.CheckState(!isComplete); 56 return sliceBuffer; 57 } 58 SetPayloadLength(int payloadLength)59 public override void SetPayloadLength(int payloadLength) 60 { 61 // Length is calculated using the buffer writer 62 } 63 64 /// <summary> 65 /// Complete the payload written so far. 66 /// </summary> Complete()67 public override void Complete() 68 { 69 GrpcPreconditions.CheckState(!isComplete); 70 sliceBuffer.Complete(); 71 this.isComplete = true; 72 } 73 GetPayload()74 internal SliceBufferSafeHandle GetPayload() 75 { 76 if (!isComplete) 77 { 78 // mimic the legacy behavior when byte[] was used to represent the payload. 79 throw new NullReferenceException("No payload was set. Complete() needs to be called before payload can be used."); 80 } 81 return sliceBuffer; 82 } 83 Reset()84 public void Reset() 85 { 86 this.isComplete = false; 87 this.sliceBuffer.Reset(); 88 } 89 90 // Get a cached thread local instance of deserialization context 91 // and wrap it in a disposable struct that allows easy resetting 92 // via "using" statement. GetInitializedThreadLocalScope()93 public static UsageScope GetInitializedThreadLocalScope() 94 { 95 var instance = threadLocalInstance.Value; 96 return new UsageScope(instance); 97 } 98 99 public struct UsageScope : IDisposable 100 { 101 readonly DefaultSerializationContext context; 102 UsageScopeGrpc.Core.Internal.DefaultSerializationContext.UsageScope103 public UsageScope(DefaultSerializationContext context) 104 { 105 this.context = context; 106 } 107 108 public DefaultSerializationContext Context => context; DisposeGrpc.Core.Internal.DefaultSerializationContext.UsageScope109 public void Dispose() 110 { 111 context.Reset(); 112 } 113 } 114 } 115 } 116