ByteStringTest.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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. using System;
  33. using System.Text;
  34. using NUnit.Framework;
  35. using System.IO;
  36. using System.Collections.Generic;
  37. using System.Collections;
  38. using System.Linq;
  39. using System.Buffers;
  40. using System.Runtime.InteropServices;
  41. using System.Threading;
  42. using System.Runtime.CompilerServices;
  43. #if !NET35
  44. using System.Threading.Tasks;
  45. #endif
  46. namespace Google.Protobuf
  47. {
  48. public class ByteStringTest
  49. {
  50. [Test]
  51. public void Equality()
  52. {
  53. ByteString b1 = ByteString.CopyFrom(1, 2, 3);
  54. ByteString b2 = ByteString.CopyFrom(1, 2, 3);
  55. ByteString b3 = ByteString.CopyFrom(1, 2, 4);
  56. ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
  57. EqualityTester.AssertEquality(b1, b1);
  58. EqualityTester.AssertEquality(b1, b2);
  59. EqualityTester.AssertInequality(b1, b3);
  60. EqualityTester.AssertInequality(b1, b4);
  61. EqualityTester.AssertInequality(b1, null);
  62. EqualityTester.AssertEquality(ByteString.Empty, ByteString.Empty);
  63. #pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
  64. Assert.IsTrue(b1 == b1);
  65. Assert.IsTrue(b1 == b2);
  66. Assert.IsFalse(b1 == b3);
  67. Assert.IsFalse(b1 == b4);
  68. Assert.IsFalse(b1 == null);
  69. Assert.IsTrue((ByteString) null == null);
  70. Assert.IsFalse(b1 != b1);
  71. Assert.IsFalse(b1 != b2);
  72. Assert.IsTrue(ByteString.Empty == ByteString.Empty);
  73. #pragma warning disable 1718
  74. Assert.IsTrue(b1 != b3);
  75. Assert.IsTrue(b1 != b4);
  76. Assert.IsTrue(b1 != null);
  77. Assert.IsFalse((ByteString) null != null);
  78. }
  79. [Test]
  80. public void EmptyByteStringHasZeroSize()
  81. {
  82. Assert.AreEqual(0, ByteString.Empty.Length);
  83. }
  84. [Test]
  85. public void CopyFromStringWithExplicitEncoding()
  86. {
  87. ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
  88. Assert.AreEqual(4, bs.Length);
  89. Assert.AreEqual(65, bs[0]);
  90. Assert.AreEqual(0, bs[1]);
  91. Assert.AreEqual(66, bs[2]);
  92. Assert.AreEqual(0, bs[3]);
  93. }
  94. [Test]
  95. public void IsEmptyWhenEmpty()
  96. {
  97. Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
  98. }
  99. [Test]
  100. public void IsEmptyWhenNotEmpty()
  101. {
  102. Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
  103. }
  104. [Test]
  105. public void CopyFromByteArrayCopiesContents()
  106. {
  107. byte[] data = new byte[1];
  108. data[0] = 10;
  109. ByteString bs = ByteString.CopyFrom(data);
  110. Assert.AreEqual(10, bs[0]);
  111. data[0] = 5;
  112. Assert.AreEqual(10, bs[0]);
  113. }
  114. [Test]
  115. public void CopyFromReadOnlySpanCopiesContents()
  116. {
  117. byte[] data = new byte[1];
  118. data[0] = 10;
  119. ReadOnlySpan<byte> byteSpan = data;
  120. var bs = ByteString.CopyFrom(byteSpan);
  121. Assert.AreEqual(10, bs[0]);
  122. data[0] = 5;
  123. Assert.AreEqual(10, bs[0]);
  124. }
  125. [Test]
  126. public void ToByteArrayCopiesContents()
  127. {
  128. ByteString bs = ByteString.CopyFromUtf8("Hello");
  129. byte[] data = bs.ToByteArray();
  130. Assert.AreEqual((byte)'H', data[0]);
  131. Assert.AreEqual((byte)'H', bs[0]);
  132. data[0] = 0;
  133. Assert.AreEqual(0, data[0]);
  134. Assert.AreEqual((byte)'H', bs[0]);
  135. }
  136. [Test]
  137. public void CopyFromUtf8UsesUtf8()
  138. {
  139. ByteString bs = ByteString.CopyFromUtf8("\u20ac");
  140. Assert.AreEqual(3, bs.Length);
  141. Assert.AreEqual(0xe2, bs[0]);
  142. Assert.AreEqual(0x82, bs[1]);
  143. Assert.AreEqual(0xac, bs[2]);
  144. }
  145. [Test]
  146. public void CopyFromPortion()
  147. {
  148. byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
  149. ByteString bs = ByteString.CopyFrom(data, 2, 3);
  150. Assert.AreEqual(3, bs.Length);
  151. Assert.AreEqual(2, bs[0]);
  152. Assert.AreEqual(3, bs[1]);
  153. }
  154. [Test]
  155. public void CopyTo()
  156. {
  157. byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
  158. ByteString bs = ByteString.CopyFrom(data);
  159. byte[] dest = new byte[data.Length];
  160. bs.CopyTo(dest, 0);
  161. CollectionAssert.AreEqual(data, dest);
  162. }
  163. [Test]
  164. public void GetEnumerator()
  165. {
  166. byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
  167. ByteString bs = ByteString.CopyFrom(data);
  168. IEnumerator<byte> genericEnumerator = bs.GetEnumerator();
  169. Assert.IsTrue(genericEnumerator.MoveNext());
  170. Assert.AreEqual(0, genericEnumerator.Current);
  171. IEnumerator enumerator = ((IEnumerable)bs).GetEnumerator();
  172. Assert.IsTrue(enumerator.MoveNext());
  173. Assert.AreEqual(0, enumerator.Current);
  174. // Call via LINQ
  175. CollectionAssert.AreEqual(bs.Span.ToArray(), bs.ToArray());
  176. }
  177. [Test]
  178. public void UnsafeWrap()
  179. {
  180. byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
  181. ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
  182. ReadOnlySpan<byte> s = bs.Span;
  183. Assert.AreEqual(3, s.Length);
  184. Assert.AreEqual(2, s[0]);
  185. Assert.AreEqual(3, s[1]);
  186. Assert.AreEqual(4, s[2]);
  187. // Check that the value is not a copy
  188. data[2] = byte.MaxValue;
  189. Assert.AreEqual(byte.MaxValue, s[0]);
  190. }
  191. [Test]
  192. public void WriteToStream()
  193. {
  194. byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
  195. ByteString bs = ByteString.CopyFrom(data);
  196. MemoryStream ms = new MemoryStream();
  197. bs.WriteTo(ms);
  198. CollectionAssert.AreEqual(data, ms.ToArray());
  199. }
  200. [Test]
  201. public void WriteToStream_Stackalloc()
  202. {
  203. byte[] data = Encoding.UTF8.GetBytes("Hello world");
  204. Span<byte> s = stackalloc byte[data.Length];
  205. data.CopyTo(s);
  206. MemoryStream ms = new MemoryStream();
  207. using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))
  208. {
  209. ByteString bs = ByteString.AttachBytes(manager.Memory);
  210. bs.WriteTo(ms);
  211. }
  212. CollectionAssert.AreEqual(data, ms.ToArray());
  213. }
  214. [Test]
  215. public void ToStringUtf8()
  216. {
  217. ByteString bs = ByteString.CopyFromUtf8("\u20ac");
  218. Assert.AreEqual("\u20ac", bs.ToStringUtf8());
  219. }
  220. [Test]
  221. public void ToStringWithExplicitEncoding()
  222. {
  223. ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
  224. Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
  225. }
  226. [Test]
  227. public void ToString_Stackalloc()
  228. {
  229. byte[] data = Encoding.UTF8.GetBytes("Hello world");
  230. Span<byte> s = stackalloc byte[data.Length];
  231. data.CopyTo(s);
  232. using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))
  233. {
  234. ByteString bs = ByteString.AttachBytes(manager.Memory);
  235. Assert.AreEqual("Hello world", bs.ToString(Encoding.UTF8));
  236. }
  237. }
  238. [Test]
  239. public void FromBase64_WithText()
  240. {
  241. byte[] data = new byte[] {0, 1, 2, 3, 4, 5, 6};
  242. string base64 = Convert.ToBase64String(data);
  243. ByteString bs = ByteString.FromBase64(base64);
  244. Assert.AreEqual(data, bs.ToByteArray());
  245. }
  246. [Test]
  247. public void FromBase64_Empty()
  248. {
  249. // Optimization which also fixes issue 61.
  250. Assert.AreSame(ByteString.Empty, ByteString.FromBase64(""));
  251. }
  252. [Test]
  253. public void ToBase64_Array()
  254. {
  255. ByteString bs = ByteString.CopyFrom(Encoding.UTF8.GetBytes("Hello world"));
  256. Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
  257. }
  258. [Test]
  259. public void ToBase64_Stackalloc()
  260. {
  261. byte[] data = Encoding.UTF8.GetBytes("Hello world");
  262. Span<byte> s = stackalloc byte[data.Length];
  263. data.CopyTo(s);
  264. using (UnmanagedMemoryManager<byte> manager = new UnmanagedMemoryManager<byte>(s))
  265. {
  266. ByteString bs = ByteString.AttachBytes(manager.Memory);
  267. Assert.AreEqual("SGVsbG8gd29ybGQ=", bs.ToBase64());
  268. }
  269. }
  270. [Test]
  271. public void FromStream_Seekable()
  272. {
  273. var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
  274. // Consume the first byte, just to test that it's "from current position"
  275. stream.ReadByte();
  276. var actual = ByteString.FromStream(stream);
  277. ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
  278. Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
  279. }
  280. [Test]
  281. public void FromStream_NotSeekable()
  282. {
  283. var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
  284. // Consume the first byte, just to test that it's "from current position"
  285. stream.ReadByte();
  286. // Wrap the original stream in LimitedInputStream, which has CanSeek=false
  287. var limitedStream = new LimitedInputStream(stream, 3);
  288. var actual = ByteString.FromStream(limitedStream);
  289. ByteString expected = ByteString.CopyFrom(2, 3, 4);
  290. Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
  291. }
  292. #if !NET35
  293. [Test]
  294. public async Task FromStreamAsync_Seekable()
  295. {
  296. var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
  297. // Consume the first byte, just to test that it's "from current position"
  298. stream.ReadByte();
  299. var actual = await ByteString.FromStreamAsync(stream);
  300. ByteString expected = ByteString.CopyFrom(2, 3, 4, 5);
  301. Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
  302. }
  303. [Test]
  304. public async Task FromStreamAsync_NotSeekable()
  305. {
  306. var stream = new MemoryStream(new byte[] { 1, 2, 3, 4, 5 });
  307. // Consume the first byte, just to test that it's "from current position"
  308. stream.ReadByte();
  309. // Wrap the original stream in LimitedInputStream, which has CanSeek=false
  310. var limitedStream = new LimitedInputStream(stream, 3);
  311. var actual = await ByteString.FromStreamAsync(limitedStream);
  312. ByteString expected = ByteString.CopyFrom(2, 3, 4);
  313. Assert.AreEqual(expected, actual, $"{expected.ToBase64()} != {actual.ToBase64()}");
  314. }
  315. #endif
  316. [Test]
  317. public void GetHashCode_Regression()
  318. {
  319. // We used to have an awful hash algorithm where only the last four
  320. // bytes were relevant. This is a regression test for
  321. // https://github.com/protocolbuffers/protobuf/issues/2511
  322. ByteString b1 = ByteString.CopyFrom(100, 1, 2, 3, 4);
  323. ByteString b2 = ByteString.CopyFrom(200, 1, 2, 3, 4);
  324. Assert.AreNotEqual(b1.GetHashCode(), b2.GetHashCode());
  325. }
  326. [Test]
  327. public void GetContentsAsReadOnlySpan()
  328. {
  329. var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
  330. var copied = byteString.Span.ToArray();
  331. CollectionAssert.AreEqual(byteString, copied);
  332. }
  333. [Test]
  334. public void GetContentsAsReadOnlyMemory()
  335. {
  336. var byteString = ByteString.CopyFrom(1, 2, 3, 4, 5);
  337. var copied = byteString.Memory.ToArray();
  338. CollectionAssert.AreEqual(byteString, copied);
  339. }
  340. // Create Memory<byte> from non-array source.
  341. // Use by ByteString tests that have optimized path for array backed Memory<byte>.
  342. private sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T> where T : unmanaged
  343. {
  344. private readonly T* _pointer;
  345. private readonly int _length;
  346. public UnmanagedMemoryManager(Span<T> span)
  347. {
  348. fixed (T* ptr = &MemoryMarshal.GetReference(span))
  349. {
  350. _pointer = ptr;
  351. _length = span.Length;
  352. }
  353. }
  354. public override Span<T> GetSpan() => new Span<T>(_pointer, _length);
  355. public override MemoryHandle Pin(int elementIndex = 0)
  356. {
  357. if (elementIndex < 0 || elementIndex >= _length)
  358. {
  359. throw new ArgumentOutOfRangeException(nameof(elementIndex));
  360. }
  361. return new MemoryHandle(_pointer + elementIndex);
  362. }
  363. public override void Unpin() { }
  364. protected override void Dispose(bool disposing) { }
  365. }
  366. }
  367. }