1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // https://developers.google.com/protocol-buffers/ 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 #endregion 32 33 using System; 34 using System.Runtime.CompilerServices; 35 using System.Runtime.InteropServices.ComTypes; 36 using System.Security; 37 38 namespace Google.Protobuf 39 { 40 /// <summary> 41 /// Writing messages / groups. 42 /// </summary> 43 [SecuritySafeCritical] 44 internal static class WritingPrimitivesMessages 45 { 46 /// <summary> 47 /// Writes a message, without a tag. 48 /// The data is length-prefixed. 49 /// </summary> 50 [MethodImpl(MethodImplOptions.AggressiveInlining)] WriteMessage(ref WriteContext ctx, IMessage value)51 public static void WriteMessage(ref WriteContext ctx, IMessage value) 52 { 53 WritingPrimitives.WriteLength(ref ctx.buffer, ref ctx.state, value.CalculateSize()); 54 WriteRawMessage(ref ctx, value); 55 } 56 57 /// <summary> 58 /// Writes a group, without a tag. 59 /// </summary> 60 [MethodImpl(MethodImplOptions.AggressiveInlining)] WriteGroup(ref WriteContext ctx, IMessage value)61 public static void WriteGroup(ref WriteContext ctx, IMessage value) 62 { 63 WriteRawMessage(ref ctx, value); 64 } 65 66 /// <summary> 67 /// Writes a message, without a tag. 68 /// Message will be written without a length prefix. 69 /// </summary> 70 [MethodImpl(MethodImplOptions.AggressiveInlining)] WriteRawMessage(ref WriteContext ctx, IMessage message)71 public static void WriteRawMessage(ref WriteContext ctx, IMessage message) 72 { 73 if (message is IBufferMessage bufferMessage) 74 { 75 bufferMessage.InternalWriteTo(ref ctx); 76 } 77 else 78 { 79 // If we reached here, it means we've ran into a nested message with older generated code 80 // which doesn't provide the InternalWriteTo method that takes a WriteContext. 81 // With a slight performance overhead, we can still serialize this message just fine, 82 // but we need to find the original CodedOutputStream instance that initiated this 83 // serialization process and make sure its internal state is up to date. 84 // Note that this performance overhead is not very high (basically copying contents of a struct) 85 // and it will only be incurred in case the application mixes older and newer generated code. 86 // Regenerating the code from .proto files will remove this overhead because it will 87 // generate the InternalWriteTo method we need. 88 89 if (ctx.state.CodedOutputStream == null) 90 { 91 // This can only happen when the serialization started without providing a CodedOutputStream instance 92 // (e.g. WriteContext was created directly from a IBufferWriter). 93 // That also means that one of the new parsing APIs was used at the top level 94 // and in such case it is reasonable to require that all the nested message provide 95 // up-to-date generated code with WriteContext support (and fail otherwise). 96 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."); 97 } 98 99 ctx.CopyStateTo(ctx.state.CodedOutputStream); 100 try 101 { 102 // fallback parse using the CodedOutputStream that started current serialization tree 103 message.WriteTo(ctx.state.CodedOutputStream); 104 } 105 finally 106 { 107 ctx.LoadStateFrom(ctx.state.CodedOutputStream); 108 } 109 } 110 } 111 } 112 }