普通文本  |  133行  |  4.16 KB

// 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/set_errors.h"

namespace puffin {

using std::vector;

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