// Copyright 2017 The Chromium OS 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 "puffin/src/extent_stream.h" #include <algorithm> #include <utility> #include "puffin/src/logging.h" using std::vector; namespace puffin { UniqueStreamPtr ExtentStream::CreateForWrite( UniqueStreamPtr stream, const vector<ByteExtent>& extents) { return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, true)); } UniqueStreamPtr ExtentStream::CreateForRead(UniqueStreamPtr stream, const vector<ByteExtent>& extents) { return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, false)); } ExtentStream::ExtentStream(UniqueStreamPtr stream, const vector<ByteExtent>& extents, bool is_for_write) : stream_(std::move(stream)), extents_(extents), cur_extent_offset_(0), is_for_write_(is_for_write), offset_(0) { extents_upper_bounds_.reserve(extents_.size() + 1); extents_upper_bounds_.emplace_back(0); uint64_t total_size = 0; uint64_t extent_end = 0; for (const auto& extent : extents_) { total_size += extent.length; extents_upper_bounds_.emplace_back(total_size); extent_end = extent.offset + extent.length; } size_ = total_size; // Adding one extent at the end to avoid doing extra checks in: // - Seek: when seeking to the end of extents // - DoReadOrWrite: when changing the current extent. extents_.emplace_back(extent_end, 0); cur_extent_ = extents_.begin(); } bool ExtentStream::GetSize(uint64_t* size) const { *size = size_; return true; } bool ExtentStream::GetOffset(uint64_t* offset) const { *offset = offset_; return true; } bool ExtentStream::Seek(uint64_t offset) { TEST_AND_RETURN_FALSE(offset <= size_); // The first item is zero and upper_bound never returns it because it always // return the item which is greater than the given value. auto extent_idx = std::upper_bound(extents_upper_bounds_.begin(), extents_upper_bounds_.end(), offset) - extents_upper_bounds_.begin() - 1; cur_extent_ = std::next(extents_.begin(), extent_idx); offset_ = offset; cur_extent_offset_ = offset_ - extents_upper_bounds_[extent_idx]; TEST_AND_RETURN_FALSE( stream_->Seek(cur_extent_->offset + cur_extent_offset_)); return true; } bool ExtentStream::Close() { return stream_->Close(); } bool ExtentStream::Read(void* buffer, size_t length) { TEST_AND_RETURN_FALSE(!is_for_write_); TEST_AND_RETURN_FALSE(DoReadOrWrite(buffer, nullptr, length)); return true; } bool ExtentStream::Write(const void* buffer, size_t length) { TEST_AND_RETURN_FALSE(is_for_write_); TEST_AND_RETURN_FALSE(DoReadOrWrite(nullptr, buffer, length)); return true; } bool ExtentStream::DoReadOrWrite(void* read_buffer, const void* write_buffer, size_t length) { uint64_t bytes_passed = 0; while (bytes_passed < length) { if (cur_extent_ == extents_.end()) { return false; } uint64_t bytes_to_pass = std::min(length - bytes_passed, cur_extent_->length - cur_extent_offset_); if (read_buffer != nullptr) { TEST_AND_RETURN_FALSE( stream_->Read(reinterpret_cast<uint8_t*>(read_buffer) + bytes_passed, bytes_to_pass)); } else if (write_buffer != nullptr) { TEST_AND_RETURN_FALSE(stream_->Write( reinterpret_cast<const uint8_t*>(write_buffer) + bytes_passed, bytes_to_pass)); } else { LOG(ERROR) << "Either read or write buffer should be given!"; return false; } bytes_passed += bytes_to_pass; cur_extent_offset_ += bytes_to_pass; offset_ += bytes_to_pass; if (cur_extent_offset_ == cur_extent_->length) { // We have to advance the cur_extent_; cur_extent_++; cur_extent_offset_ = 0; if (cur_extent_ != extents_.end()) { TEST_AND_RETURN_FALSE(stream_->Seek(cur_extent_->offset)); } } } return true; } } // namespace puffin