@Override
    public LogEntry decode(final byte[] bs) {
        if (bs == null || bs.length < LogEntryV2CodecFactory.HEADER_SIZE) {
            return null;
        }

        int i = 0;
        for (; i < LogEntryV2CodecFactory.MAGIC_BYTES.length; i++) {
            if (bs[i] != LogEntryV2CodecFactory.MAGIC_BYTES[i]) {
                return null;
            }
        }

        if (bs[i++] != LogEntryV2CodecFactory.VERSION) {
            return null;
        }
        // Ignored reserved
        i += LogEntryV2CodecFactory.RESERVED.length;
        try {
            final PBLogEntry entry = PBLogEntry.parseFrom(ZeroByteStringHelper.wrap(bs, i, bs.length - i));

            final LogEntry log = new LogEntry();
            log.setType(entry.getType());
            log.getId().setIndex(entry.getIndex());
            log.getId().setTerm(entry.getTerm());

            if (entry.hasChecksum()) {
                log.setChecksum(entry.getChecksum());
            }
            if (entry.getPeersCount() > 0) {
                final List<PeerId> peers = new ArrayList<>(entry.getPeersCount());
                for (final ByteString bstring : entry.getPeersList()) {
                    peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
                }
                log.setPeers(peers);
            }
            if (entry.getOldPeersCount() > 0) {
                final List<PeerId> peers = new ArrayList<>(entry.getOldPeersCount());
                for (final ByteString bstring : entry.getOldPeersList()) {
                    peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
                }
                log.setOldPeers(peers);
            }

            if (entry.getLearnersCount() > 0) {
                final List<PeerId> peers = new ArrayList<>(entry.getLearnersCount());
                for (final ByteString bstring : entry.getLearnersList()) {
                    peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
                }
                log.setLearners(peers);
            }

            if (entry.getOldLearnersCount() > 0) {
                final List<PeerId> peers = new ArrayList<>(entry.getOldLearnersCount());
                for (final ByteString bstring : entry.getOldLearnersList()) {
                    peers.add(JRaftUtils.getPeerId(AsciiStringUtil.unsafeDecode(bstring)));
                }
                log.setOldLearners(peers);
            }

            final ByteString data = entry.getData();
            if (!data.isEmpty()) {
                log.setData(ByteBuffer.wrap(ZeroByteStringHelper.getByteArray(data)));
            }

            return log;
        } catch (final InvalidProtocolBufferException e) {
            LOG.error("Fail to decode pb log entry", e);
            return null;
        }
    }
V2 log entry decoder based on protobuf, see src/main/resources/log.proto

@author boyan(boyan@antfin.com)
jraft-core/src/main/java/com/alipay/sofa/jraft/entity/codec/v2/V2Decoder.java