1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2008 Google Inc. All rights reserved. 4 // https://developers.google.com/protocol-buffers/ 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 #endregion 32 33 using System; 34 using System.IO; 35 using Google.Protobuf.TestProtos; 36 using NUnit.Framework; 37 38 namespace Google.Protobuf 39 { 40 public class CodedOutputStreamTest 41 { 42 /// <summary> 43 /// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and 44 /// checks that the result matches the given bytes 45 /// </summary> AssertWriteVarint(byte[] data, ulong value)46 private static void AssertWriteVarint(byte[] data, ulong value) 47 { 48 // Only do 32-bit write if the value fits in 32 bits. 49 if ((value >> 32) == 0) 50 { 51 MemoryStream rawOutput = new MemoryStream(); 52 CodedOutputStream output = new CodedOutputStream(rawOutput); 53 output.WriteRawVarint32((uint) value); 54 output.Flush(); 55 Assert.AreEqual(data, rawOutput.ToArray()); 56 // Also try computing size. 57 Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value)); 58 } 59 60 { 61 MemoryStream rawOutput = new MemoryStream(); 62 CodedOutputStream output = new CodedOutputStream(rawOutput); 63 output.WriteRawVarint64(value); 64 output.Flush(); 65 Assert.AreEqual(data, rawOutput.ToArray()); 66 67 // Also try computing size. 68 Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value)); 69 } 70 71 // Try different buffer sizes. 72 for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2) 73 { 74 // Only do 32-bit write if the value fits in 32 bits. 75 if ((value >> 32) == 0) 76 { 77 MemoryStream rawOutput = new MemoryStream(); 78 CodedOutputStream output = 79 new CodedOutputStream(rawOutput, bufferSize); 80 output.WriteRawVarint32((uint) value); 81 output.Flush(); 82 Assert.AreEqual(data, rawOutput.ToArray()); 83 } 84 85 { 86 MemoryStream rawOutput = new MemoryStream(); 87 CodedOutputStream output = new CodedOutputStream(rawOutput, bufferSize); 88 output.WriteRawVarint64(value); 89 output.Flush(); 90 Assert.AreEqual(data, rawOutput.ToArray()); 91 } 92 } 93 } 94 95 /// <summary> 96 /// Tests WriteRawVarint32() and WriteRawVarint64() 97 /// </summary> 98 [Test] WriteVarint()99 public void WriteVarint() 100 { 101 AssertWriteVarint(new byte[] {0x00}, 0); 102 AssertWriteVarint(new byte[] {0x01}, 1); 103 AssertWriteVarint(new byte[] {0x7f}, 127); 104 // 14882 105 AssertWriteVarint(new byte[] {0xa2, 0x74}, (0x22 << 0) | (0x74 << 7)); 106 // 2961488830 107 AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x0b}, 108 (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | 109 (0x0bL << 28)); 110 111 // 64-bit 112 // 7256456126 113 AssertWriteVarint(new byte[] {0xbe, 0xf7, 0x92, 0x84, 0x1b}, 114 (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | 115 (0x1bL << 28)); 116 // 41256202580718336 117 AssertWriteVarint( 118 new byte[] {0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49}, 119 (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | 120 (0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49)); 121 // 11964378330978735131 122 AssertWriteVarint( 123 new byte[] {0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01}, 124 unchecked((ulong) 125 ((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | 126 (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) | 127 (0x05L << 49) | (0x26L << 56) | (0x01L << 63)))); 128 } 129 130 /// <summary> 131 /// Parses the given bytes using WriteRawLittleEndian32() and checks 132 /// that the result matches the given value. 133 /// </summary> AssertWriteLittleEndian32(byte[] data, uint value)134 private static void AssertWriteLittleEndian32(byte[] data, uint value) 135 { 136 MemoryStream rawOutput = new MemoryStream(); 137 CodedOutputStream output = new CodedOutputStream(rawOutput); 138 output.WriteRawLittleEndian32(value); 139 output.Flush(); 140 Assert.AreEqual(data, rawOutput.ToArray()); 141 142 // Try different buffer sizes. 143 for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2) 144 { 145 rawOutput = new MemoryStream(); 146 output = new CodedOutputStream(rawOutput, bufferSize); 147 output.WriteRawLittleEndian32(value); 148 output.Flush(); 149 Assert.AreEqual(data, rawOutput.ToArray()); 150 } 151 } 152 153 /// <summary> 154 /// Parses the given bytes using WriteRawLittleEndian64() and checks 155 /// that the result matches the given value. 156 /// </summary> AssertWriteLittleEndian64(byte[] data, ulong value)157 private static void AssertWriteLittleEndian64(byte[] data, ulong value) 158 { 159 MemoryStream rawOutput = new MemoryStream(); 160 CodedOutputStream output = new CodedOutputStream(rawOutput); 161 output.WriteRawLittleEndian64(value); 162 output.Flush(); 163 Assert.AreEqual(data, rawOutput.ToArray()); 164 165 // Try different block sizes. 166 for (int blockSize = 1; blockSize <= 16; blockSize *= 2) 167 { 168 rawOutput = new MemoryStream(); 169 output = new CodedOutputStream(rawOutput, blockSize); 170 output.WriteRawLittleEndian64(value); 171 output.Flush(); 172 Assert.AreEqual(data, rawOutput.ToArray()); 173 } 174 } 175 176 /// <summary> 177 /// Tests writeRawLittleEndian32() and writeRawLittleEndian64(). 178 /// </summary> 179 [Test] WriteLittleEndian()180 public void WriteLittleEndian() 181 { 182 AssertWriteLittleEndian32(new byte[] {0x78, 0x56, 0x34, 0x12}, 0x12345678); 183 AssertWriteLittleEndian32(new byte[] {0xf0, 0xde, 0xbc, 0x9a}, 0x9abcdef0); 184 185 AssertWriteLittleEndian64( 186 new byte[] {0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12}, 187 0x123456789abcdef0L); 188 AssertWriteLittleEndian64( 189 new byte[] {0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a}, 190 0x9abcdef012345678UL); 191 } 192 193 [Test] WriteWholeMessage_VaryingBlockSizes()194 public void WriteWholeMessage_VaryingBlockSizes() 195 { 196 TestAllTypes message = SampleMessages.CreateFullTestAllTypes(); 197 198 byte[] rawBytes = message.ToByteArray(); 199 200 // Try different block sizes. 201 for (int blockSize = 1; blockSize < 256; blockSize *= 2) 202 { 203 MemoryStream rawOutput = new MemoryStream(); 204 CodedOutputStream output = new CodedOutputStream(rawOutput, blockSize); 205 message.WriteTo(output); 206 output.Flush(); 207 Assert.AreEqual(rawBytes, rawOutput.ToArray()); 208 } 209 } 210 211 [Test] EncodeZigZag32()212 public void EncodeZigZag32() 213 { 214 Assert.AreEqual(0u, CodedOutputStream.EncodeZigZag32(0)); 215 Assert.AreEqual(1u, CodedOutputStream.EncodeZigZag32(-1)); 216 Assert.AreEqual(2u, CodedOutputStream.EncodeZigZag32(1)); 217 Assert.AreEqual(3u, CodedOutputStream.EncodeZigZag32(-2)); 218 Assert.AreEqual(0x7FFFFFFEu, CodedOutputStream.EncodeZigZag32(0x3FFFFFFF)); 219 Assert.AreEqual(0x7FFFFFFFu, CodedOutputStream.EncodeZigZag32(unchecked((int) 0xC0000000))); 220 Assert.AreEqual(0xFFFFFFFEu, CodedOutputStream.EncodeZigZag32(0x7FFFFFFF)); 221 Assert.AreEqual(0xFFFFFFFFu, CodedOutputStream.EncodeZigZag32(unchecked((int) 0x80000000))); 222 } 223 224 [Test] EncodeZigZag64()225 public void EncodeZigZag64() 226 { 227 Assert.AreEqual(0u, CodedOutputStream.EncodeZigZag64(0)); 228 Assert.AreEqual(1u, CodedOutputStream.EncodeZigZag64(-1)); 229 Assert.AreEqual(2u, CodedOutputStream.EncodeZigZag64(1)); 230 Assert.AreEqual(3u, CodedOutputStream.EncodeZigZag64(-2)); 231 Assert.AreEqual(0x000000007FFFFFFEuL, 232 CodedOutputStream.EncodeZigZag64(unchecked((long) 0x000000003FFFFFFFUL))); 233 Assert.AreEqual(0x000000007FFFFFFFuL, 234 CodedOutputStream.EncodeZigZag64(unchecked((long) 0xFFFFFFFFC0000000UL))); 235 Assert.AreEqual(0x00000000FFFFFFFEuL, 236 CodedOutputStream.EncodeZigZag64(unchecked((long) 0x000000007FFFFFFFUL))); 237 Assert.AreEqual(0x00000000FFFFFFFFuL, 238 CodedOutputStream.EncodeZigZag64(unchecked((long) 0xFFFFFFFF80000000UL))); 239 Assert.AreEqual(0xFFFFFFFFFFFFFFFEL, 240 CodedOutputStream.EncodeZigZag64(unchecked((long) 0x7FFFFFFFFFFFFFFFUL))); 241 Assert.AreEqual(0xFFFFFFFFFFFFFFFFL, 242 CodedOutputStream.EncodeZigZag64(unchecked((long) 0x8000000000000000UL))); 243 } 244 245 [Test] RoundTripZigZag32()246 public void RoundTripZigZag32() 247 { 248 // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1) 249 // were chosen semi-randomly via keyboard bashing. 250 Assert.AreEqual(0, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(0))); 251 Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(1))); 252 Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-1))); 253 Assert.AreEqual(14927, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(14927))); 254 Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-3612))); 255 } 256 257 [Test] RoundTripZigZag64()258 public void RoundTripZigZag64() 259 { 260 Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(0))); 261 Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(1))); 262 Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-1))); 263 Assert.AreEqual(14927, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(14927))); 264 Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-3612))); 265 266 Assert.AreEqual(856912304801416L, 267 CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L))); 268 Assert.AreEqual(-75123905439571256L, 269 CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L))); 270 } 271 272 [Test] TestNegativeEnumNoTag()273 public void TestNegativeEnumNoTag() 274 { 275 Assert.AreEqual(10, CodedOutputStream.ComputeInt32Size(-2)); 276 Assert.AreEqual(10, CodedOutputStream.ComputeEnumSize((int) SampleEnum.NegativeValue)); 277 278 byte[] bytes = new byte[10]; 279 CodedOutputStream output = new CodedOutputStream(bytes); 280 output.WriteEnum((int) SampleEnum.NegativeValue); 281 282 Assert.AreEqual(0, output.SpaceLeft); 283 Assert.AreEqual("FE-FF-FF-FF-FF-FF-FF-FF-FF-01", BitConverter.ToString(bytes)); 284 } 285 286 [Test] TestCodedInputOutputPosition()287 public void TestCodedInputOutputPosition() 288 { 289 byte[] content = new byte[110]; 290 for (int i = 0; i < content.Length; i++) 291 content[i] = (byte)i; 292 293 byte[] child = new byte[120]; 294 { 295 MemoryStream ms = new MemoryStream(child); 296 CodedOutputStream cout = new CodedOutputStream(ms, 20); 297 // Field 11: numeric value: 500 298 cout.WriteTag(11, WireFormat.WireType.Varint); 299 Assert.AreEqual(1, cout.Position); 300 cout.WriteInt32(500); 301 Assert.AreEqual(3, cout.Position); 302 //Field 12: length delimited 120 bytes 303 cout.WriteTag(12, WireFormat.WireType.LengthDelimited); 304 Assert.AreEqual(4, cout.Position); 305 cout.WriteBytes(ByteString.CopyFrom(content)); 306 Assert.AreEqual(115, cout.Position); 307 // Field 13: fixed numeric value: 501 308 cout.WriteTag(13, WireFormat.WireType.Fixed32); 309 Assert.AreEqual(116, cout.Position); 310 cout.WriteSFixed32(501); 311 Assert.AreEqual(120, cout.Position); 312 cout.Flush(); 313 } 314 315 byte[] bytes = new byte[130]; 316 { 317 CodedOutputStream cout = new CodedOutputStream(bytes); 318 // Field 1: numeric value: 500 319 cout.WriteTag(1, WireFormat.WireType.Varint); 320 Assert.AreEqual(1, cout.Position); 321 cout.WriteInt32(500); 322 Assert.AreEqual(3, cout.Position); 323 //Field 2: length delimited 120 bytes 324 cout.WriteTag(2, WireFormat.WireType.LengthDelimited); 325 Assert.AreEqual(4, cout.Position); 326 cout.WriteBytes(ByteString.CopyFrom(child)); 327 Assert.AreEqual(125, cout.Position); 328 // Field 3: fixed numeric value: 500 329 cout.WriteTag(3, WireFormat.WireType.Fixed32); 330 Assert.AreEqual(126, cout.Position); 331 cout.WriteSFixed32(501); 332 Assert.AreEqual(130, cout.Position); 333 cout.Flush(); 334 } 335 // Now test Input stream: 336 { 337 CodedInputStream cin = new CodedInputStream(new MemoryStream(bytes), new byte[50], 0, 0); 338 Assert.AreEqual(0, cin.Position); 339 // Field 1: 340 uint tag = cin.ReadTag(); 341 Assert.AreEqual(1, tag >> 3); 342 Assert.AreEqual(1, cin.Position); 343 Assert.AreEqual(500, cin.ReadInt32()); 344 Assert.AreEqual(3, cin.Position); 345 //Field 2: 346 tag = cin.ReadTag(); 347 Assert.AreEqual(2, tag >> 3); 348 Assert.AreEqual(4, cin.Position); 349 int childlen = cin.ReadLength(); 350 Assert.AreEqual(120, childlen); 351 Assert.AreEqual(5, cin.Position); 352 int oldlimit = cin.PushLimit((int)childlen); 353 Assert.AreEqual(5, cin.Position); 354 // Now we are reading child message 355 { 356 // Field 11: numeric value: 500 357 tag = cin.ReadTag(); 358 Assert.AreEqual(11, tag >> 3); 359 Assert.AreEqual(6, cin.Position); 360 Assert.AreEqual(500, cin.ReadInt32()); 361 Assert.AreEqual(8, cin.Position); 362 //Field 12: length delimited 120 bytes 363 tag = cin.ReadTag(); 364 Assert.AreEqual(12, tag >> 3); 365 Assert.AreEqual(9, cin.Position); 366 ByteString bstr = cin.ReadBytes(); 367 Assert.AreEqual(110, bstr.Length); 368 Assert.AreEqual((byte) 109, bstr[109]); 369 Assert.AreEqual(120, cin.Position); 370 // Field 13: fixed numeric value: 501 371 tag = cin.ReadTag(); 372 Assert.AreEqual(13, tag >> 3); 373 // ROK - Previously broken here, this returned 126 failing to account for bufferSizeAfterLimit 374 Assert.AreEqual(121, cin.Position); 375 Assert.AreEqual(501, cin.ReadSFixed32()); 376 Assert.AreEqual(125, cin.Position); 377 Assert.IsTrue(cin.IsAtEnd); 378 } 379 cin.PopLimit(oldlimit); 380 Assert.AreEqual(125, cin.Position); 381 // Field 3: fixed numeric value: 501 382 tag = cin.ReadTag(); 383 Assert.AreEqual(3, tag >> 3); 384 Assert.AreEqual(126, cin.Position); 385 Assert.AreEqual(501, cin.ReadSFixed32()); 386 Assert.AreEqual(130, cin.Position); 387 Assert.IsTrue(cin.IsAtEnd); 388 } 389 } 390 391 [Test] Dispose_DisposesUnderlyingStream()392 public void Dispose_DisposesUnderlyingStream() 393 { 394 var memoryStream = new MemoryStream(); 395 Assert.IsTrue(memoryStream.CanWrite); 396 using (var cos = new CodedOutputStream(memoryStream)) 397 { 398 cos.WriteRawByte(0); 399 Assert.AreEqual(0, memoryStream.Position); // Not flushed yet 400 } 401 Assert.AreEqual(1, memoryStream.ToArray().Length); // Flushed data from CodedOutputStream to MemoryStream 402 Assert.IsFalse(memoryStream.CanWrite); // Disposed 403 } 404 405 [Test] Dispose_WithLeaveOpen()406 public void Dispose_WithLeaveOpen() 407 { 408 var memoryStream = new MemoryStream(); 409 Assert.IsTrue(memoryStream.CanWrite); 410 using (var cos = new CodedOutputStream(memoryStream, true)) 411 { 412 cos.WriteRawByte(0); 413 Assert.AreEqual(0, memoryStream.Position); // Not flushed yet 414 } 415 Assert.AreEqual(1, memoryStream.Position); // Flushed data from CodedOutputStream to MemoryStream 416 Assert.IsTrue(memoryStream.CanWrite); // We left the stream open 417 } 418 } 419 }