blob: 326dbbd45e333dee06b38ead9ac5accec3b00a92 [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
Alex Vakulenkod2779df2014-06-16 13:19:00 -07007#include <algorithm>
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -07008#include <set>
9#include <utility>
10#include <vector>
11
12#include <base/logging.h>
13
Alex Deymo161c4a12014-05-16 15:56:21 -070014#include "update_engine/payload_constants.h"
15
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070016using std::set;
17using std::vector;
18
19namespace chromeos_update_engine {
20
21bool ExtentRanges::ExtentsOverlapOrTouch(const Extent& a, const Extent& b) {
22 if (a.start_block() == b.start_block())
23 return true;
Darin Petkov94817cb2013-05-08 14:33:24 +020024 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
25 return false;
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070026 if (a.start_block() < b.start_block()) {
27 return a.start_block() + a.num_blocks() >= b.start_block();
28 } else {
29 return b.start_block() + b.num_blocks() >= a.start_block();
30 }
31}
32
33bool ExtentRanges::ExtentsOverlap(const Extent& a, const Extent& b) {
34 if (a.start_block() == b.start_block())
35 return true;
Darin Petkov94817cb2013-05-08 14:33:24 +020036 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
37 return false;
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070038 if (a.start_block() < b.start_block()) {
39 return a.start_block() + a.num_blocks() > b.start_block();
40 } else {
41 return b.start_block() + b.num_blocks() > a.start_block();
42 }
43}
44
45void ExtentRanges::AddBlock(uint64_t block) {
46 AddExtent(ExtentForRange(block, 1));
47}
48
49void ExtentRanges::SubtractBlock(uint64_t block) {
50 SubtractExtent(ExtentForRange(block, 1));
51}
52
53namespace {
54
55Extent UnionOverlappingExtents(const Extent& first, const Extent& second) {
Darin Petkov94817cb2013-05-08 14:33:24 +020056 CHECK_NE(kSparseHole, first.start_block());
57 CHECK_NE(kSparseHole, second.start_block());
Alex Deymof329b932014-10-30 01:37:48 -070058 uint64_t start = std::min(first.start_block(), second.start_block());
59 uint64_t end = std::max(first.start_block() + first.num_blocks(),
60 second.start_block() + second.num_blocks());
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070061 return ExtentForRange(start, end - start);
62}
Darin Petkov94817cb2013-05-08 14:33:24 +020063
Alex Vakulenkod2779df2014-06-16 13:19:00 -070064} // namespace
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070065
66void ExtentRanges::AddExtent(Extent extent) {
Darin Petkov94817cb2013-05-08 14:33:24 +020067 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070068 return;
69
70 ExtentSet::iterator begin_del = extent_set_.end();
71 ExtentSet::iterator end_del = extent_set_.end();
72 uint64_t del_blocks = 0;
73 for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
74 it != e; ++it) {
75 if (ExtentsOverlapOrTouch(*it, extent)) {
76 end_del = it;
77 ++end_del;
78 del_blocks += it->num_blocks();
79 if (begin_del == extent_set_.end())
80 begin_del = it;
81
82 extent = UnionOverlappingExtents(extent, *it);
83 }
84 }
85 extent_set_.erase(begin_del, end_del);
86 extent_set_.insert(extent);
87 blocks_ -= del_blocks;
88 blocks_ += extent.num_blocks();
89}
90
91namespace {
92// Returns base - subtractee (set subtraction).
93ExtentRanges::ExtentSet SubtractOverlappingExtents(const Extent& base,
94 const Extent& subtractee) {
95 ExtentRanges::ExtentSet ret;
96 if (subtractee.start_block() > base.start_block()) {
97 ret.insert(ExtentForRange(base.start_block(),
98 subtractee.start_block() - base.start_block()));
99 }
100 uint64_t base_end = base.start_block() + base.num_blocks();
101 uint64_t subtractee_end = subtractee.start_block() + subtractee.num_blocks();
102 if (base_end > subtractee_end) {
103 ret.insert(ExtentForRange(subtractee_end, base_end - subtractee_end));
104 }
105 return ret;
106}
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700107} // namespace
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700108
109void ExtentRanges::SubtractExtent(const Extent& extent) {
Darin Petkov94817cb2013-05-08 14:33:24 +0200110 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700111 return;
112
113 ExtentSet::iterator begin_del = extent_set_.end();
114 ExtentSet::iterator end_del = extent_set_.end();
115 uint64_t del_blocks = 0;
116 ExtentSet new_extents;
117 for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
118 it != e; ++it) {
119 if (!ExtentsOverlap(*it, extent))
120 continue;
Darin Petkov94817cb2013-05-08 14:33:24 +0200121
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700122 if (begin_del == extent_set_.end())
123 begin_del = it;
124 end_del = it;
125 ++end_del;
Darin Petkov94817cb2013-05-08 14:33:24 +0200126
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700127 del_blocks += it->num_blocks();
128
129 ExtentSet subtraction = SubtractOverlappingExtents(*it, extent);
130 for (ExtentSet::iterator jt = subtraction.begin(), je = subtraction.end();
131 jt != je; ++jt) {
132 new_extents.insert(*jt);
133 del_blocks -= jt->num_blocks();
134 }
135 }
136 extent_set_.erase(begin_del, end_del);
137 extent_set_.insert(new_extents.begin(), new_extents.end());
138 blocks_ -= del_blocks;
139}
140
141void ExtentRanges::AddRanges(const ExtentRanges& ranges) {
142 for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
143 e = ranges.extent_set_.end(); it != e; ++it) {
144 AddExtent(*it);
145 }
146}
147
148void ExtentRanges::SubtractRanges(const ExtentRanges& ranges) {
149 for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
150 e = ranges.extent_set_.end(); it != e; ++it) {
151 SubtractExtent(*it);
152 }
153}
154
155void ExtentRanges::AddExtents(const vector<Extent>& extents) {
156 for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
157 it != e; ++it) {
158 AddExtent(*it);
159 }
160}
161
162void ExtentRanges::SubtractExtents(const vector<Extent>& extents) {
163 for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
164 it != e; ++it) {
165 SubtractExtent(*it);
166 }
167}
168
169void ExtentRanges::AddRepeatedExtents(
170 const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
171 for (int i = 0, e = exts.size(); i != e; ++i) {
172 AddExtent(exts.Get(i));
173 }
174}
175
176void ExtentRanges::SubtractRepeatedExtents(
177 const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
178 for (int i = 0, e = exts.size(); i != e; ++i) {
179 SubtractExtent(exts.Get(i));
180 }
181}
182
183void ExtentRanges::Dump() const {
184 LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_;
185 for (ExtentSet::const_iterator it = extent_set_.begin(),
186 e = extent_set_.end();
187 it != e; ++it) {
188 LOG(INFO) << "{" << it->start_block() << ", " << it->num_blocks() << "}";
189 }
190}
191
192Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks) {
193 Extent ret;
194 ret.set_start_block(start_block);
195 ret.set_num_blocks(num_blocks);
196 return ret;
197}
198
Alex Deymof329b932014-10-30 01:37:48 -0700199vector<Extent> ExtentRanges::GetExtentsForBlockCount(
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700200 uint64_t count) const {
201 vector<Extent> out;
202 if (count == 0)
203 return out;
204 uint64_t out_blocks = 0;
205 CHECK(count <= blocks_);
206 for (ExtentSet::const_iterator it = extent_set_.begin(),
207 e = extent_set_.end();
208 it != e; ++it) {
209 const uint64_t blocks_needed = count - out_blocks;
210 const Extent& extent = *it;
211 out.push_back(extent);
212 out_blocks += extent.num_blocks();
213 if (extent.num_blocks() < blocks_needed)
214 continue;
215 if (extent.num_blocks() == blocks_needed)
216 break;
217 // If we get here, we just added the last extent needed, but it's too big
218 out_blocks -= extent.num_blocks();
219 out_blocks += blocks_needed;
220 out.back().set_num_blocks(blocks_needed);
221 break;
222 }
223 return out;
224}
225
226} // namespace chromeos_update_engine