blob: 912b3cb0562b53ff43c61f0a2dbe24b9879efe14 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.rj.data;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* IO implementation for RJ data serialization.
*/
public final class RJIO {
private static final ThreadLocal<RJIO> INSTANCES= new ThreadLocal<>() {
@Override
protected RJIO initialValue() {
return new RJIO();
}
};
public static RJIO get(final ObjectOutput out) {
final RJIO io= INSTANCES.get();
io.connect(out);
return io;
}
public static RJIO get(final ObjectInput in) {
final RJIO io= INSTANCES.get();
io.connect(in);
return io;
}
private static final int BB_LENGTH= 16384;
private static final int BA_LENGTH= BB_LENGTH;
private static final int BB_PART= BB_LENGTH / 4;
private static final int CB_LENGTH= BB_LENGTH / 2;
private static final int CA_LENGTH= BB_LENGTH * 4;
private static final int IB_LENGTH= BB_LENGTH / 4;
private static final int DB_LENGTH= BB_LENGTH / 8;
private static final int[] EMPTY_INT_ARRAY= new int[0];
private static final byte MODE_BBARRAY= 0;
private static final byte MODE_IPARRAY= 1;
private final ByteBuffer bb;
private final byte[] ba;
private final CharBuffer cb;
private final char[] ca;
private final IntBuffer ib;
private final DoubleBuffer db;
private final byte mode;
private ObjectInput in;
private ObjectOutput out;
private int temp;
public int flags;
private int serialKey;
public RJIO() {
this.bb= ByteBuffer.allocateDirect(BB_LENGTH);
if (this.bb.hasArray()) {
this.mode= MODE_BBARRAY;
this.ba= this.bb.array();
}
else {
this.mode= MODE_IPARRAY;
this.ba= new byte[BB_LENGTH];
}
this.cb= this.bb.asCharBuffer();
this.ca= new char[CA_LENGTH];
this.ib= this.bb.asIntBuffer();
this.db= this.bb.asDoubleBuffer();
this.serialKey= (int)System.currentTimeMillis();
}
public void connect(final ObjectOutput out) {
this.out= out;
}
public void connect(final ObjectInput in) {
this.in= in;
}
public void disconnect(final ObjectOutput out) throws IOException {
this.out.flush();
this.out= null;
}
public void disconnect(final ObjectInput in) throws IOException {
this.in= null;
}
private void writeFullyBB(final int bn) throws IOException {
switch (this.mode) {
case MODE_BBARRAY:
this.out.write(this.ba, 0, bn);
return;
// case MODE_IPARRAY:
default:
this.bb.clear();
this.bb.get(this.ba, 0, bn);
this.out.write(this.ba, 0, bn);
return;
}
}
private void readFullyBB(final int bn) throws IOException {
switch (this.mode) {
case MODE_BBARRAY:
this.in.readFully(this.ba, 0, bn);
return;
// case MODE_IPARRAY:
default:
this.in.readFully(this.ba, 0, bn);
this.bb.clear();
this.bb.put(this.ba, 0, bn);
return;
}
}
private void readFullyBB(final int pos, final int bn) throws IOException {
switch (this.mode) {
case MODE_BBARRAY:
this.in.readFully(this.ba, pos, bn);
return;
// case MODE_IPARRAY:
default:
this.in.readFully(this.ba, pos, bn);
this.bb.clear();
this.bb.put(this.ba, 0, pos+bn);
// this.bb.position(pos);
// this.bb.put(this.ba, pos, bn);
return;
}
}
public void writeDirectly(final byte[] bytes, final int off, final int n) throws IOException {
this.out.write(bytes, off, n);
}
public void writeByte(final byte value) throws IOException {
this.out.writeByte(value);
}
public void writeByte(final int value) throws IOException {
this.out.writeByte(value);
}
public void writeBoolean(final boolean value) throws IOException {
this.out.writeByte(value ? 0x1 : 0x0);
}
public void writeInt(final int value) throws IOException {
this.out.writeInt(value);
}
public void writeIntArray(final int[] array, final int length) throws IOException {
final ObjectOutput out= this.out;
out.writeInt(length);
if (length <= 32) {
for (int i= 0; i < length; i++) {
out.writeInt(array[i]);
}
}
else if (length <= IB_LENGTH) {
this.ib.clear();
this.ib.put(array, 0, length);
writeFullyBB(length << 2);
}
else {
int iw= 0;
while (iw < length) {
final int icount= Math.min(length - iw, IB_LENGTH);
this.ib.clear();
this.ib.put(array, iw, icount);
writeFullyBB(icount << 2);
iw+= icount;
}
}
}
public void writeIntData(final int[] array, final int length) throws IOException {
final ObjectOutput out= this.out;
if (length <= 32) {
for (int i= 0; i < length; i++) {
out.writeInt(array[i]);
}
}
else if (length <= IB_LENGTH) {
this.ib.clear();
this.ib.put(array, 0, length);
writeFullyBB(length << 2);
}
else {
int iw= 0;
while (iw < length) {
final int icount= Math.min(length - iw, IB_LENGTH);
this.ib.clear();
this.ib.put(array, iw, icount);
writeFullyBB(icount << 2);
iw+= icount;
}
}
}
public void writeLong(final long value) throws IOException {
this.out.writeLong(value);
}
public byte getVULongGrade(final long value) {
if ((value & 0xffffffffffffff00L) == 0) {
return (byte) 0;
}
if ((value & 0xffffffffffff0000L) == 0) {
return (byte) 1;
}
if ((value & 0xffffffffff000000L) == 0) {
return (byte) 2;
}
if ((value & 0xffffffff00000000L) == 0) {
return (byte) 3;
}
if ((value & 0xffffff0000000000L) == 0) {
return (byte) 4;
}
if ((value & 0xffff000000000000L) == 0) {
return (byte) 5;
}
if ((value & 0xff00000000000000L) == 0) {
return (byte) 6;
}
return 7;
}
public void writeVULong(final byte grade, final long value) throws IOException {
if (grade == 0) {
this.out.writeByte((int)value);
return;
}
final byte[] ba= this.ba;
switch (grade) {
// case 0:
// ba[0]= (byte) (value);
// this.out.write(ba, 0, 1);
// return;
case 1:
ba[0]= (byte) (value >>> 8);
ba[1]= (byte) (value);
this.out.write(ba, 0, 2);
return;
case 2:
ba[0]= (byte) (value >>> 16);
ba[1]= (byte) (value >>> 8);
ba[2]= (byte) (value);
this.out.write(ba, 0, 3);
return;
case 3:
ba[0]= (byte) (value >>> 24);
ba[1]= (byte) (value >>> 16);
ba[2]= (byte) (value >>> 8);
ba[3]= (byte) (value);
this.out.write(ba, 0, 4);
return;
case 4:
ba[0]= (byte) (value >>> 32);
ba[1]= (byte) (value >>> 24);
ba[2]= (byte) (value >>> 16);
ba[3]= (byte) (value >>> 8);
ba[4]= (byte) (value);
this.out.write(ba, 0, 5);
return;
case 5:
ba[0]= (byte) (value >>> 40);
ba[1]= (byte) (value >>> 32);
ba[2]= (byte) (value >>> 24);
ba[3]= (byte) (value >>> 16);
ba[4]= (byte) (value >>> 8);
ba[5]= (byte) (value);
this.out.write(ba, 0, 6);
return;
case 6:
ba[0]= (byte) (value >>> 48);
ba[1]= (byte) (value >>> 40);
ba[2]= (byte) (value >>> 32);
ba[3]= (byte) (value >>> 24);
ba[4]= (byte) (value >>> 16);
ba[5]= (byte) (value >>> 8);
ba[6]= (byte) (value);
this.out.write(ba, 0, 7);
return;
case 7:
ba[0]= (byte) (value >>> 56);
ba[1]= (byte) (value >>> 48);
ba[2]= (byte) (value >>> 40);
ba[3]= (byte) (value >>> 32);
ba[4]= (byte) (value >>> 24);
ba[5]= (byte) (value >>> 16);
ba[6]= (byte) (value >>> 8);
ba[7]= (byte) (value);
this.out.write(ba, 0, 8);
return;
default:
throw new IOException("Unsupported data format (c= " + grade + ").");
}
}
public void writeDouble(final double value) throws IOException {
this.out.writeDouble(value);
}
public void writeDoubleData(final double[] array, final int length) throws IOException {
final ObjectOutput out= this.out;
if (length <= 16) {
for (int i= 0; i < length; i++) {
out.writeLong(Double.doubleToRawLongBits(array[i]));
}
}
else if (length <= DB_LENGTH) {
this.db.clear();
this.db.put(array, 0, length);
writeFullyBB(length << 3);
}
else {
int dw= 0;
while (dw < length) {
final int dcount= Math.min(length - dw, DB_LENGTH);
this.db.clear();
this.db.put(array, dw, dcount);
writeFullyBB(dcount << 3);
dw+= dcount;
}
}
}
public void writeFloat(final float value) throws IOException {
this.out.writeFloat(value);
}
public void writeByteData(final byte[] array, final int length) throws IOException {
final ObjectOutput out= this.out;
out.write(array, 0, length);
}
@SuppressWarnings("deprecation")
public void writeString(final String s) throws IOException {
final ObjectOutput out= this.out;
if (s != null) {
final int cn= s.length();
ASCII: if (cn <= BA_LENGTH) {
for (int ci= 0; ci < cn; ) {
if ((s.charAt(ci++) & 0xffffff00) != 0) {
break ASCII;
}
}
if (cn <= 8) {
out.writeInt(-cn);
out.writeBytes(s);
return;
}
else {
out.writeInt(-cn);
s.getBytes(0, cn, this.ba, 0);
out.write(this.ba, 0, cn);
return;
}
}
out.writeInt(cn);
out.writeChars(s);
return;
}
else {
out.writeInt(Integer.MIN_VALUE);
return;
}
}
@SuppressWarnings("deprecation")
public void writeStringData(final String[] sa, final int length) throws IOException {
final ObjectOutput out= this.out;
ARRAY: for (int i= 0; i < length; i++) {
final String s= sa[i];
if (s != null) {
final int cn= s.length();
ASCII: if (cn <= BA_LENGTH) {
for (int ci= 0; ci < cn; ) {
if ((s.charAt(ci++) & 0xffffff00) != 0) {
break ASCII;
}
}
if (cn <= 8) {
out.writeInt(-cn);
out.writeBytes(s);
continue ARRAY;
}
else {
out.writeInt(-cn);
s.getBytes(0, cn, this.ba, 0);
out.write(this.ba, 0, cn);
continue ARRAY;
}
}
out.writeInt(cn);
out.writeChars(s);
continue ARRAY;
}
else {
out.writeInt(Integer.MIN_VALUE);
continue ARRAY;
}
}
}
public void writeStringKeyMap(final Map<String, Object> map) throws IOException {
final ObjectOutput out= this.out;
if (map == null) {
out.writeInt(-1);
return;
}
out.writeInt(map.size());
for (final Entry<String, Object> entry : map.entrySet()) {
writeString(entry.getKey());
out.writeObject(entry.getValue());
}
}
public byte readByte() throws IOException {
return this.in.readByte();
}
public boolean readBoolean() throws IOException {
return (this.in.readByte() == 0x1);
}
public int readInt() throws IOException {
return this.in.readInt();
}
public int[] readIntData(final int[] array, final int length) throws IOException {
final ObjectInput in= this.in;
if (length <= 256) {
switch (length) {
case 0:
return array;
case 1:
array[0]= in.readInt();
return array;
case 2:
array[0]= in.readInt();
array[1]= in.readInt();
return array;
case 3:
array[0]= in.readInt();
array[1]= in.readInt();
array[2]= in.readInt();
return array;
case 4:
array[0]= in.readInt();
array[1]= in.readInt();
array[2]= in.readInt();
array[3]= in.readInt();
return array;
default:
final int bn= length << 2;
in.readFully(this.ba, 0, bn);
for (int ib= 0; ib < bn; ib+= 4) {
array[ib >>> 2]= (
((this.ba[ib] & 0xff) << 24) |
((this.ba[ib+1] & 0xff) << 16) |
((this.ba[ib+2] & 0xff) << 8) |
((this.ba[ib+3] & 0xff)) );
}
return array;
}
}
else if (length <= IB_LENGTH) {
readFullyBB(length << 2);
this.ib.clear();
this.ib.get(array, 0, length);
return array;
}
else {
int ir= 0;
int position= 0;
final int bToComplete;
while (true) {
position+= in.read(this.ba, position, BA_LENGTH-position);
if (position >= BB_PART) {
final int icount= (position >>> 2);
final int bcount= (icount << 2);
if (this.mode != MODE_BBARRAY) {
this.bb.clear();
this.bb.put(this.ba, 0, bcount);
}
this.ib.clear();
this.ib.get(array, ir, icount);
ir+= icount;
switch (position - bcount) {
case 0:
position= 0;
break;
case 1:
this.ba[0]= this.ba[bcount];
position= 1;
break;
case 2:
this.ba[0]= this.ba[bcount];
this.ba[1]= this.ba[bcount+1];
position= 2;
break;
case 3:
array[ir++]= (
((this.ba[bcount] & 0xff) << 24) |
((this.ba[bcount+1] & 0xff) << 16) |
((this.ba[bcount+2] & 0xff) << 8) |
((in.readByte() & 0xff)) );
position= 0;
break;
}
if (length - ir <= IB_LENGTH) {
bToComplete= ((length - ir) << 2);
break;
}
}
}
if (bToComplete > 0) {
readFullyBB(position, bToComplete-position);
this.ib.clear();
this.ib.get(array, ir, bToComplete >>> 2);
}
return array;
}
}
public int[] readIntArray() throws IOException {
final ObjectInput in= this.in;
final int length= in.readInt();
if (length <= 256) {
switch (length) {
case -1:
return null;
case 0:
return EMPTY_INT_ARRAY;
case 1:
return new int[] { in.readInt() };
case 2:
return new int[] { in.readInt(), in.readInt() };
case 3:
return new int[] { in.readInt(), in.readInt(), in.readInt() };
case 4:
return new int[] { in.readInt(), in.readInt(), in.readInt(), in.readInt() };
default:
final int bn= length << 2;
in.readFully(this.ba, 0, bn);
final int[] array= new int[length];
for (int ib= 0; ib < bn; ib+= 4) {
array[ib >>> 2]= (
((this.ba[ib] & 0xff) << 24) |
((this.ba[ib+1] & 0xff) << 16) |
((this.ba[ib+2] & 0xff) << 8) |
((this.ba[ib+3] & 0xff)) );
}
return array;
}
}
else if (length <= IB_LENGTH) {
readFullyBB(length << 2);
final int[] array= new int[length];
this.ib.clear();
this.ib.get(array, 0, length);
return array;
}
else {
final int[] array= new int[length];
int ir= 0;
int position= 0;
final int bToComplete;
while (true) {
position+= in.read(this.ba, position, BA_LENGTH-position);
if (position >= BB_PART) {
final int icount= (position >>> 2);
final int bcount= (icount << 2);
if (this.mode != MODE_BBARRAY) {
this.bb.clear();
this.bb.put(this.ba, 0, bcount);
}
this.ib.clear();
this.ib.get(array, ir, icount);
ir+= icount;
switch (position - bcount) {
case 0:
position= 0;
break;
case 1:
this.ba[0]= this.ba[bcount];
position= 1;
break;
case 2:
this.ba[0]= this.ba[bcount];
this.ba[1]= this.ba[bcount+1];
position= 2;
break;
case 3:
array[ir++]= (
((this.ba[bcount] & 0xff) << 24) |
((this.ba[bcount+1] & 0xff) << 16) |
((this.ba[bcount+2] & 0xff) << 8) |
((in.readByte() & 0xff)) );
position= 0;
break;
}
if (length - ir <= IB_LENGTH) {
bToComplete= ((length - ir) << 2);
break;
}
}
}
if (bToComplete > 0) {
readFullyBB(position, bToComplete-position);
this.ib.clear();
this.ib.get(array, ir, bToComplete >>> 2);
}
return array;
}
}
public long readLong() throws IOException {
return this.in.readLong();
}
public long readVULong(final byte grade) throws IOException {
if (grade == 0) {
return this.in.readUnsignedByte();
}
final byte[] ba= this.ba;
switch (grade) {
// case 0:
// this.in.read(ba, 0, 1);
// return (ba[0] & 0xff);
case 1:
this.in.read(ba, 0, 2);
return (((ba[0] & 0xff) << 8) |
(ba[1] & 0xff) );
case 2:
this.in.read(ba, 0, 3);
return (((ba[0] & 0xff) << 16) |
((ba[1] & 0xff) << 8) |
(ba[2] & 0xff) );
case 3:
this.in.read(ba, 0, 4);
return (((long)(ba[0] & 0xff) << 24) |
((ba[1] & 0xff) << 16) |
((ba[2] & 0xff) << 8) |
(ba[3] & 0xff) );
case 4:
this.in.read(ba, 0, 5);
return (((long)(ba[0] & 0xff) << 32) |
((long)(ba[1] & 0xff) << 24) |
((ba[2] & 0xff) << 16) |
((ba[3] & 0xff) << 8) |
(ba[4] & 0xff) );
case 5:
this.in.read(ba, 0, 6);
return (((long)(ba[0] & 0xff) << 40) |
((long)(ba[1] & 0xff) << 32) |
((long)(ba[2] & 0xff) << 24) |
((ba[3] & 0xff) << 16) |
((ba[4] & 0xff) << 8) |
(ba[5] & 0xff) );
case 6:
this.in.read(ba, 0, 7);
return (((long)(ba[0] & 0xff) << 48) |
((long)(ba[1] & 0xff) << 40) |
((long)(ba[2] & 0xff) << 32) |
((long)(ba[3] & 0xff) << 24) |
((ba[4] & 0xff) << 16) |
((ba[5] & 0xff) << 8) |
(ba[6] & 0xff) );
case 7:
this.in.read(ba, 0, 8);
return (((long)(ba[0] & 0xff) << 56) |
((long)(ba[1] & 0xff) << 48) |
((long)(ba[2] & 0xff) << 40) |
((long)(ba[3] & 0xff) << 32) |
((long)(ba[4] & 0xff) << 24) |
((ba[5] & 0xff) << 16) |
((ba[6] & 0xff) << 8) |
(ba[7] & 0xff) );
default:
throw new IOException("Unsupported data format (c= " + grade + ").");
}
}
public double readDouble() throws IOException {
return this.in.readDouble();
}
public double[] readDoubleData(final double[] array, final int length) throws IOException {
final ObjectInput in= this.in;
if (length <= 32) {
switch (length) {
case 0:
return array;
case 1:
array[0]= Double.longBitsToDouble(in.readLong());
return array;
case 2:
array[0]= Double.longBitsToDouble(in.readLong());
array[1]= Double.longBitsToDouble(in.readLong());
return array;
default:
final int bn= length << 3;
in.readFully(this.ba, 0, bn);
for (int db= 0; db < bn; db+= 8) {
array[db >>> 3]= Double.longBitsToDouble(
((long)(this.ba[db] & 0xff) << 56) |
((long)(this.ba[db+1] & 0xff) << 48) |
((long)(this.ba[db+2] & 0xff) << 40) |
((long)(this.ba[db+3] & 0xff) << 32) |
((long)(this.ba[db+4] & 0xff) << 24) |
((this.ba[db+5] & 0xff) << 16) |
((this.ba[db+6] & 0xff) << 8) |
((this.ba[db+7] & 0xff)) );
}
return array;
}
}
else if (length <= DB_LENGTH) {
readFullyBB(length << 3);
this.db.clear();
this.db.get(array, 0, length);
return array;
}
else {
int dr= 0;
int position= 0;
final int bToComplete;
while (true) {
position+= in.read(this.ba, position, BA_LENGTH-position);
if (position >= BB_PART) {
final int dcount= (position >>> 3);
final int bcount= (dcount << 3);
if (this.mode != MODE_BBARRAY) {
this.bb.clear();
this.bb.put(this.ba, 0, bcount);
}
this.db.clear();
this.db.get(array, dr, dcount);
dr+= dcount;
switch (position - bcount) {
case 0:
position= 0;
break;
case 1:
this.ba[0]= this.ba[bcount];
position= 1;
break;
case 2:
this.ba[0]= this.ba[bcount];
this.ba[1]= this.ba[bcount+1];
position= 2;
break;
case 3:
this.ba[0]= this.ba[bcount];
this.ba[1]= this.ba[bcount+1];
this.ba[2]= this.ba[bcount+2];
position= 3;
break;
case 4:
this.ba[0]= this.ba[bcount];
this.ba[1]= this.ba[bcount+1];
this.ba[2]= this.ba[bcount+2];
this.ba[3]= this.ba[bcount+3];
position= 4;
break;
case 5:
this.ba[0]= this.ba[bcount];
this.ba[1]= this.ba[bcount+1];
this.ba[2]= this.ba[bcount+2];
this.ba[3]= this.ba[bcount+3];
this.ba[4]= this.ba[bcount+4];
position= 5;
break;
case 6:
array[dr++]= Double.longBitsToDouble(
((long)(this.ba[bcount] & 0xff) << 56) |
((long)(this.ba[bcount+1] & 0xff) << 48) |
((long)(this.ba[bcount+2] & 0xff) << 40) |
((long)(this.ba[bcount+3] & 0xff) << 32) |
((long)(this.ba[bcount+4] & 0xff) << 24) |
((this.ba[bcount+5] & 0xff) << 16) |
((in.readByte() & 0xff) << 8) |
((in.readByte() & 0xff)) );
position= 0;
break;
case 7:
array[dr++]= Double.longBitsToDouble(
((long)(this.ba[bcount] & 0xff) << 56) |
((long)(this.ba[bcount+1] & 0xff) << 48) |
((long)(this.ba[bcount+2] & 0xff) << 40) |
((long)(this.ba[bcount+3] & 0xff) << 32) |
((long)(this.ba[bcount+4] & 0xff) << 24) |
((this.ba[bcount+5] & 0xff) << 16) |
((this.ba[bcount+6] & 0xff) << 8) |
((in.readByte() & 0xff)) );
position= 0;
break;
}
if (length - dr <= DB_LENGTH) {
bToComplete= ((length - dr) << 3);
break;
}
}
}
if (bToComplete > 0) {
readFullyBB(position, bToComplete-position);
this.db.clear();
this.db.get(array, dr, bToComplete >>> 3);
}
return array;
}
}
public double[] readDoubleArray() throws IOException {
final int l= this.temp= this.in.readInt();
return readDoubleData(new double[l], l);
}
public double[] readDoubleArray2() throws IOException {
final int l= this.temp;
return readDoubleData(new double[l], l);
}
public float readFloat() throws IOException {
return this.in.readFloat();
}
public byte[] readByteData(final byte[] array, final int length) throws IOException {
this.in.readFully(array, 0, length);
return array;
}
public byte[] readByteArray() throws IOException {
final int l= this.temp= this.in.readInt();
return readByteData(new byte[l], l);
}
public byte[] readByteArray2() throws IOException {
final int l= this.temp;
return readByteData(new byte[l], l);
}
private String readString(final int cn, final char[] ca, final ObjectInput in) throws IOException {
int cr= 0;
int position= 0;
final int bToComplete;
while (true) {
position+= in.read(this.ba, position, BA_LENGTH-position);
if (position >= BB_PART) {
final int icount= (position >>> 1);
final int bcount= (icount << 1);
if (this.mode != MODE_BBARRAY) {
this.bb.clear();
this.bb.put(this.ba, 0, bcount);
}
this.cb.clear();
this.cb.get(ca, cr, icount);
cr+= icount;
if (position - bcount != 0) {
ca[cr++]= (char) (
((this.ba[bcount] & 0xff) << 8) |
((in.readByte() & 0xff)) );
}
position= 0;
if (cn - cr <= CB_LENGTH) {
bToComplete= (cn - cr) << 1;
break;
}
}
}
if (bToComplete > 0) {
readFullyBB(position, bToComplete-position);
this.cb.clear();
this.cb.get(ca, cr, bToComplete >>> 1);
}
return new String(ca, 0, cn);
}
@SuppressWarnings("deprecation")
public String readString() throws IOException {
final ObjectInput in= this.in;
final int cn= in.readInt();
if (cn >= 0) {
if (cn == 0) {
return "";
}
else if (cn <= 64) {
for (int ci= 0; ci < cn; ci++) {
this.ca[ci]= in.readChar();
}
return new String(this.ca, 0, cn);
}
else if (cn <= CB_LENGTH) {
readFullyBB(cn << 1);
this.cb.clear();
this.cb.get(this.ca, 0, cn);
return new String(this.ca, 0, cn);
}
else if (cn <= CA_LENGTH) {
return readString(cn, this.ca, in);
}
else {
return readString(cn, new char[cn], in);
}
}
else {
if (cn >= -BA_LENGTH) {
in.readFully(this.ba, 0, -cn);
return new String(this.ba, 0, 0, -cn);
}
else if (cn != Integer.MIN_VALUE) {
final byte[] bt= new byte[-cn];
in.readFully(bt, 0, -cn);
return new String(bt, 0, 0, -cn);
}
else { // cn == Integer.MIN_VALUE
return null;
}
}
}
@SuppressWarnings("deprecation")
public void readStringData(final String[] array, final int length) throws IOException {
final ObjectInput in= this.in;
ARRAY: for (int i= 0; i < length; i++) {
final int cn= in.readInt();
if (cn >= 0) {
if (cn == 0) {
array[i]= "";
continue ARRAY;
}
else if (cn <= 64) {
for (int ci= 0; ci < cn; ci++) {
this.ca[ci]= in.readChar();
}
array[i]= new String(this.ca, 0, cn);
continue ARRAY;
}
else if (cn <= CB_LENGTH) {
readFullyBB(cn << 1);
this.cb.clear();
this.cb.get(this.ca, 0, cn);
array[i]= new String(this.ca, 0, cn);
continue ARRAY;
}
else if (cn <= CA_LENGTH) {
array[i]= readString(cn, this.ca, in);
continue ARRAY;
}
else {
array[i]= readString(cn, new char[cn], in);
continue ARRAY;
}
}
else {
if (cn >= -BA_LENGTH) {
in.readFully(this.ba, 0, -cn);
array[i]= new String(this.ba, 0, 0, -cn);
continue ARRAY;
}
else if (cn != Integer.MIN_VALUE) {
final byte[] bt= new byte[-cn];
in.readFully(bt, 0, -cn);
array[i]= new String(bt, 0, 0, -cn);
continue ARRAY;
}
else { // cn == Integer.MIN_VALUE
// array[i]= null;
continue ARRAY;
}
}
}
return;
}
public HashMap<String, Object> readStringKeyHashMap() throws IOException {
final ObjectInput in= this.in;
final int length= in.readInt();
if (length < 0) {
return null;
}
try {
final HashMap<String, Object> map= new HashMap<>(length);
for (int i= 0; i < length; i++) {
map.put(readString(), in.readObject());
}
return map;
}
catch (final ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
public int writeCheck1() throws IOException {
this.out.writeInt(++this.serialKey);
return this.serialKey;
}
public void writeCheck2(final int check) throws IOException {
this.out.writeInt(check);
}
public int readCheck1() throws IOException {
return this.in.readInt();
}
public void readCheck2(final int check) throws IOException {
if (check != this.in.readInt()) {
throw new IOException("Corrupted stream detected.");
}
}
}