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