@Override
    public OFPortDesc readFrom(ByteBuf bb) throws OFParseError {
      OFPort portNo = OFPort.read4Bytes(bb);
      // pad: 4 bytes
      bb.skipBytes(4);
      MacAddress hwAddr = MacAddress.read6Bytes(bb);
      // pad: 2 bytes
      bb.skipBytes(2);
      String name = ChannelUtils.readFixedLengthString(bb, 16);
      Set<OFPortConfig> config = OFPortConfigSerializerVer12.readFrom(bb);
      Set<OFPortState> state = OFPortStateSerializerVer12.readFrom(bb);
      Set<OFPortFeatures> curr = OFPortFeaturesSerializerVer12.readFrom(bb);
      Set<OFPortFeatures> advertised = OFPortFeaturesSerializerVer12.readFrom(bb);
      Set<OFPortFeatures> supported = OFPortFeaturesSerializerVer12.readFrom(bb);
      Set<OFPortFeatures> peer = OFPortFeaturesSerializerVer12.readFrom(bb);
      long currSpeed = U32.f(bb.readInt());
      long maxSpeed = U32.f(bb.readInt());

      OFPortDescVer12 portDescVer12 =
          new OFPortDescVer12(
              portNo,
              hwAddr,
              name,
              config,
              state,
              curr,
              advertised,
              supported,
              peer,
              currSpeed,
              maxSpeed);
      if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", portDescVer12);
      return portDescVer12;
    }
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;

    result = prime * result + ((value == null) ? 0 : value.hashCode());
    return result;
  }
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;

    result = prime * (int) (xid ^ (xid >>> 32));
    result = prime * (int) (status ^ (status >>> 32));
    result = prime * result + ((portNo == null) ? 0 : portNo.hashCode());
    return result;
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    OFOxmInPhyPortVer13 other = (OFOxmInPhyPortVer13) obj;

    if (value == null) {
      if (other.value != null) return false;
    } else if (!value.equals(other.value)) return false;
    return true;
  }
    @Override
    public OFOxmInPhyPort readFrom(ByteBuf bb) throws OFParseError {
      // fixed value property typeLen == 0x80000204L
      int typeLen = bb.readInt();
      if (typeLen != (int) 0x80000204)
        throw new OFParseError("Wrong typeLen: Expected=0x80000204L(0x80000204L), got=" + typeLen);
      OFPort value = OFPort.read4Bytes(bb);

      OFOxmInPhyPortVer13 oxmInPhyPortVer13 = new OFOxmInPhyPortVer13(value);
      if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", oxmInPhyPortVer13);
      return oxmInPhyPortVer13;
    }
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;

    result = prime * (int) (xid ^ (xid >>> 32));
    result = prime * (int) (txIntervalMs ^ (txIntervalMs >>> 32));
    result = prime * result + ((portNo == null) ? 0 : portNo.hashCode());
    result = prime * result + slotNum;
    result = prime * result + Arrays.hashCode(data);
    return result;
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    OFBsnSetLacpReplyVer14 other = (OFBsnSetLacpReplyVer14) obj;

    if (xid != other.xid) return false;
    if (status != other.status) return false;
    if (portNo == null) {
      if (other.portNo != null) return false;
    } else if (!portNo.equals(other.portNo)) return false;
    return true;
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    OFBsnPduTxRequestVer10 other = (OFBsnPduTxRequestVer10) obj;

    if (xid != other.xid) return false;
    if (txIntervalMs != other.txIntervalMs) return false;
    if (portNo == null) {
      if (other.portNo != null) return false;
    } else if (!portNo.equals(other.portNo)) return false;
    if (slotNum != other.slotNum) return false;
    if (!Arrays.equals(data, other.data)) return false;
    return true;
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    OFPortStatsRequestVer13 other = (OFPortStatsRequestVer13) obj;

    if (xid != other.xid) return false;
    if (flags == null) {
      if (other.flags != null) return false;
    } else if (!flags.equals(other.flags)) return false;
    if (portNo == null) {
      if (other.portNo != null) return false;
    } else if (!portNo.equals(other.portNo)) return false;
    return true;
  }
  @Test
  public void testWrite() {
    OFBsnGentableEntryDelete.Builder builder = factory.buildBsnGentableEntryDelete();
    builder
        .setXid(0x12345678)
        .setTableId(GenTableId.of(20))
        .setKey(
            ImmutableList.<OFBsnTlv>of(
                factory.bsnTlvs().port(OFPort.of(5)),
                factory.bsnTlvs().mac(MacAddress.of("01:23:45:67:89:ab"))));
    OFBsnGentableEntryDelete bsnGentableEntryDelete = builder.build();
    ByteBuf bb = Unpooled.buffer();
    bsnGentableEntryDelete.writeTo(bb);
    byte[] written = new byte[bb.readableBytes()];
    bb.readBytes(written);

    assertThat(written, CoreMatchers.equalTo(BSN_GENTABLE_ENTRY_DELETE_SERIALIZED));
  }
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;

    result = prime * result + ((portNo == null) ? 0 : portNo.hashCode());
    result = prime * result + ((hwAddr == null) ? 0 : hwAddr.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    result = prime * result + ((config == null) ? 0 : config.hashCode());
    result = prime * result + ((state == null) ? 0 : state.hashCode());
    result = prime * result + ((curr == null) ? 0 : curr.hashCode());
    result = prime * result + ((advertised == null) ? 0 : advertised.hashCode());
    result = prime * result + ((supported == null) ? 0 : supported.hashCode());
    result = prime * result + ((peer == null) ? 0 : peer.hashCode());
    result = prime * (int) (currSpeed ^ (currSpeed >>> 32));
    result = prime * (int) (maxSpeed ^ (maxSpeed >>> 32));
    return result;
  }
    @Override
    public OFBsnPduTxRequest readFrom(ChannelBuffer bb) throws OFParseError {
      int start = bb.readerIndex();
      // fixed value property version == 1
      byte version = bb.readByte();
      if (version != (byte) 0x1)
        throw new OFParseError("Wrong version: Expected=OFVersion.OF_10(1), got=" + version);
      // fixed value property type == 4
      byte type = bb.readByte();
      if (type != (byte) 0x4)
        throw new OFParseError("Wrong type: Expected=OFType.EXPERIMENTER(4), got=" + type);
      int length = U16.f(bb.readShort());
      if (length < MINIMUM_LENGTH)
        throw new OFParseError(
            "Wrong length: Expected to be >= " + MINIMUM_LENGTH + ", was: " + 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 experimenter == 0x5c16c7L
      int experimenter = bb.readInt();
      if (experimenter != 0x5c16c7)
        throw new OFParseError(
            "Wrong experimenter: Expected=0x5c16c7L(0x5c16c7L), got=" + experimenter);
      // fixed value property subtype == 0x1fL
      int subtype = bb.readInt();
      if (subtype != 0x1f)
        throw new OFParseError("Wrong subtype: Expected=0x1fL(0x1fL), got=" + subtype);
      long txIntervalMs = U32.f(bb.readInt());
      OFPort portNo = OFPort.read2Bytes(bb);
      short slotNum = U8.f(bb.readByte());
      // pad: 3 bytes
      bb.skipBytes(3);
      byte[] data = ChannelUtils.readBytes(bb, length - (bb.readerIndex() - start));

      OFBsnPduTxRequestVer10 bsnPduTxRequestVer10 =
          new OFBsnPduTxRequestVer10(xid, txIntervalMs, portNo, slotNum, data);
      if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", bsnPduTxRequestVer10);
      return bsnPduTxRequestVer10;
    }
  @Test
  public void testRead() throws Exception {
    OFBsnGentableEntryDelete.Builder builder = factory.buildBsnGentableEntryDelete();
    builder
        .setXid(0x12345678)
        .setTableId(GenTableId.of(20))
        .setKey(
            ImmutableList.<OFBsnTlv>of(
                factory.bsnTlvs().port(OFPort.of(5)),
                factory.bsnTlvs().mac(MacAddress.of("01:23:45:67:89:ab"))));
    OFBsnGentableEntryDelete bsnGentableEntryDeleteBuilt = builder.build();

    ByteBuf input = Unpooled.copiedBuffer(BSN_GENTABLE_ENTRY_DELETE_SERIALIZED);

    // FIXME should invoke the overall reader once implemented
    OFBsnGentableEntryDelete bsnGentableEntryDeleteRead =
        OFBsnGentableEntryDeleteVer13.READER.readFrom(input);
    assertEquals(BSN_GENTABLE_ENTRY_DELETE_SERIALIZED.length, input.readerIndex());

    assertEquals(bsnGentableEntryDeleteBuilt, bsnGentableEntryDeleteRead);
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    OFPortDescVer12 other = (OFPortDescVer12) obj;

    if (portNo == null) {
      if (other.portNo != null) return false;
    } else if (!portNo.equals(other.portNo)) return false;
    if (hwAddr == null) {
      if (other.hwAddr != null) return false;
    } else if (!hwAddr.equals(other.hwAddr)) return false;
    if (name == null) {
      if (other.name != null) return false;
    } else if (!name.equals(other.name)) return false;
    if (config == null) {
      if (other.config != null) return false;
    } else if (!config.equals(other.config)) return false;
    if (state == null) {
      if (other.state != null) return false;
    } else if (!state.equals(other.state)) return false;
    if (curr == null) {
      if (other.curr != null) return false;
    } else if (!curr.equals(other.curr)) return false;
    if (advertised == null) {
      if (other.advertised != null) return false;
    } else if (!advertised.equals(other.advertised)) return false;
    if (supported == null) {
      if (other.supported != null) return false;
    } else if (!supported.equals(other.supported)) return false;
    if (peer == null) {
      if (other.peer != null) return false;
    } else if (!peer.equals(other.peer)) return false;
    if (currSpeed != other.currSpeed) return false;
    if (maxSpeed != other.maxSpeed) return false;
    return true;
  }
    @Override
    public OFBsnSetLacpReply readFrom(ChannelBuffer bb) throws OFParseError {
      int start = bb.readerIndex();
      // fixed value property version == 5
      byte version = bb.readByte();
      if (version != (byte) 0x5)
        throw new OFParseError("Wrong version: Expected=OFVersion.OF_14(5), got=" + version);
      // fixed value property type == 4
      byte type = bb.readByte();
      if (type != (byte) 0x4)
        throw new OFParseError("Wrong type: Expected=OFType.EXPERIMENTER(4), got=" + type);
      int length = U16.f(bb.readShort());
      if (length != 24) throw new OFParseError("Wrong length: Expected=24(24), 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 experimenter == 0x5c16c7L
      int experimenter = bb.readInt();
      if (experimenter != 0x5c16c7)
        throw new OFParseError(
            "Wrong experimenter: Expected=0x5c16c7L(0x5c16c7L), got=" + experimenter);
      // fixed value property subtype == 0x2aL
      int subtype = bb.readInt();
      if (subtype != 0x2a)
        throw new OFParseError("Wrong subtype: Expected=0x2aL(0x2aL), got=" + subtype);
      long status = U32.f(bb.readInt());
      OFPort portNo = OFPort.read4Bytes(bb);

      OFBsnSetLacpReplyVer14 bsnSetLacpReplyVer14 = new OFBsnSetLacpReplyVer14(xid, status, portNo);
      if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", bsnSetLacpReplyVer14);
      return bsnSetLacpReplyVer14;
    }
    @Override
    public OFPortStatsRequest readFrom(ByteBuf bb) throws OFParseError {
      int start = bb.readerIndex();
      // fixed value property version == 4
      byte version = bb.readByte();
      if (version != (byte) 0x4)
        throw new OFParseError("Wrong version: Expected=OFVersion.OF_13(4), got=" + version);
      // fixed value property type == 18
      byte type = bb.readByte();
      if (type != (byte) 0x12)
        throw new OFParseError("Wrong type: Expected=OFType.STATS_REQUEST(18), got=" + type);
      int length = U16.f(bb.readShort());
      if (length != 24) throw new OFParseError("Wrong length: Expected=24(24), 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 == 4
      short statsType = bb.readShort();
      if (statsType != (short) 0x4)
        throw new OFParseError("Wrong statsType: Expected=OFStatsType.PORT(4), got=" + statsType);
      Set<OFStatsRequestFlags> flags = OFStatsRequestFlagsSerializerVer13.readFrom(bb);
      // pad: 4 bytes
      bb.skipBytes(4);
      OFPort portNo = OFPort.read4Bytes(bb);
      // pad: 4 bytes
      bb.skipBytes(4);

      OFPortStatsRequestVer13 portStatsRequestVer13 =
          new OFPortStatsRequestVer13(xid, flags, portNo);
      if (logger.isTraceEnabled()) logger.trace("readFrom - read={}", portStatsRequestVer13);
      return portStatsRequestVer13;
    }