blob: 1089de11251ed80e3ba9370e6fa52ac5ae4c8f9e [file] [log] [blame]
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -07001// Copyright (c) 2010 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 "update_engine/extent_ranges.h"
6
7#include <set>
8#include <utility>
9#include <vector>
10
11#include <base/logging.h>
12
Alex Deymo161c4a12014-05-16 15:56:21 -070013#include "update_engine/payload_constants.h"
14
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070015using std::max;
16using std::min;
17using std::set;
18using std::vector;
19
20namespace chromeos_update_engine {
21
22bool ExtentRanges::ExtentsOverlapOrTouch(const Extent& a, const Extent& b) {
23 if (a.start_block() == b.start_block())
24 return true;
Darin Petkov94817cb2013-05-08 14:33:24 +020025 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
26 return false;
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070027 if (a.start_block() < b.start_block()) {
28 return a.start_block() + a.num_blocks() >= b.start_block();
29 } else {
30 return b.start_block() + b.num_blocks() >= a.start_block();
31 }
32}
33
34bool ExtentRanges::ExtentsOverlap(const Extent& a, const Extent& b) {
35 if (a.start_block() == b.start_block())
36 return true;
Darin Petkov94817cb2013-05-08 14:33:24 +020037 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
38 return false;
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070039 if (a.start_block() < b.start_block()) {
40 return a.start_block() + a.num_blocks() > b.start_block();
41 } else {
42 return b.start_block() + b.num_blocks() > a.start_block();
43 }
44}
45
46void ExtentRanges::AddBlock(uint64_t block) {
47 AddExtent(ExtentForRange(block, 1));
48}
49
50void ExtentRanges::SubtractBlock(uint64_t block) {
51 SubtractExtent(ExtentForRange(block, 1));
52}
53
54namespace {
55
56Extent UnionOverlappingExtents(const Extent& first, const Extent& second) {
Darin Petkov94817cb2013-05-08 14:33:24 +020057 CHECK_NE(kSparseHole, first.start_block());
58 CHECK_NE(kSparseHole, second.start_block());
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070059 uint64_t start = min(first.start_block(), second.start_block());
60 uint64_t end = max(first.start_block() + first.num_blocks(),
61 second.start_block() + second.num_blocks());
62 return ExtentForRange(start, end - start);
63}
Darin Petkov94817cb2013-05-08 14:33:24 +020064
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070065} // namespace {}
66
67void ExtentRanges::AddExtent(Extent extent) {
Darin Petkov94817cb2013-05-08 14:33:24 +020068 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070069 return;
70
71 ExtentSet::iterator begin_del = extent_set_.end();
72 ExtentSet::iterator end_del = extent_set_.end();
73 uint64_t del_blocks = 0;
74 for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
75 it != e; ++it) {
76 if (ExtentsOverlapOrTouch(*it, extent)) {
77 end_del = it;
78 ++end_del;
79 del_blocks += it->num_blocks();
80 if (begin_del == extent_set_.end())
81 begin_del = it;
82
83 extent = UnionOverlappingExtents(extent, *it);
84 }
85 }
86 extent_set_.erase(begin_del, end_del);
87 extent_set_.insert(extent);
88 blocks_ -= del_blocks;
89 blocks_ += extent.num_blocks();
90}
91
92namespace {
93// Returns base - subtractee (set subtraction).
94ExtentRanges::ExtentSet SubtractOverlappingExtents(const Extent& base,
95 const Extent& subtractee) {
96 ExtentRanges::ExtentSet ret;
97 if (subtractee.start_block() > base.start_block()) {
98 ret.insert(ExtentForRange(base.start_block(),
99 subtractee.start_block() - base.start_block()));
100 }
101 uint64_t base_end = base.start_block() + base.num_blocks();
102 uint64_t subtractee_end = subtractee.start_block() + subtractee.num_blocks();
103 if (base_end > subtractee_end) {
104 ret.insert(ExtentForRange(subtractee_end, base_end - subtractee_end));
105 }
106 return ret;
107}
108} // namespace {}
109
110void ExtentRanges::SubtractExtent(const Extent& extent) {
Darin Petkov94817cb2013-05-08 14:33:24 +0200111 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700112 return;
113
114 ExtentSet::iterator begin_del = extent_set_.end();
115 ExtentSet::iterator end_del = extent_set_.end();
116 uint64_t del_blocks = 0;
117 ExtentSet new_extents;
118 for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
119 it != e; ++it) {
120 if (!ExtentsOverlap(*it, extent))
121 continue;
Darin Petkov94817cb2013-05-08 14:33:24 +0200122
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700123 if (begin_del == extent_set_.end())
124 begin_del = it;
125 end_del = it;
126 ++end_del;
Darin Petkov94817cb2013-05-08 14:33:24 +0200127
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700128 del_blocks += it->num_blocks();
129
130 ExtentSet subtraction = SubtractOverlappingExtents(*it, extent);
131 for (ExtentSet::iterator jt = subtraction.begin(), je = subtraction.end();
132 jt != je; ++jt) {
133 new_extents.insert(*jt);
134 del_blocks -= jt->num_blocks();
135 }
136 }
137 extent_set_.erase(begin_del, end_del);
138 extent_set_.insert(new_extents.begin(), new_extents.end());
139 blocks_ -= del_blocks;
140}
141
142void ExtentRanges::AddRanges(const ExtentRanges& ranges) {
143 for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
144 e = ranges.extent_set_.end(); it != e; ++it) {
145 AddExtent(*it);
146 }
147}
148
149void ExtentRanges::SubtractRanges(const ExtentRanges& ranges) {
150 for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
151 e = ranges.extent_set_.end(); it != e; ++it) {
152 SubtractExtent(*it);
153 }
154}
155
156void ExtentRanges::AddExtents(const vector<Extent>& extents) {
157 for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
158 it != e; ++it) {
159 AddExtent(*it);
160 }
161}
162
163void ExtentRanges::SubtractExtents(const vector<Extent>& extents) {
164 for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
165 it != e; ++it) {
166 SubtractExtent(*it);
167 }
168}
169
170void ExtentRanges::AddRepeatedExtents(
171 const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
172 for (int i = 0, e = exts.size(); i != e; ++i) {
173 AddExtent(exts.Get(i));
174 }
175}
176
177void ExtentRanges::SubtractRepeatedExtents(
178 const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
179 for (int i = 0, e = exts.size(); i != e; ++i) {
180 SubtractExtent(exts.Get(i));
181 }
182}
183
184void ExtentRanges::Dump() const {
185 LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_;
186 for (ExtentSet::const_iterator it = extent_set_.begin(),
187 e = extent_set_.end();
188 it != e; ++it) {
189 LOG(INFO) << "{" << it->start_block() << ", " << it->num_blocks() << "}";
190 }
191}
192
193Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks) {
194 Extent ret;
195 ret.set_start_block(start_block);
196 ret.set_num_blocks(num_blocks);
197 return ret;
198}
199
200std::vector<Extent> ExtentRanges::GetExtentsForBlockCount(
201 uint64_t count) const {
202 vector<Extent> out;
203 if (count == 0)
204 return out;
205 uint64_t out_blocks = 0;
206 CHECK(count <= blocks_);
207 for (ExtentSet::const_iterator it = extent_set_.begin(),
208 e = extent_set_.end();
209 it != e; ++it) {
210 const uint64_t blocks_needed = count - out_blocks;
211 const Extent& extent = *it;
212 out.push_back(extent);
213 out_blocks += extent.num_blocks();
214 if (extent.num_blocks() < blocks_needed)
215 continue;
216 if (extent.num_blocks() == blocks_needed)
217 break;
218 // If we get here, we just added the last extent needed, but it's too big
219 out_blocks -= extent.num_blocks();
220 out_blocks += blocks_needed;
221 out.back().set_num_blocks(blocks_needed);
222 break;
223 }
224 return out;
225}
226
227} // namespace chromeos_update_engine