@Override
 public byte[] readStringBytes(TCDataInput input) throws IOException {
   int len = input.readInt();
   byte[] bytes = new byte[len];
   input.readFully(bytes);
   return bytes;
 }
 // private void writeCharArray(char[] chars, TCDataOutput output) {
 // output.writeInt(chars.length);
 // for (int i = 0, n = chars.length; i < n; i++) {
 // output.writeChar(chars[i]);
 // }
 // }
 protected byte[] readByteArray(TCDataInput input) throws IOException {
   final int length = input.readInt();
   if (length >= BYTE_WARN) {
     logger.warn("Attempting to allocate a large byte array of size: " + length);
   }
   final byte[] array = new byte[length];
   input.readFully(array);
   return array;
 }
  protected Object readCompressedString(TCDataInput input) throws IOException {
    final int stringUncompressedByteLength = input.readInt();
    final byte[] data = readByteArray(input);

    final int stringLength = input.readInt();
    final int stringHash = input.readInt();

    return new UTF8ByteCompressedDataHolder(
        data, stringUncompressedByteLength, stringLength, stringHash);
  }
 private boolean[] decodeBooleanArray(int len, TCDataInput input) throws IOException {
   final boolean[] rv = new boolean[len];
   for (int i = 0, n = rv.length; i < n; i++) {
     rv[i] = input.readBoolean();
   }
   return rv;
 }
 private char[] decodeCharArray(int len, TCDataInput input) throws IOException {
   final char[] rv = new char[len];
   for (int i = 0, n = rv.length; i < n; i++) {
     rv[i] = input.readChar();
   }
   return rv;
 }
 private double[] decodeDoubleArray(int len, TCDataInput input) throws IOException {
   final double[] rv = new double[len];
   for (int i = 0, n = rv.length; i < n; i++) {
     rv[i] = input.readDouble();
   }
   return rv;
 }
 private float[] decodeFloatArray(int len, TCDataInput input) throws IOException {
   final float[] rv = new float[len];
   for (int i = 0, n = rv.length; i < n; i++) {
     rv[i] = input.readFloat();
   }
   return rv;
 }
 private long[] decodeLongArray(int len, TCDataInput input) throws IOException {
   final long[] rv = new long[len];
   for (int i = 0, n = rv.length; i < n; i++) {
     rv[i] = input.readLong();
   }
   return rv;
 }
 private short[] decodeShortArray(int len, TCDataInput input) throws IOException {
   final short[] rv = new short[len];
   for (int i = 0, n = rv.length; i < n; i++) {
     rv[i] = input.readShort();
   }
   return rv;
 }
  private Object decodePrimitiveArray(int len, TCDataInput input) throws IOException {
    final byte type = input.readByte();

    switch (type) {
      case TYPE_ID_BOOLEAN:
        checkSize(Boolean.TYPE, BOOLEAN_WARN, len);
        return decodeBooleanArray(len, input);
      case TYPE_ID_BYTE:
        checkSize(Byte.TYPE, BYTE_WARN, len);
        return decodeByteArray(len, input);
      case TYPE_ID_CHAR:
        checkSize(Character.TYPE, CHAR_WARN, len);
        return decodeCharArray(len, input);
      case TYPE_ID_DOUBLE:
        checkSize(Double.TYPE, DOUBLE_WARN, len);
        return decodeDoubleArray(len, input);
      case TYPE_ID_FLOAT:
        checkSize(Float.TYPE, FLOAT_WARN, len);
        return decodeFloatArray(len, input);
      case TYPE_ID_INT:
        checkSize(Integer.TYPE, INT_WARN, len);
        return decodeIntArray(len, input);
      case TYPE_ID_LONG:
        checkSize(Long.TYPE, LONG_WARN, len);
        return decodeLongArray(len, input);
      case TYPE_ID_SHORT:
        checkSize(Short.TYPE, SHORT_WARN, len);
        return decodeShortArray(len, input);
      default:
        throw Assert.failure("unknown prim type: " + type);
    }

    // unreachable
  }
  @Override
  public Object decode(TCDataInput input, ObjectStringSerializer serializer)
      throws IOException, ClassNotFoundException {
    final byte type = input.readByte();

    switch (type) {
      case TYPE_ID_ENUM:
        return readEnum(input, type, serializer);
      case TYPE_ID_ENUM_HOLDER:
        return readEnum(input, type, serializer);
      case TYPE_ID_JAVA_LANG_CLASS:
        return readClass(input, type, serializer);
      case TYPE_ID_JAVA_LANG_CLASS_HOLDER:
        return readClass(input, type, serializer);
      case TYPE_ID_BOOLEAN:
        return Boolean.valueOf(input.readBoolean());
      case TYPE_ID_BYTE:
        return Byte.valueOf(input.readByte());
      case TYPE_ID_CHAR:
        return Character.valueOf(input.readChar());
      case TYPE_ID_DOUBLE:
        return Double.valueOf(input.readDouble());
      case TYPE_ID_FLOAT:
        return Float.valueOf(input.readFloat());
      case TYPE_ID_INT:
        return Integer.valueOf(input.readInt());
      case TYPE_ID_LONG:
        return Long.valueOf(input.readLong());
      case TYPE_ID_SHORT:
        return Short.valueOf(input.readShort());
      case TYPE_ID_STRING:
        return readString(input, type, serializer);
      case TYPE_ID_STRING_COMPRESSED:
        return readCompressedString(input);
      case TYPE_ID_STRING_BYTES:
        return readString(input, type, serializer);
      case TYPE_ID_REFERENCE:
        return new ObjectID(input.readLong());
      case TYPE_ID_ARRAY:
        return decodeArray(input);
      default:
        throw Assert.failure("Illegal type (" + type + ")");
    }

    // unreachable
  }
  private Object decodeArray(TCDataInput input) throws IOException, ClassNotFoundException {
    final int len = input.readInt();
    if (len < 0) {
      return null;
    }

    final byte arrayType = input.readByte();
    switch (arrayType) {
      case ARRAY_TYPE_PRIMITIVE:
        return decodePrimitiveArray(len, input);
      case ARRAY_TYPE_NON_PRIMITIVE:
        return decodeNonPrimitiveArray(len, input);
      default:
        throw Assert.failure("unknown array type: " + arrayType);
    }

    // unreachable
  }
 private byte[] decodeByteArray(int len, TCDataInput input) throws IOException {
   final byte[] rv = new byte[len];
   if (len != 0) {
     final int read = input.read(rv, 0, len);
     if (read != len) {
       throw new IOException("read " + read + " bytes, expected " + len);
     }
   }
   return rv;
 }
 @Override
 public String readString(TCDataInput in) throws IOException {
   return in.readString();
 }