blob: a121a4efc00353b5519b43560b02611b5803b98f [file] [log] [blame]
Tao Bao45685822017-10-13 14:54:12 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "otautil/rangeset.h"
18
19#include <stddef.h>
20
21#include <string>
22#include <utility>
23#include <vector>
24
25#include <android-base/logging.h>
26#include <android-base/parseint.h>
27#include <android-base/stringprintf.h>
28#include <android-base/strings.h>
29
30RangeSet::RangeSet(std::vector<Range>&& pairs) {
31 CHECK_NE(pairs.size(), static_cast<size_t>(0)) << "Invalid number of tokens";
32
33 // Sanity check the input.
34 size_t result = 0;
35 for (const auto& range : pairs) {
36 CHECK_LT(range.first, range.second) << "Empty or negative range: " << range.first << ", "
37 << range.second;
38 size_t sz = range.second - range.first;
39 CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow";
40 result += sz;
41 }
42
43 ranges_ = pairs;
44 blocks_ = result;
45}
46
47RangeSet RangeSet::Parse(const std::string& range_text) {
48 std::vector<std::string> pieces = android::base::Split(range_text, ",");
49 CHECK_GE(pieces.size(), static_cast<size_t>(3)) << "Invalid range text: " << range_text;
50
51 size_t num;
52 CHECK(android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX)))
53 << "Failed to parse the number of tokens: " << range_text;
54
55 CHECK_NE(num, static_cast<size_t>(0)) << "Invalid number of tokens: " << range_text;
56 CHECK_EQ(num % 2, static_cast<size_t>(0)) << "Number of tokens must be even: " << range_text;
57 CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text;
58
59 std::vector<Range> pairs;
60 for (size_t i = 0; i < num; i += 2) {
61 size_t first;
62 CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)));
63 size_t second;
64 CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX)));
65
66 pairs.emplace_back(first, second);
67 }
68
69 return RangeSet(std::move(pairs));
70}
71
72std::string RangeSet::ToString() const {
73 if (ranges_.empty()) {
74 return "";
75 }
76 std::string result = std::to_string(ranges_.size() * 2);
77 for (const auto& r : ranges_) {
78 result += android::base::StringPrintf(",%zu,%zu", r.first, r.second);
79 }
80
81 return result;
82}
83
84// Get the block number for the i-th (starting from 0) block in the RangeSet.
85size_t RangeSet::GetBlockNumber(size_t idx) const {
86 CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")";
87
88 for (const auto& range : ranges_) {
89 if (idx < range.second - range.first) {
90 return range.first + idx;
91 }
92 idx -= (range.second - range.first);
93 }
94
95 CHECK(false) << "Failed to find block number for index " << idx;
96 return 0; // Unreachable, but to make compiler happy.
97}
98
99// RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
100// and "5,7" are not overlapped.
101bool RangeSet::Overlaps(const RangeSet& other) const {
102 for (const auto& range : ranges_) {
103 size_t start = range.first;
104 size_t end = range.second;
105 for (const auto& other_range : other.ranges_) {
106 size_t other_start = other_range.first;
107 size_t other_end = other_range.second;
108 // [start, end) vs [other_start, other_end)
109 if (!(other_start >= end || start >= other_end)) {
110 return true;
111 }
112 }
113 }
114 return false;
115}
116
117static constexpr size_t kBlockSize = 4096;
118
119// Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
120SortedRangeSet::SortedRangeSet(std::vector<Range>&& pairs) : RangeSet(std::move(pairs)) {
121 std::sort(ranges_.begin(), ranges_.end());
122}
123
124void SortedRangeSet::Insert(const Range& to_insert) {
125 SortedRangeSet rs({ to_insert });
126 Insert(rs);
127}
128
129// Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges.
130void SortedRangeSet::Insert(const SortedRangeSet& rs) {
131 if (rs.size() == 0) {
132 return;
133 }
134 // Merge and sort the two RangeSets.
135 std::vector<Range> temp = std::move(ranges_);
136 std::copy(rs.begin(), rs.end(), std::back_inserter(temp));
137 std::sort(temp.begin(), temp.end());
138
139 Clear();
140 // Trim overlaps and insert the result back to ranges_.
141 Range to_insert = temp.front();
142 for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) {
143 if (it->first <= to_insert.second) {
144 to_insert.second = std::max(to_insert.second, it->second);
145 } else {
146 ranges_.push_back(to_insert);
147 blocks_ += (to_insert.second - to_insert.first);
148 to_insert = *it;
149 }
150 }
151 ranges_.push_back(to_insert);
152 blocks_ += (to_insert.second - to_insert.first);
153}
154
155// Compute the block range the file occupies, and insert that range.
156void SortedRangeSet::Insert(size_t start, size_t len) {
157 Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 };
158 Insert(to_insert);
159}
160
161void SortedRangeSet::Clear() {
162 blocks_ = 0;
163 ranges_.clear();
164}
165
166bool SortedRangeSet::Overlaps(size_t start, size_t len) const {
167 RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } });
168 return Overlaps(rs);
169}
170
171// Given an offset of the file, checks if the corresponding block (by considering the file as
172// 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset
173// within this SortedRangeSet.
174//
175// For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte.
176// The mapped offset within a SortedRangeSet("1-9 15-19") is 10.
177//
178// An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th
179// item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10
180// + 10) in a range represented by this SortedRangeSet.
181size_t SortedRangeSet::GetOffsetInRangeSet(size_t old_offset) const {
182 size_t old_block_start = old_offset / kBlockSize;
183 size_t new_block_start = 0;
184 for (const auto& range : ranges_) {
185 // Find the index of old_block_start.
186 if (old_block_start >= range.second) {
187 new_block_start += (range.second - range.first);
188 } else if (old_block_start >= range.first) {
189 new_block_start += (old_block_start - range.first);
190 return (new_block_start * kBlockSize + old_offset % kBlockSize);
191 } else {
192 CHECK(false) << "block_start " << old_block_start
193 << " is missing between two ranges: " << this->ToString();
194 return 0;
195 }
196 }
197 CHECK(false) << "block_start " << old_block_start
198 << " exceeds the limit of current RangeSet: " << this->ToString();
199 return 0;
200}