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 int[] decodeIntArray(int len, TCDataInput input) throws IOException {
   final int[] rv = new int[len];
   for (int i = 0, n = rv.length; i < n; i++) {
     rv[i] = input.readInt();
   }
   return rv;
 }
 @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;
 }
  @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
  }