blob: 3622125f4e86fe07a06a827a81bf203418afa8b7 [file] [log] [blame]
Amin Hassani27574e92017-10-09 16:06:47 -07001// 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 Hassanie2e9cb02018-03-15 14:14:58 -070010#include "puffin/src/logging.h"
Amin Hassani27574e92017-10-09 16:06:47 -070011
Amin Hassani27574e92017-10-09 16:06:47 -070012using std::vector;
13
Amin Hassani10b869c2018-03-15 13:22:32 -070014namespace puffin {
15
Amin Hassani27574e92017-10-09 16:06:47 -070016UniqueStreamPtr ExtentStream::CreateForWrite(
17 UniqueStreamPtr stream, const vector<ByteExtent>& extents) {
18 return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, true));
19}
20
21UniqueStreamPtr ExtentStream::CreateForRead(UniqueStreamPtr stream,
22 const vector<ByteExtent>& extents) {
23 return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, false));
24}
25
26ExtentStream::ExtentStream(UniqueStreamPtr stream,
27 const vector<ByteExtent>& extents,
28 bool is_for_write)
29 : stream_(std::move(stream)),
30 extents_(extents),
Amin Hassani27574e92017-10-09 16:06:47 -070031 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 Hassanid7768d52018-02-28 15:34:21 -080037 uint64_t extent_end = 0;
Amin Hassani27574e92017-10-09 16:06:47 -070038 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 Hassani278213a2018-01-30 10:09:47 -080049 cur_extent_ = extents_.begin();
Amin Hassani27574e92017-10-09 16:06:47 -070050}
51
Amin Hassanid7768d52018-02-28 15:34:21 -080052bool ExtentStream::GetSize(uint64_t* size) const {
Amin Hassani27574e92017-10-09 16:06:47 -070053 *size = size_;
54 return true;
55}
56
Amin Hassanid7768d52018-02-28 15:34:21 -080057bool ExtentStream::GetOffset(uint64_t* offset) const {
Amin Hassani27574e92017-10-09 16:06:47 -070058 *offset = offset_;
59 return true;
60}
61
Amin Hassanid7768d52018-02-28 15:34:21 -080062bool ExtentStream::Seek(uint64_t offset) {
Amin Hassani27574e92017-10-09 16:06:47 -070063 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
78bool ExtentStream::Close() {
79 return stream_->Close();
80}
81
82bool 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
88bool 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
94bool 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 Hassani278213a2018-01-30 10:09:47 -0800100 return false;
Amin Hassani27574e92017-10-09 16:06:47 -0700101 }
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 Hassani278213a2018-01-30 10:09:47 -0800124 if (cur_extent_ != extents_.end()) {
125 TEST_AND_RETURN_FALSE(stream_->Seek(cur_extent_->offset));
126 }
Amin Hassani27574e92017-10-09 16:06:47 -0700127 }
128 }
129 return true;
130}
131
132} // namespace puffin