summaryrefslogtreecommitdiff
path: root/java/core/src/main/java/com/google/protobuf/NioByteString.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/core/src/main/java/com/google/protobuf/NioByteString.java')
-rw-r--r--java/core/src/main/java/com/google/protobuf/NioByteString.java290
1 files changed, 290 insertions, 0 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/NioByteString.java b/java/core/src/main/java/com/google/protobuf/NioByteString.java
new file mode 100644
index 00000000..6163c7b1
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/NioByteString.java
@@ -0,0 +1,290 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.InvalidMarkException;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link ByteString} that wraps around a {@link ByteBuffer}.
+ */
+final class NioByteString extends ByteString.LeafByteString {
+ private final ByteBuffer buffer;
+
+ NioByteString(ByteBuffer buffer) {
+ if (buffer == null) {
+ throw new NullPointerException("buffer");
+ }
+
+ this.buffer = buffer.slice().order(ByteOrder.nativeOrder());
+ }
+
+ // =================================================================
+ // Serializable
+
+ /**
+ * Magic method that lets us override serialization behavior.
+ */
+ private Object writeReplace() {
+ return ByteString.copyFrom(buffer.slice());
+ }
+
+ /**
+ * Magic method that lets us override deserialization behavior.
+ */
+ private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException {
+ throw new InvalidObjectException("NioByteString instances are not to be serialized directly");
+ }
+
+ // =================================================================
+
+ @Override
+ public byte byteAt(int index) {
+ try {
+ return buffer.get(index);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw e;
+ } catch (IndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(e.getMessage());
+ }
+ }
+
+ @Override
+ public int size() {
+ return buffer.remaining();
+ }
+
+ @Override
+ public ByteString substring(int beginIndex, int endIndex) {
+ try {
+ ByteBuffer slice = slice(beginIndex, endIndex);
+ return new NioByteString(slice);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw e;
+ } catch (IndexOutOfBoundsException e) {
+ throw new ArrayIndexOutOfBoundsException(e.getMessage());
+ }
+ }
+
+ @Override
+ protected void copyToInternal(
+ byte[] target, int sourceOffset, int targetOffset, int numberToCopy) {
+ ByteBuffer slice = buffer.slice();
+ slice.position(sourceOffset);
+ slice.get(target, targetOffset, numberToCopy);
+ }
+
+ @Override
+ public void copyTo(ByteBuffer target) {
+ target.put(buffer.slice());
+ }
+
+ @Override
+ public void writeTo(OutputStream out) throws IOException {
+ out.write(toByteArray());
+ }
+
+ @Override
+ boolean equalsRange(ByteString other, int offset, int length) {
+ return substring(0, length).equals(other.substring(offset, offset + length));
+ }
+
+ @Override
+ void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite) throws IOException {
+ if (buffer.hasArray()) {
+ // Optimized write for array-backed buffers.
+ // Note that we're taking the risk that a malicious OutputStream could modify the array.
+ int bufferOffset = buffer.arrayOffset() + buffer.position() + sourceOffset;
+ out.write(buffer.array(), bufferOffset, numberToWrite);
+ return;
+ }
+
+ ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out);
+ }
+
+ @Override
+ void writeTo(ByteOutput output) throws IOException {
+ output.writeLazy(buffer.slice());
+ }
+
+ @Override
+ public ByteBuffer asReadOnlyByteBuffer() {
+ return buffer.asReadOnlyBuffer();
+ }
+
+ @Override
+ public List<ByteBuffer> asReadOnlyByteBufferList() {
+ return Collections.singletonList(asReadOnlyByteBuffer());
+ }
+
+ @Override
+ protected String toStringInternal(Charset charset) {
+ final byte[] bytes;
+ final int offset;
+ final int length;
+ if (buffer.hasArray()) {
+ bytes = buffer.array();
+ offset = buffer.arrayOffset() + buffer.position();
+ length = buffer.remaining();
+ } else {
+ // TODO(nathanmittler): Can we optimize this?
+ bytes = toByteArray();
+ offset = 0;
+ length = bytes.length;
+ }
+ return new String(bytes, offset, length, charset);
+ }
+
+ @Override
+ public boolean isValidUtf8() {
+ return Utf8.isValidUtf8(buffer);
+ }
+
+ @Override
+ protected int partialIsValidUtf8(int state, int offset, int length) {
+ return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof ByteString)) {
+ return false;
+ }
+ ByteString otherString = ((ByteString) other);
+ if (size() != otherString.size()) {
+ return false;
+ }
+ if (size() == 0) {
+ return true;
+ }
+ if (other instanceof NioByteString) {
+ return buffer.equals(((NioByteString) other).buffer);
+ }
+ if (other instanceof RopeByteString) {
+ return other.equals(this);
+ }
+ return buffer.equals(otherString.asReadOnlyByteBuffer());
+ }
+
+ @Override
+ protected int partialHash(int h, int offset, int length) {
+ for (int i = offset; i < offset + length; i++) {
+ h = h * 31 + buffer.get(i);
+ }
+ return h;
+ }
+
+ @Override
+ public InputStream newInput() {
+ return new InputStream() {
+ private final ByteBuffer buf = buffer.slice();
+
+ @Override
+ public void mark(int readlimit) {
+ buf.mark();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public void reset() throws IOException {
+ try {
+ buf.reset();
+ } catch (InvalidMarkException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public int available() throws IOException {
+ return buf.remaining();
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (!buf.hasRemaining()) {
+ return -1;
+ }
+ return buf.get() & 0xFF;
+ }
+
+ @Override
+ public int read(byte[] bytes, int off, int len) throws IOException {
+ if (!buf.hasRemaining()) {
+ return -1;
+ }
+
+ len = Math.min(len, buf.remaining());
+ buf.get(bytes, off, len);
+ return len;
+ }
+ };
+ }
+
+ @Override
+ public CodedInputStream newCodedInput() {
+ return CodedInputStream.newInstance(buffer);
+ }
+
+ /**
+ * Creates a slice of a range of this buffer.
+ *
+ * @param beginIndex the beginning index of the slice (inclusive).
+ * @param endIndex the end index of the slice (exclusive).
+ * @return the requested slice.
+ */
+ private ByteBuffer slice(int beginIndex, int endIndex) {
+ if (beginIndex < buffer.position() || endIndex > buffer.limit() || beginIndex > endIndex) {
+ throw new IllegalArgumentException(
+ String.format("Invalid indices [%d, %d]", beginIndex, endIndex));
+ }
+
+ ByteBuffer slice = buffer.slice();
+ slice.position(beginIndex - buffer.position());
+ slice.limit(endIndex - buffer.position());
+ return slice;
+ }
+}