blob: 7cdce41a4c597f3118b75a67603a87fff116ea0e [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "update_engine/extent_ranges.h"
#include <algorithm>
#include <set>
#include <utility>
#include <vector>
#include <base/logging.h>
#include "update_engine/payload_constants.h"
using std::max;
using std::min;
using std::set;
using std::vector;
namespace chromeos_update_engine {
bool ExtentRanges::ExtentsOverlapOrTouch(const Extent& a, const Extent& b) {
if (a.start_block() == b.start_block())
return true;
if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
return false;
if (a.start_block() < b.start_block()) {
return a.start_block() + a.num_blocks() >= b.start_block();
} else {
return b.start_block() + b.num_blocks() >= a.start_block();
}
}
bool ExtentRanges::ExtentsOverlap(const Extent& a, const Extent& b) {
if (a.start_block() == b.start_block())
return true;
if (a.start_block() == kSparseHole || b.start_block() == kSparseHole)
return false;
if (a.start_block() < b.start_block()) {
return a.start_block() + a.num_blocks() > b.start_block();
} else {
return b.start_block() + b.num_blocks() > a.start_block();
}
}
void ExtentRanges::AddBlock(uint64_t block) {
AddExtent(ExtentForRange(block, 1));
}
void ExtentRanges::SubtractBlock(uint64_t block) {
SubtractExtent(ExtentForRange(block, 1));
}
namespace {
Extent UnionOverlappingExtents(const Extent& first, const Extent& second) {
CHECK_NE(kSparseHole, first.start_block());
CHECK_NE(kSparseHole, second.start_block());
uint64_t start = min(first.start_block(), second.start_block());
uint64_t end = max(first.start_block() + first.num_blocks(),
second.start_block() + second.num_blocks());
return ExtentForRange(start, end - start);
}
} // namespace
void ExtentRanges::AddExtent(Extent extent) {
if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
return;
ExtentSet::iterator begin_del = extent_set_.end();
ExtentSet::iterator end_del = extent_set_.end();
uint64_t del_blocks = 0;
for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
it != e; ++it) {
if (ExtentsOverlapOrTouch(*it, extent)) {
end_del = it;
++end_del;
del_blocks += it->num_blocks();
if (begin_del == extent_set_.end())
begin_del = it;
extent = UnionOverlappingExtents(extent, *it);
}
}
extent_set_.erase(begin_del, end_del);
extent_set_.insert(extent);
blocks_ -= del_blocks;
blocks_ += extent.num_blocks();
}
namespace {
// Returns base - subtractee (set subtraction).
ExtentRanges::ExtentSet SubtractOverlappingExtents(const Extent& base,
const Extent& subtractee) {
ExtentRanges::ExtentSet ret;
if (subtractee.start_block() > base.start_block()) {
ret.insert(ExtentForRange(base.start_block(),
subtractee.start_block() - base.start_block()));
}
uint64_t base_end = base.start_block() + base.num_blocks();
uint64_t subtractee_end = subtractee.start_block() + subtractee.num_blocks();
if (base_end > subtractee_end) {
ret.insert(ExtentForRange(subtractee_end, base_end - subtractee_end));
}
return ret;
}
} // namespace
void ExtentRanges::SubtractExtent(const Extent& extent) {
if (extent.start_block() == kSparseHole || extent.num_blocks() == 0)
return;
ExtentSet::iterator begin_del = extent_set_.end();
ExtentSet::iterator end_del = extent_set_.end();
uint64_t del_blocks = 0;
ExtentSet new_extents;
for (ExtentSet::iterator it = extent_set_.begin(), e = extent_set_.end();
it != e; ++it) {
if (!ExtentsOverlap(*it, extent))
continue;
if (begin_del == extent_set_.end())
begin_del = it;
end_del = it;
++end_del;
del_blocks += it->num_blocks();
ExtentSet subtraction = SubtractOverlappingExtents(*it, extent);
for (ExtentSet::iterator jt = subtraction.begin(), je = subtraction.end();
jt != je; ++jt) {
new_extents.insert(*jt);
del_blocks -= jt->num_blocks();
}
}
extent_set_.erase(begin_del, end_del);
extent_set_.insert(new_extents.begin(), new_extents.end());
blocks_ -= del_blocks;
}
void ExtentRanges::AddRanges(const ExtentRanges& ranges) {
for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
e = ranges.extent_set_.end(); it != e; ++it) {
AddExtent(*it);
}
}
void ExtentRanges::SubtractRanges(const ExtentRanges& ranges) {
for (ExtentSet::const_iterator it = ranges.extent_set_.begin(),
e = ranges.extent_set_.end(); it != e; ++it) {
SubtractExtent(*it);
}
}
void ExtentRanges::AddExtents(const vector<Extent>& extents) {
for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
it != e; ++it) {
AddExtent(*it);
}
}
void ExtentRanges::SubtractExtents(const vector<Extent>& extents) {
for (vector<Extent>::const_iterator it = extents.begin(), e = extents.end();
it != e; ++it) {
SubtractExtent(*it);
}
}
void ExtentRanges::AddRepeatedExtents(
const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
for (int i = 0, e = exts.size(); i != e; ++i) {
AddExtent(exts.Get(i));
}
}
void ExtentRanges::SubtractRepeatedExtents(
const ::google::protobuf::RepeatedPtrField<Extent> &exts) {
for (int i = 0, e = exts.size(); i != e; ++i) {
SubtractExtent(exts.Get(i));
}
}
void ExtentRanges::Dump() const {
LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_;
for (ExtentSet::const_iterator it = extent_set_.begin(),
e = extent_set_.end();
it != e; ++it) {
LOG(INFO) << "{" << it->start_block() << ", " << it->num_blocks() << "}";
}
}
Extent ExtentForRange(uint64_t start_block, uint64_t num_blocks) {
Extent ret;
ret.set_start_block(start_block);
ret.set_num_blocks(num_blocks);
return ret;
}
std::vector<Extent> ExtentRanges::GetExtentsForBlockCount(
uint64_t count) const {
vector<Extent> out;
if (count == 0)
return out;
uint64_t out_blocks = 0;
CHECK(count <= blocks_);
for (ExtentSet::const_iterator it = extent_set_.begin(),
e = extent_set_.end();
it != e; ++it) {
const uint64_t blocks_needed = count - out_blocks;
const Extent& extent = *it;
out.push_back(extent);
out_blocks += extent.num_blocks();
if (extent.num_blocks() < blocks_needed)
continue;
if (extent.num_blocks() == blocks_needed)
break;
// If we get here, we just added the last extent needed, but it's too big
out_blocks -= extent.num_blocks();
out_blocks += blocks_needed;
out.back().set_num_blocks(blocks_needed);
break;
}
return out;
}
} // namespace chromeos_update_engine