static void add(Value::Ranges* result, int64_t begin, int64_t end) { if (begin > end) { return; } Value::Range* range = result->add_range(); range->set_begin(begin); range->set_end(end); }
// Coalesce the given 'range' into already coalesced 'ranges'. static void coalesce(Value::Ranges* ranges, const Value::Range& range) { // Note that we assume that ranges has already been coalesced. Value::Ranges result; Value::Range temp = range; for (int i = 0; i < ranges->range_size(); i++) { const Value::Range& current = ranges->range(i); // Check if current and range overlap. Note, we only need to // compare with range and not with temp to check for overlap // because we expect ranges to be coalesced to begin with! if (current.begin() <= range.end() + 1 && current.end() >= range.begin() - 1) { // current: | | // range: | | // range: | | // Update temp with new boundaries. temp.set_begin(min(temp.begin(), min(range.begin(), current.begin()))); temp.set_end(max(temp.end(), max(range.end(), current.end()))); } else { // No overlap. result.add_range()->MergeFrom(current); } } result.add_range()->MergeFrom(temp); *ranges = result; }
TEST(ResourcesTest, ParsingWithRoles) { Resources parse1 = Resources::parse("cpus(role1):2;mem(role1):3").get(); Resource cpus; cpus.set_name("cpus"); cpus.set_type(Value::SCALAR); cpus.mutable_scalar()->set_value(2); cpus.set_role("role1"); Resource mem; mem.set_name("mem"); mem.set_type(Value::SCALAR); mem.mutable_scalar()->set_value(3); mem.set_role("role1"); Resources resources1; resources1 += cpus; resources1 += mem; EXPECT_EQ(parse1, resources1); EXPECT_EQ(resources1, Resources::parse(stringify(resources1)).get()); Resources parse2 = Resources::parse( "cpus(role1):2.5;ports(role2):[0-100]").get(); Resource cpus2; cpus2.set_name("cpus"); cpus2.set_type(Value::SCALAR); cpus2.mutable_scalar()->set_value(2.5); cpus2.set_role("role1"); Resource ports; ports.set_name("ports"); ports.set_type(Value::RANGES); Value::Range* range = ports.mutable_ranges()->add_range(); range->set_begin(0); range->set_end(100); ports.set_role("role2"); Resources resources2; resources2 += ports; resources2 += cpus2; EXPECT_EQ(parse2, resources2); EXPECT_EQ(resources2, Resources::parse(stringify(resources2)).get()); Resources parse3 = Resources::parse( "cpus:2.5;ports(role2):[0-100]", "role1").get(); EXPECT_EQ(parse2, parse3); }
// Subtract `right_` from `left_`, and return the result as Value::Ranges. Value::Ranges subtract(const Value::Ranges& left_, const Value::Ranges& right_) { if (left_.range_size() == 0 || right_.range_size() == 0) { return left_; } // Convert the input `Ranges` to `vector<internal::Range>` and // sort the vector based on the start of a range. auto sortRanges = [](const Value::Ranges& ranges) { vector<internal::Range> result; result.reserve(ranges.range_size()); foreach (const Value::Range& range, ranges.range()) { result.push_back({range.begin(), range.end()}); } std::sort( result.begin(), result.end(), [](const internal::Range& left, const internal::Range& right) { return left.start < right.start; }); return result; }; Value::Ranges result; vector<internal::Range> left = sortRanges(left_); vector<internal::Range> right = sortRanges(right_); vector<internal::Range>::iterator itLeft = left.begin(); for (vector<internal::Range>::const_iterator itRight = right.cbegin(); itLeft != left.end() && itRight != right.cend();) { // Non-overlap: // L: |___| // R: |___| if (itLeft->end < itRight->start) { Value::Range* newRange = result.add_range(); newRange->set_begin(itLeft->start); newRange->set_end(itLeft->end); itLeft++; continue; } // Non-overlap: // L: |___| // R: |___| if (itLeft->start > itRight->end) { itRight++; continue; } if (itLeft->start < itRight->start) { Value::Range* newRange = result.add_range(); newRange->set_begin(itLeft->start); newRange->set_end(itRight->start - 1); if (itLeft->end <= itRight->end) { // L: |_____| // R: |____| itLeft++; } else { // L: |________| // R: |___| itLeft->start = itRight->end + 1; itRight++; } } else { // itLeft->start >= itRight->start if (itLeft->end <= itRight->end) { // L: |____| // R: |________| itLeft++; } else { // L: |_____| // R: |____| itLeft->start = itRight->end + 1; itRight++; } } } // Traverse what's left in the `left`, if any. while (itLeft != left.end()) { // TODO(mzhu): Consider reserving the exact size. Value::Range* newRange = result.add_range(); newRange->set_begin(itLeft->start); newRange->set_end(itLeft->end); itLeft++; } return result; }
static void remove(Value::Ranges* ranges, const Value::Range& range) { // Note that we assume that ranges has already been coalesced. Value::Ranges result; for (int i = 0; i < ranges->range_size(); i++) { const Value::Range& current = ranges->range(i); // Note that these if/else if conditionals are in a particular // order. In particular, the last two assume that the "subsumes" // checks have already occured. if (range.begin() <= current.begin() && range.end() >= current.end()) { // Range subsumes current. // current: | | // range: | | // range: | | // range: | | // range: | | } else if (range.begin() >= current.begin() && range.end() <= current.end()) { // Range is subsumed by current. // current: | | // range: | | // range: | | // range: | | ranges::add(&result, current.begin(), range.begin() - 1); ranges::add(&result, range.end() + 1, current.end()); } else if (range.begin() <= current.begin() && range.end() >= current.begin()) { // Range overlaps to the left. // current: | | // range: | | // range: | | ranges::add(&result, range.end() + 1, current.end()); } else if (range.begin() <= current.end() && range.end() >= current.end()) { // Range overlaps to the right. // current: | | // range: | | // range: | | ranges::add(&result, current.begin(), range.begin() - 1); } else { // Range doesn't overlap current. // current: | | // range: | | // range: | | ranges::add(&result, current.begin(), current.end()); } } *ranges = result; }