• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2015 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 Google.Protobuf.Reflection;
34 using System.Buffers;
35 using System.Collections;
36 using System;
37 using System.IO;
38 using System.Linq;
39 using System.Security;
40 
41 namespace Google.Protobuf
42 {
43     /// <summary>
44     /// Extension methods on <see cref="IMessage"/> and <see cref="IMessage{T}"/>.
45     /// </summary>
46     public static class MessageExtensions
47     {
48         /// <summary>
49         /// Merges data from the given byte array into an existing message.
50         /// </summary>
51         /// <param name="message">The message to merge the data into.</param>
52         /// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
MergeFrom(this IMessage message, byte[] data)53         public static void MergeFrom(this IMessage message, byte[] data) =>
54             MergeFrom(message, data, false, null);
55 
56         /// <summary>
57         /// Merges data from the given byte array slice into an existing message.
58         /// </summary>
59         /// <param name="message">The message to merge the data into.</param>
60         /// <param name="data">The data containing the slice to merge, which must be protobuf-encoded binary data.</param>
61         /// <param name="offset">The offset of the slice to merge.</param>
62         /// <param name="length">The length of the slice to merge.</param>
MergeFrom(this IMessage message, byte[] data, int offset, int length)63         public static void MergeFrom(this IMessage message, byte[] data, int offset, int length) =>
64             MergeFrom(message, data, offset, length, false, null);
65 
66         /// <summary>
67         /// Merges data from the given byte string into an existing message.
68         /// </summary>
69         /// <param name="message">The message to merge the data into.</param>
70         /// <param name="data">The data to merge, which must be protobuf-encoded binary data.</param>
MergeFrom(this IMessage message, ByteString data)71         public static void MergeFrom(this IMessage message, ByteString data) =>
72             MergeFrom(message, data, false, null);
73 
74         /// <summary>
75         /// Merges data from the given stream into an existing message.
76         /// </summary>
77         /// <param name="message">The message to merge the data into.</param>
78         /// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
MergeFrom(this IMessage message, Stream input)79         public static void MergeFrom(this IMessage message, Stream input) =>
80             MergeFrom(message, input, false, null);
81 
82         /// <summary>
83         /// Merges length-delimited data from the given stream into an existing message.
84         /// </summary>
85         /// <remarks>
86         /// The stream is expected to contain a length and then the data. Only the amount of data
87         /// specified by the length will be consumed.
88         /// </remarks>
89         /// <param name="message">The message to merge the data into.</param>
90         /// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
MergeDelimitedFrom(this IMessage message, Stream input)91         public static void MergeDelimitedFrom(this IMessage message, Stream input) =>
92             MergeDelimitedFrom(message, input, false, null);
93 
94         /// <summary>
95         /// Converts the given message into a byte array in protobuf encoding.
96         /// </summary>
97         /// <param name="message">The message to convert.</param>
98         /// <returns>The message data as a byte array.</returns>
ToByteArray(this IMessage message)99         public static byte[] ToByteArray(this IMessage message)
100         {
101             ProtoPreconditions.CheckNotNull(message, "message");
102             byte[] result = new byte[message.CalculateSize()];
103             CodedOutputStream output = new CodedOutputStream(result);
104             message.WriteTo(output);
105             output.CheckNoSpaceLeft();
106             return result;
107         }
108 
109         /// <summary>
110         /// Writes the given message data to the given stream in protobuf encoding.
111         /// </summary>
112         /// <param name="message">The message to write to the stream.</param>
113         /// <param name="output">The stream to write to.</param>
WriteTo(this IMessage message, Stream output)114         public static void WriteTo(this IMessage message, Stream output)
115         {
116             ProtoPreconditions.CheckNotNull(message, "message");
117             ProtoPreconditions.CheckNotNull(output, "output");
118             CodedOutputStream codedOutput = new CodedOutputStream(output);
119             message.WriteTo(codedOutput);
120             codedOutput.Flush();
121         }
122 
123         /// <summary>
124         /// Writes the length and then data of the given message to a stream.
125         /// </summary>
126         /// <param name="message">The message to write.</param>
127         /// <param name="output">The output stream to write to.</param>
WriteDelimitedTo(this IMessage message, Stream output)128         public static void WriteDelimitedTo(this IMessage message, Stream output)
129         {
130             ProtoPreconditions.CheckNotNull(message, "message");
131             ProtoPreconditions.CheckNotNull(output, "output");
132             CodedOutputStream codedOutput = new CodedOutputStream(output);
133             codedOutput.WriteLength(message.CalculateSize());
134             message.WriteTo(codedOutput);
135             codedOutput.Flush();
136         }
137 
138         /// <summary>
139         /// Converts the given message into a byte string in protobuf encoding.
140         /// </summary>
141         /// <param name="message">The message to convert.</param>
142         /// <returns>The message data as a byte string.</returns>
ToByteString(this IMessage message)143         public static ByteString ToByteString(this IMessage message)
144         {
145             ProtoPreconditions.CheckNotNull(message, "message");
146             return ByteString.AttachBytes(message.ToByteArray());
147         }
148 
149         /// <summary>
150         /// Writes the given message data to the given buffer writer in protobuf encoding.
151         /// </summary>
152         /// <param name="message">The message to write to the stream.</param>
153         /// <param name="output">The stream to write to.</param>
154         [SecuritySafeCritical]
WriteTo(this IMessage message, IBufferWriter<byte> output)155         public static void WriteTo(this IMessage message, IBufferWriter<byte> output)
156         {
157             ProtoPreconditions.CheckNotNull(message, nameof(message));
158             ProtoPreconditions.CheckNotNull(output, nameof(output));
159 
160             WriteContext.Initialize(output, out WriteContext ctx);
161             WritingPrimitivesMessages.WriteRawMessage(ref ctx, message);
162             ctx.Flush();
163         }
164 
165         /// <summary>
166         /// Writes the given message data to the given span in protobuf encoding.
167         /// The size of the destination span needs to fit the serialized size
168         /// of the message exactly, otherwise an exception is thrown.
169         /// </summary>
170         /// <param name="message">The message to write to the stream.</param>
171         /// <param name="output">The span to write to. Size must match size of the message exactly.</param>
172         [SecuritySafeCritical]
WriteTo(this IMessage message, Span<byte> output)173         public static void WriteTo(this IMessage message, Span<byte> output)
174         {
175             ProtoPreconditions.CheckNotNull(message, nameof(message));
176 
177             WriteContext.Initialize(ref output, out WriteContext ctx);
178             WritingPrimitivesMessages.WriteRawMessage(ref ctx, message);
179             ctx.CheckNoSpaceLeft();
180         }
181 
182         /// <summary>
183         /// Checks if all required fields in a message have values set. For proto3 messages, this returns true
184         /// </summary>
IsInitialized(this IMessage message)185         public static bool IsInitialized(this IMessage message)
186         {
187             if (message.Descriptor.File.Syntax == Syntax.Proto3)
188             {
189                 return true;
190             }
191 
192             if (!message.Descriptor.IsExtensionsInitialized(message))
193             {
194                 return false;
195             }
196 
197             return message.Descriptor
198                 .Fields
199                 .InDeclarationOrder()
200                 .All(f =>
201                 {
202                     if (f.IsMap)
203                     {
204                         var valueField = f.MessageType.Fields[2];
205                         if (valueField.FieldType == FieldType.Message)
206                         {
207                             var map = (IDictionary)f.Accessor.GetValue(message);
208                             return map.Values.Cast<IMessage>().All(IsInitialized);
209                         }
210                         else
211                         {
212                             return true;
213                         }
214                     }
215                     else if (f.IsRepeated && f.FieldType == FieldType.Message || f.FieldType == FieldType.Group)
216                     {
217                         var enumerable = (IEnumerable)f.Accessor.GetValue(message);
218                         return enumerable.Cast<IMessage>().All(IsInitialized);
219                     }
220                     else if (f.FieldType == FieldType.Message || f.FieldType == FieldType.Group)
221                     {
222                         if (f.Accessor.HasValue(message))
223                         {
224                             return ((IMessage)f.Accessor.GetValue(message)).IsInitialized();
225                         }
226                         else
227                         {
228                             return !f.IsRequired;
229                         }
230                     }
231                     else if (f.IsRequired)
232                     {
233                         return f.Accessor.HasValue(message);
234                     }
235                     else
236                     {
237                         return true;
238                     }
239                 });
240         }
241 
242         // Implementations allowing unknown fields to be discarded.
MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields, ExtensionRegistry registry)243         internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields, ExtensionRegistry registry)
244         {
245             ProtoPreconditions.CheckNotNull(message, "message");
246             ProtoPreconditions.CheckNotNull(data, "data");
247             CodedInputStream input = new CodedInputStream(data);
248             input.DiscardUnknownFields = discardUnknownFields;
249             input.ExtensionRegistry = registry;
250             message.MergeFrom(input);
251             input.CheckReadEndOfStreamTag();
252         }
253 
MergeFrom(this IMessage message, byte[] data, int offset, int length, bool discardUnknownFields, ExtensionRegistry registry)254         internal static void MergeFrom(this IMessage message, byte[] data, int offset, int length, bool discardUnknownFields, ExtensionRegistry registry)
255         {
256             ProtoPreconditions.CheckNotNull(message, "message");
257             ProtoPreconditions.CheckNotNull(data, "data");
258             CodedInputStream input = new CodedInputStream(data, offset, length);
259             input.DiscardUnknownFields = discardUnknownFields;
260             input.ExtensionRegistry = registry;
261             message.MergeFrom(input);
262             input.CheckReadEndOfStreamTag();
263         }
264 
MergeFrom(this IMessage message, ByteString data, bool discardUnknownFields, ExtensionRegistry registry)265         internal static void MergeFrom(this IMessage message, ByteString data, bool discardUnknownFields, ExtensionRegistry registry)
266         {
267             ProtoPreconditions.CheckNotNull(message, "message");
268             ProtoPreconditions.CheckNotNull(data, "data");
269             CodedInputStream input = data.CreateCodedInput();
270             input.DiscardUnknownFields = discardUnknownFields;
271             input.ExtensionRegistry = registry;
272             message.MergeFrom(input);
273             input.CheckReadEndOfStreamTag();
274         }
275 
MergeFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)276         internal static void MergeFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
277         {
278             ProtoPreconditions.CheckNotNull(message, "message");
279             ProtoPreconditions.CheckNotNull(input, "input");
280             CodedInputStream codedInput = new CodedInputStream(input);
281             codedInput.DiscardUnknownFields = discardUnknownFields;
282             codedInput.ExtensionRegistry = registry;
283             message.MergeFrom(codedInput);
284             codedInput.CheckReadEndOfStreamTag();
285         }
286 
287         [SecuritySafeCritical]
MergeFrom(this IMessage message, ReadOnlySequence<byte> data, bool discardUnknownFields, ExtensionRegistry registry)288         internal static void MergeFrom(this IMessage message, ReadOnlySequence<byte> data, bool discardUnknownFields, ExtensionRegistry registry)
289         {
290             ParseContext.Initialize(data, out ParseContext ctx);
291             ctx.DiscardUnknownFields = discardUnknownFields;
292             ctx.ExtensionRegistry = registry;
293             ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);
294             ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
295         }
296 
MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)297         internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
298         {
299             ProtoPreconditions.CheckNotNull(message, "message");
300             ProtoPreconditions.CheckNotNull(input, "input");
301             int size = (int) CodedInputStream.ReadRawVarint32(input);
302             Stream limitedStream = new LimitedInputStream(input, size);
303             MergeFrom(message, limitedStream, discardUnknownFields, registry);
304         }
305     }
306 }
307