Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 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 | |
Amin Hassani | e2e9cb0 | 2018-03-15 14:14:58 -0700 | [diff] [blame] | 10 | #include "puffin/src/logging.h" |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 11 | |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 12 | using std::vector; |
| 13 | |
Amin Hassani | 10b869c | 2018-03-15 13:22:32 -0700 | [diff] [blame] | 14 | namespace puffin { |
| 15 | |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 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), |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 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; |
Amin Hassani | d7768d5 | 2018-02-28 15:34:21 -0800 | [diff] [blame] | 37 | uint64_t extent_end = 0; |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 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); |
Amin Hassani | 278213a | 2018-01-30 10:09:47 -0800 | [diff] [blame] | 49 | cur_extent_ = extents_.begin(); |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 50 | } |
| 51 | |
Amin Hassani | d7768d5 | 2018-02-28 15:34:21 -0800 | [diff] [blame] | 52 | bool ExtentStream::GetSize(uint64_t* size) const { |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 53 | *size = size_; |
| 54 | return true; |
| 55 | } |
| 56 | |
Amin Hassani | d7768d5 | 2018-02-28 15:34:21 -0800 | [diff] [blame] | 57 | bool ExtentStream::GetOffset(uint64_t* offset) const { |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 58 | *offset = offset_; |
| 59 | return true; |
| 60 | } |
| 61 | |
Amin Hassani | d7768d5 | 2018-02-28 15:34:21 -0800 | [diff] [blame] | 62 | bool ExtentStream::Seek(uint64_t offset) { |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 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()) { |
Amin Hassani | 278213a | 2018-01-30 10:09:47 -0800 | [diff] [blame] | 100 | return false; |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 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; |
Amin Hassani | 278213a | 2018-01-30 10:09:47 -0800 | [diff] [blame] | 124 | if (cur_extent_ != extents_.end()) { |
| 125 | TEST_AND_RETURN_FALSE(stream_->Seek(cur_extent_->offset)); |
| 126 | } |
Amin Hassani | 27574e9 | 2017-10-09 16:06:47 -0700 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | return true; |
| 130 | } |
| 131 | |
| 132 | } // namespace puffin |