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