/*=============================================================================#
# Copyright (c) 2010-2016 Stephan Wahlbrink (WalWare.de) and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of either (per the licensee's choosing)
# - the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html, or
# - the GNU Lesser General Public License v2.1 or newer
# which accompanies this distribution, and is available at
# http://www.gnu.org/licenses/lgpl.html
#
# Contributors:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.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<RJIO>() {
@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.read() & 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.read() & 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.read() & 0xff) << 8) |
((in.read() & 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.read() & 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.read() & 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.");
}
}
}