blob: 5ffdcde8c744aca98bcba5d1bf1c381519984a58 [file] [log] [blame]
Alex Deymo03f1deb2015-10-13 02:15:31 -07001// Copyright 2015 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
Alex Deymoddf9db52017-03-02 16:10:41 -08005#include "bsdiff/extents_file.h"
Alex Deymo03f1deb2015-10-13 02:15:31 -07006
7#include <string.h>
8
9#include <algorithm>
10
11// Extent files implementation extending FileInterface.
12//
13// This class allows to map linear positions in a file to a list of regions in
14// another file. All the reads and writes are unbuffered, passed directly to the
15// underlying file. Seeking is done in O(log(N)), where N is the number of
16// extents in the file, but sequential reads jump to the next extent in O(1).
17
18namespace bsdiff {
19
20ExtentsFile::ExtentsFile(std::unique_ptr<FileInterface> file,
21 const std::vector<ex_t>& extents)
22 : file_(std::move(file)), extents_(extents) {
23 acc_len_.reserve(extents.size());
24 for (const ex_t& extent : extents) {
25 acc_len_.push_back(total_ex_len_);
26 total_ex_len_ += extent.len;
27 }
28}
29
30ExtentsFile::~ExtentsFile() {
31 Close();
32}
33
34bool ExtentsFile::Read(void* buf, size_t count, size_t* bytes_read) {
35 return IOOperation(&FileInterface::Read, buf, count, bytes_read);
36}
37
38
39bool ExtentsFile::Write(const void* buf, size_t count, size_t* bytes_written) {
40 return IOOperation(&FileInterface::Write, buf, count, bytes_written);
41}
42
43bool ExtentsFile::Seek(off_t pos) {
44 if (pos < 0 || static_cast<uint64_t>(pos) > total_ex_len_)
45 return false;
46 if (acc_len_.empty())
47 return true;
48 // Note that the first element of acc_len_ is always 0, and pos is at least 0,
49 // so the upper_bound will never return acc_len_.begin().
50 curr_pos_ = pos;
51 curr_ex_idx_ = std::upper_bound(acc_len_.begin(), acc_len_.end(), pos) -
52 acc_len_.begin();
53 // We handle the corner case where |pos| is the size of all the extents by
54 // leaving the value of curr_ex_idx_ the same way AdvancePos(0) would leave it
55 // after the seek.
56 if (curr_pos_ < total_ex_len_)
57 curr_ex_idx_--;
58 return true;
59}
60
61bool ExtentsFile::Close() {
62 return file_->Close();
63}
64
Alex Deymodaf35162015-10-14 20:43:15 -070065bool ExtentsFile::GetSize(uint64_t* size) {
66 *size = total_ex_len_;
67 return true;
68}
69
Alex Deymo03f1deb2015-10-13 02:15:31 -070070void ExtentsFile::AdvancePos(uint64_t size) {
71 curr_pos_ += size;
72 for (; curr_ex_idx_ < extents_.size(); curr_ex_idx_++) {
73 if (curr_pos_ < acc_len_[curr_ex_idx_] + extents_[curr_ex_idx_].len)
74 return;
75 }
Alex Deymo03f1deb2015-10-13 02:15:31 -070076}
77
78template <typename T>
79bool ExtentsFile::IOOperation(bool (FileInterface::*io_op)(T*, size_t, size_t*),
80 T* buf,
81 size_t count,
82 size_t* bytes_processed) {
83 bool result = true;
84 size_t processed = 0;
85 AdvancePos(0);
86 while (count > 0 && curr_ex_idx_ < extents_.size()) {
87 const ex_t& ex = extents_[curr_ex_idx_];
Sen Jiangf822e6c2015-11-24 13:44:00 -080088 off_t curr_ex_off = curr_pos_ - acc_len_[curr_ex_idx_];
89 size_t chunk_size =
90 std::min(static_cast<uint64_t>(count), ex.len - curr_ex_off);
Alex Deymo03f1deb2015-10-13 02:15:31 -070091 size_t chunk_processed = 0;
92 if (ex.off < 0) {
93 chunk_processed = chunk_size;
94 } else {
Sen Jiangf822e6c2015-11-24 13:44:00 -080095 if (!file_->Seek(ex.off + curr_ex_off) ||
96 !(file_.get()->*io_op)(buf, chunk_size, &chunk_processed)) {
Alex Deymo03f1deb2015-10-13 02:15:31 -070097 processed += chunk_processed;
98 result = processed > 0;
99 break;
100 }
101 }
102 processed += chunk_processed;
103 count -= chunk_processed;
104 // T can be either const void* or void*. We const_cast it back to void* and
105 // then char* to do the arithmetic operation, but |buf| will continue to be
106 // const void* if it was defined that way.
107 buf = static_cast<char*>(const_cast<void*>(buf)) + chunk_processed;
108 AdvancePos(chunk_processed);
109 if (!chunk_processed)
110 break;
111 }
112 *bytes_processed = processed;
113 return result;
114}
115
116} // namespace bsdiff