// 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
|