• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }