@SuppressWarnings("NonAtomicOperationOnVolatileField")
    private boolean recover(final SegmentFileOptions opts) throws IOException {
        LOG.info("Start to recover segment file {} from position {}.", this.path, opts.pos);
        this.wrotePos = opts.pos;
        if (this.wrotePos < HEADER_SIZE) {
            this.wrotePos = HEADER_SIZE;
        }
        BufferUtils.position(this.buffer, this.wrotePos);
        final long start = Utils.monotonicMs();
        while (this.wrotePos < this.size) {
            if (this.buffer.remaining() < RECORD_MAGIC_BYTES_SIZE) {
                LOG.error("Fail to recover segment file {}, missing magic bytes.", this.path);
                return false;
            }
            final byte[] magicBytes = new byte[RECORD_MAGIC_BYTES_SIZE];
            this.buffer.get(magicBytes);

            if (!Arrays.equals(RECORD_MAGIC_BYTES, magicBytes)) {

                boolean truncateDirty = false;

                int i = 0;
                for (final byte b : magicBytes) {
                    i++;
                    if (b != 0) {
                        if (opts.isLastFile) {
                            // It's the last file
                            // Truncate the dirty data from wrotePos
                            LOG.error("Corrupted magic bytes in segment file {} at pos={}, will truncate it.",
                                this.path, this.wrotePos + i);
                            truncateDirty = true;
                            break;
                        } else {
                            // It's not the last file, but has invalid magic bytes, the data is corrupted.
                            LOG.error("Fail to recover segment file {}, invalid magic bytes: {} at pos={}.", this.path,
                                BytesUtil.toHex(magicBytes), this.wrotePos);
                            return false;
                        }
                    }
                }

                if (truncateDirty) {
                    truncateFile(opts.sync);
                } else {
                    // Reach blank hole, rewind position.
                    BufferUtils.position(this.buffer, this.buffer.position() - RECORD_MAGIC_BYTES_SIZE);
                }
                // Reach end or dirty magic bytes, we should break out.
                break;
            }

            if (this.buffer.remaining() < RECORD_DATA_LENGTH_SIZE) {
                if (opts.isLastFile) {
                    LOG.error("Corrupted data length in segment file {} at pos={}, will truncate it.", this.path,
                        this.buffer.position());
                    truncateFile(opts.sync);
                    break;
                } else {
                    LOG.error(
                        "Fail to recover segment file {}, invalid data length remaining: {}, expected {} at pos={}.",
                        this.path, this.buffer.remaining(), RECORD_DATA_LENGTH_SIZE, this.wrotePos);
                    return false;
                }
            }

            final int dataLen = this.buffer.getInt();
            if (this.buffer.remaining() < dataLen) {
                if (opts.isLastFile) {
                    LOG.error(
                        "Corrupted data in segment file {} at pos={},  expectDataLength={}, but remaining is {}, will truncate it.",
                        this.path, this.buffer.position(), dataLen, this.buffer.remaining());
                    truncateFile(opts.sync);
                    break;
                } else {
                    LOG.error(
                        "Fail to recover segment file {}, invalid data: expected {} bytes in buf but actual {} at pos={}.",
                        this.path, dataLen, this.buffer.remaining(), this.wrotePos);
                    return false;
                }

            }
            // Skip data
            this.buffer.position(this.buffer.position() + dataLen);
            this.wrotePos += RECORD_MAGIC_BYTES_SIZE + RECORD_DATA_LENGTH_SIZE + dataLen;
        }
        LOG.info("Recover segment file {} cost {} millis.", this.path, Utils.monotonicMs() - start);
        return true;
    }
Clear data in [startPos, startPos+64).

@param startPos the start position(inclusive)
jraft-core/src/main/java/com/alipay/sofa/jraft/storage/log/SegmentFile.java