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