1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // http://code.google.com/p/protobuf/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import java.io.InputStream; 34 import java.io.ByteArrayInputStream; 35 import java.io.ByteArrayOutputStream; 36 import java.io.FilterOutputStream; 37 import java.io.UnsupportedEncodingException; 38 import java.nio.ByteBuffer; 39 import java.util.List; 40 41 /** 42 * Immutable array of bytes. 43 * 44 * @author crazybob (at) google.com Bob Lee 45 * @author kenton (at) google.com Kenton Varda 46 */ 47 public final class ByteString { 48 private final byte[] bytes; 49 50 private ByteString(final byte[] bytes) { 51 this.bytes = bytes; 52 } 53 54 /** 55 * Gets the byte at the given index. 56 * 57 * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size 58 */ 59 public byte byteAt(final int index) { 60 return bytes[index]; 61 } 62 63 /** 64 * Gets the number of bytes. 65 */ 66 public int size() { 67 return bytes.length; 68 } 69 70 /** 71 * Returns {@code true} if the size is {@code 0}, {@code false} otherwise. 72 */ 73 public boolean isEmpty() { 74 return bytes.length == 0; 75 } 76 77 // ================================================================= 78 // byte[] -> ByteString 79 80 /** 81 * Empty ByteString. 82 */ 83 public static final ByteString EMPTY = new ByteString(new byte[0]); 84 85 /** 86 * Copies the given bytes into a {@code ByteString}. 87 */ 88 public static ByteString copyFrom(final byte[] bytes, final int offset, 89 final int size) { 90 final byte[] copy = new byte[size]; 91 System.arraycopy(bytes, offset, copy, 0, size); 92 return new ByteString(copy); 93 } 94 95 /** 96 * Copies the given bytes into a {@code ByteString}. 97 */ 98 public static ByteString copyFrom(final byte[] bytes) { 99 return copyFrom(bytes, 0, bytes.length); 100 } 101 102 /** 103 * Copies {@code size} bytes from a {@code java.nio.ByteBuffer} into 104 * a {@code ByteString}. 105 */ 106 public static ByteString copyFrom(final ByteBuffer bytes, final int size) { 107 final byte[] copy = new byte[size]; 108 bytes.get(copy); 109 return new ByteString(copy); 110 } 111 112 /** 113 * Copies the remaining bytes from a {@code java.nio.ByteBuffer} into 114 * a {@code ByteString}. 115 */ 116 public static ByteString copyFrom(final ByteBuffer bytes) { 117 return copyFrom(bytes, bytes.remaining()); 118 } 119 120 /** 121 * Encodes {@code text} into a sequence of bytes using the named charset 122 * and returns the result as a {@code ByteString}. 123 */ 124 public static ByteString copyFrom(final String text, final String charsetName) 125 throws UnsupportedEncodingException { 126 return new ByteString(text.getBytes(charsetName)); 127 } 128 129 /** 130 * Encodes {@code text} into a sequence of UTF-8 bytes and returns the 131 * result as a {@code ByteString}. 132 */ 133 public static ByteString copyFromUtf8(final String text) { 134 try { 135 return new ByteString(text.getBytes("UTF-8")); 136 } catch (UnsupportedEncodingException e) { 137 throw new RuntimeException("UTF-8 not supported?", e); 138 } 139 } 140 141 /** 142 * Concatenates all byte strings in the list and returns the result. 143 * 144 * <p>The returned {@code ByteString} is not necessarily a unique object. 145 * If the list is empty, the returned object is the singleton empty 146 * {@code ByteString}. If the list has only one element, that 147 * {@code ByteString} will be returned without copying. 148 */ 149 public static ByteString copyFrom(List<ByteString> list) { 150 if (list.size() == 0) { 151 return EMPTY; 152 } else if (list.size() == 1) { 153 return list.get(0); 154 } 155 156 int size = 0; 157 for (ByteString str : list) { 158 size += str.size(); 159 } 160 byte[] bytes = new byte[size]; 161 int pos = 0; 162 for (ByteString str : list) { 163 System.arraycopy(str.bytes, 0, bytes, pos, str.size()); 164 pos += str.size(); 165 } 166 return new ByteString(bytes); 167 } 168 169 // ================================================================= 170 // ByteString -> byte[] 171 172 /** 173 * Copies bytes into a buffer at the given offset. 174 * 175 * @param target buffer to copy into 176 * @param offset in the target buffer 177 */ 178 public void copyTo(final byte[] target, final int offset) { 179 System.arraycopy(bytes, 0, target, offset, bytes.length); 180 } 181 182 /** 183 * Copies bytes into a buffer. 184 * 185 * @param target buffer to copy into 186 * @param sourceOffset offset within these bytes 187 * @param targetOffset offset within the target buffer 188 * @param size number of bytes to copy 189 */ 190 public void copyTo(final byte[] target, final int sourceOffset, 191 final int targetOffset, 192 final int size) { 193 System.arraycopy(bytes, sourceOffset, target, targetOffset, size); 194 } 195 196 /** 197 * Copies bytes to a {@code byte[]}. 198 */ 199 public byte[] toByteArray() { 200 final int size = bytes.length; 201 final byte[] copy = new byte[size]; 202 System.arraycopy(bytes, 0, copy, 0, size); 203 return copy; 204 } 205 206 /** 207 * Constructs a new read-only {@code java.nio.ByteBuffer} with the 208 * same backing byte array. 209 */ 210 public ByteBuffer asReadOnlyByteBuffer() { 211 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 212 return byteBuffer.asReadOnlyBuffer(); 213 } 214 215 /** 216 * Constructs a new {@code String} by decoding the bytes using the 217 * specified charset. 218 */ 219 public String toString(final String charsetName) 220 throws UnsupportedEncodingException { 221 return new String(bytes, charsetName); 222 } 223 224 /** 225 * Constructs a new {@code String} by decoding the bytes as UTF-8. 226 */ 227 public String toStringUtf8() { 228 try { 229 return new String(bytes, "UTF-8"); 230 } catch (UnsupportedEncodingException e) { 231 throw new RuntimeException("UTF-8 not supported?", e); 232 } 233 } 234 235 // ================================================================= 236 // equals() and hashCode() 237 238 @Override 239 public boolean equals(final Object o) { 240 if (o == this) { 241 return true; 242 } 243 244 if (!(o instanceof ByteString)) { 245 return false; 246 } 247 248 final ByteString other = (ByteString) o; 249 final int size = bytes.length; 250 if (size != other.bytes.length) { 251 return false; 252 } 253 254 final byte[] thisBytes = bytes; 255 final byte[] otherBytes = other.bytes; 256 for (int i = 0; i < size; i++) { 257 if (thisBytes[i] != otherBytes[i]) { 258 return false; 259 } 260 } 261 262 return true; 263 } 264 265 private volatile int hash = 0; 266 267 @Override 268 public int hashCode() { 269 int h = hash; 270 271 if (h == 0) { 272 final byte[] thisBytes = bytes; 273 final int size = bytes.length; 274 275 h = size; 276 for (int i = 0; i < size; i++) { 277 h = h * 31 + thisBytes[i]; 278 } 279 if (h == 0) { 280 h = 1; 281 } 282 283 hash = h; 284 } 285 286 return h; 287 } 288 289 // ================================================================= 290 // Input stream 291 292 /** 293 * Creates an {@code InputStream} which can be used to read the bytes. 294 */ 295 public InputStream newInput() { 296 return new ByteArrayInputStream(bytes); 297 } 298 299 /** 300 * Creates a {@link CodedInputStream} which can be used to read the bytes. 301 * Using this is more efficient than creating a {@link CodedInputStream} 302 * wrapping the result of {@link #newInput()}. 303 */ 304 public CodedInputStream newCodedInput() { 305 // We trust CodedInputStream not to modify the bytes, or to give anyone 306 // else access to them. 307 return CodedInputStream.newInstance(bytes); 308 } 309 310 // ================================================================= 311 // Output stream 312 313 /** 314 * Creates a new {@link Output} with the given initial capacity. 315 */ 316 public static Output newOutput(final int initialCapacity) { 317 return new Output(new ByteArrayOutputStream(initialCapacity)); 318 } 319 320 /** 321 * Creates a new {@link Output}. 322 */ 323 public static Output newOutput() { 324 return newOutput(32); 325 } 326 327 /** 328 * Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to 329 * create the {@code ByteString} instance. 330 */ 331 public static final class Output extends FilterOutputStream { 332 private final ByteArrayOutputStream bout; 333 334 /** 335 * Constructs a new output with the given initial capacity. 336 */ 337 private Output(final ByteArrayOutputStream bout) { 338 super(bout); 339 this.bout = bout; 340 } 341 342 /** 343 * Creates a {@code ByteString} instance from this {@code Output}. 344 */ 345 public ByteString toByteString() { 346 final byte[] byteArray = bout.toByteArray(); 347 return new ByteString(byteArray); 348 } 349 } 350 351 /** 352 * Constructs a new ByteString builder, which allows you to efficiently 353 * construct a {@code ByteString} by writing to a {@link CodedOutputStream}. 354 * Using this is much more efficient than calling {@code newOutput()} and 355 * wrapping that in a {@code CodedOutputStream}. 356 * 357 * <p>This is package-private because it's a somewhat confusing interface. 358 * Users can call {@link Message#toByteString()} instead of calling this 359 * directly. 360 * 361 * @param size The target byte size of the {@code ByteString}. You must 362 * write exactly this many bytes before building the result. 363 */ 364 static CodedBuilder newCodedBuilder(final int size) { 365 return new CodedBuilder(size); 366 } 367 368 /** See {@link ByteString#newCodedBuilder(int)}. */ 369 static final class CodedBuilder { 370 private final CodedOutputStream output; 371 private final byte[] buffer; 372 373 private CodedBuilder(final int size) { 374 buffer = new byte[size]; 375 output = CodedOutputStream.newInstance(buffer); 376 } 377 378 public ByteString build() { 379 output.checkNoSpaceLeft(); 380 381 // We can be confident that the CodedOutputStream will not modify the 382 // underlying bytes anymore because it already wrote all of them. So, 383 // no need to make a copy. 384 return new ByteString(buffer); 385 } 386 387 public CodedOutputStream getCodedOutput() { 388 return output; 389 } 390 } 391 } 392