• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/logging.h"
11 
12 using std::vector;
13 
14 namespace puffin {
15 
CreateForWrite(UniqueStreamPtr stream,const vector<ByteExtent> & extents)16 UniqueStreamPtr ExtentStream::CreateForWrite(
17     UniqueStreamPtr stream, const vector<ByteExtent>& extents) {
18   return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, true));
19 }
20 
CreateForRead(UniqueStreamPtr stream,const vector<ByteExtent> & extents)21 UniqueStreamPtr ExtentStream::CreateForRead(UniqueStreamPtr stream,
22                                             const vector<ByteExtent>& extents) {
23   return UniqueStreamPtr(new ExtentStream(std::move(stream), extents, false));
24 }
25 
ExtentStream(UniqueStreamPtr stream,const vector<ByteExtent> & extents,bool is_for_write)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 
GetSize(uint64_t * size) const52 bool ExtentStream::GetSize(uint64_t* size) const {
53   *size = size_;
54   return true;
55 }
56 
GetOffset(uint64_t * offset) const57 bool ExtentStream::GetOffset(uint64_t* offset) const {
58   *offset = offset_;
59   return true;
60 }
61 
Seek(uint64_t offset)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 
Close()78 bool ExtentStream::Close() {
79   return stream_->Close();
80 }
81 
Read(void * buffer,size_t length)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 
Write(const void * buffer,size_t length)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 
DoReadOrWrite(void * read_buffer,const void * write_buffer,size_t length)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