Home | History | Annotate | Download | only in src
      1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "puffin/src/extent_stream.h"
      6 
      7 #include <algorithm>
      8 #include <utility>
      9 
     10 #include "puffin/src/set_errors.h"
     11 
     12 namespace puffin {
     13 
     14 using std::vector;
     15 
     16 UniqueStreamPtr ExtentStream::CreateForWrite(
     17     UniqueStreamPtr stream, const vector<ByteExtent>& extents) {
     18   return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, true));
     19 }
     20 
     21 UniqueStreamPtr ExtentStream::CreateForRead(UniqueStreamPtr stream,
     22                                             const vector<ByteExtent>& extents) {
     23   return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, false));
     24 }
     25 
     26 ExtentStream::ExtentStream(UniqueStreamPtr stream,
     27                            const vector<ByteExtent>& extents,
     28                            bool is_for_write)
     29     : stream_(std::move(stream)),
     30       extents_(extents),
     31       cur_extent_offset_(0),
     32       is_for_write_(is_for_write),
     33       offset_(0) {
     34   extents_upper_bounds_.reserve(extents_.size() + 1);
     35   extents_upper_bounds_.emplace_back(0);
     36   uint64_t total_size = 0;
     37   uint64_t extent_end = 0;
     38   for (const auto& extent : extents_) {
     39     total_size += extent.length;
     40     extents_upper_bounds_.emplace_back(total_size);
     41     extent_end = extent.offset + extent.length;
     42   }
     43   size_ = total_size;
     44 
     45   // Adding one extent at the end to avoid doing extra checks in:
     46   // - Seek: when seeking to the end of extents
     47   // - DoReadOrWrite: when changing the current extent.
     48   extents_.emplace_back(extent_end, 0);
     49   cur_extent_ = extents_.begin();
     50 }
     51 
     52 bool ExtentStream::GetSize(uint64_t* size) const {
     53   *size = size_;
     54   return true;
     55 }
     56 
     57 bool ExtentStream::GetOffset(uint64_t* offset) const {
     58   *offset = offset_;
     59   return true;
     60 }
     61 
     62 bool ExtentStream::Seek(uint64_t offset) {
     63   TEST_AND_RETURN_FALSE(offset <= size_);
     64 
     65   // The first item is zero and upper_bound never returns it because it always
     66   // return the item which is greater than the given value.
     67   auto extent_idx = std::upper_bound(extents_upper_bounds_.begin(),
     68                                      extents_upper_bounds_.end(), offset) -
     69                     extents_upper_bounds_.begin() - 1;
     70   cur_extent_ = std::next(extents_.begin(), extent_idx);
     71   offset_ = offset;
     72   cur_extent_offset_ = offset_ - extents_upper_bounds_[extent_idx];
     73   TEST_AND_RETURN_FALSE(
     74       stream_->Seek(cur_extent_->offset + cur_extent_offset_));
     75   return true;
     76 }
     77 
     78 bool ExtentStream::Close() {
     79   return stream_->Close();
     80 }
     81 
     82 bool ExtentStream::Read(void* buffer, size_t length) {
     83   TEST_AND_RETURN_FALSE(!is_for_write_);
     84   TEST_AND_RETURN_FALSE(DoReadOrWrite(buffer, nullptr, length));
     85   return true;
     86 }
     87 
     88 bool ExtentStream::Write(const void* buffer, size_t length) {
     89   TEST_AND_RETURN_FALSE(is_for_write_);
     90   TEST_AND_RETURN_FALSE(DoReadOrWrite(nullptr, buffer, length));
     91   return true;
     92 }
     93 
     94 bool ExtentStream::DoReadOrWrite(void* read_buffer,
     95                                  const void* write_buffer,
     96                                  size_t length) {
     97   uint64_t bytes_passed = 0;
     98   while (bytes_passed < length) {
     99     if (cur_extent_ == extents_.end()) {
    100       return false;
    101     }
    102     uint64_t bytes_to_pass = std::min(length - bytes_passed,
    103                                       cur_extent_->length - cur_extent_offset_);
    104     if (read_buffer != nullptr) {
    105       TEST_AND_RETURN_FALSE(
    106           stream_->Read(reinterpret_cast<uint8_t*>(read_buffer) + bytes_passed,
    107                         bytes_to_pass));
    108     } else if (write_buffer != nullptr) {
    109       TEST_AND_RETURN_FALSE(stream_->Write(
    110           reinterpret_cast<const uint8_t*>(write_buffer) + bytes_passed,
    111           bytes_to_pass));
    112     } else {
    113       LOG(ERROR) << "Either read or write buffer should be given!";
    114       return false;
    115     }
    116 
    117     bytes_passed += bytes_to_pass;
    118     cur_extent_offset_ += bytes_to_pass;
    119     offset_ += bytes_to_pass;
    120     if (cur_extent_offset_ == cur_extent_->length) {
    121       // We have to advance the cur_extent_;
    122       cur_extent_++;
    123       cur_extent_offset_ = 0;
    124       if (cur_extent_ != extents_.end()) {
    125         TEST_AND_RETURN_FALSE(stream_->Seek(cur_extent_->offset));
    126       }
    127     }
    128   }
    129   return true;
    130 }
    131 
    132 }  // namespace puffin
    133