private RowsIEventData.Row parseRow(ByteArrayInputStream input, RowsIEventData data) throws IOException {
        RowsIEventData.Row row = new RowsIEventData.Row();
//        row.setNullColumns(input.readBitSet(getNonZeroLength(data.getColumnUsageBeforeUpdate()), true));
        TableMapIEventData tableData = TableMapContext.getTableMap(data.getTableId());
        // get null column bit set.
        BitSet nullColumn = input.readBitSet(data.getColumnNum(), true);
        for (int i = 0, j = 0; i < data.getColumnNum(); i++) {
            if (!data.getColumnUsageBeforeUpdate().get(i)) {
                continue;
            }
            if (!nullColumn.get(j)) {
                // column j is not null
                int typeCode = tableData.getColumnTypeArray()[j] & 0xFF, meta = tableData.getMetadata()[i], length = 0;
                if (typeCode == ColumnType.STRING.getCode()) {
                    if(meta > 256) {
                        int byte0 = meta >> 8, byte1 = meta & 0xFF;
                        if ((byte0 & 0x30) != 0x30) { // long CHAR field
                            typeCode = byte0 | 0x30;
                            length = byte1 | (((byte0 & 0x30) ^ 0x30) << 4);
                        } else {
                            if (byte0 == ColumnType.SET.getCode() || byte0 == ColumnType.ENUM.getCode() ||
                                    byte0 == ColumnType.STRING.getCode()) {
                                typeCode = byte0;
                                length = byte1;
                            } else {
                                throw new IOException("Unexpected meta " + meta + " for column of type " + typeCode);
                            }
                        }
                    } else {
                        length = meta;
                    }
                }
                row.getCells().add(parseCell(input, getColumnType(typeCode), tableData.getMetadata()[j], length));
            }
            j++;
        }
        return row;
    }