blob: 5b8fc8d606bf2d9c3bab20cc4d42563bf9cea7af [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/storage/volume_image/utils/block_writer.h"
#include <iostream>
#include "src/storage/volume_image/utils/block_utils.h"
namespace storage::volume_image {
fpromise::result<void, std::string> BlockWriter::Write(uint64_t offset,
cpp20::span<const uint8_t> buffer) {
if (buffer.size() == 0) {
return fpromise::ok();
}
if (offset + buffer.size() > block_count_ * block_size_) {
return fpromise::error("BlockWriter::Write out of bounds. Offset " + std::to_string(offset) +
" Write Size: " + std::to_string(buffer.size()) + " with " +
std::to_string(block_count_) + " blocks of size " +
std::to_string(block_size_) +
" (Max Offset: " + std::to_string(block_size_ * block_count_) + ").");
}
// First block is unaligned.
if (!IsOffsetBlockAligned(offset, block_size_)) {
uint64_t block_byte_offset = GetBlockFromBytes(offset, block_size_) * block_size_;
if (auto result = reader_->Read(block_byte_offset, block_buffer_); result.is_error()) {
return result;
}
uint64_t offset_from_block_buffer = GetOffsetFromBlockStart(offset, block_size_);
uint64_t block_bytes_to_patch =
std::min(block_size_ - offset_from_block_buffer, static_cast<uint64_t>(buffer.size()));
memcpy(block_buffer_.data() + offset_from_block_buffer, buffer.data(), block_bytes_to_patch);
if (auto result = writer_->Write(block_byte_offset, block_buffer_); result.is_error()) {
return result.take_error_result();
}
// We consumed all the bytes to write.
if (block_bytes_to_patch == buffer.size()) {
return fpromise::ok();
}
offset += block_bytes_to_patch;
buffer = buffer.subspan(block_bytes_to_patch);
}
// offset is now aligned and at least one block.
uint64_t aligned_block_count = GetBlockCount(offset, buffer.size(), block_size_);
bool last_block_is_aligned = true;
// If the buffer has trailing data, then the aligned blocks is reduced by one.
if (buffer.size() % block_size_ != 0) {
aligned_block_count = aligned_block_count > 0 ? aligned_block_count - 1 : 0;
last_block_is_aligned = false;
}
if (aligned_block_count > 0) {
uint64_t aligned_data_bytes = aligned_block_count * block_size_;
if (auto result = writer_->Write(offset, buffer.subspan(0, aligned_data_bytes));
result.is_error()) {
return result;
}
// We consumed all the bytes to write.
if (aligned_data_bytes == buffer.size()) {
return fpromise::ok();
}
offset += aligned_data_bytes;
buffer = buffer.subspan(aligned_data_bytes);
}
// Now write the trailing data from last block.
if (!last_block_is_aligned) {
if (auto result = reader_->Read(offset, block_buffer_); result.is_error()) {
return result;
}
memcpy(block_buffer_.data(), buffer.data(), buffer.size());
if (auto result = writer_->Write(offset, block_buffer_); result.is_error()) {
return result;
}
}
return fpromise::ok();
}
} // namespace storage::volume_image