extent_ranges.cc revision 1beda780333ce51d7872603b70712772eb2383fb
1// 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/payload_generator/extent_ranges.h" 6 7#include <algorithm> 8#include <set> 9#include <utility> 10#include <vector> 11 12#include <base/logging.h> 13 14#include "update_engine/payload_constants.h" 15 16using 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; 24 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole) 25 return false; 26 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; 36 if (a.start_block() == kSparseHole || b.start_block() == kSparseHole) 37 return false; 38 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) { 56 CHECK_NE(kSparseHole, first.start_block()); 57 CHECK_NE(kSparseHole, second.start_block()); 58 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()); 61 return ExtentForRange(start, end - start); 62} 63 64} // namespace 65 66void ExtentRanges::AddExtent(Extent extent) { 67 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0) 68 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} 107} // namespace 108 109void ExtentRanges::SubtractExtent(const Extent& extent) { 110 if (extent.start_block() == kSparseHole || extent.num_blocks() == 0) 111 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; 121 122 if (begin_del == extent_set_.end()) 123 begin_del = it; 124 end_del = it; 125 ++end_del; 126 127 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 199vector<Extent> ExtentRanges::GetExtentsForBlockCount( 200 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 226vector<Extent> FilterExtentRanges(const std::vector<Extent>& extents, 227 const ExtentRanges& ranges) { 228 vector<Extent> result; 229 const ExtentRanges::ExtentSet& extent_set = ranges.extent_set(); 230 for (Extent extent : extents) { 231 // The extents are sorted by the start_block. We want to iterate all the 232 // Extents in the ExtentSet possibly overlapping the current |extent|. This 233 // is achieved by looking from the extent whose start_block is *lower* than 234 // the extent.start_block() up to the greatest extent whose start_block is 235 // lower than extent.start_block() + extent.num_blocks(). 236 auto lower = extent_set.lower_bound(extent); 237 // We need to decrement the lower_bound to look at the extent that could 238 // overlap the beginning of the current |extent|. 239 if (lower != extent_set.begin()) 240 lower--; 241 auto upper = extent_set.lower_bound( 242 ExtentForRange(extent.start_block() + extent.num_blocks(), 0)); 243 for (auto iter = lower; iter != upper; ++iter) { 244 if (!ExtentRanges::ExtentsOverlap(extent, *iter)) 245 continue; 246 if (iter->start_block() <= extent.start_block()) { 247 // We need to cut blocks from the beginning of the |extent|. 248 uint64_t cut_blocks = iter->start_block() + iter->num_blocks() - 249 extent.start_block(); 250 if (cut_blocks >= extent.num_blocks()) { 251 extent.set_num_blocks(0); 252 break; 253 } 254 extent = ExtentForRange(extent.start_block() + cut_blocks, 255 extent.num_blocks() - cut_blocks); 256 } else { 257 // We need to cut blocks on the middle of the extent, possible up to the 258 // end of it. 259 result.push_back( 260 ExtentForRange(extent.start_block(), 261 iter->start_block() - extent.start_block())); 262 uint64_t new_start = iter->start_block() + iter->num_blocks(); 263 uint64_t old_end = extent.start_block() + extent.num_blocks(); 264 if (new_start >= old_end) { 265 extent.set_num_blocks(0); 266 break; 267 } 268 extent = ExtentForRange(new_start, old_end - new_start); 269 } 270 } 271 if (extent.num_blocks() > 0) 272 result.push_back(extent); 273 } 274 return result; 275} 276 277} // namespace chromeos_update_engine 278