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