• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
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.ByteArrayInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.OutputStream;
37 import java.io.UnsupportedEncodingException;
38 import java.nio.ByteBuffer;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.NoSuchElementException;
42 
43 /**
44  * This class implements a {@link com.google.protobuf.ByteString} backed by a
45  * single array of bytes, contiguous in memory. It supports substring by
46  * pointing to only a sub-range of the underlying byte array, meaning that a
47  * substring will reference the full byte-array of the string it's made from,
48  * exactly as with {@link String}.
49  *
50  * @author carlanton@google.com (Carl Haverl)
51  */
52 class LiteralByteString extends ByteString {
53 
54   protected final byte[] bytes;
55 
56   /**
57    * Creates a {@code LiteralByteString} backed by the given array, without
58    * copying.
59    *
60    * @param bytes array to wrap
61    */
LiteralByteString(byte[] bytes)62   LiteralByteString(byte[] bytes) {
63     this.bytes = bytes;
64   }
65 
66   @Override
byteAt(int index)67   public byte byteAt(int index) {
68     // Unlike most methods in this class, this one is a direct implementation
69     // ignoring the potential offset because we need to do range-checking in the
70     // substring case anyway.
71     return bytes[index];
72   }
73 
74   @Override
size()75   public int size() {
76     return bytes.length;
77   }
78 
79   // =================================================================
80   // ByteString -> substring
81 
82   @Override
substring(int beginIndex, int endIndex)83   public ByteString substring(int beginIndex, int endIndex) {
84     if (beginIndex < 0) {
85       throw new IndexOutOfBoundsException(
86           "Beginning index: " + beginIndex + " < 0");
87     }
88     if (endIndex > size()) {
89       throw new IndexOutOfBoundsException("End index: " + endIndex + " > " +
90           size());
91     }
92     int substringLength = endIndex - beginIndex;
93     if (substringLength < 0) {
94       throw new IndexOutOfBoundsException(
95           "Beginning index larger than ending index: " + beginIndex + ", "
96               + endIndex);
97     }
98 
99     ByteString result;
100     if (substringLength == 0) {
101       result = ByteString.EMPTY;
102     } else {
103       result = new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex,
104           substringLength);
105     }
106     return result;
107   }
108 
109   // =================================================================
110   // ByteString -> byte[]
111 
112   @Override
copyToInternal(byte[] target, int sourceOffset, int targetOffset, int numberToCopy)113   protected void copyToInternal(byte[] target, int sourceOffset,
114       int targetOffset, int numberToCopy) {
115     // Optimized form, not for subclasses, since we don't call
116     // getOffsetIntoBytes() or check the 'numberToCopy' parameter.
117     System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy);
118   }
119 
120   @Override
copyTo(ByteBuffer target)121   public void copyTo(ByteBuffer target) {
122     target.put(bytes, getOffsetIntoBytes(), size());  // Copies bytes
123   }
124 
125   @Override
asReadOnlyByteBuffer()126   public ByteBuffer asReadOnlyByteBuffer() {
127     ByteBuffer byteBuffer =
128         ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size());
129     return byteBuffer.asReadOnlyBuffer();
130   }
131 
132   @Override
asReadOnlyByteBufferList()133   public List<ByteBuffer> asReadOnlyByteBufferList() {
134     // Return the ByteBuffer generated by asReadOnlyByteBuffer() as a singleton
135     List<ByteBuffer> result = new ArrayList<ByteBuffer>(1);
136     result.add(asReadOnlyByteBuffer());
137     return result;
138  }
139 
140  @Override
writeTo(OutputStream outputStream)141   public void writeTo(OutputStream outputStream) throws IOException {
142     outputStream.write(toByteArray());
143   }
144 
145   @Override
writeToInternal(OutputStream outputStream, int sourceOffset, int numberToWrite)146   void writeToInternal(OutputStream outputStream, int sourceOffset,
147       int numberToWrite) throws IOException {
148     outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset,
149         numberToWrite);
150   }
151 
152   @Override
toString(String charsetName)153   public String toString(String charsetName)
154       throws UnsupportedEncodingException {
155     return new String(bytes, getOffsetIntoBytes(), size(), charsetName);
156   }
157 
158   // =================================================================
159   // UTF-8 decoding
160 
161   @Override
isValidUtf8()162   public boolean isValidUtf8() {
163     int offset = getOffsetIntoBytes();
164     return Utf8.isValidUtf8(bytes, offset, offset + size());
165   }
166 
167   @Override
partialIsValidUtf8(int state, int offset, int length)168   protected int partialIsValidUtf8(int state, int offset, int length) {
169     int index = getOffsetIntoBytes() + offset;
170     return Utf8.partialIsValidUtf8(state, bytes, index, index + length);
171   }
172 
173   // =================================================================
174   // equals() and hashCode()
175 
176   @Override
equals(Object other)177   public boolean equals(Object other) {
178     if (other == this) {
179       return true;
180     }
181     if (!(other instanceof ByteString)) {
182       return false;
183     }
184 
185     if (size() != ((ByteString) other).size()) {
186       return false;
187     }
188     if (size() == 0) {
189       return true;
190     }
191 
192     if (other instanceof LiteralByteString) {
193       return equalsRange((LiteralByteString) other, 0, size());
194     } else if (other instanceof RopeByteString) {
195       return other.equals(this);
196     } else {
197       throw new IllegalArgumentException(
198           "Has a new type of ByteString been created? Found "
199               + other.getClass());
200     }
201   }
202 
203   /**
204    * Check equality of the substring of given length of this object starting at
205    * zero with another {@code LiteralByteString} substring starting at offset.
206    *
207    * @param other  what to compare a substring in
208    * @param offset offset into other
209    * @param length number of bytes to compare
210    * @return true for equality of substrings, else false.
211    */
equalsRange(LiteralByteString other, int offset, int length)212   boolean equalsRange(LiteralByteString other, int offset, int length) {
213     if (length > other.size()) {
214       throw new IllegalArgumentException(
215           "Length too large: " + length + size());
216     }
217     if (offset + length > other.size()) {
218       throw new IllegalArgumentException(
219           "Ran off end of other: " + offset + ", " + length + ", " +
220               other.size());
221     }
222 
223     byte[] thisBytes = bytes;
224     byte[] otherBytes = other.bytes;
225     int thisLimit = getOffsetIntoBytes() + length;
226     for (int thisIndex = getOffsetIntoBytes(), otherIndex =
227         other.getOffsetIntoBytes() + offset;
228         (thisIndex < thisLimit); ++thisIndex, ++otherIndex) {
229       if (thisBytes[thisIndex] != otherBytes[otherIndex]) {
230         return false;
231       }
232     }
233     return true;
234   }
235 
236   /**
237    * Cached hash value.  Intentionally accessed via a data race, which
238    * is safe because of the Java Memory Model's "no out-of-thin-air values"
239    * guarantees for ints.
240    */
241   private int hash = 0;
242 
243   /**
244    * Compute the hashCode using the traditional algorithm from {@link
245    * ByteString}.
246    *
247    * @return hashCode value
248    */
249   @Override
hashCode()250   public int hashCode() {
251     int h = hash;
252 
253     if (h == 0) {
254       int size = size();
255       h = partialHash(size, 0, size);
256       if (h == 0) {
257         h = 1;
258       }
259       hash = h;
260     }
261     return h;
262   }
263 
264   @Override
peekCachedHashCode()265   protected int peekCachedHashCode() {
266     return hash;
267   }
268 
269   @Override
partialHash(int h, int offset, int length)270   protected int partialHash(int h, int offset, int length) {
271     return hashCode(h, bytes, getOffsetIntoBytes() + offset, length);
272   }
273 
hashCode(int h, byte[] bytes, int offset, int length)274   static int hashCode(int h, byte[] bytes, int offset, int length) {
275     for (int i = offset; i < offset + length; i++) {
276       h = h * 31 + bytes[i];
277     }
278     return h;
279   }
280 
hashCode(byte[] bytes)281   static int hashCode(byte[] bytes) {
282     int h = hashCode(bytes.length, bytes, 0, bytes.length);
283     return h == 0 ? 1 : h;
284   }
285 
286   // =================================================================
287   // Input stream
288 
289   @Override
newInput()290   public InputStream newInput() {
291     return new ByteArrayInputStream(bytes, getOffsetIntoBytes(),
292         size());  // No copy
293   }
294 
295   @Override
newCodedInput()296   public CodedInputStream newCodedInput() {
297     // We trust CodedInputStream not to modify the bytes, or to give anyone
298     // else access to them.
299     return CodedInputStream.newInstance(this);
300   }
301 
302   // =================================================================
303   // ByteIterator
304 
305   @Override
iterator()306   public ByteIterator iterator() {
307     return new LiteralByteIterator();
308   }
309 
310   private class LiteralByteIterator implements ByteIterator {
311     private int position;
312     private final int limit;
313 
LiteralByteIterator()314     private LiteralByteIterator() {
315       position = 0;
316       limit = size();
317     }
318 
hasNext()319     public boolean hasNext() {
320       return (position < limit);
321     }
322 
next()323     public Byte next() {
324       // Boxing calls Byte.valueOf(byte), which does not instantiate.
325       return nextByte();
326     }
327 
nextByte()328     public byte nextByte() {
329       try {
330         return bytes[position++];
331       } catch (ArrayIndexOutOfBoundsException e) {
332         throw new NoSuchElementException(e.getMessage());
333       }
334     }
335 
remove()336     public void remove() {
337       throw new UnsupportedOperationException();
338     }
339   }
340 
341   // =================================================================
342   // Internal methods
343 
344   @Override
getTreeDepth()345   protected int getTreeDepth() {
346     return 0;
347   }
348 
349   @Override
isBalanced()350   protected boolean isBalanced() {
351     return true;
352   }
353 
354   /**
355    * Offset into {@code bytes[]} to use, non-zero for substrings.
356    *
357    * @return always 0 for this class
358    */
getOffsetIntoBytes()359   protected int getOffsetIntoBytes() {
360     return 0;
361   }
362 }
363