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.IOException; 34 import java.io.InputStream; 35 import java.io.InvalidObjectException; 36 import java.io.ObjectInputStream; 37 import java.io.OutputStream; 38 import java.nio.ByteBuffer; 39 import java.nio.ByteOrder; 40 import java.nio.InvalidMarkException; 41 import java.nio.charset.Charset; 42 import java.util.Collections; 43 import java.util.List; 44 45 /** 46 * A {@link ByteString} that wraps around a {@link ByteBuffer}. 47 */ 48 final class NioByteString extends ByteString.LeafByteString { 49 private final ByteBuffer buffer; 50 NioByteString(ByteBuffer buffer)51 NioByteString(ByteBuffer buffer) { 52 if (buffer == null) { 53 throw new NullPointerException("buffer"); 54 } 55 56 this.buffer = buffer.slice().order(ByteOrder.nativeOrder()); 57 } 58 59 // ================================================================= 60 // Serializable 61 62 /** 63 * Magic method that lets us override serialization behavior. 64 */ writeReplace()65 private Object writeReplace() { 66 return ByteString.copyFrom(buffer.slice()); 67 } 68 69 /** 70 * Magic method that lets us override deserialization behavior. 71 */ readObject(@uppressWarnings"unused") ObjectInputStream in)72 private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException { 73 throw new InvalidObjectException("NioByteString instances are not to be serialized directly"); 74 } 75 76 // ================================================================= 77 78 @Override byteAt(int index)79 public byte byteAt(int index) { 80 try { 81 return buffer.get(index); 82 } catch (ArrayIndexOutOfBoundsException e) { 83 throw e; 84 } catch (IndexOutOfBoundsException e) { 85 throw new ArrayIndexOutOfBoundsException(e.getMessage()); 86 } 87 } 88 89 @Override size()90 public int size() { 91 return buffer.remaining(); 92 } 93 94 @Override substring(int beginIndex, int endIndex)95 public ByteString substring(int beginIndex, int endIndex) { 96 try { 97 ByteBuffer slice = slice(beginIndex, endIndex); 98 return new NioByteString(slice); 99 } catch (ArrayIndexOutOfBoundsException e) { 100 throw e; 101 } catch (IndexOutOfBoundsException e) { 102 throw new ArrayIndexOutOfBoundsException(e.getMessage()); 103 } 104 } 105 106 @Override copyToInternal( byte[] target, int sourceOffset, int targetOffset, int numberToCopy)107 protected void copyToInternal( 108 byte[] target, int sourceOffset, int targetOffset, int numberToCopy) { 109 ByteBuffer slice = buffer.slice(); 110 slice.position(sourceOffset); 111 slice.get(target, targetOffset, numberToCopy); 112 } 113 114 @Override copyTo(ByteBuffer target)115 public void copyTo(ByteBuffer target) { 116 target.put(buffer.slice()); 117 } 118 119 @Override writeTo(OutputStream out)120 public void writeTo(OutputStream out) throws IOException { 121 out.write(toByteArray()); 122 } 123 124 @Override equalsRange(ByteString other, int offset, int length)125 boolean equalsRange(ByteString other, int offset, int length) { 126 return substring(0, length).equals(other.substring(offset, offset + length)); 127 } 128 129 @Override writeToInternal(OutputStream out, int sourceOffset, int numberToWrite)130 void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite) throws IOException { 131 if (buffer.hasArray()) { 132 // Optimized write for array-backed buffers. 133 // Note that we're taking the risk that a malicious OutputStream could modify the array. 134 int bufferOffset = buffer.arrayOffset() + buffer.position() + sourceOffset; 135 out.write(buffer.array(), bufferOffset, numberToWrite); 136 return; 137 } 138 139 ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out); 140 } 141 142 @Override writeTo(ByteOutput output)143 void writeTo(ByteOutput output) throws IOException { 144 output.writeLazy(buffer.slice()); 145 } 146 147 @Override asReadOnlyByteBuffer()148 public ByteBuffer asReadOnlyByteBuffer() { 149 return buffer.asReadOnlyBuffer(); 150 } 151 152 @Override asReadOnlyByteBufferList()153 public List<ByteBuffer> asReadOnlyByteBufferList() { 154 return Collections.singletonList(asReadOnlyByteBuffer()); 155 } 156 157 @Override toStringInternal(Charset charset)158 protected String toStringInternal(Charset charset) { 159 final byte[] bytes; 160 final int offset; 161 final int length; 162 if (buffer.hasArray()) { 163 bytes = buffer.array(); 164 offset = buffer.arrayOffset() + buffer.position(); 165 length = buffer.remaining(); 166 } else { 167 // TODO(nathanmittler): Can we optimize this? 168 bytes = toByteArray(); 169 offset = 0; 170 length = bytes.length; 171 } 172 return new String(bytes, offset, length, charset); 173 } 174 175 @Override isValidUtf8()176 public boolean isValidUtf8() { 177 return Utf8.isValidUtf8(buffer); 178 } 179 180 @Override partialIsValidUtf8(int state, int offset, int length)181 protected int partialIsValidUtf8(int state, int offset, int length) { 182 return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length); 183 } 184 185 @Override equals(Object other)186 public boolean equals(Object other) { 187 if (other == this) { 188 return true; 189 } 190 if (!(other instanceof ByteString)) { 191 return false; 192 } 193 ByteString otherString = ((ByteString) other); 194 if (size() != otherString.size()) { 195 return false; 196 } 197 if (size() == 0) { 198 return true; 199 } 200 if (other instanceof NioByteString) { 201 return buffer.equals(((NioByteString) other).buffer); 202 } 203 if (other instanceof RopeByteString) { 204 return other.equals(this); 205 } 206 return buffer.equals(otherString.asReadOnlyByteBuffer()); 207 } 208 209 @Override partialHash(int h, int offset, int length)210 protected int partialHash(int h, int offset, int length) { 211 for (int i = offset; i < offset + length; i++) { 212 h = h * 31 + buffer.get(i); 213 } 214 return h; 215 } 216 217 @Override newInput()218 public InputStream newInput() { 219 return new InputStream() { 220 private final ByteBuffer buf = buffer.slice(); 221 222 @Override 223 public void mark(int readlimit) { 224 buf.mark(); 225 } 226 227 @Override 228 public boolean markSupported() { 229 return true; 230 } 231 232 @Override 233 public void reset() throws IOException { 234 try { 235 buf.reset(); 236 } catch (InvalidMarkException e) { 237 throw new IOException(e); 238 } 239 } 240 241 @Override 242 public int available() throws IOException { 243 return buf.remaining(); 244 } 245 246 @Override 247 public int read() throws IOException { 248 if (!buf.hasRemaining()) { 249 return -1; 250 } 251 return buf.get() & 0xFF; 252 } 253 254 @Override 255 public int read(byte[] bytes, int off, int len) throws IOException { 256 if (!buf.hasRemaining()) { 257 return -1; 258 } 259 260 len = Math.min(len, buf.remaining()); 261 buf.get(bytes, off, len); 262 return len; 263 } 264 }; 265 } 266 267 @Override newCodedInput()268 public CodedInputStream newCodedInput() { 269 return CodedInputStream.newInstance(buffer); 270 } 271 272 /** 273 * Creates a slice of a range of this buffer. 274 * 275 * @param beginIndex the beginning index of the slice (inclusive). 276 * @param endIndex the end index of the slice (exclusive). 277 * @return the requested slice. 278 */ slice(int beginIndex, int endIndex)279 private ByteBuffer slice(int beginIndex, int endIndex) { 280 if (beginIndex < buffer.position() || endIndex > buffer.limit() || beginIndex > endIndex) { 281 throw new IllegalArgumentException( 282 String.format("Invalid indices [%d, %d]", beginIndex, endIndex)); 283 } 284 285 ByteBuffer slice = buffer.slice(); 286 slice.position(beginIndex - buffer.position()); 287 slice.limit(endIndex - buffer.position()); 288 return slice; 289 } 290 } 291