| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/dom/time_ranges.h" |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include "base/logging.h" |
| #include "cobalt/dom/dom_exception.h" |
| |
| namespace cobalt { |
| namespace dom { |
| |
| bool TimeRanges::TimeRange::IsOverlapped(const TimeRange& that) const { |
| return end_ >= that.start_ && start_ <= that.end_; |
| } |
| |
| void TimeRanges::TimeRange::MergeWith(const TimeRange& that) { |
| DCHECK(IsOverlapped(that)); |
| start_ = std::min(start_, that.start_); |
| end_ = std::max(end_, that.end_); |
| } |
| |
| double TimeRanges::Start(uint32 index, |
| script::ExceptionState* exception_state) const { |
| if (index >= ranges_.size()) { |
| exception_state->SetException( |
| make_scoped_refptr(new DOMException(DOMException::kIndexSizeErr))); |
| // Return value should be ignored. |
| return 0.0; |
| } |
| return ranges_[index].start(); |
| } |
| |
| double TimeRanges::End(uint32 index, |
| script::ExceptionState* exception_state) const { |
| if (index >= ranges_.size()) { |
| exception_state->SetException( |
| make_scoped_refptr(new DOMException(DOMException::kIndexSizeErr))); |
| // Return value should be ignored. |
| return 0.0; |
| } |
| return ranges_[index].end(); |
| } |
| |
| bool TimeRanges::Contains(double time) const { |
| std::vector<TimeRange>::const_iterator lower_bound = |
| std::lower_bound(ranges_.begin(), ranges_.end(), time, LessThan); |
| return (lower_bound != ranges_.end()) ? lower_bound->Contains(time) : false; |
| } |
| |
| void TimeRanges::Add(double start, double end) { |
| // Use index instead of iterator as we are going to modify ranges_. |
| uint32 lower_bound = static_cast<uint32>( |
| std::lower_bound(ranges_.begin(), ranges_.end(), start, LessThan) - |
| ranges_.begin()); |
| ranges_.insert(ranges_.begin() + lower_bound, TimeRange(start, end)); |
| while (ranges_.size() > lower_bound + 1) { |
| if (!ranges_[lower_bound].IsOverlapped(ranges_[lower_bound + 1])) { |
| break; |
| } |
| ranges_[lower_bound].MergeWith(ranges_[lower_bound + 1]); |
| ranges_.erase(ranges_.begin() + lower_bound + 1); |
| } |
| } |
| |
| double TimeRanges::Nearest(double time) const { |
| // Assume an empty TimeRanges contains [0.0, 0.0] and return 0.0 in this case. |
| if (length() == 0) return 0.0; |
| |
| std::vector<TimeRange>::const_iterator lower_bound = |
| std::lower_bound(ranges_.begin(), ranges_.end(), time, LessThan); |
| |
| // Return the end of last range if time is after all ranges. |
| if (lower_bound == ranges_.end()) { |
| DCHECK_LT((lower_bound - 1)->end(), time); |
| return (lower_bound - 1)->end(); |
| } |
| |
| if (lower_bound->Contains(time)) return time; |
| |
| // Return the start of first range if time is before all ranges. |
| if (lower_bound == ranges_.begin()) { |
| DCHECK_LT(time, lower_bound->start()); |
| return lower_bound->start(); |
| } |
| |
| // time is in between (lower_bound - 1) and lower_bound. |
| double prev = (lower_bound - 1)->end(); |
| double next = lower_bound->start(); |
| DCHECK_LT(prev, time); |
| DCHECK_LT(time, next); |
| return (time - prev <= next - time) ? prev : next; |
| } |
| |
| scoped_refptr<TimeRanges> TimeRanges::Clone() const { |
| scoped_refptr<TimeRanges> time_ranges = new TimeRanges; |
| time_ranges->ranges_ = ranges_; |
| return time_ranges; |
| } |
| |
| scoped_refptr<TimeRanges> TimeRanges::UnionWith( |
| const scoped_refptr<TimeRanges>& that) const { |
| scoped_refptr<TimeRanges> result = this->Clone(); |
| for (uint32 i = 0; i < that->length(); ++i) { |
| result->Add(that->Start(i, NULL), that->End(i, NULL)); |
| } |
| |
| return result; |
| } |
| |
| scoped_refptr<TimeRanges> TimeRanges::IntersectWith( |
| const scoped_refptr<TimeRanges>& that) const { |
| DCHECK(that); |
| |
| if (that == this) { |
| return this->Clone(); |
| } |
| |
| if (this->length() == 0 || that->length() == 0) { |
| return new TimeRanges; |
| } |
| |
| scoped_refptr<TimeRanges> inverted_this = this->Clone()->Invert(); |
| scoped_refptr<TimeRanges> inverted_that = that->Clone()->Invert(); |
| |
| return inverted_this->UnionWith(inverted_that)->Invert(); |
| } |
| |
| scoped_refptr<TimeRanges> TimeRanges::Invert() const { |
| const double kInfinity = std::numeric_limits<double>::infinity(); |
| scoped_refptr<TimeRanges> inverted = new TimeRanges; |
| |
| if (ranges_.empty()) { |
| inverted->Add(-kInfinity, kInfinity); |
| } else { |
| double start = Start(0, NULL); |
| if (start != -kInfinity) { |
| inverted->Add(-kInfinity, start); |
| } |
| |
| for (uint32 i = 0; i + 1 < length(); ++i) { |
| inverted->Add(End(i, NULL), Start(i + 1, NULL)); |
| } |
| |
| double end = End(length() - 1, NULL); |
| if (end != kInfinity) { |
| inverted->Add(end, kInfinity); |
| } |
| } |
| |
| return inverted; |
| } |
| |
| } // namespace dom |
| } // namespace cobalt |