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