// 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 static com.google.protobuf.
Internal.checkNotNull;
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) {
checkNotNull(
buffer, "buffer");
// Use native byte order for fast fixed32/64 operations.
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, true);
}
/**
* 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;
}
}