1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license
2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format
3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2019 Google Inc.  All rights reserved.
4*1b3f573fSAndroid Build Coastguard Worker // https://github.com/protocolbuffers/protobuf
5*1b3f573fSAndroid Build Coastguard Worker //
6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without
7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are
8*1b3f573fSAndroid Build Coastguard Worker // met:
9*1b3f573fSAndroid Build Coastguard Worker //
10*1b3f573fSAndroid Build Coastguard Worker //     * Redistributions of source code must retain the above copyright
11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer.
12*1b3f573fSAndroid Build Coastguard Worker //     * Redistributions in binary form must reproduce the above
13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer
14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the
15*1b3f573fSAndroid Build Coastguard Worker // distribution.
16*1b3f573fSAndroid Build Coastguard Worker //     * Neither the name of Google Inc. nor the names of its
17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from
18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission.
19*1b3f573fSAndroid Build Coastguard Worker //
20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31*1b3f573fSAndroid Build Coastguard Worker #endregion
32*1b3f573fSAndroid Build Coastguard Worker 
33*1b3f573fSAndroid Build Coastguard Worker using BenchmarkDotNet.Attributes;
34*1b3f573fSAndroid Build Coastguard Worker using System;
35*1b3f573fSAndroid Build Coastguard Worker using System.Buffers.Binary;
36*1b3f573fSAndroid Build Coastguard Worker using System.Collections.Generic;
37*1b3f573fSAndroid Build Coastguard Worker using System.IO;
38*1b3f573fSAndroid Build Coastguard Worker using System.Buffers;
39*1b3f573fSAndroid Build Coastguard Worker 
40*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf.Benchmarks
41*1b3f573fSAndroid Build Coastguard Worker {
42*1b3f573fSAndroid Build Coastguard Worker     /// <summary>
43*1b3f573fSAndroid Build Coastguard Worker     /// Benchmarks throughput when parsing raw primitives.
44*1b3f573fSAndroid Build Coastguard Worker     /// </summary>
45*1b3f573fSAndroid Build Coastguard Worker     [MemoryDiagnoser]
46*1b3f573fSAndroid Build Coastguard Worker     public class ParseRawPrimitivesBenchmark
47*1b3f573fSAndroid Build Coastguard Worker     {
48*1b3f573fSAndroid Build Coastguard Worker         // key is the encodedSize of varint values
49*1b3f573fSAndroid Build Coastguard Worker         Dictionary<int, byte[]> varintInputBuffers;
50*1b3f573fSAndroid Build Coastguard Worker 
51*1b3f573fSAndroid Build Coastguard Worker         byte[] doubleInputBuffer;
52*1b3f573fSAndroid Build Coastguard Worker         byte[] floatInputBuffer;
53*1b3f573fSAndroid Build Coastguard Worker         byte[] fixedIntInputBuffer;
54*1b3f573fSAndroid Build Coastguard Worker 
55*1b3f573fSAndroid Build Coastguard Worker         // key is the encodedSize of string values
56*1b3f573fSAndroid Build Coastguard Worker         Dictionary<int, byte[]> stringInputBuffers;
57*1b3f573fSAndroid Build Coastguard Worker         Dictionary<int, ReadOnlySequence<byte>> stringInputBuffersSegmented;
58*1b3f573fSAndroid Build Coastguard Worker 
59*1b3f573fSAndroid Build Coastguard Worker         Random random = new Random(417384220);  // random but deterministic seed
60*1b3f573fSAndroid Build Coastguard Worker 
61*1b3f573fSAndroid Build Coastguard Worker         public IEnumerable<int> StringEncodedSizes => new[] { 1, 4, 10, 105, 10080 };
62*1b3f573fSAndroid Build Coastguard Worker         public IEnumerable<int> StringSegmentedEncodedSizes => new[] { 105, 10080 };
63*1b3f573fSAndroid Build Coastguard Worker 
64*1b3f573fSAndroid Build Coastguard Worker         [GlobalSetup]
GlobalSetup()65*1b3f573fSAndroid Build Coastguard Worker         public void GlobalSetup()
66*1b3f573fSAndroid Build Coastguard Worker         {
67*1b3f573fSAndroid Build Coastguard Worker             // add some extra values that we won't read just to make sure we are far enough from the end of the buffer
68*1b3f573fSAndroid Build Coastguard Worker             // which allows the parser fastpath to always kick in.
69*1b3f573fSAndroid Build Coastguard Worker             const int paddingValueCount = 100;
70*1b3f573fSAndroid Build Coastguard Worker 
71*1b3f573fSAndroid Build Coastguard Worker             varintInputBuffers = new Dictionary<int, byte[]>();
72*1b3f573fSAndroid Build Coastguard Worker             for (int encodedSize = 1; encodedSize <= 10; encodedSize++)
73*1b3f573fSAndroid Build Coastguard Worker             {
74*1b3f573fSAndroid Build Coastguard Worker                 byte[] buffer = CreateBufferWithRandomVarints(random, BytesToParse / encodedSize, encodedSize, paddingValueCount);
75*1b3f573fSAndroid Build Coastguard Worker                 varintInputBuffers.Add(encodedSize, buffer);
76*1b3f573fSAndroid Build Coastguard Worker             }
77*1b3f573fSAndroid Build Coastguard Worker 
78*1b3f573fSAndroid Build Coastguard Worker             doubleInputBuffer = CreateBufferWithRandomDoubles(random, BytesToParse / sizeof(double), paddingValueCount);
79*1b3f573fSAndroid Build Coastguard Worker             floatInputBuffer = CreateBufferWithRandomFloats(random, BytesToParse / sizeof(float), paddingValueCount);
80*1b3f573fSAndroid Build Coastguard Worker             fixedIntInputBuffer = CreateBufferWithRandomData(random, BytesToParse / sizeof(long), sizeof(long), paddingValueCount);
81*1b3f573fSAndroid Build Coastguard Worker 
82*1b3f573fSAndroid Build Coastguard Worker             stringInputBuffers = new Dictionary<int, byte[]>();
83*1b3f573fSAndroid Build Coastguard Worker             foreach (var encodedSize in StringEncodedSizes)
84*1b3f573fSAndroid Build Coastguard Worker             {
85*1b3f573fSAndroid Build Coastguard Worker                 byte[] buffer = CreateBufferWithStrings(BytesToParse / encodedSize, encodedSize, encodedSize < 10 ? 10 : 1 );
86*1b3f573fSAndroid Build Coastguard Worker                 stringInputBuffers.Add(encodedSize, buffer);
87*1b3f573fSAndroid Build Coastguard Worker             }
88*1b3f573fSAndroid Build Coastguard Worker 
89*1b3f573fSAndroid Build Coastguard Worker             stringInputBuffersSegmented = new Dictionary<int, ReadOnlySequence<byte>>();
90*1b3f573fSAndroid Build Coastguard Worker             foreach (var encodedSize in StringSegmentedEncodedSizes)
91*1b3f573fSAndroid Build Coastguard Worker             {
92*1b3f573fSAndroid Build Coastguard Worker                 byte[] buffer = CreateBufferWithStrings(BytesToParse / encodedSize, encodedSize, encodedSize < 10 ? 10 : 1);
93*1b3f573fSAndroid Build Coastguard Worker                 stringInputBuffersSegmented.Add(encodedSize, ReadOnlySequenceFactory.CreateWithContent(buffer, segmentSize: 128, addEmptySegmentDelimiters: false));
94*1b3f573fSAndroid Build Coastguard Worker             }
95*1b3f573fSAndroid Build Coastguard Worker         }
96*1b3f573fSAndroid Build Coastguard Worker 
97*1b3f573fSAndroid Build Coastguard Worker         // Total number of bytes that each benchmark will parse.
98*1b3f573fSAndroid Build Coastguard Worker         // Measuring the time taken to parse buffer of given size makes it easier to compare parsing speed for different
99*1b3f573fSAndroid Build Coastguard Worker         // types and makes it easy to calculate the througput (in MB/s)
100*1b3f573fSAndroid Build Coastguard Worker         // 10800 bytes is chosen because it is divisible by all possible encoded sizes for all primitive types {1..10}
101*1b3f573fSAndroid Build Coastguard Worker         [Params(10080)]
102*1b3f573fSAndroid Build Coastguard Worker         public int BytesToParse { get; set; }
103*1b3f573fSAndroid Build Coastguard Worker 
104*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
105*1b3f573fSAndroid Build Coastguard Worker         [Arguments(1)]
106*1b3f573fSAndroid Build Coastguard Worker         [Arguments(2)]
107*1b3f573fSAndroid Build Coastguard Worker         [Arguments(3)]
108*1b3f573fSAndroid Build Coastguard Worker         [Arguments(4)]
109*1b3f573fSAndroid Build Coastguard Worker         [Arguments(5)]
ParseRawVarint32_CodedInputStream(int encodedSize)110*1b3f573fSAndroid Build Coastguard Worker         public int ParseRawVarint32_CodedInputStream(int encodedSize)
111*1b3f573fSAndroid Build Coastguard Worker         {
112*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(varintInputBuffers[encodedSize]);
113*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
114*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
115*1b3f573fSAndroid Build Coastguard Worker             {
116*1b3f573fSAndroid Build Coastguard Worker                 sum += cis.ReadInt32();
117*1b3f573fSAndroid Build Coastguard Worker             }
118*1b3f573fSAndroid Build Coastguard Worker             return sum;
119*1b3f573fSAndroid Build Coastguard Worker         }
120*1b3f573fSAndroid Build Coastguard Worker 
121*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
122*1b3f573fSAndroid Build Coastguard Worker         [Arguments(1)]
123*1b3f573fSAndroid Build Coastguard Worker         [Arguments(2)]
124*1b3f573fSAndroid Build Coastguard Worker         [Arguments(3)]
125*1b3f573fSAndroid Build Coastguard Worker         [Arguments(4)]
126*1b3f573fSAndroid Build Coastguard Worker         [Arguments(5)]
ParseRawVarint32_ParseContext(int encodedSize)127*1b3f573fSAndroid Build Coastguard Worker         public int ParseRawVarint32_ParseContext(int encodedSize)
128*1b3f573fSAndroid Build Coastguard Worker         {
129*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(varintInputBuffers[encodedSize], out ParseContext ctx);
130*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
131*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
132*1b3f573fSAndroid Build Coastguard Worker             {
133*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadInt32();
134*1b3f573fSAndroid Build Coastguard Worker             }
135*1b3f573fSAndroid Build Coastguard Worker             return sum;
136*1b3f573fSAndroid Build Coastguard Worker         }
137*1b3f573fSAndroid Build Coastguard Worker 
138*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
139*1b3f573fSAndroid Build Coastguard Worker         [Arguments(1)]
140*1b3f573fSAndroid Build Coastguard Worker         [Arguments(2)]
141*1b3f573fSAndroid Build Coastguard Worker         [Arguments(3)]
142*1b3f573fSAndroid Build Coastguard Worker         [Arguments(4)]
143*1b3f573fSAndroid Build Coastguard Worker         [Arguments(5)]
144*1b3f573fSAndroid Build Coastguard Worker         [Arguments(6)]
145*1b3f573fSAndroid Build Coastguard Worker         [Arguments(7)]
146*1b3f573fSAndroid Build Coastguard Worker         [Arguments(8)]
147*1b3f573fSAndroid Build Coastguard Worker         [Arguments(9)]
148*1b3f573fSAndroid Build Coastguard Worker         [Arguments(10)]
ParseRawVarint64_CodedInputStream(int encodedSize)149*1b3f573fSAndroid Build Coastguard Worker         public long ParseRawVarint64_CodedInputStream(int encodedSize)
150*1b3f573fSAndroid Build Coastguard Worker         {
151*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(varintInputBuffers[encodedSize]);
152*1b3f573fSAndroid Build Coastguard Worker             long sum = 0;
153*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
154*1b3f573fSAndroid Build Coastguard Worker             {
155*1b3f573fSAndroid Build Coastguard Worker                 sum += cis.ReadInt64();
156*1b3f573fSAndroid Build Coastguard Worker             }
157*1b3f573fSAndroid Build Coastguard Worker             return sum;
158*1b3f573fSAndroid Build Coastguard Worker         }
159*1b3f573fSAndroid Build Coastguard Worker 
160*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
161*1b3f573fSAndroid Build Coastguard Worker         [Arguments(1)]
162*1b3f573fSAndroid Build Coastguard Worker         [Arguments(2)]
163*1b3f573fSAndroid Build Coastguard Worker         [Arguments(3)]
164*1b3f573fSAndroid Build Coastguard Worker         [Arguments(4)]
165*1b3f573fSAndroid Build Coastguard Worker         [Arguments(5)]
166*1b3f573fSAndroid Build Coastguard Worker         [Arguments(6)]
167*1b3f573fSAndroid Build Coastguard Worker         [Arguments(7)]
168*1b3f573fSAndroid Build Coastguard Worker         [Arguments(8)]
169*1b3f573fSAndroid Build Coastguard Worker         [Arguments(9)]
170*1b3f573fSAndroid Build Coastguard Worker         [Arguments(10)]
ParseRawVarint64_ParseContext(int encodedSize)171*1b3f573fSAndroid Build Coastguard Worker         public long ParseRawVarint64_ParseContext(int encodedSize)
172*1b3f573fSAndroid Build Coastguard Worker         {
173*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(varintInputBuffers[encodedSize], out ParseContext ctx);
174*1b3f573fSAndroid Build Coastguard Worker             long sum = 0;
175*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
176*1b3f573fSAndroid Build Coastguard Worker             {
177*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadInt64();
178*1b3f573fSAndroid Build Coastguard Worker             }
179*1b3f573fSAndroid Build Coastguard Worker             return sum;
180*1b3f573fSAndroid Build Coastguard Worker         }
181*1b3f573fSAndroid Build Coastguard Worker 
182*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseFixed32_CodedInputStream()183*1b3f573fSAndroid Build Coastguard Worker         public uint ParseFixed32_CodedInputStream()
184*1b3f573fSAndroid Build Coastguard Worker         {
185*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(uint);
186*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(fixedIntInputBuffer);
187*1b3f573fSAndroid Build Coastguard Worker             uint sum = 0;
188*1b3f573fSAndroid Build Coastguard Worker             for (uint i = 0; i < BytesToParse / encodedSize; i++)
189*1b3f573fSAndroid Build Coastguard Worker             {
190*1b3f573fSAndroid Build Coastguard Worker                 sum += cis.ReadFixed32();
191*1b3f573fSAndroid Build Coastguard Worker             }
192*1b3f573fSAndroid Build Coastguard Worker             return sum;
193*1b3f573fSAndroid Build Coastguard Worker         }
194*1b3f573fSAndroid Build Coastguard Worker 
195*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseFixed32_ParseContext()196*1b3f573fSAndroid Build Coastguard Worker         public uint ParseFixed32_ParseContext()
197*1b3f573fSAndroid Build Coastguard Worker         {
198*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(uint);
199*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(fixedIntInputBuffer, out ParseContext ctx);
200*1b3f573fSAndroid Build Coastguard Worker             uint sum = 0;
201*1b3f573fSAndroid Build Coastguard Worker             for (uint i = 0; i < BytesToParse / encodedSize; i++)
202*1b3f573fSAndroid Build Coastguard Worker             {
203*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadFixed32();
204*1b3f573fSAndroid Build Coastguard Worker             }
205*1b3f573fSAndroid Build Coastguard Worker             return sum;
206*1b3f573fSAndroid Build Coastguard Worker         }
207*1b3f573fSAndroid Build Coastguard Worker 
208*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseFixed64_CodedInputStream()209*1b3f573fSAndroid Build Coastguard Worker         public ulong ParseFixed64_CodedInputStream()
210*1b3f573fSAndroid Build Coastguard Worker         {
211*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(ulong);
212*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(fixedIntInputBuffer);
213*1b3f573fSAndroid Build Coastguard Worker             ulong sum = 0;
214*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
215*1b3f573fSAndroid Build Coastguard Worker             {
216*1b3f573fSAndroid Build Coastguard Worker                 sum += cis.ReadFixed64();
217*1b3f573fSAndroid Build Coastguard Worker             }
218*1b3f573fSAndroid Build Coastguard Worker             return sum;
219*1b3f573fSAndroid Build Coastguard Worker         }
220*1b3f573fSAndroid Build Coastguard Worker 
221*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseFixed64_ParseContext()222*1b3f573fSAndroid Build Coastguard Worker         public ulong ParseFixed64_ParseContext()
223*1b3f573fSAndroid Build Coastguard Worker         {
224*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(ulong);
225*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(fixedIntInputBuffer, out ParseContext ctx);
226*1b3f573fSAndroid Build Coastguard Worker             ulong sum = 0;
227*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
228*1b3f573fSAndroid Build Coastguard Worker             {
229*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadFixed64();
230*1b3f573fSAndroid Build Coastguard Worker             }
231*1b3f573fSAndroid Build Coastguard Worker             return sum;
232*1b3f573fSAndroid Build Coastguard Worker         }
233*1b3f573fSAndroid Build Coastguard Worker 
234*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseRawFloat_CodedInputStream()235*1b3f573fSAndroid Build Coastguard Worker         public float ParseRawFloat_CodedInputStream()
236*1b3f573fSAndroid Build Coastguard Worker         {
237*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(float);
238*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(floatInputBuffer);
239*1b3f573fSAndroid Build Coastguard Worker             float sum = 0;
240*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
241*1b3f573fSAndroid Build Coastguard Worker             {
242*1b3f573fSAndroid Build Coastguard Worker                sum += cis.ReadFloat();
243*1b3f573fSAndroid Build Coastguard Worker             }
244*1b3f573fSAndroid Build Coastguard Worker             return sum;
245*1b3f573fSAndroid Build Coastguard Worker         }
246*1b3f573fSAndroid Build Coastguard Worker 
247*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseRawFloat_ParseContext()248*1b3f573fSAndroid Build Coastguard Worker         public float ParseRawFloat_ParseContext()
249*1b3f573fSAndroid Build Coastguard Worker         {
250*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(float);
251*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(floatInputBuffer, out ParseContext ctx);
252*1b3f573fSAndroid Build Coastguard Worker             float sum = 0;
253*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
254*1b3f573fSAndroid Build Coastguard Worker             {
255*1b3f573fSAndroid Build Coastguard Worker                sum += ctx.ReadFloat();
256*1b3f573fSAndroid Build Coastguard Worker             }
257*1b3f573fSAndroid Build Coastguard Worker             return sum;
258*1b3f573fSAndroid Build Coastguard Worker         }
259*1b3f573fSAndroid Build Coastguard Worker 
260*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseRawDouble_CodedInputStream()261*1b3f573fSAndroid Build Coastguard Worker         public double ParseRawDouble_CodedInputStream()
262*1b3f573fSAndroid Build Coastguard Worker         {
263*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(double);
264*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(doubleInputBuffer);
265*1b3f573fSAndroid Build Coastguard Worker             double sum = 0;
266*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
267*1b3f573fSAndroid Build Coastguard Worker             {
268*1b3f573fSAndroid Build Coastguard Worker                 sum += cis.ReadDouble();
269*1b3f573fSAndroid Build Coastguard Worker             }
270*1b3f573fSAndroid Build Coastguard Worker             return sum;
271*1b3f573fSAndroid Build Coastguard Worker         }
272*1b3f573fSAndroid Build Coastguard Worker 
273*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
ParseRawDouble_ParseContext()274*1b3f573fSAndroid Build Coastguard Worker         public double ParseRawDouble_ParseContext()
275*1b3f573fSAndroid Build Coastguard Worker         {
276*1b3f573fSAndroid Build Coastguard Worker             const int encodedSize = sizeof(double);
277*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(doubleInputBuffer, out ParseContext ctx);
278*1b3f573fSAndroid Build Coastguard Worker             double sum = 0;
279*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
280*1b3f573fSAndroid Build Coastguard Worker             {
281*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadDouble();
282*1b3f573fSAndroid Build Coastguard Worker             }
283*1b3f573fSAndroid Build Coastguard Worker             return sum;
284*1b3f573fSAndroid Build Coastguard Worker         }
285*1b3f573fSAndroid Build Coastguard Worker 
286*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
287*1b3f573fSAndroid Build Coastguard Worker         [ArgumentsSource(nameof(StringEncodedSizes))]
ParseString_CodedInputStream(int encodedSize)288*1b3f573fSAndroid Build Coastguard Worker         public int ParseString_CodedInputStream(int encodedSize)
289*1b3f573fSAndroid Build Coastguard Worker         {
290*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]);
291*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
292*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
293*1b3f573fSAndroid Build Coastguard Worker             {
294*1b3f573fSAndroid Build Coastguard Worker                 sum += cis.ReadString().Length;
295*1b3f573fSAndroid Build Coastguard Worker             }
296*1b3f573fSAndroid Build Coastguard Worker             return sum;
297*1b3f573fSAndroid Build Coastguard Worker         }
298*1b3f573fSAndroid Build Coastguard Worker 
299*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
300*1b3f573fSAndroid Build Coastguard Worker         [ArgumentsSource(nameof(StringEncodedSizes))]
ParseString_ParseContext(int encodedSize)301*1b3f573fSAndroid Build Coastguard Worker         public int ParseString_ParseContext(int encodedSize)
302*1b3f573fSAndroid Build Coastguard Worker         {
303*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx);
304*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
305*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
306*1b3f573fSAndroid Build Coastguard Worker             {
307*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadString().Length;
308*1b3f573fSAndroid Build Coastguard Worker             }
309*1b3f573fSAndroid Build Coastguard Worker             return sum;
310*1b3f573fSAndroid Build Coastguard Worker         }
311*1b3f573fSAndroid Build Coastguard Worker 
312*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
313*1b3f573fSAndroid Build Coastguard Worker         [ArgumentsSource(nameof(StringSegmentedEncodedSizes))]
ParseString_ParseContext_MultipleSegments(int encodedSize)314*1b3f573fSAndroid Build Coastguard Worker         public int ParseString_ParseContext_MultipleSegments(int encodedSize)
315*1b3f573fSAndroid Build Coastguard Worker         {
316*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(stringInputBuffersSegmented[encodedSize], out ParseContext ctx);
317*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
318*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
319*1b3f573fSAndroid Build Coastguard Worker             {
320*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadString().Length;
321*1b3f573fSAndroid Build Coastguard Worker             }
322*1b3f573fSAndroid Build Coastguard Worker             return sum;
323*1b3f573fSAndroid Build Coastguard Worker         }
324*1b3f573fSAndroid Build Coastguard Worker 
325*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
326*1b3f573fSAndroid Build Coastguard Worker         [ArgumentsSource(nameof(StringEncodedSizes))]
ParseBytes_CodedInputStream(int encodedSize)327*1b3f573fSAndroid Build Coastguard Worker         public int ParseBytes_CodedInputStream(int encodedSize)
328*1b3f573fSAndroid Build Coastguard Worker         {
329*1b3f573fSAndroid Build Coastguard Worker             CodedInputStream cis = new CodedInputStream(stringInputBuffers[encodedSize]);
330*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
331*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
332*1b3f573fSAndroid Build Coastguard Worker             {
333*1b3f573fSAndroid Build Coastguard Worker                 sum += cis.ReadBytes().Length;
334*1b3f573fSAndroid Build Coastguard Worker             }
335*1b3f573fSAndroid Build Coastguard Worker             return sum;
336*1b3f573fSAndroid Build Coastguard Worker         }
337*1b3f573fSAndroid Build Coastguard Worker 
338*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
339*1b3f573fSAndroid Build Coastguard Worker         [ArgumentsSource(nameof(StringEncodedSizes))]
ParseBytes_ParseContext(int encodedSize)340*1b3f573fSAndroid Build Coastguard Worker         public int ParseBytes_ParseContext(int encodedSize)
341*1b3f573fSAndroid Build Coastguard Worker         {
342*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(stringInputBuffers[encodedSize], out ParseContext ctx);
343*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
344*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
345*1b3f573fSAndroid Build Coastguard Worker             {
346*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadBytes().Length;
347*1b3f573fSAndroid Build Coastguard Worker             }
348*1b3f573fSAndroid Build Coastguard Worker             return sum;
349*1b3f573fSAndroid Build Coastguard Worker         }
350*1b3f573fSAndroid Build Coastguard Worker 
351*1b3f573fSAndroid Build Coastguard Worker         [Benchmark]
352*1b3f573fSAndroid Build Coastguard Worker         [ArgumentsSource(nameof(StringSegmentedEncodedSizes))]
ParseBytes_ParseContext_MultipleSegments(int encodedSize)353*1b3f573fSAndroid Build Coastguard Worker         public int ParseBytes_ParseContext_MultipleSegments(int encodedSize)
354*1b3f573fSAndroid Build Coastguard Worker         {
355*1b3f573fSAndroid Build Coastguard Worker             InitializeParseContext(stringInputBuffersSegmented[encodedSize], out ParseContext ctx);
356*1b3f573fSAndroid Build Coastguard Worker             int sum = 0;
357*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < BytesToParse / encodedSize; i++)
358*1b3f573fSAndroid Build Coastguard Worker             {
359*1b3f573fSAndroid Build Coastguard Worker                 sum += ctx.ReadBytes().Length;
360*1b3f573fSAndroid Build Coastguard Worker             }
361*1b3f573fSAndroid Build Coastguard Worker             return sum;
362*1b3f573fSAndroid Build Coastguard Worker         }
363*1b3f573fSAndroid Build Coastguard Worker 
InitializeParseContext(byte[] buffer, out ParseContext ctx)364*1b3f573fSAndroid Build Coastguard Worker         private static void InitializeParseContext(byte[] buffer, out ParseContext ctx)
365*1b3f573fSAndroid Build Coastguard Worker         {
366*1b3f573fSAndroid Build Coastguard Worker             ParseContext.Initialize(new ReadOnlySequence<byte>(buffer), out ctx);
367*1b3f573fSAndroid Build Coastguard Worker         }
368*1b3f573fSAndroid Build Coastguard Worker 
InitializeParseContext(ReadOnlySequence<byte> buffer, out ParseContext ctx)369*1b3f573fSAndroid Build Coastguard Worker         private static void InitializeParseContext(ReadOnlySequence<byte> buffer, out ParseContext ctx)
370*1b3f573fSAndroid Build Coastguard Worker         {
371*1b3f573fSAndroid Build Coastguard Worker             ParseContext.Initialize(buffer, out ctx);
372*1b3f573fSAndroid Build Coastguard Worker         }
373*1b3f573fSAndroid Build Coastguard Worker 
CreateBufferWithRandomVarints(Random random, int valueCount, int encodedSize, int paddingValueCount)374*1b3f573fSAndroid Build Coastguard Worker         private static byte[] CreateBufferWithRandomVarints(Random random, int valueCount, int encodedSize, int paddingValueCount)
375*1b3f573fSAndroid Build Coastguard Worker         {
376*1b3f573fSAndroid Build Coastguard Worker             MemoryStream ms = new MemoryStream();
377*1b3f573fSAndroid Build Coastguard Worker             CodedOutputStream cos = new CodedOutputStream(ms);
378*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < valueCount + paddingValueCount; i++)
379*1b3f573fSAndroid Build Coastguard Worker             {
380*1b3f573fSAndroid Build Coastguard Worker                 cos.WriteUInt64(RandomUnsignedVarint(random, encodedSize, false));
381*1b3f573fSAndroid Build Coastguard Worker             }
382*1b3f573fSAndroid Build Coastguard Worker             cos.Flush();
383*1b3f573fSAndroid Build Coastguard Worker             var buffer = ms.ToArray();
384*1b3f573fSAndroid Build Coastguard Worker 
385*1b3f573fSAndroid Build Coastguard Worker             if (buffer.Length != encodedSize * (valueCount + paddingValueCount))
386*1b3f573fSAndroid Build Coastguard Worker             {
387*1b3f573fSAndroid Build Coastguard Worker                 throw new InvalidOperationException($"Unexpected output buffer length {buffer.Length}");
388*1b3f573fSAndroid Build Coastguard Worker             }
389*1b3f573fSAndroid Build Coastguard Worker             return buffer;
390*1b3f573fSAndroid Build Coastguard Worker         }
391*1b3f573fSAndroid Build Coastguard Worker 
CreateBufferWithRandomFloats(Random random, int valueCount, int paddingValueCount)392*1b3f573fSAndroid Build Coastguard Worker         private static byte[] CreateBufferWithRandomFloats(Random random, int valueCount, int paddingValueCount)
393*1b3f573fSAndroid Build Coastguard Worker         {
394*1b3f573fSAndroid Build Coastguard Worker             MemoryStream ms = new MemoryStream();
395*1b3f573fSAndroid Build Coastguard Worker             CodedOutputStream cos = new CodedOutputStream(ms);
396*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < valueCount + paddingValueCount; i++)
397*1b3f573fSAndroid Build Coastguard Worker             {
398*1b3f573fSAndroid Build Coastguard Worker                 cos.WriteFloat((float)random.NextDouble());
399*1b3f573fSAndroid Build Coastguard Worker             }
400*1b3f573fSAndroid Build Coastguard Worker             cos.Flush();
401*1b3f573fSAndroid Build Coastguard Worker             var buffer = ms.ToArray();
402*1b3f573fSAndroid Build Coastguard Worker             return buffer;
403*1b3f573fSAndroid Build Coastguard Worker         }
404*1b3f573fSAndroid Build Coastguard Worker 
CreateBufferWithRandomDoubles(Random random, int valueCount, int paddingValueCount)405*1b3f573fSAndroid Build Coastguard Worker         private static byte[] CreateBufferWithRandomDoubles(Random random, int valueCount, int paddingValueCount)
406*1b3f573fSAndroid Build Coastguard Worker         {
407*1b3f573fSAndroid Build Coastguard Worker             MemoryStream ms = new MemoryStream();
408*1b3f573fSAndroid Build Coastguard Worker             CodedOutputStream cos = new CodedOutputStream(ms);
409*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < valueCount + paddingValueCount; i++)
410*1b3f573fSAndroid Build Coastguard Worker             {
411*1b3f573fSAndroid Build Coastguard Worker                 cos.WriteDouble(random.NextDouble());
412*1b3f573fSAndroid Build Coastguard Worker             }
413*1b3f573fSAndroid Build Coastguard Worker             cos.Flush();
414*1b3f573fSAndroid Build Coastguard Worker             var buffer = ms.ToArray();
415*1b3f573fSAndroid Build Coastguard Worker             return buffer;
416*1b3f573fSAndroid Build Coastguard Worker         }
417*1b3f573fSAndroid Build Coastguard Worker 
CreateBufferWithRandomData(Random random, int valueCount, int encodedSize, int paddingValueCount)418*1b3f573fSAndroid Build Coastguard Worker         private static byte[] CreateBufferWithRandomData(Random random, int valueCount, int encodedSize, int paddingValueCount)
419*1b3f573fSAndroid Build Coastguard Worker         {
420*1b3f573fSAndroid Build Coastguard Worker             int bufferSize = (valueCount + paddingValueCount) * encodedSize;
421*1b3f573fSAndroid Build Coastguard Worker             byte[] buffer = new byte[bufferSize];
422*1b3f573fSAndroid Build Coastguard Worker             random.NextBytes(buffer);
423*1b3f573fSAndroid Build Coastguard Worker             return buffer;
424*1b3f573fSAndroid Build Coastguard Worker         }
425*1b3f573fSAndroid Build Coastguard Worker 
426*1b3f573fSAndroid Build Coastguard Worker         /// <summary>
427*1b3f573fSAndroid Build Coastguard Worker         /// Generate a random value that will take exactly "encodedSize" bytes when varint-encoded.
428*1b3f573fSAndroid Build Coastguard Worker         /// </summary>
RandomUnsignedVarint(Random random, int encodedSize, bool fitsIn32Bits)429*1b3f573fSAndroid Build Coastguard Worker         public static ulong RandomUnsignedVarint(Random random, int encodedSize, bool fitsIn32Bits)
430*1b3f573fSAndroid Build Coastguard Worker         {
431*1b3f573fSAndroid Build Coastguard Worker             Span<byte> randomBytesBuffer = stackalloc byte[8];
432*1b3f573fSAndroid Build Coastguard Worker 
433*1b3f573fSAndroid Build Coastguard Worker             if (encodedSize < 1 || encodedSize > 10 || (fitsIn32Bits && encodedSize > 5))
434*1b3f573fSAndroid Build Coastguard Worker             {
435*1b3f573fSAndroid Build Coastguard Worker                 throw new ArgumentException("Illegal encodedSize value requested", nameof(encodedSize));
436*1b3f573fSAndroid Build Coastguard Worker             }
437*1b3f573fSAndroid Build Coastguard Worker             const int bitsPerByte = 7;
438*1b3f573fSAndroid Build Coastguard Worker 
439*1b3f573fSAndroid Build Coastguard Worker             ulong result = 0;
440*1b3f573fSAndroid Build Coastguard Worker             while (true)
441*1b3f573fSAndroid Build Coastguard Worker             {
442*1b3f573fSAndroid Build Coastguard Worker                 random.NextBytes(randomBytesBuffer);
443*1b3f573fSAndroid Build Coastguard Worker                 ulong randomValue = BinaryPrimitives.ReadUInt64LittleEndian(randomBytesBuffer);
444*1b3f573fSAndroid Build Coastguard Worker 
445*1b3f573fSAndroid Build Coastguard Worker                 // only use the number of random bits we need
446*1b3f573fSAndroid Build Coastguard Worker                 ulong bitmask = encodedSize < 10 ? ((1UL << (encodedSize * bitsPerByte)) - 1) : ulong.MaxValue;
447*1b3f573fSAndroid Build Coastguard Worker                 result = randomValue & bitmask;
448*1b3f573fSAndroid Build Coastguard Worker 
449*1b3f573fSAndroid Build Coastguard Worker                 if (fitsIn32Bits)
450*1b3f573fSAndroid Build Coastguard Worker                 {
451*1b3f573fSAndroid Build Coastguard Worker                     // make sure the resulting value is representable by a uint.
452*1b3f573fSAndroid Build Coastguard Worker                     result &= uint.MaxValue;
453*1b3f573fSAndroid Build Coastguard Worker                 }
454*1b3f573fSAndroid Build Coastguard Worker 
455*1b3f573fSAndroid Build Coastguard Worker                 if (encodedSize == 10)
456*1b3f573fSAndroid Build Coastguard Worker                 {
457*1b3f573fSAndroid Build Coastguard Worker                     // for 10-byte values the highest bit always needs to be set (7*9=63)
458*1b3f573fSAndroid Build Coastguard Worker                     result |= ulong.MaxValue;
459*1b3f573fSAndroid Build Coastguard Worker                     break;
460*1b3f573fSAndroid Build Coastguard Worker                 }
461*1b3f573fSAndroid Build Coastguard Worker 
462*1b3f573fSAndroid Build Coastguard Worker                 // some random values won't require the full "encodedSize" bytes, check that at least
463*1b3f573fSAndroid Build Coastguard Worker                 // one of the top 7 bits is set. Retrying is fine since it only happens rarely
464*1b3f573fSAndroid Build Coastguard Worker                 if (encodedSize == 1 || (result & (0x7FUL << ((encodedSize - 1) * bitsPerByte))) != 0)
465*1b3f573fSAndroid Build Coastguard Worker                 {
466*1b3f573fSAndroid Build Coastguard Worker                     break;
467*1b3f573fSAndroid Build Coastguard Worker                 }
468*1b3f573fSAndroid Build Coastguard Worker             }
469*1b3f573fSAndroid Build Coastguard Worker             return result;
470*1b3f573fSAndroid Build Coastguard Worker         }
471*1b3f573fSAndroid Build Coastguard Worker 
CreateBufferWithStrings(int valueCount, int encodedSize, int paddingValueCount)472*1b3f573fSAndroid Build Coastguard Worker         private static byte[] CreateBufferWithStrings(int valueCount, int encodedSize, int paddingValueCount)
473*1b3f573fSAndroid Build Coastguard Worker         {
474*1b3f573fSAndroid Build Coastguard Worker             var str = CreateStringWithEncodedSize(encodedSize);
475*1b3f573fSAndroid Build Coastguard Worker 
476*1b3f573fSAndroid Build Coastguard Worker             MemoryStream ms = new MemoryStream();
477*1b3f573fSAndroid Build Coastguard Worker             CodedOutputStream cos = new CodedOutputStream(ms);
478*1b3f573fSAndroid Build Coastguard Worker             for (int i = 0; i < valueCount + paddingValueCount; i++)
479*1b3f573fSAndroid Build Coastguard Worker             {
480*1b3f573fSAndroid Build Coastguard Worker                 cos.WriteString(str);
481*1b3f573fSAndroid Build Coastguard Worker             }
482*1b3f573fSAndroid Build Coastguard Worker             cos.Flush();
483*1b3f573fSAndroid Build Coastguard Worker             var buffer = ms.ToArray();
484*1b3f573fSAndroid Build Coastguard Worker 
485*1b3f573fSAndroid Build Coastguard Worker             if (buffer.Length != encodedSize * (valueCount + paddingValueCount))
486*1b3f573fSAndroid Build Coastguard Worker             {
487*1b3f573fSAndroid Build Coastguard Worker                 throw new InvalidOperationException($"Unexpected output buffer length {buffer.Length}");
488*1b3f573fSAndroid Build Coastguard Worker             }
489*1b3f573fSAndroid Build Coastguard Worker             return buffer;
490*1b3f573fSAndroid Build Coastguard Worker         }
491*1b3f573fSAndroid Build Coastguard Worker 
CreateStringWithEncodedSize(int encodedSize)492*1b3f573fSAndroid Build Coastguard Worker         public static string CreateStringWithEncodedSize(int encodedSize)
493*1b3f573fSAndroid Build Coastguard Worker         {
494*1b3f573fSAndroid Build Coastguard Worker             var str = new string('a', encodedSize);
495*1b3f573fSAndroid Build Coastguard Worker             while (CodedOutputStream.ComputeStringSize(str) > encodedSize)
496*1b3f573fSAndroid Build Coastguard Worker             {
497*1b3f573fSAndroid Build Coastguard Worker                 str = str.Substring(1);
498*1b3f573fSAndroid Build Coastguard Worker             }
499*1b3f573fSAndroid Build Coastguard Worker 
500*1b3f573fSAndroid Build Coastguard Worker             if (CodedOutputStream.ComputeStringSize(str) != encodedSize)
501*1b3f573fSAndroid Build Coastguard Worker             {
502*1b3f573fSAndroid Build Coastguard Worker                 throw new InvalidOperationException($"Generated string with wrong encodedSize");
503*1b3f573fSAndroid Build Coastguard Worker             }
504*1b3f573fSAndroid Build Coastguard Worker             return str;
505*1b3f573fSAndroid Build Coastguard Worker         }
506*1b3f573fSAndroid Build Coastguard Worker 
CreateNonAsciiStringWithEncodedSize(int encodedSize)507*1b3f573fSAndroid Build Coastguard Worker         public static string CreateNonAsciiStringWithEncodedSize(int encodedSize)
508*1b3f573fSAndroid Build Coastguard Worker         {
509*1b3f573fSAndroid Build Coastguard Worker             if (encodedSize < 3)
510*1b3f573fSAndroid Build Coastguard Worker             {
511*1b3f573fSAndroid Build Coastguard Worker                 throw new ArgumentException("Illegal encoded size for a string with non-ascii chars.");
512*1b3f573fSAndroid Build Coastguard Worker             }
513*1b3f573fSAndroid Build Coastguard Worker             var twoByteChar = '\u00DC';  // U-umlaut, UTF8 encoding has 2 bytes
514*1b3f573fSAndroid Build Coastguard Worker             var str = new string(twoByteChar, encodedSize / 2);
515*1b3f573fSAndroid Build Coastguard Worker             while (CodedOutputStream.ComputeStringSize(str) > encodedSize)
516*1b3f573fSAndroid Build Coastguard Worker             {
517*1b3f573fSAndroid Build Coastguard Worker                 str = str.Substring(1);
518*1b3f573fSAndroid Build Coastguard Worker             }
519*1b3f573fSAndroid Build Coastguard Worker 
520*1b3f573fSAndroid Build Coastguard Worker             // add padding of ascii characters to reach the desired encoded size.
521*1b3f573fSAndroid Build Coastguard Worker             while (CodedOutputStream.ComputeStringSize(str) < encodedSize)
522*1b3f573fSAndroid Build Coastguard Worker             {
523*1b3f573fSAndroid Build Coastguard Worker                 str += 'a';
524*1b3f573fSAndroid Build Coastguard Worker             }
525*1b3f573fSAndroid Build Coastguard Worker 
526*1b3f573fSAndroid Build Coastguard Worker             // Note that for a few specific encodedSize values, it might be impossible to generate
527*1b3f573fSAndroid Build Coastguard Worker             // the string with the desired encodedSize using the algorithm above. For testing purposes, checking that
528*1b3f573fSAndroid Build Coastguard Worker             // the encoded size we got is actually correct is good enough.
529*1b3f573fSAndroid Build Coastguard Worker             if (CodedOutputStream.ComputeStringSize(str) != encodedSize)
530*1b3f573fSAndroid Build Coastguard Worker             {
531*1b3f573fSAndroid Build Coastguard Worker                 throw new InvalidOperationException($"Generated string with wrong encodedSize");
532*1b3f573fSAndroid Build Coastguard Worker             }
533*1b3f573fSAndroid Build Coastguard Worker             return str;
534*1b3f573fSAndroid Build Coastguard Worker         }
535*1b3f573fSAndroid Build Coastguard Worker     }
536*1b3f573fSAndroid Build Coastguard Worker }
537