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