blob: 5193d3d7d195e13d9e597ccf8346c375d6adb014 [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
13using std::max;
14using std::min;
15using std::set;
16using std::vector;
17
18namespace chromeos_update_engine {
19
20bool ExtentRanges::ExtentsOverlapOrTouch(const Extent& a, const Extent& b) {
21 if (a.start_block() == b.start_block())
22 return true;
Darin Petkov94817cb2013-05-08 14:33:24 +020023 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
24 return false;
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070025 if (a.start_block() < b.start_block()) {
26 return a.start_block() + a.num_blocks() >= b.start_block();
27 } else {
28 return b.start_block() + b.num_blocks() >= a.start_block();
29 }
30}
31
32bool ExtentRanges::ExtentsOverlap(const Extent& a, const Extent& b) {
33 if (a.start_block() == b.start_block())
34 return true;
Darin Petkov94817cb2013-05-08 14:33:24 +020035 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
36 return false;
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070037 if (a.start_block() < b.start_block()) {
38 return a.start_block() + a.num_blocks() > b.start_block();
39 } else {
40 return b.start_block() + b.num_blocks() > a.start_block();
41 }
42}
43
44void ExtentRanges::AddBlock(uint64_t block) {
45 AddExtent(ExtentForRange(block, 1));
46}
47
48void ExtentRanges::SubtractBlock(uint64_t block) {
49 SubtractExtent(ExtentForRange(block, 1));
50}
51
52namespace {
53
54Extent UnionOverlappingExtents(const Extent& first, const Extent& second) {
Darin Petkov94817cb2013-05-08 14:33:24 +020055 CHECK_NE(kSparseHole, first.start_block());
56 CHECK_NE(kSparseHole, second.start_block());
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070057 uint64_t start = min(first.start_block(), second.start_block());
58 uint64_t end = max(first.start_block() + first.num_blocks(),
59 second.start_block() + second.num_blocks());
60 return ExtentForRange(start, end - start);
61}
Darin Petkov94817cb2013-05-08 14:33:24 +020062
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070063} // namespace {}
64
65void ExtentRanges::AddExtent(Extent extent) {
Darin Petkov94817cb2013-05-08 14:33:24 +020066 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -070067 return;
68
69 ExtentSet::iterator begin_del = extent_set_.end();
70 ExtentSet::iterator end_del = extent_set_.end();
71 uint64_t del_blocks = 0;
72 for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
73 it != e; ++it) {
74 if (ExtentsOverlapOrTouch(*it, extent)) {
75 end_del = it;
76 ++end_del;
77 del_blocks += it->num_blocks();
78 if (begin_del == extent_set_.end())
79 begin_del = it;
80
81 extent = UnionOverlappingExtents(extent, *it);
82 }
83 }
84 extent_set_.erase(begin_del, end_del);
85 extent_set_.insert(extent);
86 blocks_ -= del_blocks;
87 blocks_ += extent.num_blocks();
88}
89
90namespace {
91// Returns base - subtractee (set subtraction).
92ExtentRanges::ExtentSet SubtractOverlappingExtents(const Extent& base,
93 const Extent& subtractee) {
94 ExtentRanges::ExtentSet ret;
95 if (subtractee.start_block() > base.start_block()) {
96 ret.insert(ExtentForRange(base.start_block(),
97 subtractee.start_block() - base.start_block()));
98 }
99 uint64_t base_end = base.start_block() + base.num_blocks();
100 uint64_t subtractee_end = subtractee.start_block() + subtractee.num_blocks();
101 if (base_end > subtractee_end) {
102 ret.insert(ExtentForRange(subtractee_end, base_end - subtractee_end));
103 }
104 return ret;
105}
106} // namespace {}
107
108void ExtentRanges::SubtractExtent(const Extent& extent) {
Darin Petkov94817cb2013-05-08 14:33:24 +0200109 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700110 return;
111
112 ExtentSet::iterator begin_del = extent_set_.end();
113 ExtentSet::iterator end_del = extent_set_.end();
114 uint64_t del_blocks = 0;
115 ExtentSet new_extents;
116 for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
117 it != e; ++it) {
118 if (!ExtentsOverlap(*it, extent))
119 continue;
Darin Petkov94817cb2013-05-08 14:33:24 +0200120
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700121 if (begin_del == extent_set_.end())
122 begin_del = it;
123 end_del = it;
124 ++end_del;
Darin Petkov94817cb2013-05-08 14:33:24 +0200125
Andrew de los Reyes5fdae4a2010-10-05 10:47:42 -0700126 del_blocks += it->num_blocks();
127
128 ExtentSet subtraction = SubtractOverlappingExtents(*it, extent);
129 for (ExtentSet::iterator jt = subtraction.begin(), je = subtraction.end();
130 jt != je; ++jt) {
131 new_extents.insert(*jt);
132 del_blocks -= jt->num_blocks();
133 }
134 }
135 extent_set_.erase(begin_del, end_del);
136 extent_set_.insert(new_extents.begin(), new_extents.end());
137 blocks_ -= del_blocks;
138}
139
140void ExtentRanges::AddRanges(const ExtentRanges& ranges) {
141 for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
142 e = ranges.extent_set_.end(); it != e; ++it) {
143 AddExtent(*it);
144 }
145}
146
147void ExtentRanges::SubtractRanges(const ExtentRanges& ranges) {
148 for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
149 e = ranges.extent_set_.end(); it != e; ++it) {
150 SubtractExtent(*it);
151 }
152}
153
154void ExtentRanges::AddExtents(const vector<Extent>& extents) {
155 for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
156 it != e; ++it) {
157 AddExtent(*it);
158 }
159}
160
161void ExtentRanges::SubtractExtents(const vector<Extent>& extents) {
162 for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
163 it != e; ++it) {
164 SubtractExtent(*it);
165 }
166}
167
168void ExtentRanges::AddRepeatedExtents(
169 const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
170 for (int i = 0, e = exts.size(); i != e; ++i) {
171 AddExtent(exts.Get(i));
172 }
173}
174
175void ExtentRanges::SubtractRepeatedExtents(
176 const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
177 for (int i = 0, e = exts.size(); i != e; ++i) {
178 SubtractExtent(exts.Get(i));
179 }
180}
181
182void ExtentRanges::Dump() const {
183 LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_;
184 for (ExtentSet::const_iterator it = extent_set_.begin(),
185 e = extent_set_.end();
186 it != e; ++it) {
187 LOG(INFO) << "{" << it->start_block() << ", " << it->num_blocks() << "}";
188 }
189}
190
191Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks) {
192 Extent ret;
193 ret.set_start_block(start_block);
194 ret.set_num_blocks(num_blocks);
195 return ret;
196}
197
198std::vector<Extent> ExtentRanges::GetExtentsForBlockCount(
199 uint64_t count) const {
200 vector<Extent> out;
201 if (count == 0)
202 return out;
203 uint64_t out_blocks = 0;
204 CHECK(count <= blocks_);
205 for (ExtentSet::const_iterator it = extent_set_.begin(),
206 e = extent_set_.end();
207 it != e; ++it) {
208 const uint64_t blocks_needed = count - out_blocks;
209 const Extent& extent = *it;
210 out.push_back(extent);
211 out_blocks += extent.num_blocks();
212 if (extent.num_blocks() < blocks_needed)
213 continue;
214 if (extent.num_blocks() == blocks_needed)
215 break;
216 // If we get here, we just added the last extent needed, but it's too big
217 out_blocks -= extent.num_blocks();
218 out_blocks += blocks_needed;
219 out.back().set_num_blocks(blocks_needed);
220 break;
221 }
222 return out;
223}
224
225} // namespace chromeos_update_engine