Use safe buffers
This commit is contained in:
parent
cb24914793
commit
9b36d831c6
@ -33,6 +33,8 @@ import it.cavallium.data.generator.plugin.classgen.GenUpgraderBaseX;
|
||||
import it.cavallium.data.generator.plugin.classgen.GenUpgraderSuperX;
|
||||
import it.cavallium.data.generator.plugin.classgen.GenVersion;
|
||||
import it.cavallium.data.generator.plugin.classgen.GenVersions;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanList;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteList;
|
||||
import it.unimi.dsi.fastutil.chars.CharList;
|
||||
@ -41,8 +43,6 @@ import it.unimi.dsi.fastutil.floats.FloatList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -282,10 +282,9 @@ public class SourcesGenerator {
|
||||
serializeMethod.addModifiers(Modifier.PUBLIC);
|
||||
serializeMethod.addModifiers(Modifier.FINAL);
|
||||
serializeMethod.returns(TypeName.VOID);
|
||||
serializeMethod.addParameter(ParameterSpec.builder(DataOutput.class, "dataOutput").build());
|
||||
serializeMethod.addParameter(ParameterSpec.builder(SafeDataOutput.class, "dataOutput").build());
|
||||
serializeMethod
|
||||
.addParameter(ParameterSpec.builder(classType, "data").addAnnotation(NotNull.class).build());
|
||||
serializeMethod.addException(IOException.class);
|
||||
serializeMethod.addStatement("$T.requireNonNull(data)", Objects.class);
|
||||
return serializeMethod;
|
||||
}
|
||||
@ -297,8 +296,7 @@ public class SourcesGenerator {
|
||||
deserializeMethod.addModifiers(Modifier.PUBLIC);
|
||||
deserializeMethod.addModifiers(Modifier.FINAL);
|
||||
deserializeMethod.returns(classType);
|
||||
deserializeMethod.addParameter(ParameterSpec.builder(DataInput.class, "dataInput").build());
|
||||
deserializeMethod.addException(IOException.class);
|
||||
deserializeMethod.addParameter(ParameterSpec.builder(SafeDataInput.class, "dataInput").build());
|
||||
return deserializeMethod;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ import com.squareup.javapoet.WildcardTypeName;
|
||||
import it.cavallium.data.generator.plugin.ClassGenerator;
|
||||
import it.cavallium.data.generator.plugin.ComputedType;
|
||||
import it.cavallium.data.generator.plugin.ComputedVersion;
|
||||
import java.io.DataInput;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -119,8 +119,8 @@ public class GenCurrentVersion extends ClassGenerator {
|
||||
.addModifiers(Modifier.PUBLIC).addModifiers(Modifier.STATIC).addModifiers(Modifier.FINAL).returns(TypeVariableName.get("U"))
|
||||
.addParameter(ParameterSpec.builder(TypeName.INT, "oldVersion").build()).addParameter(
|
||||
ParameterSpec.builder(ClassName.get(dataModel.getRootPackage(basePackageName), "BaseType"), "type").build())
|
||||
.addParameter(ParameterSpec.builder(DataInput.class, "oldDataInput").build())
|
||||
.addException(IOException.class).beginControlFlow("return upgradeDataToLatestVersion(oldVersion, switch (oldVersion)");
|
||||
.addParameter(ParameterSpec.builder(SafeDataInput.class, "oldDataInput").build())
|
||||
.beginControlFlow("return upgradeDataToLatestVersion(oldVersion, switch (oldVersion)");
|
||||
for (var versionConfiguration : dataModel.getVersionsSet()) {
|
||||
// Add a case in which the data version deserializes the serialized data and upgrades it
|
||||
var versions = ClassName.get(dataModel.getRootPackage(basePackageName), "Versions");
|
||||
@ -131,7 +131,7 @@ public class GenCurrentVersion extends ClassGenerator {
|
||||
);
|
||||
}
|
||||
var upgradeDataToLatestVersion1Method = upgradeDataToLatestVersion1MethodBuilder
|
||||
.addStatement("default -> throw new $T(\"Unknown version: \" + oldVersion)", IOException.class)
|
||||
.addStatement("default -> throw new $T(\"Unknown version: \" + oldVersion)", UnsupportedOperationException.class)
|
||||
.addCode(CodeBlock.of("$<});"))
|
||||
.build();
|
||||
currentVersionClass.addMethod(upgradeDataToLatestVersion1Method);
|
||||
@ -145,7 +145,6 @@ public class GenCurrentVersion extends ClassGenerator {
|
||||
.returns(TypeVariableName.get("U"))
|
||||
.addParameter(ParameterSpec.builder(TypeName.INT, "oldVersion").build())
|
||||
.addParameter(ParameterSpec.builder(TypeVariableName.get("T"), "oldData").build())
|
||||
.addException(IOException.class)
|
||||
.addStatement("$T data = oldData", Object.class);
|
||||
upgradeDataToLatestVersion2MethodBuilder.beginControlFlow("switch (oldVersion)");
|
||||
for (var versionConfiguration : dataModel.getVersionsSet()) {
|
||||
@ -168,7 +167,7 @@ public class GenCurrentVersion extends ClassGenerator {
|
||||
);
|
||||
}
|
||||
}
|
||||
upgradeDataToLatestVersion2MethodBuilder.addStatement("default: throw new $T(\"Unknown version: \" + oldVersion)", IOException.class);
|
||||
upgradeDataToLatestVersion2MethodBuilder.addStatement("default: throw new $T(\"Unknown version: \" + oldVersion)", UnsupportedOperationException.class);
|
||||
upgradeDataToLatestVersion2MethodBuilder.endControlFlow();
|
||||
currentVersionClass.addMethod(upgradeDataToLatestVersion2MethodBuilder.build());
|
||||
}
|
||||
|
@ -14,9 +14,8 @@ import it.cavallium.data.generator.plugin.ComputedTypeArray;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeArrayFixed;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeArrayVersioned;
|
||||
import it.cavallium.data.generator.plugin.ComputedVersion;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import java.io.NotSerializableException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
@ -63,9 +62,8 @@ public class GenSerializerArrayX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("serialize");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec
|
||||
.builder(typeArray.getJTypeName(basePackageName), "data")
|
||||
.addAnnotation(NotNull.class)
|
||||
@ -96,13 +94,12 @@ public class GenSerializerArrayX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("deserialize");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
var typeArrayClassName = typeArray.getJTypeName(basePackageName);
|
||||
method.returns(typeArrayClassName);
|
||||
method.addAnnotation(NotNull.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataInput.class, "in").build());
|
||||
|
||||
method.addStatement("int sz = in.readInt()");
|
||||
var arrayTypeName = ArrayTypeName.of(typeArray.getBase().getJTypeName(basePackageName));
|
||||
|
@ -11,9 +11,8 @@ import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.data.generator.plugin.ClassGenerator;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeBase;
|
||||
import it.cavallium.data.generator.plugin.ComputedVersion;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import java.io.NotSerializableException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
@ -60,9 +59,8 @@ public class GenSerializerBaseX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("serialize");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec
|
||||
.builder(typeBase.getJTypeName(basePackageName), "data")
|
||||
.addAnnotation(NotNull.class)
|
||||
@ -95,13 +93,12 @@ public class GenSerializerBaseX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("deserialize");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
ClassName typeBaseClassName = typeBase.getJTypeName(basePackageName);
|
||||
method.returns(typeBaseClassName);
|
||||
method.addAnnotation(NotNull.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataInput.class, "in").build());
|
||||
|
||||
method.addCode("return new $T(\n$>", typeBaseClassName);
|
||||
typeBase.getData().entrySet().stream().flatMap(entry -> {
|
||||
|
@ -12,9 +12,8 @@ import it.cavallium.data.generator.plugin.ComputedTypeNullable;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeNullableFixed;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeNullableVersioned;
|
||||
import it.cavallium.data.generator.plugin.ComputedVersion;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import java.io.NotSerializableException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
@ -66,9 +65,8 @@ public class GenSerializerNullableX extends ClassGenerator {
|
||||
var baseSerializerInstance = base.getJSerializerInstance(basePackageName);
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec
|
||||
.builder(typeNullable.getJTypeName(basePackageName), "data")
|
||||
.addAnnotation(NotNull.class)
|
||||
@ -101,13 +99,12 @@ public class GenSerializerNullableX extends ClassGenerator {
|
||||
var baseSerializerInstance = base.getJSerializerInstance(basePackageName);
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
var typeNullableClassName = typeNullable.getJTypeName(basePackageName);
|
||||
method.returns(typeNullableClassName);
|
||||
method.addAnnotation(NotNull.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataInput.class, "in").build());
|
||||
|
||||
method.addStatement("return in.readBoolean() ? new $T(($T) $T.$N.deserialize(in)) : $T.empty()",
|
||||
typeNullableClassName,
|
||||
|
@ -11,8 +11,8 @@ import it.cavallium.data.generator.plugin.ClassGenerator;
|
||||
import it.cavallium.data.generator.plugin.ComputedType;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeSuper;
|
||||
import it.cavallium.data.generator.plugin.ComputedVersion;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.NotSerializableException;
|
||||
import java.util.Objects;
|
||||
@ -61,11 +61,10 @@ public class GenSerializerSuperX extends ClassGenerator {
|
||||
int max = typeSuper.subTypes().size();
|
||||
var method = MethodSpec.methodBuilder("checkIdValidity");
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
method.addParameter(ParameterSpec.builder(int.class, "id").build());
|
||||
|
||||
method.beginControlFlow("if (id < 0 || id >= $L)", max);
|
||||
method.addStatement("throw new $T(new $T(id))", IOException.class, IndexOutOfBoundsException.class);
|
||||
method.addStatement("throw new new $T(id)", IndexOutOfBoundsException.class);
|
||||
method.endControlFlow();
|
||||
|
||||
classBuilder.addMethod(method.build());
|
||||
@ -75,9 +74,8 @@ public class GenSerializerSuperX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("serialize");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataOutput.class, "out").build());
|
||||
method.addParameter(ParameterSpec
|
||||
.builder(typeSuper.getJTypeName(basePackageName), "data")
|
||||
.addAnnotation(NotNull.class)
|
||||
@ -118,13 +116,12 @@ public class GenSerializerSuperX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("deserialize");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
ClassName typeSuperClassName = typeSuper.getJTypeName(basePackageName);
|
||||
method.returns(typeSuperClassName);
|
||||
method.addAnnotation(NotNull.class);
|
||||
|
||||
method.addParameter(ParameterSpec.builder(DataInput.class, "in").build());
|
||||
method.addParameter(ParameterSpec.builder(SafeDataInput.class, "in").build());
|
||||
|
||||
method.addStatement("int id = in.readUnsignedByte()");
|
||||
method.beginControlFlow("return switch (id)");
|
||||
|
@ -79,7 +79,6 @@ public class GenUpgraderBaseX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("upgrade");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
ClassName typeBaseClassName = typeBase.getJTypeName(basePackageName);
|
||||
ClassName nextTypeBaseClassName = nextTypeBase.getJTypeName(basePackageName);
|
||||
|
@ -13,7 +13,6 @@ import it.cavallium.data.generator.plugin.ComputedType;
|
||||
import it.cavallium.data.generator.plugin.ComputedType.VersionedComputedType;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeSuper;
|
||||
import it.cavallium.data.generator.plugin.ComputedVersion;
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -61,7 +60,6 @@ public class GenUpgraderSuperX extends ClassGenerator {
|
||||
var method = MethodSpec.methodBuilder("upgrade");
|
||||
|
||||
method.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||
method.addException(IOException.class);
|
||||
|
||||
ClassName typeSuperClassName = typeSuper.getJTypeName(basePackageName);
|
||||
ClassName nextTypeSuperClassName = nextTypeSuper.getJTypeName(basePackageName);
|
||||
|
@ -19,7 +19,6 @@ import it.cavallium.data.generator.plugin.ComputedTypeCustom;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeNullableFixed;
|
||||
import it.cavallium.data.generator.plugin.ComputedTypeSuper;
|
||||
import it.cavallium.data.generator.plugin.ComputedVersion;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import javax.lang.model.element.Modifier;
|
||||
@ -105,7 +104,6 @@ public class GenVersion extends ClassGenerator {
|
||||
var methodBuilder = MethodSpec.methodBuilder("upgradeToNextVersion");
|
||||
|
||||
methodBuilder.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL);
|
||||
methodBuilder.addException(ClassName.get(IOException.class));
|
||||
|
||||
var nextIBaseType = ClassName.get(nextVersion.getPackage(basePackageName), "IBaseType");
|
||||
methodBuilder.returns(nextIBaseType);
|
||||
|
@ -0,0 +1,186 @@
|
||||
package it.cavallium.buffer;
|
||||
|
||||
import it.cavallium.stream.SafeByteArrayInputStream;
|
||||
import it.cavallium.stream.SafeByteArrayOutputStream;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteList;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.RandomAccess;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Buf extends ByteList, RandomAccess {
|
||||
static Buf wrap(ByteList bytes) {
|
||||
if (bytes instanceof Buf buf) {
|
||||
return buf;
|
||||
} else if (bytes instanceof ByteArrayList byteArrayList) {
|
||||
return ByteListBuf.wrap(byteArrayList.elements(), byteArrayList.size());
|
||||
} else {
|
||||
return ByteListBuf.wrap(bytes.toByteArray());
|
||||
}
|
||||
}
|
||||
static Buf wrap(ByteList bytes, int from, int to) {
|
||||
if (bytes instanceof Buf buf) {
|
||||
return buf.subList(from, to);
|
||||
} else if (bytes instanceof ByteArrayList byteArrayList) {
|
||||
return ByteListBuf.wrap(byteArrayList.elements(), byteArrayList.size()).subList(from, to);
|
||||
} else {
|
||||
return ByteListBuf.wrap(bytes.toByteArray()).subList(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
static Buf wrap(byte[] bytes) {
|
||||
return ByteListBuf.wrap(bytes);
|
||||
}
|
||||
|
||||
static Buf wrap(byte[] bytes, int from, int to) {
|
||||
return ByteListBuf.wrap(bytes, to).subList(from, to);
|
||||
}
|
||||
|
||||
static Buf create(int initialCapacity) {
|
||||
return new ByteListBuf(initialCapacity);
|
||||
}
|
||||
|
||||
static Buf copyOf(byte[] original) {
|
||||
return new ByteListBuf(original);
|
||||
}
|
||||
|
||||
static Buf create() {
|
||||
return new ByteListBuf();
|
||||
}
|
||||
|
||||
static Buf wrap(byte[] array, int length) {
|
||||
return ByteListBuf.wrap(array, length);
|
||||
}
|
||||
|
||||
static Buf createZeroes(int length) {
|
||||
return ByteListBuf.wrap(new byte[length], length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this element as an array, converting it if needed
|
||||
*/
|
||||
byte @NotNull[] asArray();
|
||||
|
||||
/**
|
||||
* Get this element as an array, only if it's already an array, otherwise return null
|
||||
*/
|
||||
byte @Nullable[] asArrayStrict();
|
||||
|
||||
/**
|
||||
* Get this element as an array with equal or bigger size, converting it if needed
|
||||
* The returned array may be bigger than expected!
|
||||
*/
|
||||
byte @Nullable[] asUnboundedArray();
|
||||
|
||||
/**
|
||||
* Get this element as an array with equal or bigger size, only if it's already an array, otherwise return null
|
||||
* The returned array may be bigger than expected!
|
||||
*/
|
||||
byte @Nullable[] asUnboundedArrayStrict();
|
||||
|
||||
boolean isMutable();
|
||||
|
||||
void freeze();
|
||||
|
||||
@Override
|
||||
Buf subList(int from, int to);
|
||||
|
||||
Buf copy();
|
||||
|
||||
SafeByteArrayInputStream binaryInputStream();
|
||||
|
||||
void writeTo(SafeDataOutput dataOutput);
|
||||
|
||||
default long getLong(int i) {
|
||||
byte b1 = getByte(i);
|
||||
byte b2 = getByte(i + 1);
|
||||
byte b3 = getByte(i + 2);
|
||||
byte b4 = getByte(i + 3);
|
||||
byte b5 = getByte(i + 4);
|
||||
byte b6 = getByte(i + 5);
|
||||
byte b7 = getByte(i + 6);
|
||||
byte b8 = getByte(i + 7);
|
||||
return (b1 & 0xFFL) << 56
|
||||
| (b2 & 0xFFL) << 48
|
||||
| (b3 & 0xFFL) << 40
|
||||
| (b4 & 0xFFL) << 32
|
||||
| (b5 & 0xFFL) << 24
|
||||
| (b6 & 0xFFL) << 16
|
||||
| (b7 & 0xFFL) << 8
|
||||
| (b8 & 0xFFL);
|
||||
}
|
||||
|
||||
default int getInt(int i) {
|
||||
byte b1 = getByte(i);
|
||||
byte b2 = getByte(i + 1);
|
||||
byte b3 = getByte(i + 2);
|
||||
byte b4 = getByte(i + 3);
|
||||
return b1 << 24 | (b2 & 0xFF) << 16 | (b3 & 0xFF) << 8 | (b4 & 0xFF);
|
||||
}
|
||||
|
||||
default float getFloat(int i) {
|
||||
return Float.intBitsToFloat(getInt(i));
|
||||
}
|
||||
|
||||
default double getDouble(int i) {
|
||||
return Double.longBitsToDouble(getLong(i));
|
||||
}
|
||||
|
||||
default boolean getBoolean(int i) {
|
||||
return getByte(i) != 0;
|
||||
}
|
||||
|
||||
default void setBoolean(int i, boolean val) {
|
||||
set(i, val ? (byte) 1 : 0);
|
||||
}
|
||||
|
||||
default void setByte(int i, byte val) {
|
||||
set(i, val);
|
||||
}
|
||||
|
||||
default void setInt(int i, int val) {
|
||||
set(i, (byte) (val >> 24));
|
||||
set(i + 1, (byte) (val >> 16));
|
||||
set(i + 2, (byte) (val >> 8));
|
||||
set(i + 3, (byte) val);
|
||||
}
|
||||
|
||||
default void setLong(int i, long val) {
|
||||
set(i, (byte) (val >> 56));
|
||||
set(i + 1, (byte) (val >> 48));
|
||||
set(i + 2, (byte) (val >> 40));
|
||||
set(i + 3, (byte) (val >> 32));
|
||||
set(i + 4, (byte) (val >> 24));
|
||||
set(i + 5, (byte) (val >> 16));
|
||||
set(i + 6, (byte) (val >> 8));
|
||||
set(i + 7, (byte) val);
|
||||
}
|
||||
|
||||
default void setFloat(int i, float val) {
|
||||
setInt(i, Float.floatToRawIntBits(val));
|
||||
}
|
||||
|
||||
default void setDouble(int i, double val) {
|
||||
setLong(i, Double.doubleToRawLongBits(val));
|
||||
}
|
||||
|
||||
default SafeByteArrayOutputStream binaryOutputStream() {
|
||||
return binaryOutputStream(0, size());
|
||||
}
|
||||
|
||||
default SafeByteArrayOutputStream binaryOutputStream(int from) {
|
||||
return binaryOutputStream(from, size());
|
||||
}
|
||||
|
||||
SafeByteArrayOutputStream binaryOutputStream(int from, int to);
|
||||
|
||||
boolean equals(int aStartIndex, Buf b, int bStartIndex, int length);
|
||||
|
||||
boolean equals(int aStartIndex, byte[] b, int bStartIndex, int length);
|
||||
|
||||
default String toString(Charset charset) {
|
||||
return new String(this.asArray(), charset);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package it.cavallium.buffer;
|
||||
|
||||
import it.cavallium.stream.SafeByteArrayInputStream;
|
||||
import it.cavallium.stream.SafeDataInputStream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class BufDataInput extends SafeDataInputStream {
|
||||
|
||||
/**
|
||||
* Creates a DataInputStream that uses the specified underlying InputStream.
|
||||
*
|
||||
* @param in the specified input stream
|
||||
*/
|
||||
private BufDataInput(@NotNull SafeByteArrayInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public static BufDataInput create(Buf byteList) {
|
||||
return new BufDataInput(byteList.binaryInputStream());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int readlimit) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,247 @@
|
||||
package it.cavallium.buffer;
|
||||
|
||||
import static it.cavallium.stream.SafeDataOutputStream.strLen;
|
||||
import static it.cavallium.stream.SafeDataOutputStream.utfLen;
|
||||
|
||||
import it.cavallium.stream.SafeByteArrayOutputStream;
|
||||
import it.cavallium.stream.SafeDataOutputStream;
|
||||
import it.unimi.dsi.fastutil.Arrays;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BufDataOutput implements DataOutput {
|
||||
|
||||
private final SafeByteArrayOutputStream buf;
|
||||
private final SafeDataOutputStream dOut;
|
||||
private final int limit;
|
||||
|
||||
private BufDataOutput(SafeByteArrayOutputStream buf) {
|
||||
this.buf = buf;
|
||||
this.dOut = new SafeDataOutputStream(buf);
|
||||
limit = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
private BufDataOutput(SafeByteArrayOutputStream buf, int maxSize) {
|
||||
this.buf = buf;
|
||||
this.dOut = new SafeDataOutputStream(buf);
|
||||
this.limit = maxSize;
|
||||
}
|
||||
|
||||
public static BufDataOutput createLimited(int maxSize, int hint) {
|
||||
if (hint >= 0) {
|
||||
if (maxSize < 0 || maxSize == Integer.MAX_VALUE) {
|
||||
return create(hint);
|
||||
} else {
|
||||
return new BufDataOutput(new SafeByteArrayOutputStream(Math.min(maxSize, hint)), maxSize);
|
||||
}
|
||||
} else {
|
||||
return createLimited(maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
public static BufDataOutput createLimited(int maxSize) {
|
||||
if (maxSize < 0 || maxSize == Integer.MAX_VALUE) {
|
||||
return create();
|
||||
} else {
|
||||
return new BufDataOutput(new SafeByteArrayOutputStream(maxSize), maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
public static BufDataOutput create() {
|
||||
return new BufDataOutput(new SafeByteArrayOutputStream());
|
||||
}
|
||||
|
||||
public static BufDataOutput create(int hint) {
|
||||
if (hint >= 0) {
|
||||
return new BufDataOutput(new SafeByteArrayOutputStream(hint));
|
||||
} else {
|
||||
return create();
|
||||
}
|
||||
}
|
||||
|
||||
public static BufDataOutput wrap(Buf buf, int from, int to) {
|
||||
Arrays.ensureFromTo(buf.size(), from, to);
|
||||
if (buf.isEmpty()) {
|
||||
return createLimited(0);
|
||||
} else {
|
||||
return new BufDataOutput(buf.binaryOutputStream(from), to - from);
|
||||
}
|
||||
}
|
||||
|
||||
public static BufDataOutput wrap(Buf buf) {
|
||||
if (buf.isEmpty()) {
|
||||
return createLimited(0);
|
||||
} else {
|
||||
return new BufDataOutput(buf.binaryOutputStream(), buf.size());
|
||||
}
|
||||
}
|
||||
|
||||
private IllegalStateException unreachable(IOException ex) {
|
||||
return new IllegalStateException(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
// Fast inlined checkOutOfBounds
|
||||
if (dOut.size() >= limit) {
|
||||
throw new IndexOutOfBoundsException(limit);
|
||||
}
|
||||
|
||||
dOut.write(b);
|
||||
}
|
||||
|
||||
private void checkOutOfBounds(int delta) {
|
||||
if (dOut.size() + delta > limit) {
|
||||
throw new IndexOutOfBoundsException(limit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte @NotNull [] b) {
|
||||
checkOutOfBounds(b.length);
|
||||
dOut.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte @NotNull [] b, int off, int len) {
|
||||
checkOutOfBounds(Math.max(0, Math.min(b.length - off, len)));
|
||||
dOut.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(boolean v) {
|
||||
// Fast inlined checkOutOfBounds
|
||||
if (dOut.size() >= limit) {
|
||||
throw new IndexOutOfBoundsException(limit);
|
||||
}
|
||||
|
||||
dOut.writeBoolean(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(int v) {
|
||||
this.write(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeShort(int v) {
|
||||
checkOutOfBounds(Short.BYTES);
|
||||
dOut.writeShort(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChar(int v) {
|
||||
checkOutOfBounds(Character.BYTES);
|
||||
dOut.writeChar(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInt(int v) {
|
||||
checkOutOfBounds(Integer.BYTES);
|
||||
dOut.writeInt(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLong(long v) {
|
||||
checkOutOfBounds(Long.BYTES);
|
||||
dOut.writeLong(v);
|
||||
}
|
||||
|
||||
public void writeInt52(long v) {
|
||||
checkOutOfBounds(7);
|
||||
dOut.writeInt52(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFloat(float v) {
|
||||
checkOutOfBounds(Float.BYTES);
|
||||
dOut.writeFloat(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(double v) {
|
||||
checkOutOfBounds(Double.BYTES);
|
||||
dOut.writeDouble(v);
|
||||
}
|
||||
|
||||
public void ensureWritable(int size) {
|
||||
dOut.flush();
|
||||
buf.ensureWritable(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBytes(@NotNull String s) {
|
||||
checkOutOfBounds(s.length() * Byte.BYTES);
|
||||
dOut.writeBytes(s);
|
||||
}
|
||||
|
||||
// todo: check
|
||||
public void writeBytes(Buf deserialized) {
|
||||
checkOutOfBounds(deserialized.size());
|
||||
deserialized.writeTo(dOut);
|
||||
}
|
||||
|
||||
public void writeBytes(byte[] b, int off, int len) {
|
||||
write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChars(@NotNull String s) {
|
||||
checkOutOfBounds(Character.BYTES * s.length());
|
||||
dOut.writeChars(s);
|
||||
}
|
||||
|
||||
private static String tooLongMsg(String s, int bits32) {
|
||||
int slen = s.length();
|
||||
String head = s.substring(0, 8);
|
||||
String tail = s.substring(slen - 8, slen);
|
||||
// handle int overflow with max 3x expansion
|
||||
long actualLength = (long)slen + Integer.toUnsignedLong(bits32 - slen);
|
||||
return "encoded string (" + head + "..." + tail + ") too long: "
|
||||
+ actualLength + " bytes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUTF(@NotNull String str) {
|
||||
var strlen = strLen(str);
|
||||
var utflen = utfLen(str, strlen);
|
||||
var bytes = Short.BYTES + utflen;
|
||||
checkOutOfBounds(bytes);
|
||||
dOut.writeUTF(strlen, utflen, str);
|
||||
}
|
||||
|
||||
public Buf asList() {
|
||||
dOut.flush();
|
||||
return Buf.wrap(this.buf.array, this.buf.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return dOut.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return dOut.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BufDataOutput that = (BufDataOutput) o;
|
||||
|
||||
return Objects.equals(dOut, that.dOut);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return dOut.size();
|
||||
}
|
||||
}
|
@ -0,0 +1,467 @@
|
||||
package it.cavallium.buffer;
|
||||
|
||||
import it.cavallium.stream.SafeByteArrayInputStream;
|
||||
import it.cavallium.stream.SafeByteArrayOutputStream;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.bytes.AbstractByteList;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteCollection;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteConsumer;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteIterator;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteIterators;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteList;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteListIterator;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteSpliterator;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteSpliterators;
|
||||
import java.io.Serial;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class ByteListBuf extends ByteArrayList implements Buf {
|
||||
|
||||
private boolean mutable = true;
|
||||
|
||||
protected ByteListBuf(byte[] a, boolean wrapped) {
|
||||
super(a, wrapped);
|
||||
}
|
||||
|
||||
public ByteListBuf(int capacity) {
|
||||
super(capacity);
|
||||
}
|
||||
|
||||
public ByteListBuf() {
|
||||
}
|
||||
|
||||
public ByteListBuf(Collection<? extends Byte> c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
public ByteListBuf(ByteCollection c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
public ByteListBuf(ByteList l) {
|
||||
super(l);
|
||||
}
|
||||
|
||||
public ByteListBuf(byte[] a) {
|
||||
super(a);
|
||||
}
|
||||
|
||||
public ByteListBuf(byte[] a, int offset, int length) {
|
||||
super(a, offset, length);
|
||||
}
|
||||
|
||||
public ByteListBuf(Iterator<? extends Byte> i) {
|
||||
super(i);
|
||||
}
|
||||
|
||||
public ByteListBuf(ByteIterator i) {
|
||||
super(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a given array into an array list of given size.
|
||||
*
|
||||
* <p>
|
||||
* Note it is guaranteed that the type of the array returned by {@link #elements()} will be the same
|
||||
* (see the comments in the class documentation).
|
||||
*
|
||||
* @param a an array to wrap.
|
||||
* @param length the length of the resulting array list.
|
||||
* @return a new array list of the given size, wrapping the given array.
|
||||
*/
|
||||
public static ByteListBuf wrap(final byte[] a, final int length) {
|
||||
if (length > a.length) throw new IllegalArgumentException("The specified length (" + length + ") is greater than the array size (" + a.length + ")");
|
||||
final ByteListBuf l = new ByteListBuf(a, true);
|
||||
l.size = length;
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a given array into an array list.
|
||||
*
|
||||
* <p>
|
||||
* Note it is guaranteed that the type of the array returned by {@link #elements()} will be the same
|
||||
* (see the comments in the class documentation).
|
||||
*
|
||||
* @param a an array to wrap.
|
||||
* @return a new array list wrapping the given array.
|
||||
*/
|
||||
public static ByteListBuf wrap(final byte[] a) {
|
||||
return wrap(a, a.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty array list.
|
||||
*
|
||||
* @return a new empty array list.
|
||||
*/
|
||||
public static ByteListBuf of() {
|
||||
return new ByteListBuf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array list using an array of elements.
|
||||
*
|
||||
* @param init a the array the will become the new backing array of the array list.
|
||||
* @return a new array list backed by the given array.
|
||||
* @see #wrap
|
||||
*/
|
||||
|
||||
public static ByteListBuf of(final byte... init) {
|
||||
return wrap(init);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @NotNull [] asArray() {
|
||||
if (this.size() == a.length) {
|
||||
return this.a;
|
||||
} else {
|
||||
return this.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] asArrayStrict() {
|
||||
if (this.size() == a.length) {
|
||||
return a;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] asUnboundedArray() {
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] asUnboundedArrayStrict() {
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return mutable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeze() {
|
||||
mutable = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Buf subList(int from, int to) {
|
||||
if (from == 0 && to == size()) return this;
|
||||
ensureIndex(from);
|
||||
ensureIndex(to);
|
||||
if (from > to) throw new IndexOutOfBoundsException("Start index (" + from + ") is greater than end index (" + to + ")");
|
||||
return new SubList(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Buf copy() {
|
||||
var copied = ByteListBuf.wrap(this.a.clone());
|
||||
copied.size = this.size;
|
||||
return copied;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SafeByteArrayInputStream binaryInputStream() {
|
||||
return new SafeByteArrayInputStream(this.a, 0, this.size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(SafeDataOutput dataOutput) {
|
||||
dataOutput.write(this.a, 0, this.size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SafeByteArrayOutputStream binaryOutputStream(int from, int to) {
|
||||
it.unimi.dsi.fastutil.Arrays.ensureFromTo(size, from, to);
|
||||
return new SafeByteArrayOutputStream(a, from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(int aStartIndex, Buf b, int bStartIndex, int length) {
|
||||
return b.equals(bStartIndex, this.a, aStartIndex, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(int aStartIndex, byte[] b, int bStartIndex, int length) {
|
||||
if (aStartIndex < 0) return false;
|
||||
if (aStartIndex + length > this.size) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(a, aStartIndex, aStartIndex + length, b, bStartIndex, bStartIndex + length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Charset charset) {
|
||||
return new String(a, 0, size, charset);
|
||||
}
|
||||
|
||||
private class SubList extends AbstractByteList.ByteRandomAccessSubList implements Buf {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -3185226345314976296L;
|
||||
|
||||
private boolean subMutable = true;
|
||||
|
||||
protected SubList(int from, int to) {
|
||||
super(ByteListBuf.this, from, to);
|
||||
}
|
||||
|
||||
// Most of the inherited methods should be fine, but we can override a few of them for performance.
|
||||
// Needed because we can't access the parent class' instance variables directly in a different
|
||||
// instance of SubList.
|
||||
private byte[] getParentArray() {
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Buf subList(int from, int to) {
|
||||
it.unimi.dsi.fastutil.Arrays.ensureFromTo(a.length, from, to);
|
||||
if (from > to) throw new IllegalArgumentException("Start index (" + from + ") is greater than end index (" + to + ")");
|
||||
// Sadly we have to rewrap this, because if there is a sublist of a sublist, and the
|
||||
// subsublist adds, both sublists need to update their "to" value.
|
||||
return new SubList(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Buf copy() {
|
||||
return Buf.wrap(Arrays.copyOfRange(a, from, to));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SafeByteArrayInputStream binaryInputStream() {
|
||||
return new SafeByteArrayInputStream(a, from, size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(SafeDataOutput dataOutput) {
|
||||
dataOutput.write(a, from, size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SafeByteArrayOutputStream binaryOutputStream(int from, int to) {
|
||||
it.unimi.dsi.fastutil.Arrays.ensureFromTo(size(), from, to);
|
||||
return new SafeByteArrayOutputStream(a, from + this.from, to + this.from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(int aStartIndex, Buf b, int bStartIndex, int length) {
|
||||
return b.equals(bStartIndex, a, aStartIndex + from, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(int aStartIndex, byte[] b, int bStartIndex, int length) {
|
||||
var aFrom = from + aStartIndex;
|
||||
var aTo = from + aStartIndex + length;
|
||||
if (aFrom < from) return false;
|
||||
if (aTo > to) return false;
|
||||
return Arrays.equals(a, aFrom, aTo, b, bStartIndex, bStartIndex + length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getByte(int i) {
|
||||
ensureRestrictedIndex(i);
|
||||
return a[i + from];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @NotNull [] asArray() {
|
||||
if (this.from == 0 && this.to == a.length) {
|
||||
return a;
|
||||
} else {
|
||||
return toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] asArrayStrict() {
|
||||
if (this.from == 0 && this.to == a.length) {
|
||||
return a;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] asUnboundedArray() {
|
||||
if (from == 0) {
|
||||
return a;
|
||||
} else {
|
||||
return toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte @Nullable [] asUnboundedArrayStrict() {
|
||||
if (from == 0) {
|
||||
return a;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return mutable && subMutable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void freeze() {
|
||||
subMutable = false;
|
||||
}
|
||||
|
||||
private final class SubListIterator extends ByteIterators.AbstractIndexBasedListIterator {
|
||||
// We are using pos == 0 to be 0 relative to SubList.from (meaning you need to do a[from + i] when
|
||||
// accessing array).
|
||||
SubListIterator(int index) {
|
||||
super(0, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte get(int i) {
|
||||
return a[from + i];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void add(int i, byte k) {
|
||||
ByteListBuf.SubList.this.add(i, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void set(int i, byte k) {
|
||||
ByteListBuf.SubList.this.set(i, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void remove(int i) {
|
||||
ByteListBuf.SubList.this.removeByte(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxPos() {
|
||||
return to - from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte nextByte() {
|
||||
if (!hasNext()) throw new NoSuchElementException();
|
||||
return a[from + (lastReturned = pos++)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte previousByte() {
|
||||
if (!hasPrevious()) throw new NoSuchElementException();
|
||||
return a[from + (lastReturned = --pos)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachRemaining(final ByteConsumer action) {
|
||||
final int max = to - from;
|
||||
while (pos < max) {
|
||||
action.accept(a[from + (lastReturned = pos++)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ByteListIterator listIterator(int index) {
|
||||
return new ByteListBuf.SubList.SubListIterator(index);
|
||||
}
|
||||
|
||||
private final class SubListSpliterator extends ByteSpliterators.LateBindingSizeIndexBasedSpliterator {
|
||||
// We are using pos == 0 to be 0 relative to real array 0
|
||||
SubListSpliterator() {
|
||||
super(from);
|
||||
}
|
||||
|
||||
private SubListSpliterator(int pos, int maxPos) {
|
||||
super(pos, maxPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxPosFromBackingStore() {
|
||||
return to;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte get(int i) {
|
||||
return a[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteListBuf.SubList.SubListSpliterator makeForSplit(int pos, int maxPos) {
|
||||
return new ByteListBuf.SubList.SubListSpliterator(pos, maxPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(final ByteConsumer action) {
|
||||
if (pos >= getMaxPos()) return false;
|
||||
action.accept(a[pos++]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachRemaining(final ByteConsumer action) {
|
||||
final int max = getMaxPos();
|
||||
while (pos < max) {
|
||||
action.accept(a[pos++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteSpliterator spliterator() {
|
||||
return new ByteListBuf.SubList.SubListSpliterator();
|
||||
}
|
||||
|
||||
boolean contentsEquals(byte[] otherA, int otherAFrom, int otherATo) {
|
||||
if (a == otherA && from == otherAFrom && to == otherATo) return true;
|
||||
return Arrays.equals(a, from, to, otherA, otherAFrom, otherATo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
if (o == null) return false;
|
||||
if (!(o instanceof java.util.List)) return false;
|
||||
if (o instanceof ByteListBuf other) {
|
||||
return contentsEquals(other.a, 0, other.size());
|
||||
}
|
||||
if (o instanceof SubList other) {
|
||||
return contentsEquals(other.getParentArray(), other.from, other.to);
|
||||
}
|
||||
return super.equals(o);
|
||||
}
|
||||
|
||||
int contentsCompareTo(byte[] otherA, int otherAFrom, int otherATo) {
|
||||
if (a == otherA && from == otherAFrom && to == otherATo) return 0;
|
||||
return Arrays.compareUnsigned(a, from, to, otherA, otherAFrom, otherATo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final java.util.@NotNull List<? extends Byte> l) {
|
||||
if (l instanceof ByteListBuf other) {
|
||||
return contentsCompareTo(other.a, 0, other.size());
|
||||
}
|
||||
if (l instanceof ByteListBuf.SubList other) {
|
||||
return contentsCompareTo(other.getParentArray(), other.from, other.to);
|
||||
}
|
||||
return super.compareTo(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Charset charset) {
|
||||
return new String(a, from, to, charset);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
package it.cavallium.data.generator;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface DataSerializer<T> {
|
||||
|
||||
void serialize(DataOutput dataOutput, @NotNull T data) throws IOException;
|
||||
void serialize(SafeDataOutput dataOutput, @NotNull T data);
|
||||
|
||||
@NotNull T deserialize(DataInput dataInput) throws IOException;
|
||||
@NotNull T deserialize(SafeDataInput dataInput);
|
||||
}
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArrayInt52Serializer implements DataSerializer< List<Int52>> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, List<Int52> data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, List<Int52> data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (Int52 item : data) {
|
||||
Int52Serializer.INSTANCE.serialize(dataOutput, item);
|
||||
@ -19,7 +18,7 @@ public class ArrayInt52Serializer implements DataSerializer< List<Int52>> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Int52> deserialize(DataInput dataInput) throws IOException {
|
||||
public List<Int52> deserialize(SafeDataInput dataInput) {
|
||||
var data = new Int52[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = Int52Serializer.INSTANCE.deserialize(dataInput);
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArrayStringSerializer implements DataSerializer<List<String>> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull List<String> data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull List<String> data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (String item : data) {
|
||||
dataOutput.writeUTF(item);
|
||||
@ -19,7 +18,7 @@ public class ArrayStringSerializer implements DataSerializer<List<String>> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> deserialize(DataInput dataInput) throws IOException {
|
||||
public List<String> deserialize(SafeDataInput dataInput) {
|
||||
var data = new String[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readUTF();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArraybooleanSerializer implements DataSerializer<BooleanList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull BooleanList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull BooleanList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeBoolean(data.getBoolean(i));
|
||||
@ -19,7 +18,7 @@ public class ArraybooleanSerializer implements DataSerializer<BooleanList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public BooleanList deserialize(DataInput dataInput) throws IOException {
|
||||
public BooleanList deserialize(SafeDataInput dataInput) {
|
||||
var data = new boolean[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readBoolean();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArraybyteSerializer implements DataSerializer<ByteList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull ByteList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull ByteList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeByte(data.getByte(i));
|
||||
@ -19,7 +18,7 @@ public class ArraybyteSerializer implements DataSerializer<ByteList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ByteList deserialize(DataInput dataInput) throws IOException {
|
||||
public ByteList deserialize(SafeDataInput dataInput) {
|
||||
var data = new byte[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readByte();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.chars.CharList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArraycharSerializer implements DataSerializer<CharList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull CharList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull CharList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeChar(data.getChar(i));
|
||||
@ -19,7 +18,7 @@ public class ArraycharSerializer implements DataSerializer<CharList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CharList deserialize(DataInput dataInput) throws IOException {
|
||||
public CharList deserialize(SafeDataInput dataInput) {
|
||||
var data = new char[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readChar();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArraydoubleSerializer implements DataSerializer<DoubleList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull DoubleList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull DoubleList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeDouble(data.getDouble(i));
|
||||
@ -19,7 +18,7 @@ public class ArraydoubleSerializer implements DataSerializer<DoubleList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DoubleList deserialize(DataInput dataInput) throws IOException {
|
||||
public DoubleList deserialize(SafeDataInput dataInput) {
|
||||
var data = new double[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readDouble();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.floats.FloatList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArrayfloatSerializer implements DataSerializer<FloatList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull FloatList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull FloatList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeFloat(data.getFloat(i));
|
||||
@ -19,7 +18,7 @@ public class ArrayfloatSerializer implements DataSerializer<FloatList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public FloatList deserialize(DataInput dataInput) throws IOException {
|
||||
public FloatList deserialize(SafeDataInput dataInput) {
|
||||
var data = new float[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readFloat();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArrayintSerializer implements DataSerializer<IntList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull IntList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull IntList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeInt(data.getInt(i));
|
||||
@ -19,7 +18,7 @@ public class ArrayintSerializer implements DataSerializer<IntList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public IntList deserialize(DataInput dataInput) throws IOException {
|
||||
public IntList deserialize(SafeDataInput dataInput) {
|
||||
var data = new int[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readInt();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArraylongSerializer implements DataSerializer<LongList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull LongList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull LongList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeLong(data.getLong(i));
|
||||
@ -19,7 +18,7 @@ public class ArraylongSerializer implements DataSerializer<LongList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LongList deserialize(DataInput dataInput) throws IOException {
|
||||
public LongList deserialize(SafeDataInput dataInput) {
|
||||
var data = new long[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readLong();
|
||||
|
@ -1,16 +1,15 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArrayshortSerializer implements DataSerializer<ShortList> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull ShortList data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull ShortList data) {
|
||||
dataOutput.writeInt(data.size());
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
dataOutput.writeShort(data.getShort(i));
|
||||
@ -19,7 +18,7 @@ public class ArrayshortSerializer implements DataSerializer<ShortList> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ShortList deserialize(DataInput dataInput) throws IOException {
|
||||
public ShortList deserialize(SafeDataInput dataInput) {
|
||||
var data = new short[dataInput.readInt()];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
data[i] = dataInput.readShort();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Int52Serializer implements DataSerializer<Int52> {
|
||||
@ -11,17 +10,17 @@ public class Int52Serializer implements DataSerializer<Int52> {
|
||||
public static final Int52Serializer INSTANCE = new Int52Serializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Int52 data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Int52 data) {
|
||||
serializeValue(dataOutput, data);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Int52 deserialize(DataInput dataInput) throws IOException {
|
||||
public Int52 deserialize(SafeDataInput dataInput) {
|
||||
return deserializeValue(dataInput);
|
||||
}
|
||||
|
||||
public static void serializeValue(DataOutput dataOutput, @NotNull Int52 data) throws IOException {
|
||||
public static void serializeValue(SafeDataOutput dataOutput, @NotNull Int52 data) {
|
||||
long value = data.getValue();
|
||||
|
||||
for(int i = 0; i < 7; i++) {
|
||||
@ -29,18 +28,9 @@ public class Int52Serializer implements DataSerializer<Int52> {
|
||||
}
|
||||
}
|
||||
|
||||
public static Int52 deserializeValue(DataInput dataInput) throws IOException {
|
||||
long value = 0;
|
||||
|
||||
return Int52.fromLong(
|
||||
((long) dataInput.readUnsignedByte() & 0b00001111) << 48
|
||||
| ((long) dataInput.readUnsignedByte()) << 40
|
||||
| ((long) dataInput.readUnsignedByte() << 32)
|
||||
| ((long) dataInput.readUnsignedByte() << 24)
|
||||
| ((long) dataInput.readUnsignedByte()) << 16
|
||||
| ((long) dataInput.readUnsignedByte()) << 8
|
||||
| ((long) dataInput.readUnsignedByte())
|
||||
);
|
||||
public static Int52 deserializeValue(SafeDataInput dataInput) {
|
||||
long value = dataInput.readInt52();
|
||||
return Int52.fromLong(value);
|
||||
}
|
||||
|
||||
public static byte[] toByteArray(long value) {
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullableInt52Serializer implements DataSerializer<NullableInt52> {
|
||||
@ -11,7 +10,7 @@ public class NullableInt52Serializer implements DataSerializer<NullableInt52> {
|
||||
public static final NullableInt52Serializer INSTANCE = new NullableInt52Serializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull NullableInt52 data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull NullableInt52 data) {
|
||||
// 0b10000000 = empty, 0b00000000 = with value
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeByte(0b10000000);
|
||||
@ -22,7 +21,7 @@ public class NullableInt52Serializer implements DataSerializer<NullableInt52> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NullableInt52 deserialize(DataInput dataInput) throws IOException {
|
||||
public NullableInt52 deserialize(SafeDataInput dataInput) {
|
||||
// 0b10000000 = empty, 0b00000000 = with value
|
||||
byte firstByteAndIsPresent = dataInput.readByte();
|
||||
if ((firstByteAndIsPresent & 0b10000000) != 0) {
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullableStringSerializer implements DataSerializer<NullableString> {
|
||||
@ -11,7 +10,7 @@ public class NullableStringSerializer implements DataSerializer<NullableString>
|
||||
public static final NullableStringSerializer INSTANCE = new NullableStringSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull NullableString data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull NullableString data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullableStringSerializer implements DataSerializer<NullableString>
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public NullableString deserialize(DataInput dataInput) throws IOException {
|
||||
public NullableString deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return NullableString.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullablebooleanSerializer implements DataSerializer<Nullableboolean> {
|
||||
@ -11,7 +10,7 @@ public class NullablebooleanSerializer implements DataSerializer<Nullableboolean
|
||||
public static final NullablebooleanSerializer INSTANCE = new NullablebooleanSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullableboolean data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullableboolean data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullablebooleanSerializer implements DataSerializer<Nullableboolean
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullableboolean deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullableboolean deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullableboolean.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullablebyteSerializer implements DataSerializer<Nullablebyte> {
|
||||
@ -11,7 +10,7 @@ public class NullablebyteSerializer implements DataSerializer<Nullablebyte> {
|
||||
public static final NullablebyteSerializer INSTANCE = new NullablebyteSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullablebyte data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullablebyte data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullablebyteSerializer implements DataSerializer<Nullablebyte> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullablebyte deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullablebyte deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullablebyte.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullablecharSerializer implements DataSerializer<Nullablechar> {
|
||||
@ -11,7 +10,7 @@ public class NullablecharSerializer implements DataSerializer<Nullablechar> {
|
||||
public static final NullablecharSerializer INSTANCE = new NullablecharSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullablechar data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullablechar data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullablecharSerializer implements DataSerializer<Nullablechar> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullablechar deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullablechar deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullablechar.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullabledoubleSerializer implements DataSerializer<Nullabledouble> {
|
||||
@ -11,7 +10,7 @@ public class NullabledoubleSerializer implements DataSerializer<Nullabledouble>
|
||||
public static final NullabledoubleSerializer INSTANCE = new NullabledoubleSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullabledouble data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullabledouble data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullabledoubleSerializer implements DataSerializer<Nullabledouble>
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullabledouble deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullabledouble deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullabledouble.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullablefloatSerializer implements DataSerializer<Nullablefloat> {
|
||||
@ -11,7 +10,7 @@ public class NullablefloatSerializer implements DataSerializer<Nullablefloat> {
|
||||
public static final NullablefloatSerializer INSTANCE = new NullablefloatSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullablefloat data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullablefloat data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullablefloatSerializer implements DataSerializer<Nullablefloat> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullablefloat deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullablefloat deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullablefloat.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullableintSerializer implements DataSerializer<Nullableint> {
|
||||
@ -11,7 +10,7 @@ public class NullableintSerializer implements DataSerializer<Nullableint> {
|
||||
public static final NullableintSerializer INSTANCE = new NullableintSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullableint data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullableint data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullableintSerializer implements DataSerializer<Nullableint> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullableint deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullableint deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullableint.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullablelongSerializer implements DataSerializer<Nullablelong> {
|
||||
@ -11,7 +10,7 @@ public class NullablelongSerializer implements DataSerializer<Nullablelong> {
|
||||
public static final NullablelongSerializer INSTANCE = new NullablelongSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullablelong data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullablelong data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullablelongSerializer implements DataSerializer<Nullablelong> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullablelong deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullablelong deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullablelong.empty();
|
||||
|
@ -1,9 +1,8 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NullableshortSerializer implements DataSerializer<Nullableshort> {
|
||||
@ -11,7 +10,7 @@ public class NullableshortSerializer implements DataSerializer<Nullableshort> {
|
||||
public static final NullableshortSerializer INSTANCE = new NullableshortSerializer();
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull Nullableshort data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull Nullableshort data) {
|
||||
if (data.isEmpty()) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@ -23,7 +22,7 @@ public class NullableshortSerializer implements DataSerializer<Nullableshort> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Nullableshort deserialize(DataInput dataInput) throws IOException {
|
||||
public Nullableshort deserialize(SafeDataInput dataInput) {
|
||||
var isPresent = dataInput.readBoolean();
|
||||
if (!isPresent) {
|
||||
return Nullableshort.empty();
|
||||
|
@ -1,19 +1,16 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import it.cavallium.data.generator.DataSerializer;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import it.cavallium.stream.SafeDataInput;
|
||||
import it.cavallium.stream.SafeDataOutput;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CharsetEncoder;
|
||||
import java.nio.charset.CodingErrorAction;
|
||||
import java.nio.charset.MalformedInputException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.UnmappableCharacterException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class StringSerializer implements DataSerializer<String> {
|
||||
@ -31,7 +28,7 @@ public class StringSerializer implements DataSerializer<String> {
|
||||
);
|
||||
|
||||
@Override
|
||||
public void serialize(DataOutput dataOutput, @NotNull String data) throws IOException {
|
||||
public void serialize(SafeDataOutput dataOutput, @NotNull String data) {
|
||||
try {
|
||||
var bytes = UTF8_ENCODER.get().reset().encode(CharBuffer.wrap(data));
|
||||
|
||||
@ -43,21 +40,21 @@ public class StringSerializer implements DataSerializer<String> {
|
||||
dataOutput.writeByte(bytes.get());
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException | CharacterCodingException ex) {
|
||||
throw new IOException("Can't encode this UTF-8 string", ex);
|
||||
} catch (CharacterCodingException ex) {
|
||||
throw new IllegalStateException("Can't encode this UTF-8 string", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String deserialize(DataInput dataInput) throws IOException {
|
||||
public String deserialize(SafeDataInput dataInput) {
|
||||
byte[] bytes = new byte[dataInput.readInt()];
|
||||
dataInput.readFully(bytes);
|
||||
try {
|
||||
CharBuffer decoded = UTF8_DECODER.get().reset().decode(ByteBuffer.wrap(bytes));
|
||||
return decoded.toString();
|
||||
} catch (IllegalStateException | CharacterCodingException ex) {
|
||||
throw new IOException("Can't decode this UTF-8 string", ex);
|
||||
throw new IllegalStateException("Can't decode this UTF-8 string", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Simple {@link InputStream} implementation that exposes currently
|
||||
* available content of a {@link ByteBuffer}.
|
||||
*/
|
||||
public class ByteBufferBackedInputStream extends InputStream {
|
||||
protected final ByteBuffer _b;
|
||||
|
||||
public ByteBufferBackedInputStream(ByteBuffer buf) { _b = buf; }
|
||||
|
||||
@Override public int available() { return _b.remaining(); }
|
||||
|
||||
@Override
|
||||
public int read() { return _b.hasRemaining() ? (_b.get() & 0xFF) : -1; }
|
||||
|
||||
@Override
|
||||
public int read(byte @NotNull [] bytes, int off, int len) {
|
||||
if (!_b.hasRemaining()) return -1;
|
||||
len = Math.min(len, _b.remaining());
|
||||
_b.get(bytes, off, len);
|
||||
return len;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
|
||||
public interface DataInputOutput extends DataInput, DataOutput {
|
||||
|
||||
DataInput getIn();
|
||||
|
||||
DataOutput getOut();
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DataInputOutputImpl implements DataInputOutput {
|
||||
|
||||
private final DataInput in;
|
||||
private final DataOutput out;
|
||||
|
||||
public DataInputOutputImpl(DataInput in, DataOutput out) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataInput getIn() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataOutput getOut() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte @NotNull [] bytes) throws IOException {
|
||||
in.readFully(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte @NotNull [] bytes, int i, int i1) throws IOException {
|
||||
in.readFully(bytes, i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int skipBytes(int i) throws IOException {
|
||||
return in.skipBytes(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean() throws IOException {
|
||||
return in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() throws IOException {
|
||||
return in.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte() throws IOException {
|
||||
return in.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
return in.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort() throws IOException {
|
||||
return in.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar() throws IOException {
|
||||
return in.readChar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
return in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
return in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat() throws IOException {
|
||||
return in.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble() throws IOException {
|
||||
return in.readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readLine() throws IOException {
|
||||
return in.readLine();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String readUTF() throws IOException {
|
||||
return in.readUTF();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int i) throws IOException {
|
||||
out.write(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte @NotNull [] bytes) throws IOException {
|
||||
out.write(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte @NotNull [] bytes, int i, int i1) throws IOException {
|
||||
out.write(bytes, i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(boolean b) throws IOException {
|
||||
out.writeBoolean(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(int i) throws IOException {
|
||||
out.writeByte(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeShort(int i) throws IOException {
|
||||
out.writeShort(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChar(int i) throws IOException {
|
||||
out.writeChar(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInt(int i) throws IOException {
|
||||
out.writeInt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLong(long l) throws IOException {
|
||||
out.writeLong(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFloat(float v) throws IOException {
|
||||
out.writeFloat(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(double v) throws IOException {
|
||||
out.writeDouble(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBytes(@NotNull String s) throws IOException {
|
||||
out.writeBytes(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeChars(@NotNull String s) throws IOException {
|
||||
out.writeChars(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUTF(@NotNull String s) throws IOException {
|
||||
out.writeUTF(s);
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DataInputOutputStream extends DataOutputStream implements DataInputOutput {
|
||||
|
||||
private final DataInputStream in;
|
||||
|
||||
public DataInputOutputStream(DataInputStream in, DataOutputStream out) {
|
||||
super(out);
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataInputStream getIn() {
|
||||
return in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataOutputStream getOut() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte @NotNull [] bytes) throws IOException {
|
||||
in.readFully(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(byte @NotNull [] bytes, int i, int i1) throws IOException {
|
||||
in.readFully(bytes, i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int skipBytes(int i) throws IOException {
|
||||
return in.skipBytes(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean() throws IOException {
|
||||
return in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() throws IOException {
|
||||
return in.readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte() throws IOException {
|
||||
return in.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
return in.readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort() throws IOException {
|
||||
return in.readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar() throws IOException {
|
||||
return in.readChar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
return in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
return in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat() throws IOException {
|
||||
return in.readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble() throws IOException {
|
||||
return in.readDouble();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public String readLine() throws IOException {
|
||||
return in.readLine();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String readUTF() throws IOException {
|
||||
return in.readUTF();
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2022 Sebastiano Vigna
|
||||
*
|
||||
* Licensed 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 it.cavallium.stream;
|
||||
|
||||
/** Simple, fast and repositionable byte-array input stream.
|
||||
*
|
||||
* <p><strong>Warning</strong>: this class implements the correct semantics
|
||||
* of {@link #read(byte[], int, int)} as described in {@link java.io.InputStream}.
|
||||
* The implementation given in {@link java.io.ByteArrayInputStream} is broken,
|
||||
* but it will never be fixed because it's too late.
|
||||
*
|
||||
* @author Sebastiano Vigna
|
||||
*/
|
||||
|
||||
public class SafeByteArrayInputStream extends SafeMeasurableInputStream implements SafeRepositionableStream {
|
||||
|
||||
/** The array backing the input stream. */
|
||||
public byte[] array;
|
||||
|
||||
/** The first valid entry. */
|
||||
public int offset;
|
||||
|
||||
/** The number of valid bytes in {@link #array} starting from {@link #offset}. */
|
||||
public int length;
|
||||
|
||||
/** The current position as a distance from {@link #offset}. */
|
||||
private int position;
|
||||
|
||||
/** The current mark as a position, or -1 if no mark exists. */
|
||||
private int mark;
|
||||
|
||||
/** Creates a new array input stream using a given array fragment.
|
||||
*
|
||||
* @param array the backing array.
|
||||
* @param offset the first valid entry of the array.
|
||||
* @param length the number of valid bytes.
|
||||
*/
|
||||
public SafeByteArrayInputStream(final byte[] array, final int offset, final int length) {
|
||||
this.array = array;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
/** Creates a new array input stream using a given array.
|
||||
*
|
||||
* @param array the backing array.
|
||||
*/
|
||||
public SafeByteArrayInputStream(final byte[] array) {
|
||||
this(array, 0, array.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
position = mark;
|
||||
}
|
||||
|
||||
/** Closing a fast byte array input stream has no effect. */
|
||||
@Override
|
||||
public void close() {}
|
||||
|
||||
@Override
|
||||
public void mark(final int dummy) {
|
||||
mark = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() {
|
||||
return length - position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) {
|
||||
if (n <= length - position) {
|
||||
position += (int)n;
|
||||
return n;
|
||||
}
|
||||
n = length - position;
|
||||
position = length;
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
if (length == position) return -1;
|
||||
return array[offset + position++] & 0xFF;
|
||||
}
|
||||
|
||||
/** Reads bytes from this byte-array input stream as
|
||||
* specified in {@link java.io.InputStream#read(byte[], int, int)}.
|
||||
* Note that the implementation given in {@link java.io.ByteArrayInputStream#read(byte[], int, int)}
|
||||
* will return -1 on a zero-length read at EOF, contrarily to the specification. We won't.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public int read(final byte b[], final int offset, final int length) {
|
||||
if (this.length == this.position) return length == 0 ? 0 : -1;
|
||||
final int n = Math.min(length, this.length - this.position);
|
||||
System.arraycopy(array, this.offset + this.position, b, offset, n);
|
||||
this.position += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void position(final long newPosition) {
|
||||
position = (int)Math.min(newPosition, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length() {
|
||||
return length;
|
||||
}
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2022 Sebastiano Vigna
|
||||
*
|
||||
* Licensed 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 it.cavallium.stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.Arrays;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrays;
|
||||
|
||||
/** Simple, fast byte-array output stream that exposes the backing array.
|
||||
*
|
||||
* <p>{@link java.io.ByteArrayOutputStream} is nice, but to get its content you
|
||||
* must generate each time a new object. This doesn't happen here.
|
||||
*
|
||||
* <p>This class will automatically enlarge the backing array, doubling its
|
||||
* size whenever new space is needed. The {@link #reset()} method will
|
||||
* mark the content as empty, but will not decrease the capacity: use
|
||||
* {@link #trim()} for that purpose.
|
||||
*
|
||||
* @author Sebastiano Vigna
|
||||
*/
|
||||
|
||||
public class SafeByteArrayOutputStream extends SafeMeasurableOutputStream implements SafeRepositionableStream {
|
||||
|
||||
/** The array backing the output stream. */
|
||||
public static final int DEFAULT_INITIAL_CAPACITY = 16;
|
||||
private final boolean wrapped;
|
||||
private final int initialPosition;
|
||||
private final int initialLength;
|
||||
|
||||
/** The array backing the output stream. */
|
||||
public byte[] array;
|
||||
|
||||
/** The number of valid bytes in {@link #array}. */
|
||||
public int length;
|
||||
|
||||
/** The current writing position. */
|
||||
private int position;
|
||||
|
||||
/** Creates a new array output stream with an initial capacity of {@link #DEFAULT_INITIAL_CAPACITY} bytes. */
|
||||
public SafeByteArrayOutputStream() {
|
||||
this(DEFAULT_INITIAL_CAPACITY);
|
||||
}
|
||||
|
||||
/** Creates a new array output stream with a given initial capacity.
|
||||
*
|
||||
* @param initialCapacity the initial length of the backing array.
|
||||
*/
|
||||
public SafeByteArrayOutputStream(final int initialCapacity) {
|
||||
array = new byte[initialCapacity];
|
||||
wrapped = false;
|
||||
initialPosition = 0;
|
||||
initialLength = 0;
|
||||
}
|
||||
|
||||
/** Creates a new array output stream wrapping a given byte array.
|
||||
*
|
||||
* @param a the byte array to wrap.
|
||||
*/
|
||||
public SafeByteArrayOutputStream(final byte[] a) {
|
||||
array = a;
|
||||
wrapped = true;
|
||||
initialPosition = 0;
|
||||
initialLength = a.length;
|
||||
}
|
||||
|
||||
/** Creates a new array output stream wrapping a given byte array.
|
||||
*
|
||||
* @param a the byte array to wrap.
|
||||
*/
|
||||
public SafeByteArrayOutputStream(final byte[] a, int from, int to) {
|
||||
Arrays.ensureFromTo(a.length, from, to);
|
||||
wrapped = true;
|
||||
array = a;
|
||||
initialPosition = from;
|
||||
initialLength = to;
|
||||
position = from;
|
||||
length = to - from;
|
||||
}
|
||||
|
||||
/** Marks this array output stream as empty. */
|
||||
public void reset() {
|
||||
length = initialLength;
|
||||
position = initialPosition;
|
||||
}
|
||||
|
||||
/** Ensures that the length of the backing array is equal to {@link #length}. */
|
||||
public void trim() {
|
||||
if (!wrapped) {
|
||||
array = ByteArrays.trim(array, length);
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureWritable(int size) {
|
||||
growBy(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final int b) {
|
||||
if (position >= array.length) {
|
||||
if (wrapped) {
|
||||
throw new ArrayIndexOutOfBoundsException(position);
|
||||
} else {
|
||||
array = ByteArrays.grow(array, position + 1, length);
|
||||
}
|
||||
}
|
||||
array[position++] = (byte)b;
|
||||
if (length < position) length = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final byte[] b, final int off, final int len) {
|
||||
ByteArrays.ensureOffsetLength(b, off, len);
|
||||
growBy(len);
|
||||
System.arraycopy(b, off, array, position, len);
|
||||
if (position + len > length) length = position += len;
|
||||
}
|
||||
|
||||
private void growBy(int len) {
|
||||
if (position + len > array.length) {
|
||||
if (wrapped) {
|
||||
throw new ArrayIndexOutOfBoundsException(position + len - 1);
|
||||
} else {
|
||||
array = ByteArrays.grow(array, position + len, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void position(final long newPosition) {
|
||||
position = (int)newPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method copies the array
|
||||
*/
|
||||
public byte[] toByteArray() {
|
||||
return java.util.Arrays.copyOf(array, length);
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInput;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A data input stream lets an application read primitive Java data
|
||||
* types from an underlying input stream in a machine-independent
|
||||
* way. An application uses a data output stream to write data that
|
||||
* can later be read by a data input stream.
|
||||
* <p>
|
||||
* DataInputStream is not necessarily safe for multithreaded access.
|
||||
* Thread safety is optional and is the responsibility of users of
|
||||
* methods in this class.
|
||||
*
|
||||
* @author Arthur van Hoff
|
||||
* @see java.io.DataOutputStream
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface SafeDataInput extends Closeable, DataInput {
|
||||
|
||||
/**
|
||||
* Reads some number of bytes from the contained input stream and
|
||||
* stores them into the buffer array {@code b}. The number of
|
||||
* bytes actually read is returned as an integer. This method blocks
|
||||
* until input data is available, end of file is detected, or an
|
||||
* exception is thrown.
|
||||
*
|
||||
* <p>If {@code b} is null, a {@code NullPointerException} is
|
||||
* thrown. If the length of {@code b} is zero, then no bytes are
|
||||
* read and {@code 0} is returned; otherwise, there is an attempt
|
||||
* to read at least one byte. If no byte is available because the
|
||||
* stream is at end of file, the value {@code -1} is returned;
|
||||
* otherwise, at least one byte is read and stored into {@code b}.
|
||||
*
|
||||
* <p>The first byte read is stored into element {@code b[0]}, the
|
||||
* next one into {@code b[1]}, and so on. The number of bytes read
|
||||
* is, at most, equal to the length of {@code b}. Let {@code k}
|
||||
* be the number of bytes actually read; these bytes will be stored in
|
||||
* elements {@code b[0]} through {@code b[k-1]}, leaving
|
||||
* elements {@code b[k]} through {@code b[b.length-1]}
|
||||
* unaffected.
|
||||
*
|
||||
* <p>The {@code read(b)} method has the same effect as:
|
||||
* <blockquote><pre>
|
||||
* read(b, 0, b.length)
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* {@code -1} if there is no more data because the end
|
||||
* of the stream has been reached.
|
||||
* @see SafeFilterInputStream#in
|
||||
* @see java.io.InputStream#read(byte[], int, int)
|
||||
*/
|
||||
int read(byte[] b);
|
||||
|
||||
/**
|
||||
* Reads up to {@code len} bytes of data from the contained
|
||||
* input stream into an array of bytes. An attempt is made to read
|
||||
* as many as {@code len} bytes, but a smaller number may be read,
|
||||
* possibly zero. The number of bytes actually read is returned as an
|
||||
* integer.
|
||||
*
|
||||
* <p> This method blocks until input data is available, end of file is
|
||||
* detected, or an exception is thrown.
|
||||
*
|
||||
* <p> If {@code len} is zero, then no bytes are read and
|
||||
* {@code 0} is returned; otherwise, there is an attempt to read at
|
||||
* least one byte. If no byte is available because the stream is at end of
|
||||
* file, the value {@code -1} is returned; otherwise, at least one
|
||||
* byte is read and stored into {@code b}.
|
||||
*
|
||||
* <p> The first byte read is stored into element {@code b[off]}, the
|
||||
* next one into {@code b[off+1]}, and so on. The number of bytes read
|
||||
* is, at most, equal to {@code len}. Let <i>k</i> be the number of
|
||||
* bytes actually read; these bytes will be stored in elements
|
||||
* {@code b[off]} through {@code b[off+}<i>k</i>{@code -1]},
|
||||
* leaving elements {@code b[off+}<i>k</i>{@code ]} through
|
||||
* {@code b[off+len-1]} unaffected.
|
||||
*
|
||||
* <p> In every case, elements {@code b[0]} through
|
||||
* {@code b[off]} and elements {@code b[off+len]} through
|
||||
* {@code b[b.length-1]} are unaffected.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset in the destination array {@code b}
|
||||
* @param len the maximum number of bytes read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* {@code -1} if there is no more data because the end
|
||||
* of the stream has been reached.
|
||||
* @throws NullPointerException If {@code b} is {@code null}.
|
||||
* @throws IndexOutOfBoundsException If {@code off} is negative,
|
||||
* {@code len} is negative, or {@code len} is greater than
|
||||
* {@code b.length - off}
|
||||
* @see SafeFilterInputStream#in
|
||||
* @see java.io.InputStream#read(byte[], int, int)
|
||||
*/
|
||||
int read(byte[] b, int off, int len);
|
||||
|
||||
void readFully(byte @NotNull [] b);
|
||||
|
||||
void readFully(byte @NotNull [] b, int off, int len);
|
||||
|
||||
int skipBytes(int n);
|
||||
|
||||
boolean readBoolean();
|
||||
|
||||
byte readByte();
|
||||
|
||||
int readUnsignedByte();
|
||||
|
||||
short readShort();
|
||||
|
||||
int readUnsignedShort();
|
||||
|
||||
char readChar();
|
||||
|
||||
int readInt();
|
||||
|
||||
long readLong();
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readInt52}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next seven bytes of this input stream, interpreted as a
|
||||
* {@code Int52}.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
long readInt52();
|
||||
|
||||
float readFloat();
|
||||
|
||||
double readDouble();
|
||||
|
||||
@Deprecated
|
||||
String readLine();
|
||||
|
||||
@NotNull String readUTF();
|
||||
}
|
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package it.cavallium.stream;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SafeDataInputStream extends SafeFilterInputStream implements SafeDataInput {
|
||||
|
||||
/**
|
||||
* Creates a DataInputStream that uses the specified
|
||||
* underlying InputStream.
|
||||
*
|
||||
* @param in the specified input stream
|
||||
*/
|
||||
public SafeDataInputStream(SafeInputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* working arrays initialized on demand by readUTF
|
||||
*/
|
||||
private byte[] bytearr = new byte[80];
|
||||
private char[] chararr = new char[80];
|
||||
|
||||
@Override
|
||||
public final int read(byte[] b) {
|
||||
return in.read(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int read(byte[] b, int off, int len) {
|
||||
return in.read(b, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readFully}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @throws NullPointerException if {@code b} is {@code null}.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final void readFully(byte @NotNull [] b) {
|
||||
readFully(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readFully}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset in the data array {@code b}.
|
||||
* @param len the number of bytes to read.
|
||||
* @throws NullPointerException if {@code b} is {@code null}.
|
||||
* @throws IndexOutOfBoundsException if {@code off} is negative,
|
||||
* {@code len} is negative, or {@code len} is greater than
|
||||
* {@code b.length - off}.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final void readFully(byte @NotNull [] b, int off, int len) {
|
||||
if (len < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
int n = 0;
|
||||
while (n < len) {
|
||||
int count = in.read(b, off + n, len - n);
|
||||
if (count < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
n += count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code skipBytes}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @param n the number of bytes to be skipped.
|
||||
* @return the actual number of bytes skipped.
|
||||
*/
|
||||
@Override
|
||||
public final int skipBytes(int n) {
|
||||
int total = 0;
|
||||
int cur;
|
||||
|
||||
while ((total<n) && ((cur = (int) in.skip(n-total)) > 0)) {
|
||||
total += cur;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readBoolean}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the {@code boolean} value read.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final boolean readBoolean() {
|
||||
int ch = in.read();
|
||||
if (ch < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (ch != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readByte}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next byte of this input stream as a signed 8-bit
|
||||
* {@code byte}.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final byte readByte() {
|
||||
int ch = in.read();
|
||||
if (ch < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (byte)(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readUnsignedByte}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next byte of this input stream, interpreted as an
|
||||
* unsigned 8-bit number.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final int readUnsignedByte() {
|
||||
int ch = in.read();
|
||||
if (ch < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readShort}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next two bytes of this input stream, interpreted as a
|
||||
* signed 16-bit number.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final short readShort() {
|
||||
int ch1 = in.read();
|
||||
int ch2 = in.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (short)((ch1 << 8) + (ch2));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readUnsignedShort}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next two bytes of this input stream, interpreted as an
|
||||
* unsigned 16-bit integer.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final int readUnsignedShort() {
|
||||
int ch1 = in.read();
|
||||
int ch2 = in.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (ch1 << 8) + (ch2);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readChar}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next two bytes of this input stream, interpreted as a
|
||||
* {@code char}.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final char readChar() {
|
||||
int ch1 = in.read();
|
||||
int ch2 = in.read();
|
||||
if ((ch1 | ch2) < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (char)((ch1 << 8) + (ch2));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readInt}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next four bytes of this input stream, interpreted as an
|
||||
* {@code int}.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final int readInt() {
|
||||
int ch1 = in.read();
|
||||
int ch2 = in.read();
|
||||
int ch3 = in.read();
|
||||
int ch4 = in.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4));
|
||||
}
|
||||
|
||||
private final byte[] readBuffer = new byte[8];
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readLong}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next eight bytes of this input stream, interpreted as a
|
||||
* {@code long}.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
public final long readLong() {
|
||||
readFully(readBuffer, 0, 8);
|
||||
return (((long)readBuffer[0] << 56) +
|
||||
((long)(readBuffer[1] & 255) << 48) +
|
||||
((long)(readBuffer[2] & 255) << 40) +
|
||||
((long)(readBuffer[3] & 255) << 32) +
|
||||
((long)(readBuffer[4] & 255) << 24) +
|
||||
((readBuffer[5] & 255) << 16) +
|
||||
((readBuffer[6] & 255) << 8) +
|
||||
((readBuffer[7] & 255)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long readInt52() {
|
||||
readFully(readBuffer, 0, 7);
|
||||
return ((long) (readBuffer[0] & 0xf) << 48)
|
||||
+ ((long) (readBuffer[1] & 0xff) << 40)
|
||||
+ ((long) (readBuffer[2] & 0xff) << 32)
|
||||
+ ((long) (readBuffer[3] & 0xff) << 24)
|
||||
+ ((long) (readBuffer[4] & 0xff) << 16)
|
||||
+ ((long) (readBuffer[5] & 0xff) << 8)
|
||||
+ (long) (readBuffer[6] & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readFloat}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next four bytes of this input stream, interpreted as a
|
||||
* {@code float}.
|
||||
* @see SafeDataInputStream#readInt()
|
||||
* @see Float#intBitsToFloat(int)
|
||||
*/
|
||||
@Override
|
||||
public final float readFloat() {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readDouble}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return the next eight bytes of this input stream, interpreted as a
|
||||
* {@code double}.
|
||||
* @see SafeDataInputStream#readLong()
|
||||
* @see Double#longBitsToDouble(long)
|
||||
*/
|
||||
@Override
|
||||
public final double readDouble() {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
private char[] lineBuffer;
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readLine}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @deprecated This method does not properly convert bytes to characters.
|
||||
* As of JDK 1.1, the preferred way to read lines of text is via the
|
||||
* {@code BufferedReader.readLine()} method. Programs that use the
|
||||
* {@code DataInputStream} class to read lines can be converted to use
|
||||
* the {@code BufferedReader} class by replacing code of the form:
|
||||
* <blockquote><pre>
|
||||
* DataInputStream d = new DataInputStream(in);
|
||||
* </pre></blockquote>
|
||||
* with:
|
||||
* <blockquote><pre>
|
||||
* BufferedReader d
|
||||
* = new BufferedReader(new InputStreamReader(in));
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @return the next line of text from this input stream.
|
||||
* @see java.io.BufferedReader#readLine()
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public final String readLine() {
|
||||
char[] buf = lineBuffer;
|
||||
|
||||
if (buf == null) {
|
||||
buf = lineBuffer = new char[128];
|
||||
}
|
||||
|
||||
int room = buf.length;
|
||||
int offset = 0;
|
||||
int c;
|
||||
|
||||
loop: while (true) {
|
||||
switch (c = in.read()) {
|
||||
case -1:
|
||||
case '\n':
|
||||
break loop;
|
||||
|
||||
case '\r':
|
||||
int c2 = in.read();
|
||||
if ((c2 != '\n') && (c2 != -1)) {
|
||||
if (!(in instanceof SafePushbackInputStream)) {
|
||||
this.in = new SafePushbackInputStream(in);
|
||||
}
|
||||
((SafePushbackInputStream)in).unread(c2);
|
||||
}
|
||||
break loop;
|
||||
|
||||
default:
|
||||
if (--room < 0) {
|
||||
buf = new char[offset + 128];
|
||||
room = buf.length - offset - 1;
|
||||
System.arraycopy(lineBuffer, 0, buf, 0, offset);
|
||||
lineBuffer = buf;
|
||||
}
|
||||
buf[offset++] = (char) c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((c == -1) && (offset == 0)) {
|
||||
return null;
|
||||
}
|
||||
return String.copyValueOf(buf, 0, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* See the general contract of the {@code readUTF}
|
||||
* method of {@code DataInput}.
|
||||
* <p>
|
||||
* Bytes
|
||||
* for this operation are read from the contained
|
||||
* input stream.
|
||||
*
|
||||
* @return a Unicode string.
|
||||
* @see SafeDataInputStream#readUTF(SafeDataInputStream)
|
||||
*/
|
||||
@Override
|
||||
public final @NotNull String readUTF() {
|
||||
return readUTF(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the
|
||||
* stream {@code in} a representation
|
||||
* of a Unicode character string encoded in
|
||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a> format;
|
||||
* this string of characters is then returned as a {@code String}.
|
||||
* The details of the modified UTF-8 representation
|
||||
* are exactly the same as for the {@code readUTF}
|
||||
* method of {@code DataInput}.
|
||||
*
|
||||
* @param in a data input stream.
|
||||
* @return a Unicode string.
|
||||
* @see SafeDataInputStream#readUnsignedShort()
|
||||
*/
|
||||
public static String readUTF(SafeDataInputStream in) {
|
||||
int utflen = in.readUnsignedShort();
|
||||
byte[] bytearr;
|
||||
char[] chararr;
|
||||
if (in.bytearr.length < utflen){
|
||||
in.bytearr = new byte[utflen*2];
|
||||
in.chararr = new char[utflen*2];
|
||||
}
|
||||
chararr = in.chararr;
|
||||
bytearr = in.bytearr;
|
||||
|
||||
int c, char2, char3;
|
||||
int count = 0;
|
||||
int chararr_count=0;
|
||||
|
||||
in.readFully(bytearr, 0, utflen);
|
||||
|
||||
while (count < utflen) {
|
||||
c = (int) bytearr[count] & 0xff;
|
||||
if (c > 127) break;
|
||||
count++;
|
||||
chararr[chararr_count++]=(char)c;
|
||||
}
|
||||
|
||||
while (count < utflen) {
|
||||
c = (int) bytearr[count] & 0xff;
|
||||
switch (c >> 4) {
|
||||
case 0, 1, 2, 3, 4, 5, 6, 7 -> {
|
||||
/* 0xxxxxxx*/
|
||||
count++;
|
||||
chararr[chararr_count++] = (char) c;
|
||||
}
|
||||
case 12, 13 -> {
|
||||
/* 110x xxxx 10xx xxxx*/
|
||||
count += 2;
|
||||
if (count > utflen)
|
||||
throw new IllegalArgumentException("malformed input: partial character at end");
|
||||
char2 = bytearr[count - 1];
|
||||
if ((char2 & 0xC0) != 0x80)
|
||||
throw new IllegalArgumentException("malformed input around byte " + count);
|
||||
chararr[chararr_count++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
|
||||
}
|
||||
case 14 -> {
|
||||
/* 1110 xxxx 10xx xxxx 10xx xxxx */
|
||||
count += 3;
|
||||
if (count > utflen)
|
||||
throw new IllegalArgumentException("malformed input: partial character at end");
|
||||
char2 = bytearr[count - 2];
|
||||
char3 = bytearr[count - 1];
|
||||
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
|
||||
throw new IllegalArgumentException("malformed input around byte " + (count - 1));
|
||||
chararr[chararr_count++] = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F)));
|
||||
}
|
||||
default ->
|
||||
/* 10xx xxxx, 1111 xxxx */
|
||||
throw new IllegalArgumentException("malformed input around byte " + count);
|
||||
}
|
||||
}
|
||||
// The number of chars produced may be less than utflen
|
||||
return new String(chararr, 0, chararr_count);
|
||||
}
|
||||
}
|
@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package it.cavallium.stream;
|
||||
|
||||
/**
|
||||
* The {@code SafeDataOutput} interface provides
|
||||
* for converting data from any of the Java
|
||||
* primitive types to a series of bytes and
|
||||
* writing these bytes to a binary stream.
|
||||
* There is also a facility for converting
|
||||
* a {@code String} into
|
||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
||||
* format and writing the resulting series
|
||||
* of bytes.
|
||||
* <p>
|
||||
* For all the methods in this interface that
|
||||
* write bytes, it is generally true that if
|
||||
* a byte cannot be written for any reason,
|
||||
* an {@code IOException} is thrown.
|
||||
*
|
||||
* @author Frank Yellin
|
||||
* @see java.io.DataInput
|
||||
* @see java.io.DataOutputStream
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface SafeDataOutput {
|
||||
/**
|
||||
* Writes to the output stream the eight
|
||||
* low-order bits of the argument {@code b}.
|
||||
* The 24 high-order bits of {@code b}
|
||||
* are ignored.
|
||||
*
|
||||
* @param b the byte to be written.
|
||||
*/
|
||||
void write(int b);
|
||||
|
||||
/**
|
||||
* Writes to the output stream all the bytes in array {@code b}.
|
||||
* If {@code b} is {@code null},
|
||||
* a {@code NullPointerException} is thrown.
|
||||
* If {@code b.length} is zero, then
|
||||
* no bytes are written. Otherwise, the byte
|
||||
* {@code b[0]} is written first, then
|
||||
* {@code b[1]}, and so on; the last byte
|
||||
* written is {@code b[b.length-1]}.
|
||||
*
|
||||
* @param b the data.
|
||||
*/
|
||||
void write(byte b[]);
|
||||
|
||||
/**
|
||||
* Writes {@code len} bytes from array
|
||||
* {@code b}, in order, to
|
||||
* the output stream. If {@code b}
|
||||
* is {@code null}, a {@code NullPointerException}
|
||||
* is thrown. If {@code off} is negative,
|
||||
* or {@code len} is negative, or {@code off+len}
|
||||
* is greater than the length of the array
|
||||
* {@code b}, then an {@code IndexOutOfBoundsException}
|
||||
* is thrown. If {@code len} is zero,
|
||||
* then no bytes are written. Otherwise, the
|
||||
* byte {@code b[off]} is written first,
|
||||
* then {@code b[off+1]}, and so on; the
|
||||
* last byte written is {@code b[off+len-1]}.
|
||||
*
|
||||
* @param b the data.
|
||||
* @param off the start offset in the data.
|
||||
* @param len the number of bytes to write.
|
||||
*/
|
||||
void write(byte b[], int off, int len);
|
||||
|
||||
/**
|
||||
* Writes a {@code boolean} value to this output stream.
|
||||
* If the argument {@code v}
|
||||
* is {@code true}, the value {@code (byte)1}
|
||||
* is written; if {@code v} is {@code false},
|
||||
* the value {@code (byte)0} is written.
|
||||
* The byte written by this method may
|
||||
* be read by the {@code readBoolean}
|
||||
* method of interface {@code DataInput},
|
||||
* which will then return a {@code boolean}
|
||||
* equal to {@code v}.
|
||||
*
|
||||
* @param v the boolean to be written.
|
||||
*/
|
||||
void writeBoolean(boolean v);
|
||||
|
||||
/**
|
||||
* Writes to the output stream the eight low-order
|
||||
* bits of the argument {@code v}.
|
||||
* The 24 high-order bits of {@code v}
|
||||
* are ignored. (This means that {@code writeByte}
|
||||
* does exactly the same thing as {@code write}
|
||||
* for an integer argument.) The byte written
|
||||
* by this method may be read by the {@code readByte}
|
||||
* method of interface {@code DataInput},
|
||||
* which will then return a {@code byte}
|
||||
* equal to {@code (byte)v}.
|
||||
*
|
||||
* @param v the byte value to be written.
|
||||
*/
|
||||
void writeByte(int v);
|
||||
|
||||
/**
|
||||
* Writes two bytes to the output
|
||||
* stream to represent the value of the argument.
|
||||
* The byte values to be written, in the order
|
||||
* shown, are:
|
||||
* <pre>{@code
|
||||
* (byte)(0xff & (v >> 8))
|
||||
* (byte)(0xff & v)
|
||||
* }</pre> <p>
|
||||
* The bytes written by this method may be
|
||||
* read by the {@code readShort} method
|
||||
* of interface {@code DataInput}, which
|
||||
* will then return a {@code short} equal
|
||||
* to {@code (short)v}.
|
||||
*
|
||||
* @param v the {@code short} value to be written.
|
||||
*/
|
||||
void writeShort(int v);
|
||||
|
||||
/**
|
||||
* Writes a {@code char} value, which
|
||||
* is comprised of two bytes, to the
|
||||
* output stream.
|
||||
* The byte values to be written, in the order
|
||||
* shown, are:
|
||||
* <pre>{@code
|
||||
* (byte)(0xff & (v >> 8))
|
||||
* (byte)(0xff & v)
|
||||
* }</pre><p>
|
||||
* The bytes written by this method may be
|
||||
* read by the {@code readChar} method
|
||||
* of interface {@code DataInput}, which
|
||||
* will then return a {@code char} equal
|
||||
* to {@code (char)v}.
|
||||
*
|
||||
* @param v the {@code char} value to be written.
|
||||
*/
|
||||
void writeChar(int v);
|
||||
|
||||
/**
|
||||
* Writes an {@code int} value, which is
|
||||
* comprised of four bytes, to the output stream.
|
||||
* The byte values to be written, in the order
|
||||
* shown, are:
|
||||
* <pre>{@code
|
||||
* (byte)(0xff & (v >> 24))
|
||||
* (byte)(0xff & (v >> 16))
|
||||
* (byte)(0xff & (v >> 8))
|
||||
* (byte)(0xff & v)
|
||||
* }</pre><p>
|
||||
* The bytes written by this method may be read
|
||||
* by the {@code readInt} method of interface
|
||||
* {@code DataInput}, which will then
|
||||
* return an {@code int} equal to {@code v}.
|
||||
*
|
||||
* @param v the {@code int} value to be written.
|
||||
*/
|
||||
void writeInt(int v);
|
||||
|
||||
/**
|
||||
* Writes a {@code long} value, which is
|
||||
* comprised of eight bytes, to the output stream.
|
||||
* The byte values to be written, in the order
|
||||
* shown, are:
|
||||
* <pre>{@code
|
||||
* (byte)(0xff & (v >> 56))
|
||||
* (byte)(0xff & (v >> 48))
|
||||
* (byte)(0xff & (v >> 40))
|
||||
* (byte)(0xff & (v >> 32))
|
||||
* (byte)(0xff & (v >> 24))
|
||||
* (byte)(0xff & (v >> 16))
|
||||
* (byte)(0xff & (v >> 8))
|
||||
* (byte)(0xff & v)
|
||||
* }</pre><p>
|
||||
* The bytes written by this method may be
|
||||
* read by the {@code readLong} method
|
||||
* of interface {@code DataInput}, which
|
||||
* will then return a {@code long} equal
|
||||
* to {@code v}.
|
||||
*
|
||||
* @param v the {@code long} value to be written.
|
||||
*/
|
||||
void writeLong(long v);
|
||||
|
||||
/**
|
||||
* Writes a {@code Int52} value, which is
|
||||
* comprised of seven bytes, to the output stream.
|
||||
* The byte values to be written, in the order
|
||||
* shown, are:
|
||||
* <pre>{@code
|
||||
* (byte)(v >> 48 & 0xf);
|
||||
* (byte)(v >> 40 & 0xff);
|
||||
* (byte)(v >> 32 & 0xff);
|
||||
* (byte)(v >> 24 & 0xff);
|
||||
* (byte)(v >> 16 & 0xff);
|
||||
* (byte)(v >> 8 & 0xff);
|
||||
* (byte)(v & 0xff);
|
||||
* }</pre><p>
|
||||
* The bytes written by this method may be
|
||||
* read by the {@code readInt52} method
|
||||
* of interface {@code DataInput}, which
|
||||
* will then return a {@code Int52} equal
|
||||
* to {@code v}.
|
||||
*
|
||||
* @param v the {@code Int52} value to be written.
|
||||
*/
|
||||
void writeInt52(long v);
|
||||
|
||||
/**
|
||||
* Writes a {@code float} value,
|
||||
* which is comprised of four bytes, to the output stream.
|
||||
* It does this as if it first converts this
|
||||
* {@code float} value to an {@code int}
|
||||
* in exactly the manner of the {@code Float.floatToIntBits}
|
||||
* method and then writes the {@code int}
|
||||
* value in exactly the manner of the {@code writeInt}
|
||||
* method. The bytes written by this method
|
||||
* may be read by the {@code readFloat}
|
||||
* method of interface {@code DataInput},
|
||||
* which will then return a {@code float}
|
||||
* equal to {@code v}.
|
||||
*
|
||||
* @param v the {@code float} value to be written.
|
||||
*/
|
||||
void writeFloat(float v);
|
||||
|
||||
/**
|
||||
* Writes a {@code double} value,
|
||||
* which is comprised of eight bytes, to the output stream.
|
||||
* It does this as if it first converts this
|
||||
* {@code double} value to a {@code long}
|
||||
* in exactly the manner of the {@code Double.doubleToLongBits}
|
||||
* method and then writes the {@code long}
|
||||
* value in exactly the manner of the {@code writeLong}
|
||||
* method. The bytes written by this method
|
||||
* may be read by the {@code readDouble}
|
||||
* method of interface {@code DataInput},
|
||||
* which will then return a {@code double}
|
||||
* equal to {@code v}.
|
||||
*
|
||||
* @param v the {@code double} value to be written.
|
||||
*/
|
||||
void writeDouble(double v);
|
||||
|
||||
/**
|
||||
* Writes a string to the output stream.
|
||||
* For every character in the string
|
||||
* {@code s}, taken in order, one byte
|
||||
* is written to the output stream. If
|
||||
* {@code s} is {@code null}, a {@code NullPointerException}
|
||||
* is thrown.<p> If {@code s.length}
|
||||
* is zero, then no bytes are written. Otherwise,
|
||||
* the character {@code s[0]} is written
|
||||
* first, then {@code s[1]}, and so on;
|
||||
* the last character written is {@code s[s.length-1]}.
|
||||
* For each character, one byte is written,
|
||||
* the low-order byte, in exactly the manner
|
||||
* of the {@code writeByte} method . The
|
||||
* high-order eight bits of each character
|
||||
* in the string are ignored.
|
||||
*
|
||||
* @param s the string of bytes to be written.
|
||||
*/
|
||||
void writeBytes(String s);
|
||||
|
||||
/**
|
||||
* Writes every character in the string {@code s},
|
||||
* to the output stream, in order,
|
||||
* two bytes per character. If {@code s}
|
||||
* is {@code null}, a {@code NullPointerException}
|
||||
* is thrown. If {@code s.length}
|
||||
* is zero, then no characters are written.
|
||||
* Otherwise, the character {@code s[0]}
|
||||
* is written first, then {@code s[1]},
|
||||
* and so on; the last character written is
|
||||
* {@code s[s.length-1]}. For each character,
|
||||
* two bytes are actually written, high-order
|
||||
* byte first, in exactly the manner of the
|
||||
* {@code writeChar} method.
|
||||
*
|
||||
* @param s the string value to be written.
|
||||
*/
|
||||
void writeChars(String s);
|
||||
|
||||
/**
|
||||
* Writes two bytes of length information
|
||||
* to the output stream, followed
|
||||
* by the
|
||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
||||
* representation
|
||||
* of every character in the string {@code s}.
|
||||
* If {@code s} is {@code null},
|
||||
* a {@code NullPointerException} is thrown.
|
||||
* Each character in the string {@code s}
|
||||
* is converted to a group of one, two, or
|
||||
* three bytes, depending on the value of the
|
||||
* character.<p>
|
||||
* If a character {@code c}
|
||||
* is in the range <code>\u0001</code> through
|
||||
* <code>\u007f</code>, it is represented
|
||||
* by one byte:
|
||||
* <pre>(byte)c </pre> <p>
|
||||
* If a character {@code c} is <code>\u0000</code>
|
||||
* or is in the range <code>\u0080</code>
|
||||
* through <code>\u07ff</code>, then it is
|
||||
* represented by two bytes, to be written
|
||||
* in the order shown: <pre>{@code
|
||||
* (byte)(0xc0 | (0x1f & (c >> 6)))
|
||||
* (byte)(0x80 | (0x3f & c))
|
||||
* }</pre> <p> If a character
|
||||
* {@code c} is in the range <code>\u0800</code>
|
||||
* through {@code uffff}, then it is
|
||||
* represented by three bytes, to be written
|
||||
* in the order shown: <pre>{@code
|
||||
* (byte)(0xe0 | (0x0f & (c >> 12)))
|
||||
* (byte)(0x80 | (0x3f & (c >> 6)))
|
||||
* (byte)(0x80 | (0x3f & c))
|
||||
* }</pre> <p> First,
|
||||
* the total number of bytes needed to represent
|
||||
* all the characters of {@code s} is
|
||||
* calculated. If this number is larger than
|
||||
* {@code 65535}, then a {@code UTFDataFormatException}
|
||||
* is thrown. Otherwise, this length is written
|
||||
* to the output stream in exactly the manner
|
||||
* of the {@code writeShort} method;
|
||||
* after this, the one-, two-, or three-byte
|
||||
* representation of each character in the
|
||||
* string {@code s} is written.<p> The
|
||||
* bytes written by this method may be read
|
||||
* by the {@code readUTF} method of interface
|
||||
* {@code DataInput}, which will then
|
||||
* return a {@code String} equal to {@code s}.
|
||||
*
|
||||
* @param s the string value to be written.
|
||||
*/
|
||||
void writeUTF(String s);
|
||||
}
|
@ -0,0 +1,442 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
|
||||
/**
|
||||
* A data output stream lets an application write primitive Java data
|
||||
* types to an output stream in a portable way. An application can
|
||||
* then use a data input stream to read the data back in.
|
||||
*
|
||||
* @author unascribed
|
||||
* @see java.io.DataInputStream
|
||||
* @since 1.0
|
||||
*/
|
||||
public class SafeDataOutputStream extends SafeFilterOutputStream implements SafeDataOutput {
|
||||
/**
|
||||
* The number of bytes written to the data output stream so far.
|
||||
* If this counter overflows, it will be wrapped to Integer.MAX_VALUE.
|
||||
*/
|
||||
protected int written;
|
||||
|
||||
/**
|
||||
* bytearr is initialized on demand by writeUTF
|
||||
*/
|
||||
private byte[] bytearr = null;
|
||||
|
||||
/**
|
||||
* Creates a new data output stream to write data to the specified
|
||||
* underlying output stream. The counter {@code written} is
|
||||
* set to zero.
|
||||
*
|
||||
* @param out the underlying output stream, to be saved for later
|
||||
* use.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public SafeDataOutputStream(SafeOutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the written counter by the specified value
|
||||
* until it reaches Integer.MAX_VALUE.
|
||||
*/
|
||||
private void incCount(int value) {
|
||||
int temp = written + value;
|
||||
if (temp < 0) {
|
||||
temp = Integer.MAX_VALUE;
|
||||
}
|
||||
written = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte (the low eight bits of the argument
|
||||
* {@code b}) to the underlying output stream. If no exception
|
||||
* is thrown, the counter {@code written} is incremented by
|
||||
* {@code 1}.
|
||||
* <p>
|
||||
* Implements the {@code write} method of {@code OutputStream}.
|
||||
*
|
||||
* @param b the {@code byte} to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public void write(int b) {
|
||||
out.write(b);
|
||||
incCount(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code len} bytes from the specified byte array
|
||||
* starting at offset {@code off} to the underlying output stream.
|
||||
* If no exception is thrown, the counter {@code written} is
|
||||
* incremented by {@code len}.
|
||||
*
|
||||
* @param b the data.
|
||||
* @param off the start offset in the data.
|
||||
* @param len the number of bytes to write.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public void write(byte[] b, int off, int len)
|
||||
{
|
||||
out.write(b, off, len);
|
||||
incCount(len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes this data output stream. This forces any buffered output
|
||||
* bytes to be written out to the stream.
|
||||
* <p>
|
||||
* The {@code flush} method of {@code SafeDataOutputStream}
|
||||
* calls the {@code flush} method of its underlying output stream.
|
||||
*
|
||||
* @see SafeFilterOutputStream#out
|
||||
* @see java.io.OutputStream#flush()
|
||||
*/
|
||||
public void flush() {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@code boolean} to the underlying output stream as
|
||||
* a 1-byte value. The value {@code true} is written out as the
|
||||
* value {@code (byte)1}; the value {@code false} is
|
||||
* written out as the value {@code (byte)0}. If no exception is
|
||||
* thrown, the counter {@code written} is incremented by
|
||||
* {@code 1}.
|
||||
*
|
||||
* @param v a {@code boolean} value to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeBoolean(boolean v) {
|
||||
out.write(v ? 1 : 0);
|
||||
incCount(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out a {@code byte} to the underlying output stream as
|
||||
* a 1-byte value. If no exception is thrown, the counter
|
||||
* {@code written} is incremented by {@code 1}.
|
||||
*
|
||||
* @param v a {@code byte} value to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeByte(int v) {
|
||||
this.write(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@code short} to the underlying output stream as two
|
||||
* bytes, high byte first. If no exception is thrown, the counter
|
||||
* {@code written} is incremented by {@code 2}.
|
||||
*
|
||||
* @param v a {@code short} to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeShort(int v) {
|
||||
out.write((v >>> 8) & 0xFF);
|
||||
out.write((v) & 0xFF);
|
||||
incCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@code char} to the underlying output stream as a
|
||||
* 2-byte value, high byte first. If no exception is thrown, the
|
||||
* counter {@code written} is incremented by {@code 2}.
|
||||
*
|
||||
* @param v a {@code char} value to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeChar(int v) {
|
||||
out.write((v >>> 8) & 0xFF);
|
||||
out.write((v) & 0xFF);
|
||||
incCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an {@code int} to the underlying output stream as four
|
||||
* bytes, high byte first. If no exception is thrown, the counter
|
||||
* {@code written} is incremented by {@code 4}.
|
||||
*
|
||||
* @param v an {@code int} to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeInt(int v) {
|
||||
out.write((v >>> 24) & 0xFF);
|
||||
out.write((v >>> 16) & 0xFF);
|
||||
out.write((v >>> 8) & 0xFF);
|
||||
out.write((v) & 0xFF);
|
||||
incCount(4);
|
||||
}
|
||||
|
||||
private final byte[] writeBuffer = new byte[8];
|
||||
|
||||
/**
|
||||
* Writes a {@code long} to the underlying output stream as eight
|
||||
* bytes, high byte first. In no exception is thrown, the counter
|
||||
* {@code written} is incremented by {@code 8}.
|
||||
*
|
||||
* @param v a {@code long} to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeLong(long v) {
|
||||
writeBuffer[0] = (byte)(v >>> 56);
|
||||
writeBuffer[1] = (byte)(v >>> 48);
|
||||
writeBuffer[2] = (byte)(v >>> 40);
|
||||
writeBuffer[3] = (byte)(v >>> 32);
|
||||
writeBuffer[4] = (byte)(v >>> 24);
|
||||
writeBuffer[5] = (byte)(v >>> 16);
|
||||
writeBuffer[6] = (byte)(v >>> 8);
|
||||
writeBuffer[7] = (byte)(v);
|
||||
out.write(writeBuffer, 0, 8);
|
||||
incCount(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a {@code Int52} to the underlying output stream as seven
|
||||
* bytes, high byte first. In no exception is thrown, the counter
|
||||
* {@code written} is incremented by {@code 7}.
|
||||
*
|
||||
* @param v a {@code Int52} to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeInt52(long v) {
|
||||
writeBuffer[0] = (byte)(v >> 48 & 0xf);
|
||||
writeBuffer[1] = (byte)(v >> 40 & 0xff);
|
||||
writeBuffer[2] = (byte)(v >> 32 & 0xff);
|
||||
writeBuffer[3] = (byte)(v >> 24 & 0xff);
|
||||
writeBuffer[4] = (byte)(v >> 16 & 0xff);
|
||||
writeBuffer[5] = (byte)(v >> 8 & 0xff);
|
||||
writeBuffer[6] = (byte)(v & 0xff);
|
||||
out.write(writeBuffer, 0, 7);
|
||||
incCount(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the float argument to an {@code int} using the
|
||||
* {@code floatToIntBits} method in class {@code Float},
|
||||
* and then writes that {@code int} value to the underlying
|
||||
* output stream as a 4-byte quantity, high byte first. If no
|
||||
* exception is thrown, the counter {@code written} is
|
||||
* incremented by {@code 4}.
|
||||
*
|
||||
* @param v a {@code float} value to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
* @see Float#floatToIntBits(float)
|
||||
*/
|
||||
public final void writeFloat(float v) {
|
||||
writeInt(Float.floatToIntBits(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the double argument to a {@code long} using the
|
||||
* {@code doubleToLongBits} method in class {@code Double},
|
||||
* and then writes that {@code long} value to the underlying
|
||||
* output stream as an 8-byte quantity, high byte first. If no
|
||||
* exception is thrown, the counter {@code written} is
|
||||
* incremented by {@code 8}.
|
||||
*
|
||||
* @param v a {@code double} value to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
* @see Double#doubleToLongBits(double)
|
||||
*/
|
||||
public final void writeDouble(double v) {
|
||||
writeLong(Double.doubleToLongBits(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out the string to the underlying output stream as a
|
||||
* sequence of bytes. Each character in the string is written out, in
|
||||
* sequence, by discarding its high eight bits. If no exception is
|
||||
* thrown, the counter {@code written} is incremented by the
|
||||
* length of {@code s}.
|
||||
*
|
||||
* @param s a string of bytes to be written.
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeBytes(String s) {
|
||||
int len = s.length();
|
||||
for (int i = 0 ; i < len ; i++) {
|
||||
out.write((byte)s.charAt(i));
|
||||
}
|
||||
incCount(len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the underlying output stream as a sequence of
|
||||
* characters. Each character is written to the data output stream as
|
||||
* if by the {@code writeChar} method. If no exception is
|
||||
* thrown, the counter {@code written} is incremented by twice
|
||||
* the length of {@code s}.
|
||||
*
|
||||
* @param s a {@code String} value to be written.
|
||||
* @see SafeDataOutputStream#writeChar(int)
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
public final void writeChars(String s) {
|
||||
int len = s.length();
|
||||
for (int i = 0 ; i < len ; i++) {
|
||||
int v = s.charAt(i);
|
||||
out.write((v >>> 8) & 0xFF);
|
||||
out.write((v) & 0xFF);
|
||||
}
|
||||
incCount(len * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the underlying output stream using
|
||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
||||
* encoding in a machine-independent manner.
|
||||
* <p>
|
||||
* First, two bytes are written to the output stream as if by the
|
||||
* {@code writeShort} method giving the number of bytes to
|
||||
* follow. This value is the number of bytes actually written out,
|
||||
* not the length of the string. Following the length, each character
|
||||
* of the string is output, in sequence, using the modified UTF-8 encoding
|
||||
* for the character. If no exception is thrown, the counter
|
||||
* {@code written} is incremented by the total number of
|
||||
* bytes written to the output stream. This will be at least two
|
||||
* plus the length of {@code str}, and at most two plus
|
||||
* thrice the length of {@code str}.
|
||||
*
|
||||
* @param str a string to be written.
|
||||
* @see #writeChars(String)
|
||||
*/
|
||||
public final void writeUTF(String str) {
|
||||
writeUTF(str, this);
|
||||
}
|
||||
|
||||
public final void writeUTF(int strlen, int utflen, String str) {
|
||||
writeUTF(strlen, utflen, str, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the specified DataOutput using
|
||||
* <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
|
||||
* encoding in a machine-independent manner.
|
||||
* <p>
|
||||
* First, two bytes are written to out as if by the {@code writeShort}
|
||||
* method giving the number of bytes to follow. This value is the number of
|
||||
* bytes actually written out, not the length of the string. Following the
|
||||
* length, each character of the string is output, in sequence, using the
|
||||
* modified UTF-8 encoding for the character. If no exception is thrown, the
|
||||
* counter {@code written} is incremented by the total number of
|
||||
* bytes written to the output stream. This will be at least two
|
||||
* plus the length of {@code str}, and at most two plus
|
||||
* thrice the length of {@code str}.
|
||||
*
|
||||
* @param str a string to be written.
|
||||
* @param out destination to write to
|
||||
* @return The number of bytes written out.
|
||||
*/
|
||||
static int writeUTF(String str, SafeDataOutput out) {
|
||||
int strlen = strLen(str);
|
||||
int utflen = utfLen(str, strlen);
|
||||
return writeUTF(strlen, utflen, str, out);
|
||||
}
|
||||
|
||||
public static int strLen(String str) {
|
||||
return str.length();
|
||||
}
|
||||
|
||||
public static int utfLen(String str, int strLen) {
|
||||
int utflen = strLen; // optimized for ASCII
|
||||
|
||||
for (int i = 0; i < strLen; i++) {
|
||||
int c = str.charAt(i);
|
||||
if (c >= 0x80 || c == 0)
|
||||
utflen += (c >= 0x800) ? 2 : 1;
|
||||
}
|
||||
|
||||
if (utflen > 65535 || /* overflow */ utflen < strLen)
|
||||
throw new IllegalArgumentException(tooLongMsg(str, utflen));
|
||||
return utflen;
|
||||
}
|
||||
|
||||
static int writeUTF(int strlen, int utflen, String str, SafeDataOutput out) {
|
||||
final byte[] bytearr;
|
||||
if (out instanceof SafeDataOutputStream dos) {
|
||||
if (dos.bytearr == null || (dos.bytearr.length < (utflen + 2)))
|
||||
dos.bytearr = new byte[(utflen*2) + 2];
|
||||
bytearr = dos.bytearr;
|
||||
} else {
|
||||
bytearr = new byte[utflen + 2];
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
|
||||
bytearr[count++] = (byte) ((utflen) & 0xFF);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < strlen; i++) { // optimized for initial run of ASCII
|
||||
int c = str.charAt(i);
|
||||
if (c >= 0x80 || c == 0) break;
|
||||
bytearr[count++] = (byte) c;
|
||||
}
|
||||
|
||||
for (; i < strlen; i++) {
|
||||
int c = str.charAt(i);
|
||||
if (c < 0x80 && c != 0) {
|
||||
bytearr[count++] = (byte) c;
|
||||
} else if (c >= 0x800) {
|
||||
bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
|
||||
bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
|
||||
bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
|
||||
} else {
|
||||
bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
|
||||
bytearr[count++] = (byte) (0x80 | ((c) & 0x3F));
|
||||
}
|
||||
}
|
||||
out.write(bytearr, 0, utflen + 2);
|
||||
return utflen + 2;
|
||||
}
|
||||
|
||||
private static String tooLongMsg(String s, int bits32) {
|
||||
int slen = s.length();
|
||||
String head = s.substring(0, 8);
|
||||
String tail = s.substring(slen - 8, slen);
|
||||
// handle int overflow with max 3x expansion
|
||||
long actualLength = (long)slen + Integer.toUnsignedLong(bits32 - slen);
|
||||
return "encoded string (" + head + "..." + tail + ") too long: "
|
||||
+ actualLength + " bytes";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value of the counter {@code written},
|
||||
* the number of bytes written to this data output stream so far.
|
||||
* If the counter overflows, it will be wrapped to Integer.MAX_VALUE.
|
||||
*
|
||||
* @return the value of the {@code written} field.
|
||||
* @see SafeDataOutputStream#written
|
||||
*/
|
||||
public final int size() {
|
||||
return written;
|
||||
}
|
||||
|
||||
public DataOutputStream asDataOutputStream() {
|
||||
return new DataOutputStream(this.out);
|
||||
}
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
/**
|
||||
* A {@code FilterInputStream} contains
|
||||
* some other input stream, which it uses as
|
||||
* its basic source of data, possibly transforming
|
||||
* the data along the way or providing additional
|
||||
* functionality. The class {@code FilterInputStream}
|
||||
* itself simply overrides all methods of
|
||||
* {@code InputStream} with versions that
|
||||
* pass all requests to the contained input
|
||||
* stream. Subclasses of {@code FilterInputStream}
|
||||
* may further override some of these methods
|
||||
* and may also provide additional methods
|
||||
* and fields.
|
||||
*
|
||||
* @author Jonathan Payne
|
||||
* @since 1.0
|
||||
*/
|
||||
public class SafeFilterInputStream extends SafeInputStream {
|
||||
/**
|
||||
* The input stream to be filtered.
|
||||
*/
|
||||
protected volatile SafeInputStream in;
|
||||
|
||||
/**
|
||||
* Creates a {@code FilterInputStream}
|
||||
* by assigning the argument {@code in}
|
||||
* to the field {@code this.in} so as
|
||||
* to remember it for later use.
|
||||
*
|
||||
* @param in the underlying input stream, or {@code null} if
|
||||
* this instance is to be created without an underlying stream.
|
||||
*/
|
||||
protected SafeFilterInputStream(SafeInputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next byte of data from this input stream. The value
|
||||
* byte is returned as an {@code int} in the range
|
||||
* {@code 0} to {@code 255}. If no byte is available
|
||||
* because the end of the stream has been reached, the value
|
||||
* {@code -1} is returned. This method blocks until input data
|
||||
* is available, the end of the stream is detected, or an exception
|
||||
* is thrown.
|
||||
* <p>
|
||||
* This method
|
||||
* simply performs {@code in.read()} and returns the result.
|
||||
*
|
||||
* @return the next byte of data, or {@code -1} if the end of the
|
||||
* stream is reached.
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
public int read() {
|
||||
return in.read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@code b.length} bytes of data from this
|
||||
* input stream into an array of bytes. This method blocks until some
|
||||
* input is available.
|
||||
* <p>
|
||||
* This method simply performs the call
|
||||
* {@code read(b, 0, b.length)} and returns
|
||||
* the result. It is important that it does
|
||||
* <i>not</i> do {@code in.read(b)} instead;
|
||||
* certain subclasses of {@code FilterInputStream}
|
||||
* depend on the implementation strategy actually
|
||||
* used.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* {@code -1} if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
* @see SafeFilterInputStream#read(byte[], int, int)
|
||||
*/
|
||||
public int read(byte b[]) {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@code len} bytes of data from this input stream
|
||||
* into an array of bytes. If {@code len} is not zero, the method
|
||||
* blocks until some input is available; otherwise, no
|
||||
* bytes are read and {@code 0} is returned.
|
||||
* <p>
|
||||
* This method simply performs {@code in.read(b, off, len)}
|
||||
* and returns the result.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset in the destination array {@code b}
|
||||
* @param len the maximum number of bytes read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* {@code -1} if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
* @throws NullPointerException If {@code b} is {@code null}.
|
||||
* @throws IndexOutOfBoundsException If {@code off} is negative,
|
||||
* {@code len} is negative, or {@code len} is greater than
|
||||
* {@code b.length - off}
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
public int read(byte b[], int off, int len) {
|
||||
return in.read(b, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips over and discards {@code n} bytes of data from the
|
||||
* input stream. The {@code skip} method may, for a variety of
|
||||
* reasons, end up skipping over some smaller number of bytes,
|
||||
* possibly {@code 0}. The actual number of bytes skipped is
|
||||
* returned.
|
||||
* <p>
|
||||
* This method simply performs {@code in.skip(n)}.
|
||||
*
|
||||
* @param n the number of bytes to be skipped.
|
||||
* @return the actual number of bytes skipped.
|
||||
*/
|
||||
public long skip(long n) {
|
||||
return in.skip(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an estimate of the number of bytes that can be read (or
|
||||
* skipped over) from this input stream without blocking by the next
|
||||
* caller of a method for this input stream. The next caller might be
|
||||
* the same thread or another thread. A single read or skip of this
|
||||
* many bytes will not block, but may read or skip fewer bytes.
|
||||
* <p>
|
||||
* This method returns the result of {@link #in in}.available().
|
||||
*
|
||||
* @return an estimate of the number of bytes that can be read (or skipped
|
||||
* over) from this input stream without blocking.
|
||||
*/
|
||||
public int available() {
|
||||
return in.available();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this input stream and releases any system resources
|
||||
* associated with the stream.
|
||||
* This
|
||||
* method simply performs {@code in.close()}.
|
||||
*
|
||||
* @see SafeFilterInputStream#in
|
||||
*/
|
||||
public void close() {
|
||||
in.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current position in this input stream. A subsequent
|
||||
* call to the {@code reset} method repositions this stream at
|
||||
* the last marked position so that subsequent reads re-read the same bytes.
|
||||
* <p>
|
||||
* The {@code readlimit} argument tells this input stream to
|
||||
* allow that many bytes to be read before the mark position gets
|
||||
* invalidated.
|
||||
* <p>
|
||||
* This method simply performs {@code in.mark(readlimit)}.
|
||||
*
|
||||
* @param readlimit the maximum limit of bytes that can be read before
|
||||
* the mark position becomes invalid.
|
||||
* @see SafeFilterInputStream#in
|
||||
* @see SafeFilterInputStream#reset()
|
||||
*/
|
||||
public void mark(int readlimit) {
|
||||
in.mark(readlimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repositions this stream to the position at the time the
|
||||
* {@code mark} method was last called on this input stream.
|
||||
* <p>
|
||||
* This method
|
||||
* simply performs {@code in.reset()}.
|
||||
* <p>
|
||||
* Stream marks are intended to be used in
|
||||
* situations where you need to read ahead a little to see what's in
|
||||
* the stream. Often this is most easily done by invoking some
|
||||
* general parser. If the stream is of the type handled by the
|
||||
* parse, it just chugs along happily. If the stream is not of
|
||||
* that type, the parser should toss an exception when it fails.
|
||||
* If this happens within readlimit bytes, it allows the outer
|
||||
* code to reset the stream and try another parser.
|
||||
*
|
||||
* @see SafeFilterInputStream#in
|
||||
* @see SafeFilterInputStream#mark(int)
|
||||
*/
|
||||
public void reset() {
|
||||
in.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this input stream supports the {@code mark}
|
||||
* and {@code reset} methods.
|
||||
* This method
|
||||
* simply performs {@code in.markSupported()}.
|
||||
*
|
||||
* @return {@code true} if this stream type supports the
|
||||
* {@code mark} and {@code reset} method;
|
||||
* {@code false} otherwise.
|
||||
* @see SafeFilterInputStream#in
|
||||
* @see java.io.InputStream#mark(int)
|
||||
* @see java.io.InputStream#reset()
|
||||
*/
|
||||
public boolean markSupported() {
|
||||
return in.markSupported();
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
|
||||
/**
|
||||
* This class is the superclass of all classes that filter output
|
||||
* streams. These streams sit on top of an already existing output
|
||||
* stream (the <i>underlying</i> output stream) which it uses as its
|
||||
* basic sink of data, but possibly transforming the data along the
|
||||
* way or providing additional functionality.
|
||||
* <p>
|
||||
* The class {@code FilterOutputStream} itself simply overrides
|
||||
* all methods of {@code SafeOutputStream} with versions that pass
|
||||
* all requests to the underlying output stream. Subclasses of
|
||||
* {@code FilterOutputStream} may further override some of these
|
||||
* methods as well as provide additional methods and fields.
|
||||
*
|
||||
* @author Jonathan Payne
|
||||
* @since 1.0
|
||||
*/
|
||||
public class SafeFilterOutputStream extends SafeOutputStream {
|
||||
/**
|
||||
* The underlying output stream to be filtered.
|
||||
*/
|
||||
protected SafeOutputStream out;
|
||||
|
||||
/**
|
||||
* Whether the stream is closed; implicitly initialized to false.
|
||||
*/
|
||||
private volatile boolean closed;
|
||||
|
||||
/**
|
||||
* Object used to prevent a race on the 'closed' instance variable.
|
||||
*/
|
||||
private final Object closeLock = new Object();
|
||||
|
||||
/**
|
||||
* Creates an output stream filter built on top of the specified
|
||||
* underlying output stream.
|
||||
*
|
||||
* @param out the underlying output stream to be assigned to
|
||||
* the field {@code this.out} for later use, or
|
||||
* {@code null} if this instance is to be
|
||||
* created without an underlying stream.
|
||||
*/
|
||||
public SafeFilterOutputStream(SafeOutputStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified {@code byte} to this output stream.
|
||||
* <p>
|
||||
* The {@code write} method of {@code FilterOutputStream}
|
||||
* calls the {@code write} method of its underlying output stream,
|
||||
* that is, it performs {@code out.write(b)}.
|
||||
* <p>
|
||||
* Implements the abstract {@code write} method of {@code SafeOutputStream}.
|
||||
*
|
||||
* @param b the {@code byte}.
|
||||
*/
|
||||
@Override
|
||||
public void write(int b) {
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code b.length} bytes to this output stream.
|
||||
* <p>
|
||||
* The {@code write} method of {@code FilterOutputStream}
|
||||
* calls its {@code write} method of three arguments with the
|
||||
* arguments {@code b}, {@code 0}, and
|
||||
* {@code b.length}.
|
||||
* <p>
|
||||
* Note that this method does not call the one-argument
|
||||
* {@code write} method of its underlying output stream with
|
||||
* the single argument {@code b}.
|
||||
*
|
||||
* @param b the data to be written.
|
||||
* @see SafeFilterOutputStream#write(byte[], int, int)
|
||||
*/
|
||||
@Override
|
||||
public void write(byte b[]) {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code len} bytes from the specified
|
||||
* {@code byte} array starting at offset {@code off} to
|
||||
* this output stream.
|
||||
* <p>
|
||||
* The {@code write} method of {@code FilterOutputStream}
|
||||
* calls the {@code write} method of one argument on each
|
||||
* {@code byte} to output.
|
||||
* <p>
|
||||
* Note that this method does not call the {@code write} method
|
||||
* of its underlying output stream with the same arguments. Subclasses
|
||||
* of {@code FilterOutputStream} should provide a more efficient
|
||||
* implementation of this method.
|
||||
*
|
||||
* @param b the data.
|
||||
* @param off the start offset in the data.
|
||||
* @param len the number of bytes to write.
|
||||
* @see SafeFilterOutputStream#write(int)
|
||||
*/
|
||||
@Override
|
||||
public void write(byte b[], int off, int len) {
|
||||
if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
|
||||
for (int i = 0 ; i < len ; i++) {
|
||||
write(b[off + i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes this output stream and forces any buffered output bytes
|
||||
* to be written out to the stream.
|
||||
* <p>
|
||||
* The {@code flush} method of {@code FilterOutputStream}
|
||||
* calls the {@code flush} method of its underlying output stream.
|
||||
*
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
@Override
|
||||
public void flush() {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this output stream and releases any system resources
|
||||
* associated with the stream.
|
||||
* <p>
|
||||
* When not already closed, the {@code close} method of {@code
|
||||
* FilterOutputStream} calls its {@code flush} method, and then
|
||||
* calls the {@code close} method of its underlying output stream.
|
||||
*
|
||||
* @see SafeFilterOutputStream#flush()
|
||||
* @see SafeFilterOutputStream#out
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
synchronized (closeLock) {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
|
||||
Throwable flushException = null;
|
||||
try {
|
||||
flush();
|
||||
} catch (Throwable e) {
|
||||
flushException = e;
|
||||
throw e;
|
||||
} finally {
|
||||
if (flushException == null) {
|
||||
out.close();
|
||||
} else {
|
||||
try {
|
||||
out.close();
|
||||
} catch (Throwable closeException) {
|
||||
// evaluate possible precedence of flushException over closeException
|
||||
if ((flushException instanceof ThreadDeath) &&
|
||||
!(closeException instanceof ThreadDeath)) {
|
||||
flushException.addSuppressed(closeException);
|
||||
throw (ThreadDeath) flushException;
|
||||
}
|
||||
|
||||
if (flushException != closeException) {
|
||||
closeException.addSuppressed(flushException);
|
||||
}
|
||||
|
||||
throw closeException;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class SafeInputStream extends InputStream {
|
||||
|
||||
// MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
|
||||
// use when skipping.
|
||||
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
|
||||
|
||||
private static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
@Override
|
||||
public abstract int read();
|
||||
|
||||
public int read(byte b[]) {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public int read(byte b[], int off, int len) {
|
||||
Objects.checkFromIndexSize(off, len, b.length);
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c = read();
|
||||
if (c == -1) {
|
||||
return -1;
|
||||
}
|
||||
b[off] = (byte)c;
|
||||
|
||||
int i = 1;
|
||||
for (; i < len ; i++) {
|
||||
c = read();
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
b[off + i] = (byte)c;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public byte[] readAllBytes() {
|
||||
return readNBytes(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
|
||||
|
||||
public byte[] readNBytes(int len) {
|
||||
if (len < 0) {
|
||||
throw new IllegalArgumentException("len < 0");
|
||||
}
|
||||
|
||||
List<byte[]> bufs = null;
|
||||
byte[] result = null;
|
||||
int total = 0;
|
||||
int remaining = len;
|
||||
int n;
|
||||
do {
|
||||
byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
|
||||
int nread = 0;
|
||||
|
||||
// read to EOF which may read more or less than buffer size
|
||||
while ((n = read(buf, nread,
|
||||
Math.min(buf.length - nread, remaining))) > 0) {
|
||||
nread += n;
|
||||
remaining -= n;
|
||||
}
|
||||
|
||||
if (nread > 0) {
|
||||
if (MAX_BUFFER_SIZE - total < nread) {
|
||||
throw new OutOfMemoryError("Required array size too large");
|
||||
}
|
||||
total += nread;
|
||||
if (result == null) {
|
||||
result = buf;
|
||||
} else {
|
||||
if (bufs == null) {
|
||||
bufs = new ArrayList<>();
|
||||
bufs.add(result);
|
||||
}
|
||||
bufs.add(buf);
|
||||
}
|
||||
}
|
||||
// if the last call to read returned -1 or the number of bytes
|
||||
// requested have been read then break
|
||||
} while (n >= 0 && remaining > 0);
|
||||
|
||||
if (bufs == null) {
|
||||
if (result == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
return result.length == total ?
|
||||
result : Arrays.copyOf(result, total);
|
||||
}
|
||||
|
||||
result = new byte[total];
|
||||
int offset = 0;
|
||||
remaining = total;
|
||||
for (byte[] b : bufs) {
|
||||
int count = Math.min(b.length, remaining);
|
||||
System.arraycopy(b, 0, result, offset, count);
|
||||
offset += count;
|
||||
remaining -= count;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readNBytes(byte[] b, int off, int len) {
|
||||
Objects.checkFromIndexSize(off, len, b.length);
|
||||
|
||||
int n = 0;
|
||||
while (n < len) {
|
||||
int count = read(b, off + n, len - n);
|
||||
if (count < 0)
|
||||
break;
|
||||
n += count;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public long skip(long n) {
|
||||
long remaining = n;
|
||||
int nr;
|
||||
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
|
||||
byte[] skipBuffer = new byte[size];
|
||||
while (remaining > 0) {
|
||||
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
|
||||
if (nr < 0) {
|
||||
break;
|
||||
}
|
||||
remaining -= nr;
|
||||
}
|
||||
|
||||
return n - remaining;
|
||||
}
|
||||
|
||||
public void skipNBytes(long n) {
|
||||
if (n > 0) {
|
||||
long ns = skip(n);
|
||||
if (ns >= 0 && ns < n) { // skipped too few bytes
|
||||
// adjust number to skip
|
||||
n -= ns;
|
||||
// read until requested number skipped or EOS reached
|
||||
while (n > 0 && read() != -1) {
|
||||
n--;
|
||||
}
|
||||
// if not enough skipped, then EOFE
|
||||
if (n != 0) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
} else if (ns != n) { // skipped negative or too many bytes
|
||||
throw new IllegalArgumentException("Unable to skip exactly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int available() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void close() {}
|
||||
|
||||
public void mark(int readlimit) {}
|
||||
|
||||
public void reset() {
|
||||
throw new UnsupportedOperationException("mark/reset not supported");
|
||||
}
|
||||
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public long transferTo(OutputStream out) {
|
||||
Objects.requireNonNull(out, "out");
|
||||
long transferred = 0;
|
||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
int read;
|
||||
while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
|
||||
try {
|
||||
out.write(buffer, 0, read);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
transferred += read;
|
||||
}
|
||||
return transferred;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/** An {@link InputStream} that implements also the {@link SafeMeasurableStream} interface.
|
||||
*
|
||||
* @since 5.0.4
|
||||
*/
|
||||
|
||||
public abstract class SafeMeasurableInputStream extends SafeInputStream implements SafeMeasurableStream {
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/** An {@link OutputStream} that implements also the {@link SafeMeasurableStream} interface.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
public abstract class SafeMeasurableOutputStream extends SafeOutputStream implements SafeMeasurableStream {
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedOutputStream;
|
||||
import it.unimi.dsi.fastutil.io.MeasurableInputStream;
|
||||
|
||||
/** An stream that provides eager access to its length,
|
||||
* and keeps track of the current position (e.g., the number of bytes read so far, or the current
|
||||
* position of the file pointer).
|
||||
*
|
||||
* <p>This class has two methods, both specified as optional. This apparently bizarre
|
||||
* behaviour is necessary because of wrapper classes which use reflection
|
||||
* to support those methods (see, e.g., {@link MeasurableInputStream}, {@link FastBufferedInputStream} and {@link FastBufferedOutputStream}).
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
public interface SafeMeasurableStream {
|
||||
|
||||
/** Returns the overall length of this stream (optional operation). In most cases, this will require the
|
||||
* stream to perform some extra action, possibly changing the state of the input stream itself (typically, reading
|
||||
* all the bytes up to the end, or flushing on output stream).
|
||||
* Implementing classes should always document what state will the input stream be in
|
||||
* after calling this method, and which kind of exception could be thrown.
|
||||
*/
|
||||
long length();
|
||||
|
||||
/** Returns the current position in this stream (optional operation).
|
||||
*
|
||||
* <p>Usually, the position is just the number of bytes read or written
|
||||
* since the stream was opened, but in the case of a
|
||||
* {@link it.unimi.dsi.fastutil.io.RepositionableStream} it
|
||||
* represent the current position.
|
||||
*/
|
||||
long position();
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.Flushable;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Objects;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This abstract class is the superclass of all classes representing
|
||||
* an output stream of bytes. An output stream accepts output bytes
|
||||
* and sends them to some sink.
|
||||
* <p>
|
||||
* Applications that need to define a subclass of
|
||||
* {@code OutputStream} must always provide at least a method
|
||||
* that writes one byte of output.
|
||||
*
|
||||
* @author Arthur van Hoff
|
||||
* @see java.io.BufferedOutputStream
|
||||
* @see java.io.ByteArrayOutputStream
|
||||
* @see java.io.DataOutputStream
|
||||
* @see java.io.FilterOutputStream
|
||||
* @see java.io.InputStream
|
||||
* @see OutputStream#write(int)
|
||||
* @since 1.0
|
||||
*/
|
||||
public abstract class SafeOutputStream extends OutputStream implements Closeable, Flushable {
|
||||
/**
|
||||
* Constructor for subclasses to call.
|
||||
*/
|
||||
public SafeOutputStream() {}
|
||||
|
||||
/**
|
||||
* Returns a new {@code OutputStream} which discards all bytes. The
|
||||
* returned stream is initially open. The stream is closed by calling
|
||||
* the {@code close()} method. Subsequent calls to {@code close()} have
|
||||
* no effect.
|
||||
*
|
||||
* <p> While the stream is open, the {@code write(int)}, {@code
|
||||
* write(byte[])}, and {@code write(byte[], int, int)} methods do nothing.
|
||||
* After the stream has been closed, these methods all throw {@code
|
||||
* IOException}.
|
||||
*
|
||||
* <p> The {@code flush()} method does nothing.
|
||||
*
|
||||
* @return an {@code OutputStream} which discards all bytes
|
||||
*
|
||||
* @since 11
|
||||
*/
|
||||
public static OutputStream nullOutputStream() {
|
||||
return new OutputStream() {
|
||||
private volatile boolean closed;
|
||||
|
||||
private void ensureOpen() {
|
||||
if (closed) {
|
||||
throw new IllegalStateException("Stream closed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
ensureOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte @NotNull [] b, int off, int len) {
|
||||
Objects.checkFromIndexSize(off, len, b.length);
|
||||
ensureOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte to this output stream. The general
|
||||
* contract for {@code write} is that one byte is written
|
||||
* to the output stream. The byte to be written is the eight
|
||||
* low-order bits of the argument {@code b}. The 24
|
||||
* high-order bits of {@code b} are ignored.
|
||||
* <p>
|
||||
* Subclasses of {@code OutputStream} must provide an
|
||||
* implementation for this method.
|
||||
*
|
||||
* @param b the {@code byte}.
|
||||
*/
|
||||
public abstract void write(int b);
|
||||
|
||||
/**
|
||||
* Writes {@code b.length} bytes from the specified byte array
|
||||
* to this output stream. The general contract for {@code write(b)}
|
||||
* is that it should have exactly the same effect as the call
|
||||
* {@code write(b, 0, b.length)}.
|
||||
*
|
||||
* @param b the data.
|
||||
* @see OutputStream#write(byte[], int, int)
|
||||
*/
|
||||
public void write(byte @NotNull [] b) {
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code len} bytes from the specified byte array
|
||||
* starting at offset {@code off} to this output stream.
|
||||
* The general contract for {@code write(b, off, len)} is that
|
||||
* some of the bytes in the array {@code b} are written to the
|
||||
* output stream in order; element {@code b[off]} is the first
|
||||
* byte written and {@code b[off+len-1]} is the last byte written
|
||||
* by this operation.
|
||||
* <p>
|
||||
* The {@code write} method of {@code OutputStream} calls
|
||||
* the write method of one argument on each of the bytes to be
|
||||
* written out. Subclasses are encouraged to override this method and
|
||||
* provide a more efficient implementation.
|
||||
* <p>
|
||||
* If {@code b} is {@code null}, a
|
||||
* {@code NullPointerException} is thrown.
|
||||
* <p>
|
||||
* If {@code off} is negative, or {@code len} is negative, or
|
||||
* {@code off+len} is greater than the length of the array
|
||||
* {@code b}, then an {@code IndexOutOfBoundsException} is thrown.
|
||||
*
|
||||
* @param b the data.
|
||||
* @param off the start offset in the data.
|
||||
* @param len the number of bytes to write.
|
||||
*/
|
||||
public void write(byte[] b, int off, int len) {
|
||||
Objects.checkFromIndexSize(off, len, b.length);
|
||||
// len == 0 condition implicitly handled by loop bounds
|
||||
for (int i = 0 ; i < len ; i++) {
|
||||
write(b[off + i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes this output stream and forces any buffered output bytes
|
||||
* to be written out. The general contract of {@code flush} is
|
||||
* that calling it is an indication that, if any bytes previously
|
||||
* written have been buffered by the implementation of the output
|
||||
* stream, such bytes should immediately be written to their
|
||||
* intended destination.
|
||||
* <p>
|
||||
* If the intended destination of this stream is an abstraction provided by
|
||||
* the underlying operating system, for example a file, then flushing the
|
||||
* stream guarantees only that bytes previously written to the stream are
|
||||
* passed to the operating system for writing; it does not guarantee that
|
||||
* they are actually written to a physical device such as a disk drive.
|
||||
* <p>
|
||||
* The {@code flush} method of {@code OutputStream} does nothing.
|
||||
*
|
||||
*/
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this output stream and releases any system resources
|
||||
* associated with this stream. The general contract of {@code close}
|
||||
* is that it closes the output stream. A closed stream cannot perform
|
||||
* output operations and cannot be reopened.
|
||||
* <p>
|
||||
* The {@code close} method of {@code OutputStream} does nothing.
|
||||
*
|
||||
*/
|
||||
public void close() {
|
||||
}
|
||||
}
|
@ -0,0 +1,332 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
/**
|
||||
* A {@code PushbackInputStream} adds
|
||||
* functionality to another input stream, namely
|
||||
* the ability to "push back" or "unread" bytes,
|
||||
* by storing pushed-back bytes in an internal buffer.
|
||||
* This is useful in situations where
|
||||
* it is convenient for a fragment of code
|
||||
* to read an indefinite number of data bytes
|
||||
* that are delimited by a particular byte
|
||||
* value; after reading the terminating byte,
|
||||
* the code fragment can "unread" it, so that
|
||||
* the next read operation on the input stream
|
||||
* will reread the byte that was pushed back.
|
||||
* For example, bytes representing the characters
|
||||
* constituting an identifier might be terminated
|
||||
* by a byte representing an operator character;
|
||||
* a method whose job is to read just an identifier
|
||||
* can read until it sees the operator and
|
||||
* then push the operator back to be re-read.
|
||||
*
|
||||
* @author David Connelly
|
||||
* @author Jonathan Payne
|
||||
* @since 1.0
|
||||
*/
|
||||
public class SafePushbackInputStream extends SafeFilterInputStream {
|
||||
/**
|
||||
* The pushback buffer.
|
||||
* @since 1.1
|
||||
*/
|
||||
protected byte[] buf;
|
||||
|
||||
/**
|
||||
* The position within the pushback buffer from which the next byte will
|
||||
* be read. When the buffer is empty, {@code pos} is equal to
|
||||
* {@code buf.length}; when the buffer is full, {@code pos} is
|
||||
* equal to zero.
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
protected int pos;
|
||||
|
||||
/**
|
||||
* Check to make sure that this stream has not been closed
|
||||
*/
|
||||
private void ensureOpen() {
|
||||
if (in == null)
|
||||
throw new IllegalStateException("Stream closed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code PushbackInputStream}
|
||||
* with a pushback buffer of the specified {@code size},
|
||||
* and saves its argument, the input stream
|
||||
* {@code in}, for later use. Initially,
|
||||
* the pushback buffer is empty.
|
||||
*
|
||||
* @param in the input stream from which bytes will be read.
|
||||
* @param size the size of the pushback buffer.
|
||||
* @throws IllegalArgumentException if {@code size <= 0}
|
||||
* @since 1.1
|
||||
*/
|
||||
public SafePushbackInputStream(SafeInputStream in, int size) {
|
||||
super(in);
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException("size <= 0");
|
||||
}
|
||||
this.buf = new byte[size];
|
||||
this.pos = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code PushbackInputStream}
|
||||
* with a 1-byte pushback buffer, and saves its argument, the input stream
|
||||
* {@code in}, for later use. Initially,
|
||||
* the pushback buffer is empty.
|
||||
*
|
||||
* @param in the input stream from which bytes will be read.
|
||||
*/
|
||||
public SafePushbackInputStream(SafeInputStream in) {
|
||||
this(in, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next byte of data from this input stream. The value
|
||||
* byte is returned as an {@code int} in the range
|
||||
* {@code 0} to {@code 255}. If no byte is available
|
||||
* because the end of the stream has been reached, the value
|
||||
* {@code -1} is returned. This method blocks until input data
|
||||
* is available, the end of the stream is detected, or an exception
|
||||
* is thrown.
|
||||
*
|
||||
* <p> This method returns the most recently pushed-back byte, if there is
|
||||
* one, and otherwise calls the {@code read} method of its underlying
|
||||
* input stream and returns whatever value that method returns.
|
||||
*
|
||||
* @return the next byte of data, or {@code -1} if the end of the
|
||||
* stream has been reached.
|
||||
* or an I/O error occurs.
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
public int read() {
|
||||
ensureOpen();
|
||||
if (pos < buf.length) {
|
||||
return buf[pos++] & 0xff;
|
||||
}
|
||||
return super.read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@code len} bytes of data from this input stream into
|
||||
* an array of bytes. This method first reads any pushed-back bytes; after
|
||||
* that, if fewer than {@code len} bytes have been read then it
|
||||
* reads from the underlying input stream. If {@code len} is not zero, the method
|
||||
* blocks until at least 1 byte of input is available; otherwise, no
|
||||
* bytes are read and {@code 0} is returned.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset in the destination array {@code b}
|
||||
* @param len the maximum number of bytes read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* {@code -1} if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
* @throws NullPointerException If {@code b} is {@code null}.
|
||||
* @throws IndexOutOfBoundsException If {@code off} is negative,
|
||||
* {@code len} is negative, or {@code len} is greater than
|
||||
* {@code b.length - off}
|
||||
* or an I/O error occurs.
|
||||
* @see java.io.InputStream#read(byte[], int, int)
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) {
|
||||
ensureOpen();
|
||||
if (b == null) {
|
||||
throw new NullPointerException();
|
||||
} else if (off < 0 || len < 0 || len > b.length - off) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
} else if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int avail = buf.length - pos;
|
||||
if (avail > 0) {
|
||||
if (len < avail) {
|
||||
avail = len;
|
||||
}
|
||||
System.arraycopy(buf, pos, b, off, avail);
|
||||
pos += avail;
|
||||
off += avail;
|
||||
len -= avail;
|
||||
}
|
||||
if (len > 0) {
|
||||
len = super.read(b, off, len);
|
||||
if (len == -1) {
|
||||
return avail == 0 ? -1 : avail;
|
||||
}
|
||||
return avail + len;
|
||||
}
|
||||
return avail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes back a byte by copying it to the front of the pushback buffer.
|
||||
* After this method returns, the next byte to be read will have the value
|
||||
* {@code (byte)b}.
|
||||
*
|
||||
* @param b the {@code int} value whose low-order
|
||||
* byte is to be pushed back.
|
||||
*/
|
||||
public void unread(int b) {
|
||||
ensureOpen();
|
||||
if (pos == 0) {
|
||||
throw new IllegalStateException("Push back buffer is full");
|
||||
}
|
||||
buf[--pos] = (byte)b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes back a portion of an array of bytes by copying it to the front
|
||||
* of the pushback buffer. After this method returns, the next byte to be
|
||||
* read will have the value {@code b[off]}, the byte after that will
|
||||
* have the value {@code b[off+1]}, and so forth.
|
||||
*
|
||||
* @param b the byte array to push back.
|
||||
* @param off the start offset of the data.
|
||||
* @param len the number of bytes to push back.
|
||||
* @throws NullPointerException If {@code b} is {@code null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
public void unread(byte[] b, int off, int len) {
|
||||
ensureOpen();
|
||||
if (len > pos) {
|
||||
throw new IllegalStateException("Push back buffer is full");
|
||||
}
|
||||
pos -= len;
|
||||
System.arraycopy(b, off, buf, pos, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes back an array of bytes by copying it to the front of the
|
||||
* pushback buffer. After this method returns, the next byte to be read
|
||||
* will have the value {@code b[0]}, the byte after that will have the
|
||||
* value {@code b[1]}, and so forth.
|
||||
*
|
||||
* @param b the byte array to push back
|
||||
* @throws NullPointerException If {@code b} is {@code null}.
|
||||
* @since 1.1
|
||||
*/
|
||||
public void unread(byte[] b) {
|
||||
unread(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an estimate of the number of bytes that can be read (or
|
||||
* skipped over) from this input stream without blocking by the next
|
||||
* invocation of a method for this input stream. The next invocation might be
|
||||
* the same thread or another thread. A single read or skip of this
|
||||
* many bytes will not block, but may read or skip fewer bytes.
|
||||
*
|
||||
* <p> The method returns the sum of the number of bytes that have been
|
||||
* pushed back and the value returned by {@link
|
||||
* SafeFilterInputStream#available available}.
|
||||
*
|
||||
* @return the number of bytes that can be read (or skipped over) from
|
||||
* the input stream without blocking.
|
||||
* @see SafeFilterInputStream#in
|
||||
* @see java.io.InputStream#available()
|
||||
*/
|
||||
public int available() {
|
||||
ensureOpen();
|
||||
int n = buf.length - pos;
|
||||
int avail = super.available();
|
||||
return n > (Integer.MAX_VALUE - avail)
|
||||
? Integer.MAX_VALUE
|
||||
: n + avail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips over and discards {@code n} bytes of data from this
|
||||
* input stream. The {@code skip} method may, for a variety of
|
||||
* reasons, end up skipping over some smaller number of bytes,
|
||||
* possibly zero. If {@code n} is negative, no bytes are skipped.
|
||||
*
|
||||
* <p> The {@code skip} method of {@code PushbackInputStream}
|
||||
* first skips over the bytes in the pushback buffer, if any. It then
|
||||
* calls the {@code skip} method of the underlying input stream if
|
||||
* more bytes need to be skipped. The actual number of bytes skipped
|
||||
* is returned.
|
||||
*
|
||||
* @param n {@inheritDoc}
|
||||
* @return {@inheritDoc}
|
||||
* @see SafeFilterInputStream#in
|
||||
* @see java.io.InputStream#skip(long n)
|
||||
* @since 1.2
|
||||
*/
|
||||
public long skip(long n) {
|
||||
ensureOpen();
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long pskip = buf.length - pos;
|
||||
if (pskip > 0) {
|
||||
if (n < pskip) {
|
||||
pskip = n;
|
||||
}
|
||||
pos += pskip;
|
||||
n -= pskip;
|
||||
}
|
||||
if (n > 0) {
|
||||
pskip += super.skip(n);
|
||||
}
|
||||
return pskip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this input stream supports the {@code mark} and
|
||||
* {@code reset} methods, which it does not.
|
||||
*
|
||||
* @return {@code false}, since this class does not support the
|
||||
* {@code mark} and {@code reset} methods.
|
||||
* @see java.io.InputStream#mark(int)
|
||||
* @see java.io.InputStream#reset()
|
||||
*/
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current position in this input stream.
|
||||
*
|
||||
* <p> The {@code mark} method of {@code PushbackInputStream}
|
||||
* does nothing.
|
||||
*
|
||||
* @param readlimit the maximum limit of bytes that can be read before
|
||||
* the mark position becomes invalid.
|
||||
* @see java.io.InputStream#reset()
|
||||
*/
|
||||
public void mark(int readlimit) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Repositions this stream to the position at the time the
|
||||
* {@code mark} method was last called on this input stream.
|
||||
*
|
||||
* <p> The method {@code reset} for class
|
||||
* {@code PushbackInputStream} does nothing except throw an
|
||||
* {@code IOException}.
|
||||
*
|
||||
* @see java.io.InputStream#mark(int)
|
||||
* @see java.io.IOException
|
||||
*/
|
||||
public void reset() {
|
||||
throw new UnsupportedOperationException("mark/reset not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this input stream and releases any system resources
|
||||
* associated with the stream.
|
||||
* Once the stream has been closed, further read(), unread(),
|
||||
* available(), reset(), or skip() invocations will throw an IOException.
|
||||
* Closing a previously closed stream has no effect.
|
||||
*
|
||||
*/
|
||||
public void close() {
|
||||
if (in == null)
|
||||
return;
|
||||
in.close();
|
||||
in = null;
|
||||
buf = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package it.cavallium.stream;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2005-2020 Sebastiano Vigna
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
|
||||
/** A basic interface specifying positioning methods for a byte stream.
|
||||
*
|
||||
* @author Sebastiano Vigna
|
||||
* @since 4.4
|
||||
*/
|
||||
|
||||
public interface SafeRepositionableStream {
|
||||
|
||||
/** Sets the current stream position.
|
||||
*
|
||||
* @param newPosition the new stream position.
|
||||
*/
|
||||
void position(long newPosition);
|
||||
|
||||
/** Returns the current stream position.
|
||||
*
|
||||
* @return the current stream position.
|
||||
*/
|
||||
long position();
|
||||
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
module data.generator.runtime {
|
||||
exports it.cavallium.data.generator.nativedata;
|
||||
exports it.cavallium.data.generator;
|
||||
exports it.cavallium.stream;
|
||||
exports it.cavallium.buffer;
|
||||
|
||||
requires org.jetbrains.annotations;
|
||||
requires it.unimi.dsi.fastutil;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package it.cavallium.data.generator.nativedata;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import it.cavallium.stream.SafeByteArrayInputStream;
|
||||
import it.cavallium.stream.SafeByteArrayOutputStream;
|
||||
import it.cavallium.stream.SafeDataInputStream;
|
||||
import it.cavallium.stream.SafeDataOutputStream;
|
||||
import java.io.IOException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
@ -24,15 +24,15 @@ public class TestInt52Serializer {
|
||||
public void testInt52Serialization(long n) throws IOException {
|
||||
var serializer = new Int52Serializer();
|
||||
byte[] out;
|
||||
try (var baos = new ByteArrayOutputStream()) {
|
||||
try (var dos = new DataOutputStream(baos)) {
|
||||
try (var baos = new SafeByteArrayOutputStream()) {
|
||||
try (var dos = new SafeDataOutputStream(baos)) {
|
||||
serializer.serialize(dos, Int52.fromLong(n));
|
||||
}
|
||||
out = baos.toByteArray();
|
||||
}
|
||||
|
||||
var bais = new ByteArrayInputStream(out);
|
||||
var dis = new DataInputStream(bais);
|
||||
var bais = new SafeByteArrayInputStream(out);
|
||||
var dis = new SafeDataInputStream(bais);
|
||||
Assertions.assertEquals(n, serializer.deserialize(dis).longValue(), "Deserialized number differ");
|
||||
}
|
||||
|
||||
@ -48,8 +48,8 @@ public class TestInt52Serializer {
|
||||
public void testInt52OptionalSerialization(@Nullable Long n) throws IOException {
|
||||
var serializer = new NullableInt52Serializer();
|
||||
byte[] out;
|
||||
try (var baos = new ByteArrayOutputStream()) {
|
||||
try (var dos = new DataOutputStream(baos)) {
|
||||
try (var baos = new SafeByteArrayOutputStream()) {
|
||||
try (var dos = new SafeDataOutputStream(baos)) {
|
||||
if (n == null) {
|
||||
serializer.serialize(dos, NullableInt52.empty());
|
||||
} else {
|
||||
@ -59,8 +59,8 @@ public class TestInt52Serializer {
|
||||
out = baos.toByteArray();
|
||||
}
|
||||
|
||||
var bais = new ByteArrayInputStream(out);
|
||||
var dis = new DataInputStream(bais);
|
||||
var bais = new SafeByteArrayInputStream(out);
|
||||
var dis = new SafeDataInputStream(bais);
|
||||
if (n == null) {
|
||||
Assertions.assertNull(serializer.deserialize(dis).getNullable(), "Deserialized number is not empty");
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user