| // Copyright 2019 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 <blobfs/unbuffered-operations-builder.h> |
| |
| namespace blobfs { |
| namespace { |
| |
| // Skew between the vmo offset and the device offset implies that |
| // operations should not be combined. |
| bool EqualVmoDeviceOffsetSkew(const Operation& a, const Operation& b) { |
| return (a.vmo_offset - b.vmo_offset) == (a.dev_offset - b.dev_offset); |
| } |
| |
| } // namespace |
| |
| UnbufferedOperationsBuilder::~UnbufferedOperationsBuilder() {} |
| |
| void UnbufferedOperationsBuilder::Add(const UnbufferedOperation& new_operation) { |
| ZX_DEBUG_ASSERT(new_operation.vmo->is_valid()); |
| |
| zx::unowned_vmo vmo = zx::unowned_vmo(new_operation.vmo->get()); |
| uint64_t vmo_offset = new_operation.op.vmo_offset; |
| uint64_t dev_offset = new_operation.op.dev_offset; |
| uint64_t length = new_operation.op.length; |
| |
| if (length == 0) { |
| return; |
| } |
| |
| for (auto& operation : operations_) { |
| if ((operation.vmo->get() != vmo->get()) || |
| (operation.op.type != new_operation.op.type) || |
| !EqualVmoDeviceOffsetSkew(operation.op, new_operation.op)) { |
| continue; |
| } |
| |
| uint64_t old_start = operation.op.vmo_offset; |
| uint64_t old_end = operation.op.vmo_offset + operation.op.length; |
| |
| uint64_t new_start = vmo_offset; |
| uint64_t new_end = vmo_offset + length; |
| |
| if ((old_start <= new_start) && (new_end <= old_end)) { |
| // The old operation is larger (on both sides) than the new operation. |
| // |
| // It's already as large as it needs to be; exit. |
| return; |
| } else if ((new_start <= old_start) && (old_end <= new_end)) { |
| // The new operation is larger (on both sides) than the old operation. |
| // |
| // Make the old operation as large as the new operation. |
| operation.op.vmo_offset = vmo_offset; |
| operation.op.dev_offset = dev_offset; |
| block_count_ += (length - operation.op.length); |
| operation.op.length = length; |
| return; |
| } else if ((new_start <= old_end) && (old_start <= new_start)) { |
| // The new op either partially or totally follows the old operation. |
| // |
| // Post-Extend the old operation. |
| size_t extension = new_end - old_end; |
| operation.op.length += extension; |
| block_count_ += extension; |
| return; |
| } else if ((old_start <= new_end) && (new_start <= old_start)) { |
| // The new op either partially or totally precedes the old operation. |
| // |
| // Pre-Extend the old operation. |
| size_t extension = old_start - new_start; |
| operation.op.vmo_offset = vmo_offset; |
| operation.op.dev_offset = dev_offset; |
| operation.op.length += extension; |
| block_count_ += extension; |
| return; |
| } |
| } |
| |
| UnbufferedOperation operation; |
| operation.vmo = zx::unowned_vmo(vmo->get()); |
| operation.op.type = new_operation.op.type; |
| operation.op.vmo_offset = vmo_offset; |
| operation.op.dev_offset = dev_offset; |
| operation.op.length = length; |
| operations_.push_back(operation); |
| block_count_ += operation.op.length; |
| } |
| |
| fbl::Vector<UnbufferedOperation> UnbufferedOperationsBuilder::TakeOperations() { |
| block_count_ = 0; |
| return std::move(operations_); |
| } |
| |
| } // namespace blobfs |