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.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 (at) 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 */ 62 LiteralByteString(byte[] bytes) { 63 this.bytes = bytes; 64 } 65 66 @Override 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 75 public int size() { 76 return bytes.length; 77 } 78 79 // ================================================================= 80 // ByteString -> substring 81 82 @Override 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 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 121 public void copyTo(ByteBuffer target) { 122 target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes 123 } 124 125 @Override 126 public ByteBuffer asReadOnlyByteBuffer() { 127 ByteBuffer byteBuffer = 128 ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size()); 129 return byteBuffer.asReadOnlyBuffer(); 130 } 131 132 @Override 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 141 public void writeTo(OutputStream outputStream) throws IOException { 142 outputStream.write(toByteArray()); 143 } 144 145 @Override 146 public String toString(String charsetName) 147 throws UnsupportedEncodingException { 148 return new String(bytes, getOffsetIntoBytes(), size(), charsetName); 149 } 150 151 // ================================================================= 152 // UTF-8 decoding 153 154 @Override 155 public boolean isValidUtf8() { 156 int offset = getOffsetIntoBytes(); 157 return Utf8.isValidUtf8(bytes, offset, offset + size()); 158 } 159 160 @Override 161 protected int partialIsValidUtf8(int state, int offset, int length) { 162 int index = getOffsetIntoBytes() + offset; 163 return Utf8.partialIsValidUtf8(state, bytes, index, index + length); 164 } 165 166 // ================================================================= 167 // equals() and hashCode() 168 169 @Override 170 public boolean equals(Object other) { 171 if (other == this) { 172 return true; 173 } 174 if (!(other instanceof ByteString)) { 175 return false; 176 } 177 178 if (size() != ((ByteString) other).size()) { 179 return false; 180 } 181 if (size() == 0) { 182 return true; 183 } 184 185 if (other instanceof LiteralByteString) { 186 return equalsRange((LiteralByteString) other, 0, size()); 187 } else if (other instanceof RopeByteString) { 188 return other.equals(this); 189 } else { 190 throw new IllegalArgumentException( 191 "Has a new type of ByteString been created? Found " 192 + other.getClass()); 193 } 194 } 195 196 /** 197 * Check equality of the substring of given length of this object starting at 198 * zero with another {@code LiteralByteString} substring starting at offset. 199 * 200 * @param other what to compare a substring in 201 * @param offset offset into other 202 * @param length number of bytes to compare 203 * @return true for equality of substrings, else false. 204 */ 205 boolean equalsRange(LiteralByteString other, int offset, int length) { 206 if (length > other.size()) { 207 throw new IllegalArgumentException( 208 "Length too large: " + length + size()); 209 } 210 if (offset + length > other.size()) { 211 throw new IllegalArgumentException( 212 "Ran off end of other: " + offset + ", " + length + ", " + 213 other.size()); 214 } 215 216 byte[] thisBytes = bytes; 217 byte[] otherBytes = other.bytes; 218 int thisLimit = getOffsetIntoBytes() + length; 219 for (int thisIndex = getOffsetIntoBytes(), otherIndex = 220 other.getOffsetIntoBytes() + offset; 221 (thisIndex < thisLimit); ++thisIndex, ++otherIndex) { 222 if (thisBytes[thisIndex] != otherBytes[otherIndex]) { 223 return false; 224 } 225 } 226 return true; 227 } 228 229 /** 230 * Cached hash value. Intentionally accessed via a data race, which 231 * is safe because of the Java Memory Model's "no out-of-thin-air values" 232 * guarantees for ints. 233 */ 234 private int hash = 0; 235 236 /** 237 * Compute the hashCode using the traditional algorithm from {@link 238 * ByteString}. 239 * 240 * @return hashCode value 241 */ 242 @Override 243 public int hashCode() { 244 int h = hash; 245 246 if (h == 0) { 247 int size = size(); 248 h = partialHash(size, 0, size); 249 if (h == 0) { 250 h = 1; 251 } 252 hash = h; 253 } 254 return h; 255 } 256 257 @Override 258 protected int peekCachedHashCode() { 259 return hash; 260 } 261 262 @Override 263 protected int partialHash(int h, int offset, int length) { 264 byte[] thisBytes = bytes; 265 for (int i = getOffsetIntoBytes() + offset, limit = i + length; i < limit; 266 i++) { 267 h = h * 31 + thisBytes[i]; 268 } 269 return h; 270 } 271 272 // ================================================================= 273 // Input stream 274 275 @Override 276 public InputStream newInput() { 277 return new ByteArrayInputStream(bytes, getOffsetIntoBytes(), 278 size()); // No copy 279 } 280 281 @Override 282 public CodedInputStream newCodedInput() { 283 // We trust CodedInputStream not to modify the bytes, or to give anyone 284 // else access to them. 285 return CodedInputStream 286 .newInstance(bytes, getOffsetIntoBytes(), size()); // No copy 287 } 288 289 // ================================================================= 290 // ByteIterator 291 292 @Override 293 public ByteIterator iterator() { 294 return new LiteralByteIterator(); 295 } 296 297 private class LiteralByteIterator implements ByteIterator { 298 private int position; 299 private final int limit; 300 301 private LiteralByteIterator() { 302 position = 0; 303 limit = size(); 304 } 305 306 public boolean hasNext() { 307 return (position < limit); 308 } 309 310 public Byte next() { 311 // Boxing calls Byte.valueOf(byte), which does not instantiate. 312 return nextByte(); 313 } 314 315 public byte nextByte() { 316 try { 317 return bytes[position++]; 318 } catch (ArrayIndexOutOfBoundsException e) { 319 throw new NoSuchElementException(e.getMessage()); 320 } 321 } 322 323 public void remove() { 324 throw new UnsupportedOperationException(); 325 } 326 } 327 328 // ================================================================= 329 // Internal methods 330 331 @Override 332 protected int getTreeDepth() { 333 return 0; 334 } 335 336 @Override 337 protected boolean isBalanced() { 338 return true; 339 } 340 341 /** 342 * Offset into {@code bytes[]} to use, non-zero for substrings. 343 * 344 * @return always 0 for this class 345 */ 346 protected int getOffsetIntoBytes() { 347 return 0; 348 } 349 } 350