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