Home | History | Annotate | Download | only in protobuf
      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