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.Collections; 35 using System.Collections.Generic; 36 using System.IO; 37 using System.Text; 38 #if !NET35 39 using System.Threading; 40 using System.Threading.Tasks; 41 #endif 42 #if NET35 43 using Google.Protobuf.Compatibility; 44 #endif 45 46 namespace Google.Protobuf 47 { 48 /// <summary> 49 /// Immutable array of bytes. 50 /// </summary> 51 public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString> 52 { 53 private static readonly ByteString empty = new ByteString(new byte[0]); 54 55 private readonly byte[] bytes; 56 57 /// <summary> 58 /// Unsafe operations that can cause IO Failure and/or other catestrophic side-effects. 59 /// </summary> 60 internal static class Unsafe 61 { 62 /// <summary> 63 /// Constructs a new ByteString from the given byte array. The array is 64 /// *not* copied, and must not be modified after this constructor is called. 65 /// </summary> FromBytes(byte[] bytes)66 internal static ByteString FromBytes(byte[] bytes) 67 { 68 return new ByteString(bytes); 69 } 70 } 71 72 /// <summary> 73 /// Internal use only. Ensure that the provided array is not mutated and belongs to this instance. 74 /// </summary> AttachBytes(byte[] bytes)75 internal static ByteString AttachBytes(byte[] bytes) 76 { 77 return new ByteString(bytes); 78 } 79 80 /// <summary> 81 /// Constructs a new ByteString from the given byte array. The array is 82 /// *not* copied, and must not be modified after this constructor is called. 83 /// </summary> ByteString(byte[] bytes)84 private ByteString(byte[] bytes) 85 { 86 this.bytes = bytes; 87 } 88 89 /// <summary> 90 /// Returns an empty ByteString. 91 /// </summary> 92 public static ByteString Empty 93 { 94 get { return empty; } 95 } 96 97 /// <summary> 98 /// Returns the length of this ByteString in bytes. 99 /// </summary> 100 public int Length 101 { 102 get { return bytes.Length; } 103 } 104 105 /// <summary> 106 /// Returns <c>true</c> if this byte string is empty, <c>false</c> otherwise. 107 /// </summary> 108 public bool IsEmpty 109 { 110 get { return Length == 0; } 111 } 112 113 #if NETSTANDARD2_0 114 /// <summary> 115 /// Provides read-only access to the data of this <see cref="ByteString"/>. 116 /// No data is copied so this is the most efficient way of accessing. 117 /// </summary> 118 public ReadOnlySpan<byte> Span => new ReadOnlySpan<byte>(bytes); 119 #endif 120 121 /// <summary> 122 /// Converts this <see cref="ByteString"/> into a byte array. 123 /// </summary> 124 /// <remarks>The data is copied - changes to the returned array will not be reflected in this <c>ByteString</c>.</remarks> 125 /// <returns>A byte array with the same data as this <c>ByteString</c>.</returns> ToByteArray()126 public byte[] ToByteArray() 127 { 128 return (byte[]) bytes.Clone(); 129 } 130 131 /// <summary> 132 /// Converts this <see cref="ByteString"/> into a standard base64 representation. 133 /// </summary> 134 /// <returns>A base64 representation of this <c>ByteString</c>.</returns> ToBase64()135 public string ToBase64() 136 { 137 return Convert.ToBase64String(bytes); 138 } 139 140 /// <summary> 141 /// Constructs a <see cref="ByteString" /> from the Base64 Encoded String. 142 /// </summary> FromBase64(string bytes)143 public static ByteString FromBase64(string bytes) 144 { 145 // By handling the empty string explicitly, we not only optimize but we fix a 146 // problem on CF 2.0. See issue 61 for details. 147 return bytes == "" ? Empty : new ByteString(Convert.FromBase64String(bytes)); 148 } 149 150 /// <summary> 151 /// Constructs a <see cref="ByteString"/> from data in the given stream, synchronously. 152 /// </summary> 153 /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position 154 /// at the start of the call.</remarks> 155 /// <param name="stream">The stream to copy into a ByteString.</param> 156 /// <returns>A ByteString with content read from the given stream.</returns> FromStream(Stream stream)157 public static ByteString FromStream(Stream stream) 158 { 159 ProtoPreconditions.CheckNotNull(stream, nameof(stream)); 160 int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0; 161 var memoryStream = new MemoryStream(capacity); 162 stream.CopyTo(memoryStream); 163 #if NETSTANDARD1_0 || NETSTANDARD2_0 164 byte[] bytes = memoryStream.ToArray(); 165 #else 166 // Avoid an extra copy if we can. 167 byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray(); 168 #endif 169 return AttachBytes(bytes); 170 } 171 172 #if !NET35 173 /// <summary> 174 /// Constructs a <see cref="ByteString"/> from data in the given stream, asynchronously. 175 /// </summary> 176 /// <remarks>If successful, <paramref name="stream"/> will be read completely, from the position 177 /// at the start of the call.</remarks> 178 /// <param name="stream">The stream to copy into a ByteString.</param> 179 /// <param name="cancellationToken">The cancellation token to use when reading from the stream, if any.</param> 180 /// <returns>A ByteString with content read from the given stream.</returns> FromStreamAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))181 public async static Task<ByteString> FromStreamAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) 182 { 183 ProtoPreconditions.CheckNotNull(stream, nameof(stream)); 184 int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0; 185 var memoryStream = new MemoryStream(capacity); 186 // We have to specify the buffer size here, as there's no overload accepting the cancellation token 187 // alone. But it's documented to use 81920 by default if not specified. 188 await stream.CopyToAsync(memoryStream, 81920, cancellationToken); 189 #if NETSTANDARD1_0 || NETSTANDARD2_0 190 byte[] bytes = memoryStream.ToArray(); 191 #else 192 // Avoid an extra copy if we can. 193 byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray(); 194 #endif 195 return AttachBytes(bytes); 196 } 197 #endif 198 199 /// <summary> 200 /// Constructs a <see cref="ByteString" /> from the given array. The contents 201 /// are copied, so further modifications to the array will not 202 /// be reflected in the returned ByteString. 203 /// This method can also be invoked in <c>ByteString.CopyFrom(0xaa, 0xbb, ...)</c> form 204 /// which is primarily useful for testing. 205 /// </summary> CopyFrom(params byte[] bytes)206 public static ByteString CopyFrom(params byte[] bytes) 207 { 208 return new ByteString((byte[]) bytes.Clone()); 209 } 210 211 /// <summary> 212 /// Constructs a <see cref="ByteString" /> from a portion of a byte array. 213 /// </summary> CopyFrom(byte[] bytes, int offset, int count)214 public static ByteString CopyFrom(byte[] bytes, int offset, int count) 215 { 216 byte[] portion = new byte[count]; 217 ByteArray.Copy(bytes, offset, portion, 0, count); 218 return new ByteString(portion); 219 } 220 221 #if NETSTANDARD2_0 222 /// <summary> 223 /// Constructs a <see cref="ByteString" /> from a read only span. The contents 224 /// are copied, so further modifications to the span will not 225 /// be reflected in the returned <see cref="ByteString" />. 226 /// </summary> CopyFrom(ReadOnlySpan<byte> bytes)227 public static ByteString CopyFrom(ReadOnlySpan<byte> bytes) 228 { 229 return new ByteString(bytes.ToArray()); 230 } 231 #endif 232 233 /// <summary> 234 /// Creates a new <see cref="ByteString" /> by encoding the specified text with 235 /// the given encoding. 236 /// </summary> CopyFrom(string text, Encoding encoding)237 public static ByteString CopyFrom(string text, Encoding encoding) 238 { 239 return new ByteString(encoding.GetBytes(text)); 240 } 241 242 /// <summary> 243 /// Creates a new <see cref="ByteString" /> by encoding the specified text in UTF-8. 244 /// </summary> CopyFromUtf8(string text)245 public static ByteString CopyFromUtf8(string text) 246 { 247 return CopyFrom(text, Encoding.UTF8); 248 } 249 250 /// <summary> 251 /// Retuns the byte at the given index. 252 /// </summary> 253 public byte this[int index] 254 { 255 get { return bytes[index]; } 256 } 257 258 /// <summary> 259 /// Converts this <see cref="ByteString"/> into a string by applying the given encoding. 260 /// </summary> 261 /// <remarks> 262 /// This method should only be used to convert binary data which was the result of encoding 263 /// text with the given encoding. 264 /// </remarks> 265 /// <param name="encoding">The encoding to use to decode the binary data into text.</param> 266 /// <returns>The result of decoding the binary data with the given decoding.</returns> ToString(Encoding encoding)267 public string ToString(Encoding encoding) 268 { 269 return encoding.GetString(bytes, 0, bytes.Length); 270 } 271 272 /// <summary> 273 /// Converts this <see cref="ByteString"/> into a string by applying the UTF-8 encoding. 274 /// </summary> 275 /// <remarks> 276 /// This method should only be used to convert binary data which was the result of encoding 277 /// text with UTF-8. 278 /// </remarks> 279 /// <returns>The result of decoding the binary data with the given decoding.</returns> ToStringUtf8()280 public string ToStringUtf8() 281 { 282 return ToString(Encoding.UTF8); 283 } 284 285 /// <summary> 286 /// Returns an iterator over the bytes in this <see cref="ByteString"/>. 287 /// </summary> 288 /// <returns>An iterator over the bytes in this object.</returns> GetEnumerator()289 public IEnumerator<byte> GetEnumerator() 290 { 291 return ((IEnumerable<byte>) bytes).GetEnumerator(); 292 } 293 294 /// <summary> 295 /// Returns an iterator over the bytes in this <see cref="ByteString"/>. 296 /// </summary> 297 /// <returns>An iterator over the bytes in this object.</returns> IEnumerable.GetEnumerator()298 IEnumerator IEnumerable.GetEnumerator() 299 { 300 return GetEnumerator(); 301 } 302 303 /// <summary> 304 /// Creates a CodedInputStream from this ByteString's data. 305 /// </summary> CreateCodedInput()306 public CodedInputStream CreateCodedInput() 307 { 308 // We trust CodedInputStream not to reveal the provided byte array or modify it 309 return new CodedInputStream(bytes); 310 } 311 312 /// <summary> 313 /// Compares two byte strings for equality. 314 /// </summary> 315 /// <param name="lhs">The first byte string to compare.</param> 316 /// <param name="rhs">The second byte string to compare.</param> 317 /// <returns><c>true</c> if the byte strings are equal; false otherwise.</returns> operator ==(ByteString lhs, ByteString rhs)318 public static bool operator ==(ByteString lhs, ByteString rhs) 319 { 320 if (ReferenceEquals(lhs, rhs)) 321 { 322 return true; 323 } 324 if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) 325 { 326 return false; 327 } 328 if (lhs.bytes.Length != rhs.bytes.Length) 329 { 330 return false; 331 } 332 for (int i = 0; i < lhs.Length; i++) 333 { 334 if (rhs.bytes[i] != lhs.bytes[i]) 335 { 336 return false; 337 } 338 } 339 return true; 340 } 341 342 /// <summary> 343 /// Compares two byte strings for inequality. 344 /// </summary> 345 /// <param name="lhs">The first byte string to compare.</param> 346 /// <param name="rhs">The second byte string to compare.</param> 347 /// <returns><c>false</c> if the byte strings are equal; true otherwise.</returns> operator !=(ByteString lhs, ByteString rhs)348 public static bool operator !=(ByteString lhs, ByteString rhs) 349 { 350 return !(lhs == rhs); 351 } 352 353 /// <summary> 354 /// Compares this byte string with another object. 355 /// </summary> 356 /// <param name="obj">The object to compare this with.</param> 357 /// <returns><c>true</c> if <paramref name="obj"/> refers to an equal <see cref="ByteString"/>; <c>false</c> otherwise.</returns> Equals(object obj)358 public override bool Equals(object obj) 359 { 360 return this == (obj as ByteString); 361 } 362 363 /// <summary> 364 /// Returns a hash code for this object. Two equal byte strings 365 /// will return the same hash code. 366 /// </summary> 367 /// <returns>A hash code for this object.</returns> GetHashCode()368 public override int GetHashCode() 369 { 370 int ret = 23; 371 foreach (byte b in bytes) 372 { 373 ret = (ret * 31) + b; 374 } 375 return ret; 376 } 377 378 /// <summary> 379 /// Compares this byte string with another. 380 /// </summary> 381 /// <param name="other">The <see cref="ByteString"/> to compare this with.</param> 382 /// <returns><c>true</c> if <paramref name="other"/> refers to an equal byte string; <c>false</c> otherwise.</returns> Equals(ByteString other)383 public bool Equals(ByteString other) 384 { 385 return this == other; 386 } 387 388 /// <summary> 389 /// Used internally by CodedOutputStream to avoid creating a copy for the write 390 /// </summary> WriteRawBytesTo(CodedOutputStream outputStream)391 internal void WriteRawBytesTo(CodedOutputStream outputStream) 392 { 393 outputStream.WriteRawBytes(bytes, 0, bytes.Length); 394 } 395 396 /// <summary> 397 /// Copies the entire byte array to the destination array provided at the offset specified. 398 /// </summary> CopyTo(byte[] array, int position)399 public void CopyTo(byte[] array, int position) 400 { 401 ByteArray.Copy(bytes, 0, array, position, bytes.Length); 402 } 403 404 /// <summary> 405 /// Writes the entire byte array to the provided stream 406 /// </summary> WriteTo(Stream outputStream)407 public void WriteTo(Stream outputStream) 408 { 409 outputStream.Write(bytes, 0, bytes.Length); 410 } 411 } 412 }