blob: c4b3f823432b6681d582b0a5db87bc6d4fd3689c [file] [log] [blame]
Jon Skeetee835a32015-06-30 17:22:26 +01001#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
Sydney Acksman54176b22018-09-24 15:42:24 -050033using Google.Protobuf.Reflection;
Jan Tattermusch6d5bc902020-04-02 15:00:34 +020034using System.Buffers;
Sydney Acksman54176b22018-09-24 15:42:24 -050035using System.Collections;
Jan Tattermusch90391032020-06-03 15:32:20 +020036using System;
Jon Skeetee835a32015-06-30 17:22:26 +010037using System.IO;
Sydney Acksman54176b22018-09-24 15:42:24 -050038using System.Linq;
Jan Tattermusch6d5bc902020-04-02 15:00:34 +020039using System.Security;
Jon Skeete38294a2015-06-09 19:30:44 +010040
41namespace Google.Protobuf
42{
Jon Skeetcdeda4b2015-06-19 17:30:13 +010043 /// <summary>
44 /// Extension methods on <see cref="IMessage"/> and <see cref="IMessage{T}"/>.
45 /// </summary>
46 public static class MessageExtensions
Jon Skeete38294a2015-06-09 19:30:44 +010047 {
Jon Skeet811fc892015-08-04 15:58:39 +010048 /// <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>
Jon Skeet47b7d2c2018-01-03 09:57:58 +000053 public static void MergeFrom(this IMessage message, byte[] data) =>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -050054 MergeFrom(message, data, false, null);
Jon Skeete38294a2015-06-09 19:30:44 +010055
Jon Skeet811fc892015-08-04 15:58:39 +010056 /// <summary>
Jan Tattermusch0c874a62017-11-09 14:23:14 +010057 /// 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>
Jon Skeet47b7d2c2018-01-03 09:57:58 +000063 public static void MergeFrom(this IMessage message, byte[] data, int offset, int length) =>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -050064 MergeFrom(message, data, offset, length, false, null);
Jan Tattermusch0c874a62017-11-09 14:23:14 +010065
66 /// <summary>
Jon Skeet811fc892015-08-04 15:58:39 +010067 /// 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>
Jon Skeet47b7d2c2018-01-03 09:57:58 +000071 public static void MergeFrom(this IMessage message, ByteString data) =>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -050072 MergeFrom(message, data, false, null);
Jon Skeete38294a2015-06-09 19:30:44 +010073
Jon Skeet811fc892015-08-04 15:58:39 +010074 /// <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>
Jon Skeet47b7d2c2018-01-03 09:57:58 +000079 public static void MergeFrom(this IMessage message, Stream input) =>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -050080 MergeFrom(message, input, false, null);
Jon Skeete38294a2015-06-09 19:30:44 +010081
Jon Skeet811fc892015-08-04 15:58:39 +010082 /// <summary>
Jensaarai76e3ffd2021-04-09 19:36:32 -070083 /// Merges data from the given span into an existing message.
84 /// </summary>
85 /// <param name="message">The message to merge the data into.</param>
86 /// <param name="span">Span containing the data to merge, which must be protobuf-encoded binary data.</param>
87 [SecuritySafeCritical]
Jan Tattermusch89244fe2021-05-04 11:24:55 +020088 public static void MergeFrom(this IMessage message, ReadOnlySpan<byte> span) =>
Jensaarai1252f3e2021-05-05 19:52:31 -070089 MergeFrom(message, span, false, null);
Jensaarai76e3ffd2021-04-09 19:36:32 -070090
91 /// <summary>
Jon Skeet811fc892015-08-04 15:58:39 +010092 /// Merges length-delimited data from the given stream into an existing message.
93 /// </summary>
94 /// <remarks>
95 /// The stream is expected to contain a length and then the data. Only the amount of data
96 /// specified by the length will be consumed.
97 /// </remarks>
98 /// <param name="message">The message to merge the data into.</param>
99 /// <param name="input">Stream containing the data to merge, which must be protobuf-encoded binary data.</param>
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000100 public static void MergeDelimitedFrom(this IMessage message, Stream input) =>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500101 MergeDelimitedFrom(message, input, false, null);
Jon Skeete38294a2015-06-09 19:30:44 +0100102
Jon Skeet811fc892015-08-04 15:58:39 +0100103 /// <summary>
104 /// Converts the given message into a byte array in protobuf encoding.
105 /// </summary>
106 /// <param name="message">The message to convert.</param>
107 /// <returns>The message data as a byte array.</returns>
Jon Skeete38294a2015-06-09 19:30:44 +0100108 public static byte[] ToByteArray(this IMessage message)
109 {
Jon Skeet7762f162016-02-04 06:51:54 +0000110 ProtoPreconditions.CheckNotNull(message, "message");
Jon Skeete38294a2015-06-09 19:30:44 +0100111 byte[] result = new byte[message.CalculateSize()];
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100112 CodedOutputStream output = new CodedOutputStream(result);
Jon Skeete38294a2015-06-09 19:30:44 +0100113 message.WriteTo(output);
114 output.CheckNoSpaceLeft();
115 return result;
116 }
117
Jon Skeet811fc892015-08-04 15:58:39 +0100118 /// <summary>
119 /// Writes the given message data to the given stream in protobuf encoding.
120 /// </summary>
121 /// <param name="message">The message to write to the stream.</param>
122 /// <param name="output">The stream to write to.</param>
Jon Skeete38294a2015-06-09 19:30:44 +0100123 public static void WriteTo(this IMessage message, Stream output)
124 {
Jon Skeet7762f162016-02-04 06:51:54 +0000125 ProtoPreconditions.CheckNotNull(message, "message");
126 ProtoPreconditions.CheckNotNull(output, "output");
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100127 CodedOutputStream codedOutput = new CodedOutputStream(output);
Jon Skeete38294a2015-06-09 19:30:44 +0100128 message.WriteTo(codedOutput);
129 codedOutput.Flush();
130 }
131
Jon Skeet811fc892015-08-04 15:58:39 +0100132 /// <summary>
133 /// Writes the length and then data of the given message to a stream.
134 /// </summary>
135 /// <param name="message">The message to write.</param>
136 /// <param name="output">The output stream to write to.</param>
Jon Skeete38294a2015-06-09 19:30:44 +0100137 public static void WriteDelimitedTo(this IMessage message, Stream output)
138 {
Jon Skeet7762f162016-02-04 06:51:54 +0000139 ProtoPreconditions.CheckNotNull(message, "message");
140 ProtoPreconditions.CheckNotNull(output, "output");
Jon Skeet0e0e0c92015-08-03 11:08:53 +0100141 CodedOutputStream codedOutput = new CodedOutputStream(output);
Jan Tattermuschee6b20a2020-05-28 16:00:00 +0200142 codedOutput.WriteLength(message.CalculateSize());
Jon Skeete38294a2015-06-09 19:30:44 +0100143 message.WriteTo(codedOutput);
144 codedOutput.Flush();
145 }
146
Jon Skeet811fc892015-08-04 15:58:39 +0100147 /// <summary>
148 /// Converts the given message into a byte string in protobuf encoding.
149 /// </summary>
150 /// <param name="message">The message to convert.</param>
151 /// <returns>The message data as a byte string.</returns>
Jon Skeete38294a2015-06-09 19:30:44 +0100152 public static ByteString ToByteString(this IMessage message)
153 {
Jon Skeet7762f162016-02-04 06:51:54 +0000154 ProtoPreconditions.CheckNotNull(message, "message");
Jon Skeete38294a2015-06-09 19:30:44 +0100155 return ByteString.AttachBytes(message.ToByteArray());
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000156 }
157
Sydney Acksman54176b22018-09-24 15:42:24 -0500158 /// <summary>
Jan Tattermusch90391032020-06-03 15:32:20 +0200159 /// Writes the given message data to the given buffer writer in protobuf encoding.
160 /// </summary>
161 /// <param name="message">The message to write to the stream.</param>
162 /// <param name="output">The stream to write to.</param>
Jan Tattermusch8a2d5882020-06-08 11:19:04 +0200163 [SecuritySafeCritical]
Jan Tattermusch90391032020-06-03 15:32:20 +0200164 public static void WriteTo(this IMessage message, IBufferWriter<byte> output)
165 {
166 ProtoPreconditions.CheckNotNull(message, nameof(message));
167 ProtoPreconditions.CheckNotNull(output, nameof(output));
168
169 WriteContext.Initialize(output, out WriteContext ctx);
170 WritingPrimitivesMessages.WriteRawMessage(ref ctx, message);
171 ctx.Flush();
Jan Tattermusch90391032020-06-03 15:32:20 +0200172 }
173
174 /// <summary>
175 /// Writes the given message data to the given span in protobuf encoding.
176 /// The size of the destination span needs to fit the serialized size
177 /// of the message exactly, otherwise an exception is thrown.
178 /// </summary>
179 /// <param name="message">The message to write to the stream.</param>
180 /// <param name="output">The span to write to. Size must match size of the message exactly.</param>
Jan Tattermusch8a2d5882020-06-08 11:19:04 +0200181 [SecuritySafeCritical]
Jan Tattermusch90391032020-06-03 15:32:20 +0200182 public static void WriteTo(this IMessage message, Span<byte> output)
183 {
184 ProtoPreconditions.CheckNotNull(message, nameof(message));
185
186 WriteContext.Initialize(ref output, out WriteContext ctx);
187 WritingPrimitivesMessages.WriteRawMessage(ref ctx, message);
Jan Tattermusch90391032020-06-03 15:32:20 +0200188 ctx.CheckNoSpaceLeft();
189 }
190
191 /// <summary>
Sydney Acksman54176b22018-09-24 15:42:24 -0500192 /// Checks if all required fields in a message have values set. For proto3 messages, this returns true
193 /// </summary>
194 public static bool IsInitialized(this IMessage message)
195 {
Sydney Acksman47f20172019-07-02 18:06:55 -0500196 if (message.Descriptor.File.Syntax == Syntax.Proto3)
Sydney Acksman54176b22018-09-24 15:42:24 -0500197 {
198 return true;
199 }
200
Sydney Acksman47f20172019-07-02 18:06:55 -0500201 if (!message.Descriptor.IsExtensionsInitialized(message))
Sydney Acksman8b7fb7d2019-03-23 11:41:57 -0500202 {
203 return false;
204 }
205
Sydney Acksman54176b22018-09-24 15:42:24 -0500206 return message.Descriptor
207 .Fields
208 .InDeclarationOrder()
209 .All(f =>
210 {
211 if (f.IsMap)
212 {
Sydney Acksman8b7fb7d2019-03-23 11:41:57 -0500213 var valueField = f.MessageType.Fields[2];
214 if (valueField.FieldType == FieldType.Message)
215 {
216 var map = (IDictionary)f.Accessor.GetValue(message);
217 return map.Values.Cast<IMessage>().All(IsInitialized);
218 }
219 else
220 {
221 return true;
222 }
Sydney Acksman54176b22018-09-24 15:42:24 -0500223 }
Sydney Acksmane7283252019-02-14 13:34:15 -0600224 else if (f.IsRepeated && f.FieldType == FieldType.Message || f.FieldType == FieldType.Group)
Sydney Acksman54176b22018-09-24 15:42:24 -0500225 {
226 var enumerable = (IEnumerable)f.Accessor.GetValue(message);
227 return enumerable.Cast<IMessage>().All(IsInitialized);
228 }
Sydney Acksmane7283252019-02-14 13:34:15 -0600229 else if (f.FieldType == FieldType.Message || f.FieldType == FieldType.Group)
Sydney Acksman54176b22018-09-24 15:42:24 -0500230 {
Sydney Acksman930db672019-07-21 04:00:21 -0500231 if (f.Accessor.HasValue(message))
Sydney Acksman54176b22018-09-24 15:42:24 -0500232 {
233 return ((IMessage)f.Accessor.GetValue(message)).IsInitialized();
234 }
235 else
236 {
237 return !f.IsRequired;
238 }
239 }
240 else if (f.IsRequired)
241 {
Sydney Acksman930db672019-07-21 04:00:21 -0500242 return f.Accessor.HasValue(message);
Sydney Acksman54176b22018-09-24 15:42:24 -0500243 }
Xiang Daie4794102019-02-21 11:28:50 +0800244 else
Sydney Acksman54176b22018-09-24 15:42:24 -0500245 {
246 return true;
247 }
248 });
249 }
250
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000251 // Implementations allowing unknown fields to be discarded.
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500252 internal static void MergeFrom(this IMessage message, byte[] data, bool discardUnknownFields, ExtensionRegistry registry)
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000253 {
254 ProtoPreconditions.CheckNotNull(message, "message");
255 ProtoPreconditions.CheckNotNull(data, "data");
256 CodedInputStream input = new CodedInputStream(data);
257 input.DiscardUnknownFields = discardUnknownFields;
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500258 input.ExtensionRegistry = registry;
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000259 message.MergeFrom(input);
260 input.CheckReadEndOfStreamTag();
261 }
262
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500263 internal static void MergeFrom(this IMessage message, byte[] data, int offset, int length, bool discardUnknownFields, ExtensionRegistry registry)
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000264 {
265 ProtoPreconditions.CheckNotNull(message, "message");
266 ProtoPreconditions.CheckNotNull(data, "data");
267 CodedInputStream input = new CodedInputStream(data, offset, length);
268 input.DiscardUnknownFields = discardUnknownFields;
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500269 input.ExtensionRegistry = registry;
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000270 message.MergeFrom(input);
271 input.CheckReadEndOfStreamTag();
272 }
273
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500274 internal static void MergeFrom(this IMessage message, ByteString data, bool discardUnknownFields, ExtensionRegistry registry)
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000275 {
276 ProtoPreconditions.CheckNotNull(message, "message");
277 ProtoPreconditions.CheckNotNull(data, "data");
278 CodedInputStream input = data.CreateCodedInput();
279 input.DiscardUnknownFields = discardUnknownFields;
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500280 input.ExtensionRegistry = registry;
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000281 message.MergeFrom(input);
282 input.CheckReadEndOfStreamTag();
283 }
284
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500285 internal static void MergeFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000286 {
287 ProtoPreconditions.CheckNotNull(message, "message");
288 ProtoPreconditions.CheckNotNull(input, "input");
289 CodedInputStream codedInput = new CodedInputStream(input);
290 codedInput.DiscardUnknownFields = discardUnknownFields;
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500291 codedInput.ExtensionRegistry = registry;
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000292 message.MergeFrom(codedInput);
293 codedInput.CheckReadEndOfStreamTag();
294 }
295
Jan Tattermusch6d5bc902020-04-02 15:00:34 +0200296 [SecuritySafeCritical]
297 internal static void MergeFrom(this IMessage message, ReadOnlySequence<byte> data, bool discardUnknownFields, ExtensionRegistry registry)
298 {
Jan Tattermusch07182a82020-04-07 15:45:18 +0200299 ParseContext.Initialize(data, out ParseContext ctx);
Jan Tattermusch6d5bc902020-04-02 15:00:34 +0200300 ctx.DiscardUnknownFields = discardUnknownFields;
301 ctx.ExtensionRegistry = registry;
302 ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);
303 ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
304 }
305
Jensaarai76e3ffd2021-04-09 19:36:32 -0700306 [SecuritySafeCritical]
Jensaarai1252f3e2021-05-05 19:52:31 -0700307 internal static void MergeFrom(this IMessage message, ReadOnlySpan<byte> data, bool discardUnknownFields, ExtensionRegistry registry)
Jensaarai76e3ffd2021-04-09 19:36:32 -0700308 {
Jensaarai1252f3e2021-05-05 19:52:31 -0700309 ParseContext.Initialize(data, out ParseContext ctx);
Jensaarai76e3ffd2021-04-09 19:36:32 -0700310 ctx.DiscardUnknownFields = discardUnknownFields;
311 ctx.ExtensionRegistry = registry;
312 ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);
313 ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
314 }
315
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500316 internal static void MergeDelimitedFrom(this IMessage message, Stream input, bool discardUnknownFields, ExtensionRegistry registry)
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000317 {
318 ProtoPreconditions.CheckNotNull(message, "message");
319 ProtoPreconditions.CheckNotNull(input, "input");
320 int size = (int) CodedInputStream.ReadRawVarint32(input);
321 Stream limitedStream = new LimitedInputStream(input, size);
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500322 MergeFrom(message, limitedStream, discardUnknownFields, registry);
Jon Skeet47b7d2c2018-01-03 09:57:58 +0000323 }
Jon Skeete38294a2015-06-09 19:30:44 +0100324 }
325}