blob: ee6bd6a0ebe386d36e81101d4d56839c4ba6d568 [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
Jon Skeet8a0312b2015-07-16 17:03:06 +010032
Jon Skeetf3e9a652017-10-25 16:03:23 +010033using Google.Protobuf.Collections;
Jon Skeetf2626112016-01-15 10:13:56 +000034using Google.Protobuf.Compatibility;
Jon Skeetfb248822015-09-04 12:41:14 +010035using Google.Protobuf.WellKnownTypes;
Jon Skeetee835a32015-06-30 17:22:26 +010036using System;
Jon Skeet0d684d32015-06-24 17:21:55 +010037using System.Collections.Generic;
James Newton-King18bfd9e2020-04-11 18:00:51 +120038using System.Security;
Jon Skeet0d684d32015-06-24 17:21:55 +010039
40namespace Google.Protobuf
41{
42 /// <summary>
43 /// Factory methods for <see cref="FieldCodec{T}"/>.
44 /// </summary>
45 public static class FieldCodec
46 {
Jon Skeetf34d37a2015-06-30 13:16:20 +010047 // TODO: Avoid the "dual hit" of lambda expressions: create open delegates instead. (At least test...)
Jon Skeet811fc892015-08-04 15:58:39 +010048
49 /// <summary>
50 /// Retrieves a codec suitable for a string field with the given tag.
51 /// </summary>
52 /// <param name="tag">The tag.</param>
Sydney Acksman9857d632019-07-19 18:25:00 -050053 /// <returns>A codec for the given tag.</returns>
54 public static FieldCodec<string> ForString(uint tag)
55 {
56 return FieldCodec.ForString(tag, "");
57 }
58
59 /// <summary>
60 /// Retrieves a codec suitable for a bytes field with the given tag.
61 /// </summary>
62 /// <param name="tag">The tag.</param>
63 /// <returns>A codec for the given tag.</returns>
64 public static FieldCodec<ByteString> ForBytes(uint tag)
65 {
66 return FieldCodec.ForBytes(tag, ByteString.Empty);
67 }
68
69 /// <summary>
70 /// Retrieves a codec suitable for a bool field with the given tag.
71 /// </summary>
72 /// <param name="tag">The tag.</param>
73 /// <returns>A codec for the given tag.</returns>
74 public static FieldCodec<bool> ForBool(uint tag)
75 {
76 return FieldCodec.ForBool(tag, false);
77 }
78
79 /// <summary>
80 /// Retrieves a codec suitable for an int32 field with the given tag.
81 /// </summary>
82 /// <param name="tag">The tag.</param>
83 /// <returns>A codec for the given tag.</returns>
84 public static FieldCodec<int> ForInt32(uint tag)
85 {
86 return FieldCodec.ForInt32(tag, 0);
87 }
88
89 /// <summary>
90 /// Retrieves a codec suitable for an sint32 field with the given tag.
91 /// </summary>
92 /// <param name="tag">The tag.</param>
93 /// <returns>A codec for the given tag.</returns>
94 public static FieldCodec<int> ForSInt32(uint tag)
95 {
96 return FieldCodec.ForSInt32(tag, 0);
97 }
98
99 /// <summary>
100 /// Retrieves a codec suitable for a fixed32 field with the given tag.
101 /// </summary>
102 /// <param name="tag">The tag.</param>
103 /// <returns>A codec for the given tag.</returns>
104 public static FieldCodec<uint> ForFixed32(uint tag)
105 {
106 return FieldCodec.ForFixed32(tag, 0);
107 }
108
109 /// <summary>
110 /// Retrieves a codec suitable for an sfixed32 field with the given tag.
111 /// </summary>
112 /// <param name="tag">The tag.</param>
113 /// <returns>A codec for the given tag.</returns>
114 public static FieldCodec<int> ForSFixed32(uint tag)
115 {
116 return FieldCodec.ForSFixed32(tag, 0);
117 }
118
119 /// <summary>
120 /// Retrieves a codec suitable for a uint32 field with the given tag.
121 /// </summary>
122 /// <param name="tag">The tag.</param>
123 /// <returns>A codec for the given tag.</returns>
124 public static FieldCodec<uint> ForUInt32(uint tag)
125 {
126 return FieldCodec.ForUInt32(tag, 0);
127 }
128
129 /// <summary>
130 /// Retrieves a codec suitable for an int64 field with the given tag.
131 /// </summary>
132 /// <param name="tag">The tag.</param>
133 /// <returns>A codec for the given tag.</returns>
134 public static FieldCodec<long> ForInt64(uint tag)
135 {
136 return FieldCodec.ForInt64(tag, 0);
137 }
138
139 /// <summary>
140 /// Retrieves a codec suitable for an sint64 field with the given tag.
141 /// </summary>
142 /// <param name="tag">The tag.</param>
143 /// <returns>A codec for the given tag.</returns>
144 public static FieldCodec<long> ForSInt64(uint tag)
145 {
146 return FieldCodec.ForSInt64(tag, 0);
147 }
148
149 /// <summary>
150 /// Retrieves a codec suitable for a fixed64 field with the given tag.
151 /// </summary>
152 /// <param name="tag">The tag.</param>
153 /// <returns>A codec for the given tag.</returns>
154 public static FieldCodec<ulong> ForFixed64(uint tag)
155 {
156 return FieldCodec.ForFixed64(tag, 0);
157 }
158
159 /// <summary>
160 /// Retrieves a codec suitable for an sfixed64 field with the given tag.
161 /// </summary>
162 /// <param name="tag">The tag.</param>
163 /// <returns>A codec for the given tag.</returns>
164 public static FieldCodec<long> ForSFixed64(uint tag)
165 {
166 return FieldCodec.ForSFixed64(tag, 0);
167 }
168
169 /// <summary>
170 /// Retrieves a codec suitable for a uint64 field with the given tag.
171 /// </summary>
172 /// <param name="tag">The tag.</param>
173 /// <returns>A codec for the given tag.</returns>
174 public static FieldCodec<ulong> ForUInt64(uint tag)
175 {
176 return FieldCodec.ForUInt64(tag, 0);
177 }
178
179 /// <summary>
180 /// Retrieves a codec suitable for a float field with the given tag.
181 /// </summary>
182 /// <param name="tag">The tag.</param>
183 /// <returns>A codec for the given tag.</returns>
184 public static FieldCodec<float> ForFloat(uint tag)
185 {
186 return FieldCodec.ForFloat(tag, 0);
187 }
188
189 /// <summary>
190 /// Retrieves a codec suitable for a double field with the given tag.
191 /// </summary>
192 /// <param name="tag">The tag.</param>
193 /// <returns>A codec for the given tag.</returns>
194 public static FieldCodec<double> ForDouble(uint tag)
195 {
196 return FieldCodec.ForDouble(tag, 0);
197 }
198
199 // Enums are tricky. We can probably use expression trees to build these delegates automatically,
200 // but it's easy to generate the code for it.
201
202 /// <summary>
203 /// Retrieves a codec suitable for an enum field with the given tag.
204 /// </summary>
205 /// <param name="tag">The tag.</param>
206 /// <param name="toInt32">A conversion function from <see cref="Int32"/> to the enum type.</param>
207 /// <param name="fromInt32">A conversion function from the enum type to <see cref="Int32"/>.</param>
208 /// <returns>A codec for the given tag.</returns>
209 public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
210 {
211 return FieldCodec.ForEnum(tag, toInt32, fromInt32, default(T));
212 }
213
214 /// <summary>
215 /// Retrieves a codec suitable for a string field with the given tag.
216 /// </summary>
217 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500218 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100219 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500220 public static FieldCodec<string> ForString(uint tag, string defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100221 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200222 return new FieldCodec<string>((ref ParseContext ctx) => ctx.ReadString(), (ref WriteContext ctx, string value) => ctx.WriteString(value), CodedOutputStream.ComputeStringSize, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100223 }
224
Jon Skeet811fc892015-08-04 15:58:39 +0100225 /// <summary>
226 /// Retrieves a codec suitable for a bytes field with the given tag.
227 /// </summary>
228 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500229 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100230 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500231 public static FieldCodec<ByteString> ForBytes(uint tag, ByteString defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100232 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200233 return new FieldCodec<ByteString>((ref ParseContext ctx) => ctx.ReadBytes(), (ref WriteContext ctx, ByteString value) => ctx.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100234 }
235
Jon Skeet811fc892015-08-04 15:58:39 +0100236 /// <summary>
237 /// Retrieves a codec suitable for a bool field with the given tag.
238 /// </summary>
239 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500240 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100241 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500242 public static FieldCodec<bool> ForBool(uint tag, bool defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100243 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200244 return new FieldCodec<bool>((ref ParseContext ctx) => ctx.ReadBool(), (ref WriteContext ctx, bool value) => ctx.WriteBool(value), CodedOutputStream.BoolSize, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100245 }
246
Jon Skeet811fc892015-08-04 15:58:39 +0100247 /// <summary>
248 /// Retrieves a codec suitable for an int32 field with the given tag.
249 /// </summary>
250 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500251 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100252 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500253 public static FieldCodec<int> ForInt32(uint tag, int defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100254 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200255 return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadInt32(), (ref WriteContext output, int value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100256 }
257
Jon Skeet811fc892015-08-04 15:58:39 +0100258 /// <summary>
259 /// Retrieves a codec suitable for an sint32 field with the given tag.
260 /// </summary>
261 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500262 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100263 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500264 public static FieldCodec<int> ForSInt32(uint tag, int defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100265 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200266 return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSInt32(), (ref WriteContext output, int value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100267 }
268
Jon Skeet811fc892015-08-04 15:58:39 +0100269 /// <summary>
270 /// Retrieves a codec suitable for a fixed32 field with the given tag.
271 /// </summary>
272 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500273 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100274 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500275 public static FieldCodec<uint> ForFixed32(uint tag, uint defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100276 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200277 return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadFixed32(), (ref WriteContext output, uint value) => output.WriteFixed32(value), 4, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100278 }
279
Jon Skeet811fc892015-08-04 15:58:39 +0100280 /// <summary>
281 /// Retrieves a codec suitable for an sfixed32 field with the given tag.
282 /// </summary>
283 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500284 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100285 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500286 public static FieldCodec<int> ForSFixed32(uint tag, int defaultValue)
Jon Skeetc1283312015-06-26 10:32:23 +0100287 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200288 return new FieldCodec<int>((ref ParseContext ctx) => ctx.ReadSFixed32(), (ref WriteContext output, int value) => output.WriteSFixed32(value), 4, tag, defaultValue);
Jon Skeetc1283312015-06-26 10:32:23 +0100289 }
290
Jon Skeet811fc892015-08-04 15:58:39 +0100291 /// <summary>
292 /// Retrieves a codec suitable for a uint32 field with the given tag.
293 /// </summary>
294 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500295 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100296 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500297 public static FieldCodec<uint> ForUInt32(uint tag, uint defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100298 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200299 return new FieldCodec<uint>((ref ParseContext ctx) => ctx.ReadUInt32(), (ref WriteContext output, uint value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100300 }
301
Jon Skeet811fc892015-08-04 15:58:39 +0100302 /// <summary>
303 /// Retrieves a codec suitable for an int64 field with the given tag.
304 /// </summary>
305 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500306 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100307 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500308 public static FieldCodec<long> ForInt64(uint tag, long defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100309 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200310 return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadInt64(), (ref WriteContext output, long value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100311 }
312
Jon Skeet811fc892015-08-04 15:58:39 +0100313 /// <summary>
314 /// Retrieves a codec suitable for an sint64 field with the given tag.
315 /// </summary>
316 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500317 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100318 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500319 public static FieldCodec<long> ForSInt64(uint tag, long defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100320 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200321 return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSInt64(), (ref WriteContext output, long value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100322 }
323
Jon Skeet811fc892015-08-04 15:58:39 +0100324 /// <summary>
325 /// Retrieves a codec suitable for a fixed64 field with the given tag.
326 /// </summary>
327 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500328 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100329 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500330 public static FieldCodec<ulong> ForFixed64(uint tag, ulong defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100331 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200332 return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadFixed64(), (ref WriteContext output, ulong value) => output.WriteFixed64(value), 8, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100333 }
334
Jon Skeet811fc892015-08-04 15:58:39 +0100335 /// <summary>
336 /// Retrieves a codec suitable for an sfixed64 field with the given tag.
337 /// </summary>
338 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500339 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100340 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500341 public static FieldCodec<long> ForSFixed64(uint tag, long defaultValue)
Jon Skeetc1283312015-06-26 10:32:23 +0100342 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200343 return new FieldCodec<long>((ref ParseContext ctx) => ctx.ReadSFixed64(), (ref WriteContext output, long value) => output.WriteSFixed64(value), 8, tag, defaultValue);
Jon Skeetc1283312015-06-26 10:32:23 +0100344 }
345
Jon Skeet811fc892015-08-04 15:58:39 +0100346 /// <summary>
347 /// Retrieves a codec suitable for a uint64 field with the given tag.
348 /// </summary>
349 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500350 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100351 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500352 public static FieldCodec<ulong> ForUInt64(uint tag, ulong defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100353 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200354 return new FieldCodec<ulong>((ref ParseContext ctx) => ctx.ReadUInt64(), (ref WriteContext output, ulong value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100355 }
356
Jon Skeet811fc892015-08-04 15:58:39 +0100357 /// <summary>
358 /// Retrieves a codec suitable for a float field with the given tag.
359 /// </summary>
360 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500361 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100362 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500363 public static FieldCodec<float> ForFloat(uint tag, float defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100364 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200365 return new FieldCodec<float>((ref ParseContext ctx) => ctx.ReadFloat(), (ref WriteContext output, float value) => output.WriteFloat(value), CodedOutputStream.FloatSize, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100366 }
367
Jon Skeet811fc892015-08-04 15:58:39 +0100368 /// <summary>
369 /// Retrieves a codec suitable for a double field with the given tag.
370 /// </summary>
371 /// <param name="tag">The tag.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500372 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100373 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500374 public static FieldCodec<double> ForDouble(uint tag, double defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100375 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200376 return new FieldCodec<double>((ref ParseContext ctx) => ctx.ReadDouble(), (ref WriteContext output, double value) => output.WriteDouble(value), CodedOutputStream.DoubleSize, tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100377 }
378
379 // Enums are tricky. We can probably use expression trees to build these delegates automatically,
Jon Skeetf34d37a2015-06-30 13:16:20 +0100380 // but it's easy to generate the code for it.
Jon Skeet811fc892015-08-04 15:58:39 +0100381
382 /// <summary>
383 /// Retrieves a codec suitable for an enum field with the given tag.
384 /// </summary>
385 /// <param name="tag">The tag.</param>
386 /// <param name="toInt32">A conversion function from <see cref="Int32"/> to the enum type.</param>
387 /// <param name="fromInt32">A conversion function from the enum type to <see cref="Int32"/>.</param>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500388 /// <param name="defaultValue">The default value.</param>
Jon Skeet811fc892015-08-04 15:58:39 +0100389 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9857d632019-07-19 18:25:00 -0500390 public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32, T defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100391 {
Jan Tattermuschea605382020-03-31 18:56:05 +0200392 return new FieldCodec<T>((ref ParseContext ctx) => fromInt32(
393 ctx.ReadEnum()),
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200394 (ref WriteContext output, T value) => output.WriteEnum(toInt32(value)),
Sydney Acksman29141f42019-07-14 00:14:36 -0500395 value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag, defaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100396 }
397
Jon Skeet811fc892015-08-04 15:58:39 +0100398 /// <summary>
399 /// Retrieves a codec suitable for a message field with the given tag.
400 /// </summary>
401 /// <param name="tag">The tag.</param>
402 /// <param name="parser">A parser to use for the message type.</param>
403 /// <returns>A codec for the given tag.</returns>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500404 public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : class, IMessage<T>
Jon Skeet0d684d32015-06-24 17:21:55 +0100405 {
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500406 return new FieldCodec<T>(
Jan Tattermuschea605382020-03-31 18:56:05 +0200407 (ref ParseContext ctx) =>
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500408 {
409 T message = parser.CreateTemplate();
Jan Tattermuschea605382020-03-31 18:56:05 +0200410 ctx.ReadMessage(message);
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500411 return message;
412 },
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200413 (ref WriteContext output, T value) => output.WriteMessage(value),
Jan Tattermuschea605382020-03-31 18:56:05 +0200414 (ref ParseContext ctx, ref T v) =>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500415 {
416 if (v == null)
417 {
418 v = parser.CreateTemplate();
419 }
420
Jan Tattermuschea605382020-03-31 18:56:05 +0200421 ctx.ReadMessage(v);
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500422 },
423 (ref T v, T v2) =>
424 {
425 if (v2 == null)
426 {
427 return false;
428 }
429 else if (v == null)
430 {
431 v = v2.Clone();
432 }
433 else
434 {
435 v.MergeFrom(v2);
436 }
437 return true;
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500438 },
439 message => CodedOutputStream.ComputeMessageSize(message), tag);
Jon Skeet0d684d32015-06-24 17:21:55 +0100440 }
Jon Skeet8a0312b2015-07-16 17:03:06 +0100441
442 /// <summary>
Sydney Acksman6f73c502018-11-05 13:37:08 -0800443 /// Retrieves a codec suitable for a group field with the given tag.
444 /// </summary>
445 /// <param name="startTag">The start group tag.</param>
446 /// <param name="endTag">The end group tag.</param>
447 /// <param name="parser">A parser to use for the group message type.</param>
448 /// <returns>A codec for given tag</returns>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500449 public static FieldCodec<T> ForGroup<T>(uint startTag, uint endTag, MessageParser<T> parser) where T : class, IMessage<T>
Sydney Acksman6f73c502018-11-05 13:37:08 -0800450 {
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500451 return new FieldCodec<T>(
Jan Tattermuschea605382020-03-31 18:56:05 +0200452 (ref ParseContext ctx) =>
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500453 {
454 T message = parser.CreateTemplate();
Jan Tattermuschea605382020-03-31 18:56:05 +0200455 ctx.ReadGroup(message);
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500456 return message;
457 },
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200458 (ref WriteContext output, T value) => output.WriteGroup(value),
Jan Tattermuschea605382020-03-31 18:56:05 +0200459 (ref ParseContext ctx, ref T v) =>
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500460 {
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500461 if (v == null)
462 {
463 v = parser.CreateTemplate();
464 }
465
Jan Tattermuschea605382020-03-31 18:56:05 +0200466 ctx.ReadGroup(v);
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500467 },
468 (ref T v, T v2) =>
469 {
470 if (v2 == null)
471 {
472 return v == null;
473 }
474 else if (v == null)
475 {
476 v = v2.Clone();
477 }
478 else
479 {
480 v.MergeFrom(v2);
481 }
482 return true;
Sydney Acksman79cf8a82019-07-21 06:13:59 -0500483 },
484 message => CodedOutputStream.ComputeGroupSize(message), startTag, endTag);
Sydney Acksman6f73c502018-11-05 13:37:08 -0800485 }
486
487 /// <summary>
Jon Skeet8a0312b2015-07-16 17:03:06 +0100488 /// Creates a codec for a wrapper type of a class - which must be string or ByteString.
489 /// </summary>
490 public static FieldCodec<T> ForClassWrapper<T>(uint tag) where T : class
491 {
492 var nestedCodec = WrapperCodecs.GetCodec<T>();
493 return new FieldCodec<T>(
Jan Tattermuschea605382020-03-31 18:56:05 +0200494 (ref ParseContext ctx) => WrapperCodecs.Read<T>(ref ctx, nestedCodec),
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200495 (ref WriteContext output, T value) => WrapperCodecs.Write<T>(ref output, value, nestedCodec),
Jan Tattermuschea605382020-03-31 18:56:05 +0200496 (ref ParseContext ctx, ref T v) => v = WrapperCodecs.Read<T>(ref ctx, nestedCodec),
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500497 (ref T v, T v2) => { v = v2; return v == null; },
Jon Skeet8a0312b2015-07-16 17:03:06 +0100498 value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
Sydney Acksman6f73c502018-11-05 13:37:08 -0800499 tag, 0,
Jon Skeet8a0312b2015-07-16 17:03:06 +0100500 null); // Default value for the wrapper
501 }
502
503 /// <summary>
504 /// Creates a codec for a wrapper type of a struct - which must be Int32, Int64, UInt32, UInt64,
505 /// Bool, Single or Double.
506 /// </summary>
507 public static FieldCodec<T?> ForStructWrapper<T>(uint tag) where T : struct
508 {
509 var nestedCodec = WrapperCodecs.GetCodec<T>();
510 return new FieldCodec<T?>(
Chris Bacone305e562019-11-04 16:58:44 +0000511 WrapperCodecs.GetReader<T>(),
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200512 (ref WriteContext output, T? value) => WrapperCodecs.Write<T>(ref output, value.Value, nestedCodec),
Jan Tattermuschea605382020-03-31 18:56:05 +0200513 (ref ParseContext ctx, ref T? v) => v = WrapperCodecs.Read<T>(ref ctx, nestedCodec),
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500514 (ref T? v, T? v2) => { if (v2.HasValue) { v = v2; } return v.HasValue; },
Jon Skeet8a0312b2015-07-16 17:03:06 +0100515 value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
Sydney Acksman6f73c502018-11-05 13:37:08 -0800516 tag, 0,
Jon Skeet8a0312b2015-07-16 17:03:06 +0100517 null); // Default value for the wrapper
518 }
519
Jon Skeet34878cb2015-07-17 06:41:46 +0100520 /// <summary>
521 /// Helper code to create codecs for wrapper types.
522 /// </summary>
523 /// <remarks>
524 /// Somewhat ugly with all the static methods, but the conversions involved to/from nullable types make it
525 /// slightly tricky to improve. So long as we keep the public API (ForClassWrapper, ForStructWrapper) in place,
526 /// we can refactor later if we come up with something cleaner.
527 /// </remarks>
Jon Skeet8a0312b2015-07-16 17:03:06 +0100528 private static class WrapperCodecs
529 {
Jon Skeetfb248822015-09-04 12:41:14 +0100530 private static readonly Dictionary<System.Type, object> Codecs = new Dictionary<System.Type, object>
Jon Skeet8a0312b2015-07-16 17:03:06 +0100531 {
Jon Skeet284bb452015-11-05 09:13:53 +0000532 { typeof(bool), ForBool(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
533 { typeof(int), ForInt32(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
534 { typeof(long), ForInt64(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
535 { typeof(uint), ForUInt32(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
536 { typeof(ulong), ForUInt64(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
537 { typeof(float), ForFloat(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Fixed32)) },
538 { typeof(double), ForDouble(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.Fixed64)) },
539 { typeof(string), ForString(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) },
540 { typeof(ByteString), ForBytes(WireFormat.MakeTag(WrappersReflection.WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) }
Jon Skeet8a0312b2015-07-16 17:03:06 +0100541 };
542
Chris Bacon2ac89462019-11-04 20:24:37 +0000543 private static readonly Dictionary<System.Type, object> Readers = new Dictionary<System.Type, object>
Chris Bacone305e562019-11-04 16:58:44 +0000544 {
545 // TODO: Provide more optimized readers.
Jan Tattermuschea605382020-03-31 18:56:05 +0200546 { typeof(bool), (ValueReader<bool?>)ParsingPrimitivesWrappers.ReadBoolWrapper },
547 { typeof(int), (ValueReader<int?>)ParsingPrimitivesWrappers.ReadInt32Wrapper },
548 { typeof(long), (ValueReader<long?>)ParsingPrimitivesWrappers.ReadInt64Wrapper },
549 { typeof(uint), (ValueReader<uint?>)ParsingPrimitivesWrappers.ReadUInt32Wrapper },
550 { typeof(ulong), (ValueReader<ulong?>)ParsingPrimitivesWrappers.ReadUInt64Wrapper },
Chris Bacon2ac89462019-11-04 20:24:37 +0000551 { typeof(float), BitConverter.IsLittleEndian ?
Jan Tattermuschea605382020-03-31 18:56:05 +0200552 (ValueReader<float?>)ParsingPrimitivesWrappers.ReadFloatWrapperLittleEndian :
553 (ValueReader<float?>)ParsingPrimitivesWrappers.ReadFloatWrapperSlow },
Chris Bacon2ac89462019-11-04 20:24:37 +0000554 { typeof(double), BitConverter.IsLittleEndian ?
Jan Tattermuschea605382020-03-31 18:56:05 +0200555 (ValueReader<double?>)ParsingPrimitivesWrappers.ReadDoubleWrapperLittleEndian :
556 (ValueReader<double?>)ParsingPrimitivesWrappers.ReadDoubleWrapperSlow },
Chris Bacon2ac89462019-11-04 20:24:37 +0000557 // `string` and `ByteString` less performance-sensitive. Do not implement for now.
Chris Bacone305e562019-11-04 16:58:44 +0000558 { typeof(string), null },
559 { typeof(ByteString), null },
560 };
561
Jon Skeet8a0312b2015-07-16 17:03:06 +0100562 /// <summary>
563 /// Returns a field codec which effectively wraps a value of type T in a message.
Xiang Daie4794102019-02-21 11:28:50 +0800564 ///
Jon Skeet8a0312b2015-07-16 17:03:06 +0100565 /// </summary>
566 internal static FieldCodec<T> GetCodec<T>()
567 {
568 object value;
569 if (!Codecs.TryGetValue(typeof(T), out value))
570 {
571 throw new InvalidOperationException("Invalid type argument requested for wrapper codec: " + typeof(T));
572 }
573 return (FieldCodec<T>) value;
574 }
575
Jan Tattermuschea605382020-03-31 18:56:05 +0200576 internal static ValueReader<T?> GetReader<T>() where T : struct
Chris Bacone305e562019-11-04 16:58:44 +0000577 {
Chris Bacon2ac89462019-11-04 20:24:37 +0000578 object value;
Chris Bacone305e562019-11-04 16:58:44 +0000579 if (!Readers.TryGetValue(typeof(T), out value))
580 {
581 throw new InvalidOperationException("Invalid type argument requested for wrapper reader: " + typeof(T));
582 }
583 if (value == null)
584 {
585 // Return default unoptimized reader for the wrapper type.
586 var nestedCoded = GetCodec<T>();
Jan Tattermuschea605382020-03-31 18:56:05 +0200587 return (ref ParseContext ctx) => Read<T>(ref ctx, nestedCoded);
Chris Bacone305e562019-11-04 16:58:44 +0000588 }
589 // Return optimized read for the wrapper type.
Jan Tattermuschea605382020-03-31 18:56:05 +0200590 return (ValueReader<T?>)value;
Chris Bacone305e562019-11-04 16:58:44 +0000591 }
592
James Newton-King18bfd9e2020-04-11 18:00:51 +1200593 [SecuritySafeCritical]
Jan Tattermuschea605382020-03-31 18:56:05 +0200594 internal static T Read<T>(ref ParseContext ctx, FieldCodec<T> codec)
595 {
596 int length = ctx.ReadLength();
597 int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length);
598
599 uint tag;
600 T value = codec.DefaultValue;
601 while ((tag = ctx.ReadTag()) != 0)
602 {
603 if (tag == codec.Tag)
604 {
605 value = codec.Read(ref ctx);
606 }
607 else
608 {
609 ParsingPrimitivesMessages.SkipLastField(ref ctx.buffer, ref ctx.state);
610 }
611
612 }
613 ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref ctx.state);
614 SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
615
616 return value;
617 }
618
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200619 internal static void Write<T>(ref WriteContext ctx, T value, FieldCodec<T> codec)
Jon Skeet8a0312b2015-07-16 17:03:06 +0100620 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200621 ctx.WriteLength(codec.CalculateSizeWithTag(value));
622 codec.WriteTagAndValue(ref ctx, value);
Jon Skeet8a0312b2015-07-16 17:03:06 +0100623 }
624
625 internal static int CalculateSize<T>(T value, FieldCodec<T> codec)
626 {
627 int fieldLength = codec.CalculateSizeWithTag(value);
628 return CodedOutputStream.ComputeLengthSize(fieldLength) + fieldLength;
629 }
630 }
Jon Skeet0d684d32015-06-24 17:21:55 +0100631 }
632
Jan Tattermuschea605382020-03-31 18:56:05 +0200633 internal delegate TValue ValueReader<out TValue>(ref ParseContext ctx);
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200634 internal delegate void ValueWriter<T>(ref WriteContext ctx, T value);
Jan Tattermuschea605382020-03-31 18:56:05 +0200635
Jon Skeet0d684d32015-06-24 17:21:55 +0100636 /// <summary>
Jon Skeetf2626112016-01-15 10:13:56 +0000637 /// <para>
Jon Skeet0d684d32015-06-24 17:21:55 +0100638 /// An encode/decode pair for a single field. This effectively encapsulates
639 /// all the information needed to read or write the field value from/to a coded
640 /// stream.
Jon Skeetf2626112016-01-15 10:13:56 +0000641 /// </para>
642 /// <para>
643 /// This class is public and has to be as it is used by generated code, but its public
644 /// API is very limited - just what the generated code needs to call directly.
645 /// </para>
Jon Skeet0d684d32015-06-24 17:21:55 +0100646 /// </summary>
647 /// <remarks>
Jon Skeetf2626112016-01-15 10:13:56 +0000648 /// This never writes default values to the stream, and does not address "packedness"
649 /// in repeated fields itself, other than to know whether or not the field *should* be packed.
Jon Skeet0d684d32015-06-24 17:21:55 +0100650 /// </remarks>
651 public sealed class FieldCodec<T>
652 {
Jon Skeetf3e9a652017-10-25 16:03:23 +0100653 private static readonly EqualityComparer<T> EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer<T>();
Jon Skeet8a0312b2015-07-16 17:03:06 +0100654 private static readonly T DefaultDefault;
Jon Skeet3df146e2016-07-08 18:21:25 +0100655 // Only non-nullable value types support packing. This is the simplest way of detecting that.
656 private static readonly bool TypeSupportsPacking = default(T) != null;
Jon Skeet0d684d32015-06-24 17:21:55 +0100657
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500658 /// <summary>
659 /// Merges an input stream into a value
660 /// </summary>
Jan Tattermuschea605382020-03-31 18:56:05 +0200661 internal delegate void InputMerger(ref ParseContext ctx, ref T value);
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500662
663 /// <summary>
664 /// Merges a value into a reference to another value, returning a boolean if the value was set
665 /// </summary>
666 internal delegate bool ValuesMerger(ref T value, T other);
667
Jon Skeet0d684d32015-06-24 17:21:55 +0100668 static FieldCodec()
669 {
670 if (typeof(T) == typeof(string))
671 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100672 DefaultDefault = (T)(object)"";
Jon Skeet0d684d32015-06-24 17:21:55 +0100673 }
674 else if (typeof(T) == typeof(ByteString))
675 {
Jon Skeet8a0312b2015-07-16 17:03:06 +0100676 DefaultDefault = (T)(object)ByteString.Empty;
Jon Skeet0d684d32015-06-24 17:21:55 +0100677 }
Jon Skeet8a0312b2015-07-16 17:03:06 +0100678 // Otherwise it's the default value of the CLR type
Jon Skeet0d684d32015-06-24 17:21:55 +0100679 }
680
Jon Skeetf2626112016-01-15 10:13:56 +0000681 internal static bool IsPackedRepeatedField(uint tag) =>
682 TypeSupportsPacking && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited;
683
684 internal bool PackedRepeatedField { get; }
685
686 /// <summary>
687 /// Returns a delegate to write a value (unconditionally) to a coded output stream.
688 /// </summary>
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200689 internal ValueWriter<T> ValueWriter { get; }
Jon Skeetf2626112016-01-15 10:13:56 +0000690
691 /// <summary>
692 /// Returns the size calculator for just a value.
693 /// </summary>
694 internal Func<T, int> ValueSizeCalculator { get; }
695
696 /// <summary>
697 /// Returns a delegate to read a value from a coded input stream. It is assumed that
698 /// the stream is already positioned on the appropriate tag.
699 /// </summary>
Jan Tattermuschea605382020-03-31 18:56:05 +0200700 internal ValueReader<T> ValueReader { get; }
Jon Skeetf2626112016-01-15 10:13:56 +0000701
702 /// <summary>
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500703 /// Returns a delegate to merge a value from a coded input stream.
704 /// It is assumed that the stream is already positioned on the appropriate tag
705 /// </summary>
706 internal InputMerger ValueMerger { get; }
707
708 /// <summary>
709 /// Returns a delegate to merge two values together.
710 /// </summary>
711 internal ValuesMerger FieldMerger { get; }
712
713 /// <summary>
Jon Skeetf2626112016-01-15 10:13:56 +0000714 /// Returns the fixed size for an entry, or 0 if sizes vary.
715 /// </summary>
716 internal int FixedSize { get; }
717
718 /// <summary>
719 /// Gets the tag of the codec.
720 /// </summary>
721 /// <value>
722 /// The tag of the codec.
723 /// </value>
724 internal uint Tag { get; }
725
726 /// <summary>
Sydney Acksman6f73c502018-11-05 13:37:08 -0800727 /// Gets the end tag of the codec or 0 if there is no end tag
728 /// </summary>
729 /// <value>
730 /// The end tag of the codec.
731 /// </value>
732 internal uint EndTag { get; }
733
734 /// <summary>
Jon Skeetf2626112016-01-15 10:13:56 +0000735 /// Default value for this codec. Usually the same for every instance of the same type, but
736 /// for string/ByteString wrapper fields the codec's default value is null, whereas for
737 /// other string/ByteString fields it's "" or ByteString.Empty.
738 /// </summary>
739 /// <value>
740 /// The default value of the codec's type.
741 /// </value>
742 internal T DefaultValue { get; }
743
Jon Skeet0d684d32015-06-24 17:21:55 +0100744 private readonly int tagSize;
Xiang Daie4794102019-02-21 11:28:50 +0800745
Jon Skeetf2626112016-01-15 10:13:56 +0000746 internal FieldCodec(
Jan Tattermuschea605382020-03-31 18:56:05 +0200747 ValueReader<T> reader,
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200748 ValueWriter<T> writer,
Jon Skeetf2626112016-01-15 10:13:56 +0000749 int fixedSize,
Sydney Acksman29141f42019-07-14 00:14:36 -0500750 uint tag,
751 T defaultValue) : this(reader, writer, _ => fixedSize, tag, defaultValue)
Jon Skeetf2626112016-01-15 10:13:56 +0000752 {
753 FixedSize = fixedSize;
754 }
Jon Skeet0d684d32015-06-24 17:21:55 +0100755
756 internal FieldCodec(
Jan Tattermuschea605382020-03-31 18:56:05 +0200757 ValueReader<T> reader,
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200758 ValueWriter<T> writer,
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100759 Func<T, int> sizeCalculator,
Sydney Acksman6f73c502018-11-05 13:37:08 -0800760 uint tag,
Jan Tattermuschea605382020-03-31 18:56:05 +0200761 T defaultValue) : this(reader, writer, (ref ParseContext ctx, ref T v) => v = reader(ref ctx), (ref T v, T v2) => { v = v2; return true; }, sizeCalculator, tag, 0, defaultValue)
Jon Skeet8a0312b2015-07-16 17:03:06 +0100762 {
763 }
764
765 internal FieldCodec(
Jan Tattermuschea605382020-03-31 18:56:05 +0200766 ValueReader<T> reader,
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200767 ValueWriter<T> writer,
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500768 InputMerger inputMerger,
769 ValuesMerger valuesMerger,
770 Func<T, int> sizeCalculator,
771 uint tag,
772 uint endTag = 0) : this(reader, writer, inputMerger, valuesMerger, sizeCalculator, tag, endTag, DefaultDefault)
773 {
774 }
775
776 internal FieldCodec(
Jan Tattermuschea605382020-03-31 18:56:05 +0200777 ValueReader<T> reader,
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200778 ValueWriter<T> writer,
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500779 InputMerger inputMerger,
780 ValuesMerger valuesMerger,
Jon Skeet8a0312b2015-07-16 17:03:06 +0100781 Func<T, int> sizeCalculator,
782 uint tag,
Sydney Acksman6f73c502018-11-05 13:37:08 -0800783 uint endTag,
Jon Skeet8a0312b2015-07-16 17:03:06 +0100784 T defaultValue)
Jon Skeet0d684d32015-06-24 17:21:55 +0100785 {
Jon Skeetf2626112016-01-15 10:13:56 +0000786 ValueReader = reader;
787 ValueWriter = writer;
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500788 ValueMerger = inputMerger;
789 FieldMerger = valuesMerger;
Jon Skeetf2626112016-01-15 10:13:56 +0000790 ValueSizeCalculator = sizeCalculator;
791 FixedSize = 0;
792 Tag = tag;
Sydney Acksmancd655352019-05-04 11:25:50 -0500793 EndTag = endTag;
Jon Skeetf2626112016-01-15 10:13:56 +0000794 DefaultValue = defaultValue;
Jon Skeet0d684d32015-06-24 17:21:55 +0100795 tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
Sydney Acksman9e89b6e2019-05-03 15:54:41 -0500796 if (endTag != 0)
797 tagSize += CodedOutputStream.ComputeRawVarint32Size(endTag);
Jon Skeetf2626112016-01-15 10:13:56 +0000798 // Detect packed-ness once, so we can check for it within RepeatedField<T>.
799 PackedRepeatedField = IsPackedRepeatedField(tag);
Jon Skeet0d684d32015-06-24 17:21:55 +0100800 }
801
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100802 /// <summary>
803 /// Write a tag and the given value, *if* the value is not the default.
804 /// </summary>
805 public void WriteTagAndValue(CodedOutputStream output, T value)
Jon Skeet0d684d32015-06-24 17:21:55 +0100806 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200807 WriteContext.Initialize(output, out WriteContext ctx);
808 try
809 {
810 WriteTagAndValue(ref ctx, value);
811 }
812 finally
813 {
814 ctx.CopyStateTo(output);
815 }
816
817
818 //if (!IsDefault(value))
819 //{
820 // output.WriteTag(Tag);
821 // ValueWriter(output, value);
822 // if (EndTag != 0)
823 // {
824 // output.WriteTag(EndTag);
825 // }
826 //}
827 }
828
829 /// <summary>
830 /// Write a tag and the given value, *if* the value is not the default.
831 /// </summary>
832 public void WriteTagAndValue(ref WriteContext ctx, T value)
833 {
Jon Skeet0d684d32015-06-24 17:21:55 +0100834 if (!IsDefault(value))
835 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200836 ctx.WriteTag(Tag);
837 ValueWriter(ref ctx, value);
Sydney Acksman6f73c502018-11-05 13:37:08 -0800838 if (EndTag != 0)
839 {
Jan Tattermuschca7bc462020-05-29 08:51:37 +0200840 ctx.WriteTag(EndTag);
Sydney Acksman6f73c502018-11-05 13:37:08 -0800841 }
Jon Skeet0d684d32015-06-24 17:21:55 +0100842 }
843 }
844
Jon Skeet811fc892015-08-04 15:58:39 +0100845 /// <summary>
846 /// Reads a value of the codec type from the given <see cref="CodedInputStream"/>.
847 /// </summary>
848 /// <param name="input">The input stream to read from.</param>
849 /// <returns>The value read from the stream.</returns>
Jan Tattermuschea605382020-03-31 18:56:05 +0200850 public T Read(CodedInputStream input)
851 {
Jan Tattermusch07182a82020-04-07 15:45:18 +0200852 ParseContext.Initialize(input, out ParseContext ctx);
Jan Tattermuschea605382020-03-31 18:56:05 +0200853 try
854 {
855 return ValueReader(ref ctx);
856 }
857 finally
858 {
859 ctx.CopyStateTo(input);
860 }
861 }
862
863 /// <summary>
864 /// Reads a value of the codec type from the given <see cref="ParseContext"/>.
865 /// </summary>
866 /// <param name="ctx">The parse context to read from.</param>
867 /// <returns>The value read.</returns>
868 public T Read(ref ParseContext ctx)
869 {
870 return ValueReader(ref ctx);
871 }
Jon Skeet0d684d32015-06-24 17:21:55 +0100872
Jon Skeetf2a27cc2015-06-26 17:37:14 +0100873 /// <summary>
874 /// Calculates the size required to write the given value, with a tag,
875 /// if the value is not the default.
876 /// </summary>
Jon Skeetf2626112016-01-15 10:13:56 +0000877 public int CalculateSizeWithTag(T value) => IsDefault(value) ? 0 : ValueSizeCalculator(value) + tagSize;
Jon Skeet8a0312b2015-07-16 17:03:06 +0100878
Jon Skeetcea36532021-01-20 08:06:34 +0000879 /// <summary>
880 /// Calculates the size required to write the given value, with a tag, even
881 /// if the value is the default.
882 /// </summary>
883 internal int CalculateUnconditionalSizeWithTag(T value) => ValueSizeCalculator(value) + tagSize;
884
Jon Skeetf3e9a652017-10-25 16:03:23 +0100885 private bool IsDefault(T value) => EqualityComparer.Equals(value, DefaultValue);
Jon Skeet0d684d32015-06-24 17:21:55 +0100886 }
887}