@Override
    public OFGroupFeaturesStatsReply readFrom(ChannelBuffer bb) throws OFParseError {
      int start = bb.readerIndex();
      // fixed value property version == 3
      byte version = bb.readByte();
      if (version != (byte) 0x3)
        throw new OFParseError("Wrong version: Expected=OFVersion.OF_12(3), got=" + version);
      // fixed value property type == 19
      byte type = bb.readByte();
      if (type != (byte) 0x13)
        throw new OFParseError("Wrong type: Expected=OFType.STATS_REPLY(19), got=" + type);
      int length = U16.f(bb.readShort());
      if (length != 56) throw new OFParseError("Wrong length: Expected=56(56), got=" + length);
      if (bb.readableBytes() + (bb.readerIndex() - start) < length) {
        // Buffer does not have all data yet
        bb.readerIndex(start);
        return null;
      }
      if (logger.isTraceEnabled()) logger.trace("readFrom - length={}", length);
      long xid = U32.f(bb.readInt());
      // fixed value property statsType == 8
      short statsType = bb.readShort();
      if (statsType != (short) 0x8)
        throw new OFParseError(
            "Wrong statsType: Expected=OFStatsType.GROUP_FEATURES(8), got=" + statsType);
      Set<OFStatsReplyFlags> flags = OFStatsReplyFlagsSerializerVer12.readFrom(bb);
      // pad: 4 bytes
      bb.skipBytes(4);
      long types = U32.f(bb.readInt());
      long capabilities = U32.f(bb.readInt());
      long maxGroupsAll = U32.f(bb.readInt());
      long maxGroupsSelect = U32.f(bb.readInt());
      long maxGroupsIndirect = U32.f(bb.readInt());
      long maxGroupsFf = U32.f(bb.readInt());
      long actionsAll = U32.f(bb.readInt());
      long actionsSelect = U32.f(bb.readInt());
      long actionsIndirect = U32.f(bb.readInt());
      long actionsFf = U32.f(bb.readInt());

      OFGroupFeaturesStatsReplyVer12 groupFeaturesStatsReplyVer12 =
          new OFGroupFeaturesStatsReplyVer12(
              xid,
              flags,
              types,
              capabilities,
              maxGroupsAll,
              maxGroupsSelect,
              maxGroupsIndirect,
              maxGroupsFf,
              actionsAll,
              actionsSelect,
              actionsIndirect,
              actionsFf);
      if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", groupFeaturesStatsReplyVer12);
      return groupFeaturesStatsReplyVer12;
    }
 @Override
 public OFExperimenterStatsReply readFrom(ByteBuf bb) throws OFParseError {
   if (bb.readableBytes() < MINIMUM_LENGTH) return null;
   int start = bb.readerIndex();
   // fixed value property version == 3
   byte version = bb.readByte();
   if (version != (byte) 0x3)
     throw new OFParseError("Wrong version: Expected=OFVersion.OF_12(3), got=" + version);
   // fixed value property type == 19
   byte type = bb.readByte();
   if (type != (byte) 0x13)
     throw new OFParseError("Wrong type: Expected=OFType.STATS_REPLY(19), got=" + type);
   int length = U16.f(bb.readShort());
   if (length < MINIMUM_LENGTH)
     throw new OFParseError(
         "Wrong length: Expected to be >= " + MINIMUM_LENGTH + ", was: " + length);
   U32.f(bb.readInt());
   // fixed value property statsType == 65535
   short statsType = bb.readShort();
   if (statsType != (short) 0xffff)
     throw new OFParseError(
         "Wrong statsType: Expected=OFStatsType.EXPERIMENTER(65535), got=" + statsType);
   OFStatsReplyFlagsSerializerVer12.readFrom(bb);
   // pad: 4 bytes
   bb.skipBytes(4);
   int experimenter = bb.readInt();
   bb.readerIndex(start);
   switch (experimenter) {
     case 0x5c16c7:
       // discriminator value 0x5c16c7L=0x5c16c7L for class OFBsnStatsReplyVer12
       return OFBsnStatsReplyVer12.READER.readFrom(bb);
     default:
       throw new OFParseError(
           "Unknown value for discriminator experimenter of class OFExperimenterStatsReplyVer12: "
               + experimenter);
   }
 }