public ValidationResult assignOffsetsNonCompressed(LongRef offsetCounter,
                                                       MetricsRecorder metricsRecorder) {
        long now = time.milliseconds();
        long maxTimestamp = RecordBatch.NO_TIMESTAMP;
        long shallowOffsetOfMaxTimestamp = -1L;
        long initialOffset = offsetCounter.value;

        RecordBatch firstBatch = getFirstBatchAndMaybeValidateNoMoreBatches(records, CompressionType.NONE);

        for (MutableRecordBatch batch : records.batches()) {
            validateBatch(topicPartition, firstBatch, batch, origin, toMagic, metricsRecorder);

            long maxBatchTimestamp = RecordBatch.NO_TIMESTAMP;

            List<ApiRecordError> recordErrors = new ArrayList<>(0);
            // This is a hot path and we want to avoid any unnecessary allocations.
            // That said, there is no benefit in using `skipKeyValueIterator` for the uncompressed
            // case since we don't do key/value copies in this path (we just slice the ByteBuffer)
            int recordIndex = 0;
            for (Record record : batch) {
                Optional<ApiRecordError> recordError = validateRecord(batch, topicPartition, record,
                    recordIndex, now, timestampType, timestampBeforeMaxMs, timestampAfterMaxMs, compactedTopic, metricsRecorder);
                recordError.ifPresent(recordErrors::add);

                offsetCounter.value++;
                if (batch.magic() > RecordBatch.MAGIC_VALUE_V0 && record.timestamp() > maxBatchTimestamp)
                    maxBatchTimestamp = record.timestamp();
                ++recordIndex;
            }

            processRecordErrors(recordErrors);

            if (batch.magic() > RecordBatch.MAGIC_VALUE_V0 && maxBatchTimestamp > maxTimestamp) {
                maxTimestamp = maxBatchTimestamp;
                shallowOffsetOfMaxTimestamp = offsetCounter.value - 1;
            }

            batch.setLastOffset(offsetCounter.value - 1);

            if (batch.magic() >= RecordBatch.MAGIC_VALUE_V2)
                batch.setPartitionLeaderEpoch(partitionLeaderEpoch);

            if (batch.magic() > RecordBatch.MAGIC_VALUE_V0) {
                if (timestampType == TimestampType.LOG_APPEND_TIME)
                    batch.setMaxTimestamp(TimestampType.LOG_APPEND_TIME, now);
                else
                    batch.setMaxTimestamp(timestampType, maxBatchTimestamp);
            }
        }

        if (timestampType == TimestampType.LOG_APPEND_TIME) {
            maxTimestamp = now;
            // those checks should be equal to MemoryRecordsBuilder#info
            switch (toMagic) {
                case RecordBatch.MAGIC_VALUE_V0:
                    maxTimestamp = RecordBatch.NO_TIMESTAMP;
                    // value will be the default value: -1
                    shallowOffsetOfMaxTimestamp = -1;
                    break;
                case RecordBatch.MAGIC_VALUE_V1:
                    // Those single-record batches have same max timestamp, so the initial offset is equal with
                    // the last offset of earliest batch
                    shallowOffsetOfMaxTimestamp = initialOffset;
                    break;
                default:
                    // there is only one batch so use the last offset
                    shallowOffsetOfMaxTimestamp = offsetCounter.value - 1;
                    break;
            }
        }

        return new ValidationResult(
            now,
            records,
            maxTimestamp,
            shallowOffsetOfMaxTimestamp,
            false,
            RecordValidationStats.EMPTY);
    }
Update the offsets for this message set and do further validation on messages including:
1. Messages for compacted topics must have keys
2. When magic value >= 1, inner messages of a compressed message set must have monotonically increasing offsets
   starting from 0.
3. When magic value >= 1, validate and maybe overwrite timestamps of messages.
4. Declared count of records in DefaultRecordBatch must match number of valid records contained therein.

This method will convert messages as necessary to the topic's configured message format version. If no format
conversion or value overwriting is required for messages, this method will perform in-place operations to
avoid expensive re-compression.

Returns a ValidationAndOffsetAssignResult containing the validated message set, maximum timestamp, the offset
of the message with the max timestamp and a boolean indicating whether the message sizes may have changed.
storage/src/main/java/org/apache/kafka/storage/internals/log/LogValidator.java