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 39 namespace Google.Protobuf 40 { 41 /// <summary> 42 /// Immutable array of bytes. 43 /// </summary> 44 public sealed class ByteString : IEnumerable<byte>, IEquatable<ByteString> 45 { 46 private static readonly ByteString empty = new ByteString(new byte[0]); 47 48 private readonly byte[] bytes; 49 50 /// <summary> 51 /// Unsafe operations that can cause IO Failure and/or other catestrophic side-effects. 52 /// </summary> 53 internal static class Unsafe 54 { 55 /// <summary> 56 /// Constructs a new ByteString from the given byte array. The array is 57 /// *not* copied, and must not be modified after this constructor is called. 58 /// </summary> FromBytes(byte[] bytes)59 internal static ByteString FromBytes(byte[] bytes) 60 { 61 return new ByteString(bytes); 62 } 63 64 /// <summary> 65 /// Provides direct, unrestricted access to the bytes contained in this instance. 66 /// You must not modify or resize the byte array returned by this method. 67 /// </summary> GetBuffer(ByteString bytes)68 internal static byte[] GetBuffer(ByteString bytes) 69 { 70 return bytes.bytes; 71 } 72 } 73 74 /// <summary> 75 /// Internal use only. Ensure that the provided array is not mutated and belongs to this instance. 76 /// </summary> AttachBytes(byte[] bytes)77 internal static ByteString AttachBytes(byte[] bytes) 78 { 79 return new ByteString(bytes); 80 } 81 82 /// <summary> 83 /// Constructs a new ByteString from the given byte array. The array is 84 /// *not* copied, and must not be modified after this constructor is called. 85 /// </summary> ByteString(byte[] bytes)86 private ByteString(byte[] bytes) 87 { 88 this.bytes = bytes; 89 } 90 91 /// <summary> 92 /// Returns an empty ByteString. 93 /// </summary> 94 public static ByteString Empty 95 { 96 get { return empty; } 97 } 98 99 /// <summary> 100 /// Returns the length of this ByteString in bytes. 101 /// </summary> 102 public int Length 103 { 104 get { return bytes.Length; } 105 } 106 107 /// <summary> 108 /// Returns <c>true</c> if this byte string is empty, <c>false</c> otherwise. 109 /// </summary> 110 public bool IsEmpty 111 { 112 get { return Length == 0; } 113 } 114 115 /// <summary> 116 /// Converts this <see cref="ByteString"/> into a byte array. 117 /// </summary> 118 /// <remarks>The data is copied - changes to the returned array will not be reflected in this <c>ByteString</c>.</remarks> 119 /// <returns>A byte array with the same data as this <c>ByteString</c>.</returns> ToByteArray()120 public byte[] ToByteArray() 121 { 122 return (byte[]) bytes.Clone(); 123 } 124 125 /// <summary> 126 /// Converts this <see cref="ByteString"/> into a standard base64 representation. 127 /// </summary> 128 /// <returns>A base64 representation of this <c>ByteString</c>.</returns> ToBase64()129 public string ToBase64() 130 { 131 return Convert.ToBase64String(bytes); 132 } 133 134 /// <summary> 135 /// Constructs a <see cref="ByteString" /> from the Base64 Encoded String. 136 /// </summary> FromBase64(string bytes)137 public static ByteString FromBase64(string bytes) 138 { 139 // By handling the empty string explicitly, we not only optimize but we fix a 140 // problem on CF 2.0. See issue 61 for details. 141 return bytes == "" ? Empty : new ByteString(Convert.FromBase64String(bytes)); 142 } 143 144 /// <summary> 145 /// Constructs a <see cref="ByteString" /> from the given array. The contents 146 /// are copied, so further modifications to the array will not 147 /// be reflected in the returned ByteString. 148 /// This method can also be invoked in <c>ByteString.CopyFrom(0xaa, 0xbb, ...)</c> form 149 /// which is primarily useful for testing. 150 /// </summary> CopyFrom(params byte[] bytes)151 public static ByteString CopyFrom(params byte[] bytes) 152 { 153 return new ByteString((byte[]) bytes.Clone()); 154 } 155 156 /// <summary> 157 /// Constructs a <see cref="ByteString" /> from a portion of a byte array. 158 /// </summary> CopyFrom(byte[] bytes, int offset, int count)159 public static ByteString CopyFrom(byte[] bytes, int offset, int count) 160 { 161 byte[] portion = new byte[count]; 162 ByteArray.Copy(bytes, offset, portion, 0, count); 163 return new ByteString(portion); 164 } 165 166 /// <summary> 167 /// Creates a new <see cref="ByteString" /> by encoding the specified text with 168 /// the given encoding. 169 /// </summary> CopyFrom(string text, Encoding encoding)170 public static ByteString CopyFrom(string text, Encoding encoding) 171 { 172 return new ByteString(encoding.GetBytes(text)); 173 } 174 175 /// <summary> 176 /// Creates a new <see cref="ByteString" /> by encoding the specified text in UTF-8. 177 /// </summary> CopyFromUtf8(string text)178 public static ByteString CopyFromUtf8(string text) 179 { 180 return CopyFrom(text, Encoding.UTF8); 181 } 182 183 /// <summary> 184 /// Retuns the byte at the given index. 185 /// </summary> 186 public byte this[int index] 187 { 188 get { return bytes[index]; } 189 } 190 191 /// <summary> 192 /// Converts this <see cref="ByteString"/> into a string by applying the given encoding. 193 /// </summary> 194 /// <remarks> 195 /// This method should only be used to convert binary data which was the result of encoding 196 /// text with the given encoding. 197 /// </remarks> 198 /// <param name="encoding">The encoding to use to decode the binary data into text.</param> 199 /// <returns>The result of decoding the binary data with the given decoding.</returns> ToString(Encoding encoding)200 public string ToString(Encoding encoding) 201 { 202 return encoding.GetString(bytes, 0, bytes.Length); 203 } 204 205 /// <summary> 206 /// Converts this <see cref="ByteString"/> into a string by applying the UTF-8 encoding. 207 /// </summary> 208 /// <remarks> 209 /// This method should only be used to convert binary data which was the result of encoding 210 /// text with UTF-8. 211 /// </remarks> 212 /// <returns>The result of decoding the binary data with the given decoding.</returns> ToStringUtf8()213 public string ToStringUtf8() 214 { 215 return ToString(Encoding.UTF8); 216 } 217 218 /// <summary> 219 /// Returns an iterator over the bytes in this <see cref="ByteString"/>. 220 /// </summary> 221 /// <returns>An iterator over the bytes in this object.</returns> GetEnumerator()222 public IEnumerator<byte> GetEnumerator() 223 { 224 return ((IEnumerable<byte>) bytes).GetEnumerator(); 225 } 226 227 /// <summary> 228 /// Returns an iterator over the bytes in this <see cref="ByteString"/>. 229 /// </summary> 230 /// <returns>An iterator over the bytes in this object.</returns> IEnumerable.GetEnumerator()231 IEnumerator IEnumerable.GetEnumerator() 232 { 233 return GetEnumerator(); 234 } 235 236 /// <summary> 237 /// Creates a CodedInputStream from this ByteString's data. 238 /// </summary> CreateCodedInput()239 public CodedInputStream CreateCodedInput() 240 { 241 // We trust CodedInputStream not to reveal the provided byte array or modify it 242 return new CodedInputStream(bytes); 243 } 244 245 /// <summary> 246 /// Compares two byte strings for equality. 247 /// </summary> 248 /// <param name="lhs">The first byte string to compare.</param> 249 /// <param name="rhs">The second byte string to compare.</param> 250 /// <returns><c>true</c> if the byte strings are equal; false otherwise.</returns> operator ==(ByteString lhs, ByteString rhs)251 public static bool operator ==(ByteString lhs, ByteString rhs) 252 { 253 if (ReferenceEquals(lhs, rhs)) 254 { 255 return true; 256 } 257 if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) 258 { 259 return false; 260 } 261 if (lhs.bytes.Length != rhs.bytes.Length) 262 { 263 return false; 264 } 265 for (int i = 0; i < lhs.Length; i++) 266 { 267 if (rhs.bytes[i] != lhs.bytes[i]) 268 { 269 return false; 270 } 271 } 272 return true; 273 } 274 275 /// <summary> 276 /// Compares two byte strings for inequality. 277 /// </summary> 278 /// <param name="lhs">The first byte string to compare.</param> 279 /// <param name="rhs">The second byte string to compare.</param> 280 /// <returns><c>false</c> if the byte strings are equal; true otherwise.</returns> operator !=(ByteString lhs, ByteString rhs)281 public static bool operator !=(ByteString lhs, ByteString rhs) 282 { 283 return !(lhs == rhs); 284 } 285 286 /// <summary> 287 /// Compares this byte string with another object. 288 /// </summary> 289 /// <param name="obj">The object to compare this with.</param> 290 /// <returns><c>true</c> if <paramref name="obj"/> refers to an equal <see cref="ByteString"/>; <c>false</c> otherwise.</returns> Equals(object obj)291 public override bool Equals(object obj) 292 { 293 return this == (obj as ByteString); 294 } 295 296 /// <summary> 297 /// Returns a hash code for this object. Two equal byte strings 298 /// will return the same hash code. 299 /// </summary> 300 /// <returns>A hash code for this object.</returns> GetHashCode()301 public override int GetHashCode() 302 { 303 int ret = 23; 304 foreach (byte b in bytes) 305 { 306 ret = (ret << 8) | b; 307 } 308 return ret; 309 } 310 311 /// <summary> 312 /// Compares this byte string with another. 313 /// </summary> 314 /// <param name="other">The <see cref="ByteString"/> to compare this with.</param> 315 /// <returns><c>true</c> if <paramref name="other"/> refers to an equal byte string; <c>false</c> otherwise.</returns> Equals(ByteString other)316 public bool Equals(ByteString other) 317 { 318 return this == other; 319 } 320 321 /// <summary> 322 /// Used internally by CodedOutputStream to avoid creating a copy for the write 323 /// </summary> WriteRawBytesTo(CodedOutputStream outputStream)324 internal void WriteRawBytesTo(CodedOutputStream outputStream) 325 { 326 outputStream.WriteRawBytes(bytes, 0, bytes.Length); 327 } 328 329 /// <summary> 330 /// Copies the entire byte array to the destination array provided at the offset specified. 331 /// </summary> CopyTo(byte[] array, int position)332 public void CopyTo(byte[] array, int position) 333 { 334 ByteArray.Copy(bytes, 0, array, position, bytes.Length); 335 } 336 337 /// <summary> 338 /// Writes the entire byte array to the provided stream 339 /// </summary> WriteTo(Stream outputStream)340 public void WriteTo(Stream outputStream) 341 { 342 outputStream.Write(bytes, 0, bytes.Length); 343 } 344 } 345 }