1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using System.Runtime.CompilerServices; 11 using System.Security; 12 13 namespace Google.Protobuf 14 { 15 /// <summary> 16 /// Writing messages / groups. 17 /// </summary> 18 [SecuritySafeCritical] 19 internal static class WritingPrimitivesMessages 20 { 21 /// <summary> 22 /// Writes a message, without a tag. 23 /// The data is length-prefixed. 24 /// </summary> 25 [MethodImpl(MethodImplOptions.AggressiveInlining)] WriteMessage(ref WriteContext ctx, IMessage value)26 public static void WriteMessage(ref WriteContext ctx, IMessage value) 27 { 28 WritingPrimitives.WriteLength(ref ctx.buffer, ref ctx.state, value.CalculateSize()); 29 WriteRawMessage(ref ctx, value); 30 } 31 32 /// <summary> 33 /// Writes a group, without a tag. 34 /// </summary> 35 [MethodImpl(MethodImplOptions.AggressiveInlining)] WriteGroup(ref WriteContext ctx, IMessage value)36 public static void WriteGroup(ref WriteContext ctx, IMessage value) 37 { 38 WriteRawMessage(ref ctx, value); 39 } 40 41 /// <summary> 42 /// Writes a message, without a tag. 43 /// Message will be written without a length prefix. 44 /// </summary> 45 [MethodImpl(MethodImplOptions.AggressiveInlining)] WriteRawMessage(ref WriteContext ctx, IMessage message)46 public static void WriteRawMessage(ref WriteContext ctx, IMessage message) 47 { 48 if (message is IBufferMessage bufferMessage) 49 { 50 bufferMessage.InternalWriteTo(ref ctx); 51 } 52 else 53 { 54 // If we reached here, it means we've ran into a nested message with older generated code 55 // which doesn't provide the InternalWriteTo method that takes a WriteContext. 56 // With a slight performance overhead, we can still serialize this message just fine, 57 // but we need to find the original CodedOutputStream instance that initiated this 58 // serialization process and make sure its internal state is up to date. 59 // Note that this performance overhead is not very high (basically copying contents of a struct) 60 // and it will only be incurred in case the application mixes older and newer generated code. 61 // Regenerating the code from .proto files will remove this overhead because it will 62 // generate the InternalWriteTo method we need. 63 64 if (ctx.state.CodedOutputStream == null) 65 { 66 // This can only happen when the serialization started without providing a CodedOutputStream instance 67 // (e.g. WriteContext was created directly from a IBufferWriter). 68 // That also means that one of the new parsing APIs was used at the top level 69 // and in such case it is reasonable to require that all the nested message provide 70 // up-to-date generated code with WriteContext support (and fail otherwise). 71 throw new InvalidProtocolBufferException($"Message {message.GetType().Name} doesn't provide the generated method that enables WriteContext-based serialization. You might need to regenerate the generated protobuf code."); 72 } 73 74 ctx.CopyStateTo(ctx.state.CodedOutputStream); 75 try 76 { 77 // fallback parse using the CodedOutputStream that started current serialization tree 78 message.WriteTo(ctx.state.CodedOutputStream); 79 } 80 finally 81 { 82 ctx.LoadStateFrom(ctx.state.CodedOutputStream); 83 } 84 } 85 } 86 } 87 }