org.elasticsearch.common.io.stream.StreamInput Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.io.stream;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexFormatTooNewException;
import org.apache.lucene.index.IndexFormatTooOldException;
import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.BitUtil;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.CharsRef;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.Version;
import org.elasticsearch.common.CharArrays;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.AccessDeniedException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystemException;
import java.nio.file.FileSystemLoopException;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.IntFunction;
import static org.elasticsearch.ElasticsearchException.readStackTrace;
/**
* A stream from this node to another node. Technically, it can also be streamed to a byte array but that is mostly for testing.
*
* This class's methods are optimized so you can put the methods that read and write a class next to each other and you can scan them
* visually for differences. That means that most variables should be read and written in a single line so even large objects fit both
* reading and writing on the screen. It also means that the methods on this class are named very similarly to {@link StreamOutput}. Finally
* it means that the "barrier to entry" for adding new methods to this class is relatively low even though it is a shared class with code
* everywhere. That being said, this class deals primarily with {@code List}s rather than Arrays. For the most part calls should adapt to
* lists, either by storing {@code List}s internally or just converting to and from a {@code List} when calling. This comment is repeated
* on {@link StreamInput}.
*/
public abstract class StreamInput extends InputStream {
private static final Map BYTE_TIME_UNIT_MAP;
static {
final Map byteTimeUnitMap = new HashMap<>();
byteTimeUnitMap.put((byte)0, TimeUnit.NANOSECONDS);
byteTimeUnitMap.put((byte)1, TimeUnit.MICROSECONDS);
byteTimeUnitMap.put((byte)2, TimeUnit.MILLISECONDS);
byteTimeUnitMap.put((byte)3, TimeUnit.SECONDS);
byteTimeUnitMap.put((byte)4, TimeUnit.MINUTES);
byteTimeUnitMap.put((byte)5, TimeUnit.HOURS);
byteTimeUnitMap.put((byte)6, TimeUnit.DAYS);
for (TimeUnit value : TimeUnit.values()) {
assert byteTimeUnitMap.containsValue(value) : value;
}
BYTE_TIME_UNIT_MAP = Collections.unmodifiableMap(byteTimeUnitMap);
}
private Version version = Version.CURRENT;
/**
* The version of the node on the other side of this stream.
*/
public Version getVersion() {
return this.version;
}
/**
* Set the version of the node on the other side of this stream.
*/
public void setVersion(Version version) {
this.version = version;
}
/**
* Reads and returns a single byte.
*/
public abstract byte readByte() throws IOException;
/**
* Reads a specified number of bytes into an array at the specified offset.
*
* @param b the array to read bytes into
* @param offset the offset in the array to start storing bytes
* @param len the number of bytes to read
*/
public abstract void readBytes(byte[] b, int offset, int len) throws IOException;
/**
* Reads a bytes reference from this stream, might hold an actual reference to the underlying
* bytes of the stream.
*/
public BytesReference readBytesReference() throws IOException {
int length = readArraySize();
return readBytesReference(length);
}
/**
* Reads an optional bytes reference from this stream. It might hold an actual reference to the underlying bytes of the stream. Use this
* only if you must differentiate null from empty. Use {@link StreamInput#readBytesReference()} and
* {@link StreamOutput#writeBytesReference(BytesReference)} if you do not.
*/
@Nullable
public BytesReference readOptionalBytesReference() throws IOException {
int length = readVInt() - 1;
if (length < 0) {
return null;
}
return readBytesReference(length);
}
/**
* Reads a bytes reference from this stream, might hold an actual reference to the underlying
* bytes of the stream.
*/
public BytesReference readBytesReference(int length) throws IOException {
if (length == 0) {
return BytesArray.EMPTY;
}
byte[] bytes = new byte[length];
readBytes(bytes, 0, length);
return new BytesArray(bytes, 0, length);
}
public BytesRef readBytesRef() throws IOException {
int length = readArraySize();
return readBytesRef(length);
}
public BytesRef readBytesRef(int length) throws IOException {
if (length == 0) {
return new BytesRef();
}
byte[] bytes = new byte[length];
readBytes(bytes, 0, length);
return new BytesRef(bytes, 0, length);
}
public void readFully(byte[] b) throws IOException {
readBytes(b, 0, b.length);
}
public short readShort() throws IOException {
return (short) (((readByte() & 0xFF) << 8) | (readByte() & 0xFF));
}
/**
* Reads four bytes and returns an int.
*/
public int readInt() throws IOException {
return ((readByte() & 0xFF) << 24) | ((readByte() & 0xFF) << 16)
| ((readByte() & 0xFF) << 8) | (readByte() & 0xFF);
}
/**
* Reads an optional {@link Integer}.
*/
public Integer readOptionalInt() throws IOException {
if (readBoolean()) {
return readInt();
}
return null;
}
/**
* Reads an int stored in variable-length format. Reads between one and
* five bytes. Smaller values take fewer bytes. Negative numbers
* will always use all 5 bytes and are therefore better serialized
* using {@link #readInt}
*/
public int readVInt() throws IOException {
byte b = readByte();
int i = b & 0x7F;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7F) << 7;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7F) << 14;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7F) << 21;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
if ((b & 0x80) != 0) {
throw new IOException("Invalid vInt ((" + Integer.toHexString(b) + " & 0x7f) << 28) | " + Integer.toHexString(i));
}
return i | ((b & 0x7F) << 28);
}
/**
* Reads eight bytes and returns a long.
*/
public long readLong() throws IOException {
return (((long) readInt()) << 32) | (readInt() & 0xFFFFFFFFL);
}
/**
* Reads a long stored in variable-length format. Reads between one and ten bytes. Smaller values take fewer bytes. Negative numbers
* are encoded in ten bytes so prefer {@link #readLong()} or {@link #readZLong()} for negative numbers.
*/
public long readVLong() throws IOException {
byte b = readByte();
long i = b & 0x7FL;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7FL) << 7;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7FL) << 14;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7FL) << 21;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7FL) << 28;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7FL) << 35;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7FL) << 42;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= (b & 0x7FL) << 49;
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
i |= ((b & 0x7FL) << 56);
if ((b & 0x80) == 0) {
return i;
}
b = readByte();
if (b != 0 && b != 1) {
throw new IOException("Invalid vlong (" + Integer.toHexString(b) + " << 63) | " + Long.toHexString(i));
}
i |= ((long) b) << 63;
return i;
}
public long readZLong() throws IOException {
long accumulator = 0L;
int i = 0;
long currentByte;
while (((currentByte = readByte()) & 0x80L) != 0) {
accumulator |= (currentByte & 0x7F) << i;
i += 7;
if (i > 63) {
throw new IOException("variable-length stream is too long");
}
}
return BitUtil.zigZagDecode(accumulator | (currentByte << i));
}
@Nullable
public Long readOptionalLong() throws IOException {
if (readBoolean()) {
return readLong();
}
return null;
}
@Nullable
public Text readOptionalText() throws IOException {
int length = readInt();
if (length == -1) {
return null;
}
return new Text(readBytesReference(length));
}
public Text readText() throws IOException {
// use StringAndBytes so we can cache the string if its ever converted to it
int length = readInt();
return new Text(readBytesReference(length));
}
@Nullable
public String readOptionalString() throws IOException {
if (readBoolean()) {
return readString();
}
return null;
}
@Nullable
public SecureString readOptionalSecureString() throws IOException {
SecureString value = null;
BytesReference bytesRef = readOptionalBytesReference();
if (bytesRef != null) {
byte[] bytes = BytesReference.toBytes(bytesRef);
try {
value = new SecureString(CharArrays.utf8BytesToChars(bytes));
} finally {
Arrays.fill(bytes, (byte) 0);
}
}
return value;
}
@Nullable
public Float readOptionalFloat() throws IOException {
if (readBoolean()) {
return readFloat();
}
return null;
}
@Nullable
public Integer readOptionalVInt() throws IOException {
if (readBoolean()) {
return readVInt();
}
return null;
}
// Maximum char-count to de-serialize via the thread-local CharsRef buffer
private static final int SMALL_STRING_LIMIT = 1024;
// Reusable bytes for deserializing strings
private static final ThreadLocal stringReadBuffer = ThreadLocal.withInitial(() -> new byte[1024]);
// Thread-local buffer for smaller strings
private static final ThreadLocal smallSpare = ThreadLocal.withInitial(() -> new CharsRef(SMALL_STRING_LIMIT));
// Larger buffer used for long strings that can't fit into the thread-local buffer
// We don't use a CharsRefBuilder since we exactly know the size of the character array up front
// this prevents calling grow for every character since we don't need this
private CharsRef largeSpare;
public String readString() throws IOException {
final int charCount = readArraySize();
final CharsRef charsRef;
if (charCount > SMALL_STRING_LIMIT) {
if (largeSpare == null) {
largeSpare = new CharsRef(ArrayUtil.oversize(charCount, Character.BYTES));
} else if (largeSpare.chars.length < charCount) {
// we don't use ArrayUtils.grow since there is no need to copy the array
largeSpare.chars = new char[ArrayUtil.oversize(charCount, Character.BYTES)];
}
charsRef = largeSpare;
} else {
charsRef = smallSpare.get();
}
charsRef.length = charCount;
int charsOffset = 0;
int offsetByteArray = 0;
int sizeByteArray = 0;
int missingFromPartial = 0;
final byte[] byteBuffer = stringReadBuffer.get();
final char[] charBuffer = charsRef.chars;
for (; charsOffset < charCount; ) {
final int charsLeft = charCount - charsOffset;
int bufferFree = byteBuffer.length - sizeByteArray;
// Determine the minimum amount of bytes that are left in the string
final int minRemainingBytes;
if (missingFromPartial > 0) {
// One byte for each remaining char except for the already partially read char
minRemainingBytes = missingFromPartial + charsLeft - 1;
missingFromPartial = 0;
} else {
// Each char has at least a single byte
minRemainingBytes = charsLeft;
}
final int toRead;
if (bufferFree < minRemainingBytes) {
// We don't have enough space left in the byte array to read as much as we'd like to so we free up as many bytes in the
// buffer by moving unused bytes that didn't make up a full char in the last iteration to the beginning of the buffer,
// if there are any
if (offsetByteArray > 0) {
sizeByteArray = sizeByteArray - offsetByteArray;
switch (sizeByteArray) { // We only have 0, 1 or 2 => no need to bother with a native call to System#arrayCopy
case 1:
byteBuffer[0] = byteBuffer[offsetByteArray];
break;
case 2:
byteBuffer[0] = byteBuffer[offsetByteArray];
byteBuffer[1] = byteBuffer[offsetByteArray + 1];
break;
}
assert sizeByteArray <= 2 : "We never copy more than 2 bytes here since a char is 3 bytes max";
toRead = Math.min(bufferFree + offsetByteArray, minRemainingBytes);
offsetByteArray = 0;
} else {
toRead = bufferFree;
}
} else {
toRead = minRemainingBytes;
}
readBytes(byteBuffer, sizeByteArray, toRead);
sizeByteArray += toRead;
// As long as we at least have three bytes buffered we don't need to do any bounds checking when getting the next char since we
// read 3 bytes per char/iteration at most
for (; offsetByteArray < sizeByteArray - 2; offsetByteArray++) {
final int c = byteBuffer[offsetByteArray] & 0xff;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
charBuffer[charsOffset++] = (char) c;
break;
case 12:
case 13:
charBuffer[charsOffset++] = (char) ((c & 0x1F) << 6 | byteBuffer[++offsetByteArray] & 0x3F);
break;
case 14:
charBuffer[charsOffset++] = (char) (
(c & 0x0F) << 12 | (byteBuffer[++offsetByteArray] & 0x3F) << 6 | (byteBuffer[++offsetByteArray] & 0x3F));
break;
default:
throwOnBrokenChar(c);
}
}
// try to extract chars from remaining bytes with bounds checks for multi-byte chars
final int bufferedBytesRemaining = sizeByteArray - offsetByteArray;
for (int i = 0; i < bufferedBytesRemaining; i++) {
final int c = byteBuffer[offsetByteArray] & 0xff;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
charBuffer[charsOffset++] = (char) c;
offsetByteArray++;
break;
case 12:
case 13:
missingFromPartial = 2 - (bufferedBytesRemaining - i);
if (missingFromPartial == 0) {
offsetByteArray++;
charBuffer[charsOffset++] = (char) ((c & 0x1F) << 6 | byteBuffer[offsetByteArray++] & 0x3F);
}
++i;
break;
case 14:
missingFromPartial = 3 - (bufferedBytesRemaining - i);
++i;
break;
default:
throwOnBrokenChar(c);
}
}
}
return charsRef.toString();
}
private static void throwOnBrokenChar(int c) throws IOException {
throw new IOException("Invalid string; unexpected character: " + c + " hex: " + Integer.toHexString(c));
}
public SecureString readSecureString() throws IOException {
BytesReference bytesRef = readBytesReference();
byte[] bytes = BytesReference.toBytes(bytesRef);
try {
return new SecureString(CharArrays.utf8BytesToChars(bytes));
} finally {
Arrays.fill(bytes, (byte) 0);
}
}
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
@Nullable
public final Double readOptionalDouble() throws IOException {
if (readBoolean()) {
return readDouble();
}
return null;
}
/**
* Reads a boolean.
*/
public final boolean readBoolean() throws IOException {
return readBoolean(readByte());
}
private boolean readBoolean(final byte value) {
if (value == 0) {
return false;
} else if (value == 1) {
return true;
} else {
final String message = String.format(Locale.ROOT, "unexpected byte [0x%02x]", value);
throw new IllegalStateException(message);
}
}
@Nullable
public final Boolean readOptionalBoolean() throws IOException {
final byte value = readByte();
if (value == 2) {
return null;
} else {
return readBoolean(value);
}
}
/**
* Closes the stream to further operations.
*/
@Override
public abstract void close() throws IOException;
@Override
public abstract int available() throws IOException;
public String[] readStringArray() throws IOException {
int size = readArraySize();
if (size == 0) {
return Strings.EMPTY_ARRAY;
}
String[] ret = new String[size];
for (int i = 0; i < size; i++) {
ret[i] = readString();
}
return ret;
}
@Nullable
public String[] readOptionalStringArray() throws IOException {
if (readBoolean()) {
return readStringArray();
}
return null;
}
/**
* If the returned map contains any entries it will be mutable. If it is empty it might be immutable.
*/
public Map readMap(Writeable.Reader keyReader, Writeable.Reader valueReader) throws IOException {
int size = readArraySize();
if (size == 0) {
return Collections.emptyMap();
}
Map map = new HashMap<>(size);
for (int i = 0; i < size; i++) {
K key = keyReader.read(this);
V value = valueReader.read(this);
map.put(key, value);
}
return map;
}
/**
* Read a {@link Map} of {@code K}-type keys to {@code V}-type {@link List}s.
*
* Map<String, List<String>> map = in.readMapOfLists(StreamInput::readString, StreamInput::readString);
*
* If the map or a list in it contains any elements it will be mutable, otherwise either the empty map or empty lists it contains
* might be immutable.
*
* @param keyReader The key reader
* @param valueReader The value reader
* @return Never {@code null}.
*/
public Map> readMapOfLists(final Writeable.Reader keyReader, final Writeable.Reader valueReader)
throws IOException {
final int size = readArraySize();
if (size == 0) {
return Collections.emptyMap();
}
final Map> map = new HashMap<>(size);
for (int i = 0; i < size; ++i) {
map.put(keyReader.read(this), readList(valueReader));
}
return map;
}
/**
* If the returned map contains any entries it will be mutable. If it is empty it might be immutable.
*/
@Nullable
@SuppressWarnings("unchecked")
public Map readMap() throws IOException {
return (Map) readGenericValue();
}
/**
* Reads a value of unspecified type. If a collection is read then the collection will be mutable if it contains any entry but might
* be immutable if it is empty.
*/
@Nullable
public Object readGenericValue() throws IOException {
byte type = readByte();
switch (type) {
case -1:
return null;
case 0:
return readString();
case 1:
return readInt();
case 2:
return readLong();
case 3:
return readFloat();
case 4:
return readDouble();
case 5:
return readBoolean();
case 6:
return readByteArray();
case 7:
return readArrayList();
case 8:
return readArray();
case 9:
return readLinkedHashMap();
case 10:
return readHashMap();
case 11:
return readByte();
case 12:
return readDate();
case 13:
return readDateTime();
case 14:
return readBytesReference();
case 15:
return readText();
case 16:
return readShort();
case 17:
return readIntArray();
case 18:
return readLongArray();
case 19:
return readFloatArray();
case 20:
return readDoubleArray();
case 21:
return readBytesRef();
case 22:
return readGeoPoint();
case 23:
return readZonedDateTime();
default:
throw new IOException("Can't read unknown type [" + type + "]");
}
}
/**
* Read an {@link Instant} from the stream with nanosecond resolution
*/
public final Instant readInstant() throws IOException {
return Instant.ofEpochSecond(readLong(), readInt());
}
/**
* Read an optional {@link Instant} from the stream. Returns null
when
* no instant is present.
*/
@Nullable
public final Instant readOptionalInstant() throws IOException {
final boolean present = readBoolean();
return present ? readInstant() : null;
}
@SuppressWarnings("unchecked")
private List readArrayList() throws IOException {
int size = readArraySize();
if (size == 0) {
return Collections.emptyList();
}
List list = new ArrayList(size);
for (int i = 0; i < size; i++) {
list.add(readGenericValue());
}
return list;
}
private DateTime readDateTime() throws IOException {
final String timeZoneId = readString();
return new DateTime(readLong(), DateTimeZone.forID(timeZoneId));
}
private ZonedDateTime readZonedDateTime() throws IOException {
final String timeZoneId = readString();
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(readLong()), ZoneId.of(timeZoneId));
}
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
private Object[] readArray() throws IOException {
int size8 = readArraySize();
if (size8 == 0) {
return EMPTY_OBJECT_ARRAY;
}
Object[] list8 = new Object[size8];
for (int i = 0; i < size8; i++) {
list8[i] = readGenericValue();
}
return list8;
}
private Map readLinkedHashMap() throws IOException {
int size9 = readArraySize();
if (size9 == 0) {
return Collections.emptyMap();
}
Map map9 = new LinkedHashMap(size9);
for (int i = 0; i < size9; i++) {
map9.put(readString(), readGenericValue());
}
return map9;
}
private Map readHashMap() throws IOException {
int size10 = readArraySize();
if (size10 == 0) {
return Collections.emptyMap();
}
Map map10 = new HashMap(size10);
for (int i = 0; i < size10; i++) {
map10.put(readString(), readGenericValue());
}
return map10;
}
private Date readDate() throws IOException {
return new Date(readLong());
}
/**
* Reads a {@link GeoPoint} from this stream input
*/
public GeoPoint readGeoPoint() throws IOException {
return new GeoPoint(readDouble(), readDouble());
}
/**
* Read a {@linkplain DateTimeZone}.
*/
public DateTimeZone readTimeZone() throws IOException {
return DateTimeZone.forID(readString());
}
/**
* Read an optional {@linkplain DateTimeZone}.
*/
public DateTimeZone readOptionalTimeZone() throws IOException {
if (readBoolean()) {
return DateTimeZone.forID(readString());
}
return null;
}
/**
* Read a {@linkplain DateTimeZone}.
*/
public ZoneId readZoneId() throws IOException {
return ZoneId.of(readString());
}
/**
* Read an optional {@linkplain ZoneId}.
*/
public ZoneId readOptionalZoneId() throws IOException {
if (readBoolean()) {
return ZoneId.of(readString());
}
return null;
}
private static final int[] EMPTY_INT_ARRAY = new int[0];
public int[] readIntArray() throws IOException {
int length = readArraySize();
if (length == 0) {
return EMPTY_INT_ARRAY;
}
int[] values = new int[length];
for (int i = 0; i < length; i++) {
values[i] = readInt();
}
return values;
}
public int[] readVIntArray() throws IOException {
int length = readArraySize();
if (length == 0) {
return EMPTY_INT_ARRAY;
}
int[] values = new int[length];
for (int i = 0; i < length; i++) {
values[i] = readVInt();
}
return values;
}
private static final long[] EMPTY_LONG_ARRAY = new long[0];
public long[] readLongArray() throws IOException {
int length = readArraySize();
if (length == 0) {
return EMPTY_LONG_ARRAY;
}
long[] values = new long[length];
for (int i = 0; i < length; i++) {
values[i] = readLong();
}
return values;
}
public long[] readVLongArray() throws IOException {
int length = readArraySize();
if (length == 0) {
return EMPTY_LONG_ARRAY;
}
long[] values = new long[length];
for (int i = 0; i < length; i++) {
values[i] = readVLong();
}
return values;
}
private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
public float[] readFloatArray() throws IOException {
int length = readArraySize();
if (length == 0) {
return EMPTY_FLOAT_ARRAY;
}
float[] values = new float[length];
for (int i = 0; i < length; i++) {
values[i] = readFloat();
}
return values;
}
private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
public double[] readDoubleArray() throws IOException {
int length = readArraySize();
if (length == 0) {
return EMPTY_DOUBLE_ARRAY;
}
double[] values = new double[length];
for (int i = 0; i < length; i++) {
values[i] = readDouble();
}
return values;
}
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
public byte[] readByteArray() throws IOException {
final int length = readArraySize();
if (length == 0) {
return EMPTY_BYTE_ARRAY;
}
final byte[] bytes = new byte[length];
readBytes(bytes, 0, bytes.length);
return bytes;
}
/**
* Reads an array from the stream using the specified {@link org.elasticsearch.common.io.stream.Writeable.Reader} to read array elements
* from the stream. This method can be seen as the reader version of {@link StreamOutput#writeArray(Writeable.Writer, Object[])}. It is
* assumed that the stream first contains a variable-length integer representing the size of the array, and then contains that many
* elements that can be read from the stream.
*
* @param reader the reader used to read individual elements
* @param arraySupplier a supplier used to construct a new array
* @param the type of the elements of the array
* @return an array read from the stream
* @throws IOException if an I/O exception occurs while reading the array
*/
public T[] readArray(final Writeable.Reader reader, final IntFunction arraySupplier) throws IOException {
final int length = readArraySize();
final T[] values = arraySupplier.apply(length);
for (int i = 0; i < length; i++) {
values[i] = reader.read(this);
}
return values;
}
public T[] readOptionalArray(Writeable.Reader reader, IntFunction arraySupplier) throws IOException {
return readBoolean() ? readArray(reader, arraySupplier) : null;
}
@Nullable
public T readOptionalWriteable(Writeable.Reader reader) throws IOException {
if (readBoolean()) {
T t = reader.read(this);
if (t == null) {
throw new IOException("Writeable.Reader [" + reader
+ "] returned null which is not allowed and probably means it screwed up the stream.");
}
return t;
} else {
return null;
}
}
public T readException() throws IOException {
if (readBoolean()) {
int key = readVInt();
switch (key) {
case 0:
final int ord = readVInt();
return (T) ElasticsearchException.readException(this, ord);
case 1:
String msg1 = readOptionalString();
String resource1 = readOptionalString();
return (T) readStackTrace(new CorruptIndexException(msg1, resource1, readException()), this);
case 2:
String resource2 = readOptionalString();
int version2 = readInt();
int minVersion2 = readInt();
int maxVersion2 = readInt();
return (T) readStackTrace(new IndexFormatTooNewException(resource2, version2, minVersion2, maxVersion2), this);
case 3:
String resource3 = readOptionalString();
if (readBoolean()) {
int version3 = readInt();
int minVersion3 = readInt();
int maxVersion3 = readInt();
return (T) readStackTrace(new IndexFormatTooOldException(resource3, version3, minVersion3, maxVersion3), this);
} else {
String version3 = readOptionalString();
return (T) readStackTrace(new IndexFormatTooOldException(resource3, version3), this);
}
case 4:
return (T) readStackTrace(new NullPointerException(readOptionalString()), this);
case 5:
return (T) readStackTrace(new NumberFormatException(readOptionalString()), this);
case 6:
return (T) readStackTrace(new IllegalArgumentException(readOptionalString(), readException()), this);
case 7:
return (T) readStackTrace(new AlreadyClosedException(readOptionalString(), readException()), this);
case 8:
return (T) readStackTrace(new EOFException(readOptionalString()), this);
case 9:
return (T) readStackTrace(new SecurityException(readOptionalString(), readException()), this);
case 10:
return (T) readStackTrace(new StringIndexOutOfBoundsException(readOptionalString()), this);
case 11:
return (T) readStackTrace(new ArrayIndexOutOfBoundsException(readOptionalString()), this);
case 12:
return (T) readStackTrace(new FileNotFoundException(readOptionalString()), this);
case 13:
final int subclass = readVInt();
final String file = readOptionalString();
final String other = readOptionalString();
final String reason = readOptionalString();
readOptionalString(); // skip the msg - it's composed from file, other and reason
final Exception exception;
switch (subclass) {
case 0:
exception = new NoSuchFileException(file, other, reason);
break;
case 1:
exception = new NotDirectoryException(file);
break;
case 2:
exception = new DirectoryNotEmptyException(file);
break;
case 3:
exception = new AtomicMoveNotSupportedException(file, other, reason);
break;
case 4:
exception = new FileAlreadyExistsException(file, other, reason);
break;
case 5:
exception = new AccessDeniedException(file, other, reason);
break;
case 6:
exception = new FileSystemLoopException(file);
break;
case 7:
exception = new FileSystemException(file, other, reason);
break;
default:
throw new IllegalStateException("unknown FileSystemException with index " + subclass);
}
return (T) readStackTrace(exception, this);
case 14:
return (T) readStackTrace(new IllegalStateException(readOptionalString(), readException()), this);
case 15:
return (T) readStackTrace(new LockObtainFailedException(readOptionalString(), readException()), this);
case 16:
return (T) readStackTrace(new InterruptedException(readOptionalString()), this);
case 17:
return (T) readStackTrace(new IOException(readOptionalString(), readException()), this);
case 18:
final boolean isExecutorShutdown = readBoolean();
return (T) readStackTrace(new EsRejectedExecutionException(readOptionalString(), isExecutorShutdown), this);
default:
throw new IOException("no such exception for id: " + key);
}
}
return null;
}
/**
* Reads a {@link NamedWriteable} from the current stream, by first reading its name and then looking for
* the corresponding entry in the registry by name, so that the proper object can be read and returned.
* Default implementation throws {@link UnsupportedOperationException} as StreamInput doesn't hold a registry.
* Use {@link FilterInputStream} instead which wraps a stream and supports a {@link NamedWriteableRegistry} too.
*/
@Nullable
public C readNamedWriteable(@SuppressWarnings("unused") Class categoryClass) throws IOException {
throw new UnsupportedOperationException("can't read named writeable from StreamInput");
}
/**
* Reads a {@link NamedWriteable} from the current stream with the given name. It is assumed that the caller obtained the name
* from other source, so it's not read from the stream. The name is used for looking for
* the corresponding entry in the registry by name, so that the proper object can be read and returned.
* Default implementation throws {@link UnsupportedOperationException} as StreamInput doesn't hold a registry.
* Use {@link FilterInputStream} instead which wraps a stream and supports a {@link NamedWriteableRegistry} too.
*
* Prefer {@link StreamInput#readNamedWriteable(Class)} and {@link StreamOutput#writeNamedWriteable(NamedWriteable)} unless you
* have a compelling reason to use this method instead.
*/
@Nullable
public C readNamedWriteable(@SuppressWarnings("unused") Class categoryClass,
@SuppressWarnings("unused") String name) throws IOException {
throw new UnsupportedOperationException("can't read named writeable from StreamInput");
}
/**
* Reads an optional {@link NamedWriteable}.
*/
@Nullable
public C readOptionalNamedWriteable(Class categoryClass) throws IOException {
if (readBoolean()) {
return readNamedWriteable(categoryClass);
}
return null;
}
/**
* Reads a list of objects. The list is expected to have been written using {@link StreamOutput#writeList(List)}.
* If the returned list contains any entries it will be mutable. If it is empty it might be immutable.
*
* @return the list of objects
* @throws IOException if an I/O exception occurs reading the list
*/
public List readList(final Writeable.Reader reader) throws IOException {
return readCollection(reader, ArrayList::new, Collections.emptyList());
}
/**
* Reads a list of strings. The list is expected to have been written using {@link StreamOutput#writeStringCollection(Collection)}.
* If the returned list contains any entries it will be mutable. If it is empty it might be immutable.
*
* @return the list of strings
* @throws IOException if an I/O exception occurs reading the list
*/
public List readStringList() throws IOException {
return readList(StreamInput::readString);
}
/**
* Reads a set of objects. If the returned set contains any entries it will be mutable. If it is empty it might be immutable.
*/
public Set readSet(Writeable.Reader reader) throws IOException {
return readCollection(reader, HashSet::new, Collections.emptySet());
}
/**
* Reads a collection of objects
*/
private > C readCollection(Writeable.Reader reader,
IntFunction constructor,
C empty) throws IOException {
int count = readArraySize();
if (count == 0) {
return empty;
}
C builder = constructor.apply(count);
for (int i=0; i List readNamedWriteableList(Class categoryClass) throws IOException {
int count = readArraySize();
if (count == 0) {
return Collections.emptyList();
}
List builder = new ArrayList<>(count);
for (int i=0; i> E readEnum(Class enumClass) throws IOException {
int ordinal = readVInt();
E[] values = enumClass.getEnumConstants();
if (ordinal < 0 || ordinal >= values.length) {
throw new IOException("Unknown " + enumClass.getSimpleName() + " ordinal [" + ordinal + "]");
}
return values[ordinal];
}
/**
* Reads an enum with type E that was serialized based on the value of it's ordinal
*/
public > EnumSet readEnumSet(Class enumClass) throws IOException {
int size = readVInt();
if (size == 0) {
return EnumSet.noneOf(enumClass);
}
Set enums = new HashSet<>(size);
for (int i = 0; i < size; i++) {
enums.add(readEnum(enumClass));
}
return EnumSet.copyOf(enums);
}
public static StreamInput wrap(byte[] bytes) {
return wrap(bytes, 0, bytes.length);
}
public static StreamInput wrap(byte[] bytes, int offset, int length) {
return new InputStreamStreamInput(new ByteArrayInputStream(bytes, offset, length), length);
}
/**
* Reads a vint via {@link #readVInt()} and applies basic checks to ensure the read array size is sane.
* This method uses {@link #ensureCanReadBytes(int)} to ensure this stream has enough bytes to read for the read array size.
*/
private int readArraySize() throws IOException {
final int arraySize = readVInt();
if (arraySize > ArrayUtil.MAX_ARRAY_LENGTH) {
throw new IllegalStateException("array length must be <= to " + ArrayUtil.MAX_ARRAY_LENGTH + " but was: " + arraySize);
}
if (arraySize < 0) {
throw new NegativeArraySizeException("array size must be positive but was: " + arraySize);
}
// lets do a sanity check that if we are reading an array size that is bigger that the remaining bytes we can safely
// throw an exception instead of allocating the array based on the size. A simple corrutpted byte can make a node go OOM
// if the size is large and for perf reasons we allocate arrays ahead of time
ensureCanReadBytes(arraySize);
return arraySize;
}
/**
* This method throws an {@link EOFException} if the given number of bytes can not be read from the this stream. This method might
* be a no-op depending on the underlying implementation if the information of the remaining bytes is not present.
*/
protected abstract void ensureCanReadBytes(int length) throws EOFException;
/**
* Read a {@link TimeValue} from the stream
*/
public TimeValue readTimeValue() throws IOException {
long duration = readZLong();
TimeUnit timeUnit = BYTE_TIME_UNIT_MAP.get(readByte());
return new TimeValue(duration, timeUnit);
}
/**
* Read an optional {@link TimeValue} from the stream, returning null if no TimeValue was written.
*/
public @Nullable TimeValue readOptionalTimeValue() throws IOException {
if (readBoolean()) {
return readTimeValue();
} else {
return null;
}
}
}