xref: /aosp_15_r20/bootable/recovery/otautil/rangeset.cpp (revision e7c364b630b241adcb6c7726a21055250b91fdac)
1*e7c364b6SAndroid Build Coastguard Worker /*
2*e7c364b6SAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*e7c364b6SAndroid Build Coastguard Worker  *
4*e7c364b6SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*e7c364b6SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*e7c364b6SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*e7c364b6SAndroid Build Coastguard Worker  *
8*e7c364b6SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*e7c364b6SAndroid Build Coastguard Worker  *
10*e7c364b6SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*e7c364b6SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*e7c364b6SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*e7c364b6SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*e7c364b6SAndroid Build Coastguard Worker  * limitations under the License.
15*e7c364b6SAndroid Build Coastguard Worker  */
16*e7c364b6SAndroid Build Coastguard Worker 
17*e7c364b6SAndroid Build Coastguard Worker #include "otautil/rangeset.h"
18*e7c364b6SAndroid Build Coastguard Worker 
19*e7c364b6SAndroid Build Coastguard Worker #include <limits.h>
20*e7c364b6SAndroid Build Coastguard Worker #include <stddef.h>
21*e7c364b6SAndroid Build Coastguard Worker 
22*e7c364b6SAndroid Build Coastguard Worker #include <algorithm>
23*e7c364b6SAndroid Build Coastguard Worker #include <string>
24*e7c364b6SAndroid Build Coastguard Worker #include <utility>
25*e7c364b6SAndroid Build Coastguard Worker #include <vector>
26*e7c364b6SAndroid Build Coastguard Worker 
27*e7c364b6SAndroid Build Coastguard Worker #include <android-base/logging.h>
28*e7c364b6SAndroid Build Coastguard Worker #include <android-base/parseint.h>
29*e7c364b6SAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
30*e7c364b6SAndroid Build Coastguard Worker #include <android-base/strings.h>
31*e7c364b6SAndroid Build Coastguard Worker 
RangeSet(std::vector<Range> && pairs)32*e7c364b6SAndroid Build Coastguard Worker RangeSet::RangeSet(std::vector<Range>&& pairs) {
33*e7c364b6SAndroid Build Coastguard Worker   blocks_ = 0;
34*e7c364b6SAndroid Build Coastguard Worker   if (pairs.empty()) {
35*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Invalid number of tokens";
36*e7c364b6SAndroid Build Coastguard Worker     return;
37*e7c364b6SAndroid Build Coastguard Worker   }
38*e7c364b6SAndroid Build Coastguard Worker 
39*e7c364b6SAndroid Build Coastguard Worker   for (const auto& range : pairs) {
40*e7c364b6SAndroid Build Coastguard Worker     if (!PushBack(range)) {
41*e7c364b6SAndroid Build Coastguard Worker       Clear();
42*e7c364b6SAndroid Build Coastguard Worker       return;
43*e7c364b6SAndroid Build Coastguard Worker     }
44*e7c364b6SAndroid Build Coastguard Worker   }
45*e7c364b6SAndroid Build Coastguard Worker }
46*e7c364b6SAndroid Build Coastguard Worker 
Parse(const std::string & range_text)47*e7c364b6SAndroid Build Coastguard Worker RangeSet RangeSet::Parse(const std::string& range_text) {
48*e7c364b6SAndroid Build Coastguard Worker   std::vector<std::string> pieces = android::base::Split(range_text, ",");
49*e7c364b6SAndroid Build Coastguard Worker   if (pieces.size() < 3) {
50*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Invalid range text: " << range_text;
51*e7c364b6SAndroid Build Coastguard Worker     return {};
52*e7c364b6SAndroid Build Coastguard Worker   }
53*e7c364b6SAndroid Build Coastguard Worker 
54*e7c364b6SAndroid Build Coastguard Worker   size_t num;
55*e7c364b6SAndroid Build Coastguard Worker   if (!android::base::ParseUint(pieces[0], &num, static_cast<size_t>(INT_MAX))) {
56*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Failed to parse the number of tokens: " << range_text;
57*e7c364b6SAndroid Build Coastguard Worker     return {};
58*e7c364b6SAndroid Build Coastguard Worker   }
59*e7c364b6SAndroid Build Coastguard Worker   if (num == 0) {
60*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Invalid number of tokens: " << range_text;
61*e7c364b6SAndroid Build Coastguard Worker     return {};
62*e7c364b6SAndroid Build Coastguard Worker   }
63*e7c364b6SAndroid Build Coastguard Worker   if (num % 2 != 0) {
64*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Number of tokens must be even: " << range_text;
65*e7c364b6SAndroid Build Coastguard Worker     return {};
66*e7c364b6SAndroid Build Coastguard Worker   }
67*e7c364b6SAndroid Build Coastguard Worker   if (num != pieces.size() - 1) {
68*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Mismatching number of tokens: " << range_text;
69*e7c364b6SAndroid Build Coastguard Worker     return {};
70*e7c364b6SAndroid Build Coastguard Worker   }
71*e7c364b6SAndroid Build Coastguard Worker 
72*e7c364b6SAndroid Build Coastguard Worker   std::vector<Range> pairs;
73*e7c364b6SAndroid Build Coastguard Worker   for (size_t i = 0; i < num; i += 2) {
74*e7c364b6SAndroid Build Coastguard Worker     size_t first;
75*e7c364b6SAndroid Build Coastguard Worker     size_t second;
76*e7c364b6SAndroid Build Coastguard Worker     if (!android::base::ParseUint(pieces[i + 1], &first, static_cast<size_t>(INT_MAX)) ||
77*e7c364b6SAndroid Build Coastguard Worker         !android::base::ParseUint(pieces[i + 2], &second, static_cast<size_t>(INT_MAX))) {
78*e7c364b6SAndroid Build Coastguard Worker       return {};
79*e7c364b6SAndroid Build Coastguard Worker     }
80*e7c364b6SAndroid Build Coastguard Worker     pairs.emplace_back(first, second);
81*e7c364b6SAndroid Build Coastguard Worker   }
82*e7c364b6SAndroid Build Coastguard Worker   return RangeSet(std::move(pairs));
83*e7c364b6SAndroid Build Coastguard Worker }
84*e7c364b6SAndroid Build Coastguard Worker 
PushBack(Range range)85*e7c364b6SAndroid Build Coastguard Worker bool RangeSet::PushBack(Range range) {
86*e7c364b6SAndroid Build Coastguard Worker   if (range.first >= range.second) {
87*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Empty or negative range: " << range.first << ", " << range.second;
88*e7c364b6SAndroid Build Coastguard Worker     return false;
89*e7c364b6SAndroid Build Coastguard Worker   }
90*e7c364b6SAndroid Build Coastguard Worker   size_t sz = range.second - range.first;
91*e7c364b6SAndroid Build Coastguard Worker   if (blocks_ >= SIZE_MAX - sz) {
92*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "RangeSet size overflow";
93*e7c364b6SAndroid Build Coastguard Worker     return false;
94*e7c364b6SAndroid Build Coastguard Worker   }
95*e7c364b6SAndroid Build Coastguard Worker 
96*e7c364b6SAndroid Build Coastguard Worker   ranges_.push_back(std::move(range));
97*e7c364b6SAndroid Build Coastguard Worker   blocks_ += sz;
98*e7c364b6SAndroid Build Coastguard Worker   return true;
99*e7c364b6SAndroid Build Coastguard Worker }
100*e7c364b6SAndroid Build Coastguard Worker 
Clear()101*e7c364b6SAndroid Build Coastguard Worker void RangeSet::Clear() {
102*e7c364b6SAndroid Build Coastguard Worker   ranges_.clear();
103*e7c364b6SAndroid Build Coastguard Worker   blocks_ = 0;
104*e7c364b6SAndroid Build Coastguard Worker }
105*e7c364b6SAndroid Build Coastguard Worker 
Split(size_t groups) const106*e7c364b6SAndroid Build Coastguard Worker std::vector<RangeSet> RangeSet::Split(size_t groups) const {
107*e7c364b6SAndroid Build Coastguard Worker   if (ranges_.empty() || groups == 0) return {};
108*e7c364b6SAndroid Build Coastguard Worker 
109*e7c364b6SAndroid Build Coastguard Worker   if (blocks_ < groups) {
110*e7c364b6SAndroid Build Coastguard Worker     groups = blocks_;
111*e7c364b6SAndroid Build Coastguard Worker   }
112*e7c364b6SAndroid Build Coastguard Worker 
113*e7c364b6SAndroid Build Coastguard Worker   // Evenly distribute blocks, with the first few groups possibly containing one more.
114*e7c364b6SAndroid Build Coastguard Worker   size_t mean = blocks_ / groups;
115*e7c364b6SAndroid Build Coastguard Worker   std::vector<size_t> blocks_per_group(groups, mean);
116*e7c364b6SAndroid Build Coastguard Worker   std::fill_n(blocks_per_group.begin(), blocks_ % groups, mean + 1);
117*e7c364b6SAndroid Build Coastguard Worker 
118*e7c364b6SAndroid Build Coastguard Worker   std::vector<RangeSet> result;
119*e7c364b6SAndroid Build Coastguard Worker 
120*e7c364b6SAndroid Build Coastguard Worker   // Forward iterate Ranges and fill up each group with the desired number of blocks.
121*e7c364b6SAndroid Build Coastguard Worker   auto it = ranges_.cbegin();
122*e7c364b6SAndroid Build Coastguard Worker   Range range = *it;
123*e7c364b6SAndroid Build Coastguard Worker   for (const auto& blocks : blocks_per_group) {
124*e7c364b6SAndroid Build Coastguard Worker     RangeSet buffer;
125*e7c364b6SAndroid Build Coastguard Worker     size_t needed = blocks;
126*e7c364b6SAndroid Build Coastguard Worker     while (needed > 0) {
127*e7c364b6SAndroid Build Coastguard Worker       size_t range_blocks = range.second - range.first;
128*e7c364b6SAndroid Build Coastguard Worker       if (range_blocks > needed) {
129*e7c364b6SAndroid Build Coastguard Worker         // Split the current range and don't advance the iterator.
130*e7c364b6SAndroid Build Coastguard Worker         buffer.PushBack({ range.first, range.first + needed });
131*e7c364b6SAndroid Build Coastguard Worker         range.first = range.first + needed;
132*e7c364b6SAndroid Build Coastguard Worker         break;
133*e7c364b6SAndroid Build Coastguard Worker       }
134*e7c364b6SAndroid Build Coastguard Worker       buffer.PushBack(range);
135*e7c364b6SAndroid Build Coastguard Worker       it++;
136*e7c364b6SAndroid Build Coastguard Worker       if (it != ranges_.cend()) {
137*e7c364b6SAndroid Build Coastguard Worker         range = *it;
138*e7c364b6SAndroid Build Coastguard Worker       }
139*e7c364b6SAndroid Build Coastguard Worker       needed -= range_blocks;
140*e7c364b6SAndroid Build Coastguard Worker     }
141*e7c364b6SAndroid Build Coastguard Worker     result.push_back(std::move(buffer));
142*e7c364b6SAndroid Build Coastguard Worker   }
143*e7c364b6SAndroid Build Coastguard Worker   return result;
144*e7c364b6SAndroid Build Coastguard Worker }
145*e7c364b6SAndroid Build Coastguard Worker 
ToString() const146*e7c364b6SAndroid Build Coastguard Worker std::string RangeSet::ToString() const {
147*e7c364b6SAndroid Build Coastguard Worker   if (ranges_.empty()) {
148*e7c364b6SAndroid Build Coastguard Worker     return "";
149*e7c364b6SAndroid Build Coastguard Worker   }
150*e7c364b6SAndroid Build Coastguard Worker   std::string result = std::to_string(ranges_.size() * 2);
151*e7c364b6SAndroid Build Coastguard Worker   for (const auto& [begin, end] : ranges_) {
152*e7c364b6SAndroid Build Coastguard Worker     result += android::base::StringPrintf(",%zu,%zu", begin, end);
153*e7c364b6SAndroid Build Coastguard Worker   }
154*e7c364b6SAndroid Build Coastguard Worker 
155*e7c364b6SAndroid Build Coastguard Worker   return result;
156*e7c364b6SAndroid Build Coastguard Worker }
157*e7c364b6SAndroid Build Coastguard Worker 
158*e7c364b6SAndroid Build Coastguard Worker // Get the block number for the i-th (starting from 0) block in the RangeSet.
GetBlockNumber(size_t idx) const159*e7c364b6SAndroid Build Coastguard Worker size_t RangeSet::GetBlockNumber(size_t idx) const {
160*e7c364b6SAndroid Build Coastguard Worker   CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")";
161*e7c364b6SAndroid Build Coastguard Worker 
162*e7c364b6SAndroid Build Coastguard Worker   for (const auto& [begin, end] : ranges_) {
163*e7c364b6SAndroid Build Coastguard Worker     if (idx < end - begin) {
164*e7c364b6SAndroid Build Coastguard Worker       return begin + idx;
165*e7c364b6SAndroid Build Coastguard Worker     }
166*e7c364b6SAndroid Build Coastguard Worker     idx -= (end - begin);
167*e7c364b6SAndroid Build Coastguard Worker   }
168*e7c364b6SAndroid Build Coastguard Worker 
169*e7c364b6SAndroid Build Coastguard Worker   CHECK(false) << "Failed to find block number for index " << idx;
170*e7c364b6SAndroid Build Coastguard Worker   return 0;  // Unreachable, but to make compiler happy.
171*e7c364b6SAndroid Build Coastguard Worker }
172*e7c364b6SAndroid Build Coastguard Worker 
173*e7c364b6SAndroid Build Coastguard Worker // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5"
174*e7c364b6SAndroid Build Coastguard Worker // and "5,7" are not overlapped.
Overlaps(const RangeSet & other) const175*e7c364b6SAndroid Build Coastguard Worker bool RangeSet::Overlaps(const RangeSet& other) const {
176*e7c364b6SAndroid Build Coastguard Worker   for (const auto& [begin, end] : ranges_) {
177*e7c364b6SAndroid Build Coastguard Worker     for (const auto& [other_begin, other_end] : other.ranges_) {
178*e7c364b6SAndroid Build Coastguard Worker       // [begin, end) vs [other_begin, other_end)
179*e7c364b6SAndroid Build Coastguard Worker       if (!(other_begin >= end || begin >= other_end)) {
180*e7c364b6SAndroid Build Coastguard Worker         return true;
181*e7c364b6SAndroid Build Coastguard Worker       }
182*e7c364b6SAndroid Build Coastguard Worker     }
183*e7c364b6SAndroid Build Coastguard Worker   }
184*e7c364b6SAndroid Build Coastguard Worker   return false;
185*e7c364b6SAndroid Build Coastguard Worker }
186*e7c364b6SAndroid Build Coastguard Worker 
GetSubRanges(size_t start_index,size_t num_of_blocks) const187*e7c364b6SAndroid Build Coastguard Worker std::optional<RangeSet> RangeSet::GetSubRanges(size_t start_index, size_t num_of_blocks) const {
188*e7c364b6SAndroid Build Coastguard Worker   size_t end_index = start_index + num_of_blocks;  // The index of final block to read plus one
189*e7c364b6SAndroid Build Coastguard Worker   if (start_index > end_index || end_index > blocks_) {
190*e7c364b6SAndroid Build Coastguard Worker     LOG(ERROR) << "Failed to get the sub ranges for start_index " << start_index
191*e7c364b6SAndroid Build Coastguard Worker                << " num_of_blocks " << num_of_blocks
192*e7c364b6SAndroid Build Coastguard Worker                << " total number of blocks the range contains is " << blocks_;
193*e7c364b6SAndroid Build Coastguard Worker     return std::nullopt;
194*e7c364b6SAndroid Build Coastguard Worker   }
195*e7c364b6SAndroid Build Coastguard Worker 
196*e7c364b6SAndroid Build Coastguard Worker   if (num_of_blocks == 0) {
197*e7c364b6SAndroid Build Coastguard Worker     LOG(WARNING) << "num_of_blocks is zero when calling GetSubRanges()";
198*e7c364b6SAndroid Build Coastguard Worker     return RangeSet();
199*e7c364b6SAndroid Build Coastguard Worker   }
200*e7c364b6SAndroid Build Coastguard Worker 
201*e7c364b6SAndroid Build Coastguard Worker   RangeSet result;
202*e7c364b6SAndroid Build Coastguard Worker   size_t current_index = 0;
203*e7c364b6SAndroid Build Coastguard Worker   for (const auto& [range_start, range_end] : ranges_) {
204*e7c364b6SAndroid Build Coastguard Worker     CHECK_LT(range_start, range_end);
205*e7c364b6SAndroid Build Coastguard Worker     size_t blocks_in_range = range_end - range_start;
206*e7c364b6SAndroid Build Coastguard Worker     // Linear search to skip the ranges until we reach start_block.
207*e7c364b6SAndroid Build Coastguard Worker     if (current_index + blocks_in_range <= start_index) {
208*e7c364b6SAndroid Build Coastguard Worker       current_index += blocks_in_range;
209*e7c364b6SAndroid Build Coastguard Worker       continue;
210*e7c364b6SAndroid Build Coastguard Worker     }
211*e7c364b6SAndroid Build Coastguard Worker 
212*e7c364b6SAndroid Build Coastguard Worker     size_t trimmed_range_start = range_start;
213*e7c364b6SAndroid Build Coastguard Worker     // We have found the first block range to read, trim the heading blocks.
214*e7c364b6SAndroid Build Coastguard Worker     if (current_index < start_index) {
215*e7c364b6SAndroid Build Coastguard Worker       trimmed_range_start += start_index - current_index;
216*e7c364b6SAndroid Build Coastguard Worker     }
217*e7c364b6SAndroid Build Coastguard Worker     // Trim the trailing blocks if the last range has more blocks than desired; also return the
218*e7c364b6SAndroid Build Coastguard Worker     // result.
219*e7c364b6SAndroid Build Coastguard Worker     if (current_index + blocks_in_range >= end_index) {
220*e7c364b6SAndroid Build Coastguard Worker       size_t trimmed_range_end = range_end - (current_index + blocks_in_range - end_index);
221*e7c364b6SAndroid Build Coastguard Worker       if (!result.PushBack({ trimmed_range_start, trimmed_range_end })) {
222*e7c364b6SAndroid Build Coastguard Worker         return std::nullopt;
223*e7c364b6SAndroid Build Coastguard Worker       }
224*e7c364b6SAndroid Build Coastguard Worker 
225*e7c364b6SAndroid Build Coastguard Worker       return result;
226*e7c364b6SAndroid Build Coastguard Worker     }
227*e7c364b6SAndroid Build Coastguard Worker 
228*e7c364b6SAndroid Build Coastguard Worker     if (!result.PushBack({ trimmed_range_start, range_end })) {
229*e7c364b6SAndroid Build Coastguard Worker       return std::nullopt;
230*e7c364b6SAndroid Build Coastguard Worker     }
231*e7c364b6SAndroid Build Coastguard Worker     current_index += blocks_in_range;
232*e7c364b6SAndroid Build Coastguard Worker   }
233*e7c364b6SAndroid Build Coastguard Worker 
234*e7c364b6SAndroid Build Coastguard Worker   LOG(ERROR) << "Failed to construct byte ranges to read, start_block: " << start_index
235*e7c364b6SAndroid Build Coastguard Worker              << ", num_of_blocks: " << num_of_blocks << " total number of blocks: " << blocks_;
236*e7c364b6SAndroid Build Coastguard Worker   return std::nullopt;
237*e7c364b6SAndroid Build Coastguard Worker }
238*e7c364b6SAndroid Build Coastguard Worker 
239*e7c364b6SAndroid Build Coastguard Worker // Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
SortedRangeSet(std::vector<Range> && pairs)240*e7c364b6SAndroid Build Coastguard Worker SortedRangeSet::SortedRangeSet(std::vector<Range>&& pairs) : RangeSet(std::move(pairs)) {
241*e7c364b6SAndroid Build Coastguard Worker   std::sort(ranges_.begin(), ranges_.end());
242*e7c364b6SAndroid Build Coastguard Worker }
243*e7c364b6SAndroid Build Coastguard Worker 
Insert(const Range & to_insert)244*e7c364b6SAndroid Build Coastguard Worker void SortedRangeSet::Insert(const Range& to_insert) {
245*e7c364b6SAndroid Build Coastguard Worker   SortedRangeSet rs({ to_insert });
246*e7c364b6SAndroid Build Coastguard Worker   Insert(rs);
247*e7c364b6SAndroid Build Coastguard Worker }
248*e7c364b6SAndroid Build Coastguard Worker 
249*e7c364b6SAndroid Build Coastguard Worker // Insert the input SortedRangeSet; keep the ranges sorted and merge the overlap ranges.
Insert(const SortedRangeSet & rs)250*e7c364b6SAndroid Build Coastguard Worker void SortedRangeSet::Insert(const SortedRangeSet& rs) {
251*e7c364b6SAndroid Build Coastguard Worker   if (rs.size() == 0) {
252*e7c364b6SAndroid Build Coastguard Worker     return;
253*e7c364b6SAndroid Build Coastguard Worker   }
254*e7c364b6SAndroid Build Coastguard Worker   // Merge and sort the two RangeSets.
255*e7c364b6SAndroid Build Coastguard Worker   std::vector<Range> temp = std::move(ranges_);
256*e7c364b6SAndroid Build Coastguard Worker   std::copy(rs.begin(), rs.end(), std::back_inserter(temp));
257*e7c364b6SAndroid Build Coastguard Worker   std::sort(temp.begin(), temp.end());
258*e7c364b6SAndroid Build Coastguard Worker 
259*e7c364b6SAndroid Build Coastguard Worker   Clear();
260*e7c364b6SAndroid Build Coastguard Worker   // Trim overlaps and insert the result back to ranges_.
261*e7c364b6SAndroid Build Coastguard Worker   Range to_insert = temp.front();
262*e7c364b6SAndroid Build Coastguard Worker   for (auto it = temp.cbegin() + 1; it != temp.cend(); it++) {
263*e7c364b6SAndroid Build Coastguard Worker     if (it->first <= to_insert.second) {
264*e7c364b6SAndroid Build Coastguard Worker       to_insert.second = std::max(to_insert.second, it->second);
265*e7c364b6SAndroid Build Coastguard Worker     } else {
266*e7c364b6SAndroid Build Coastguard Worker       ranges_.push_back(to_insert);
267*e7c364b6SAndroid Build Coastguard Worker       blocks_ += (to_insert.second - to_insert.first);
268*e7c364b6SAndroid Build Coastguard Worker       to_insert = *it;
269*e7c364b6SAndroid Build Coastguard Worker     }
270*e7c364b6SAndroid Build Coastguard Worker   }
271*e7c364b6SAndroid Build Coastguard Worker   ranges_.push_back(to_insert);
272*e7c364b6SAndroid Build Coastguard Worker   blocks_ += (to_insert.second - to_insert.first);
273*e7c364b6SAndroid Build Coastguard Worker }
274*e7c364b6SAndroid Build Coastguard Worker 
275*e7c364b6SAndroid Build Coastguard Worker // Compute the block range the file occupies, and insert that range.
Insert(size_t start,size_t len)276*e7c364b6SAndroid Build Coastguard Worker void SortedRangeSet::Insert(size_t start, size_t len) {
277*e7c364b6SAndroid Build Coastguard Worker   Range to_insert{ start / kBlockSize, (start + len - 1) / kBlockSize + 1 };
278*e7c364b6SAndroid Build Coastguard Worker   Insert(to_insert);
279*e7c364b6SAndroid Build Coastguard Worker }
280*e7c364b6SAndroid Build Coastguard Worker 
Overlaps(size_t start,size_t len) const281*e7c364b6SAndroid Build Coastguard Worker bool SortedRangeSet::Overlaps(size_t start, size_t len) const {
282*e7c364b6SAndroid Build Coastguard Worker   RangeSet rs({ { start / kBlockSize, (start + len - 1) / kBlockSize + 1 } });
283*e7c364b6SAndroid Build Coastguard Worker   return Overlaps(rs);
284*e7c364b6SAndroid Build Coastguard Worker }
285*e7c364b6SAndroid Build Coastguard Worker 
286*e7c364b6SAndroid Build Coastguard Worker // Given an offset of the file, checks if the corresponding block (by considering the file as
287*e7c364b6SAndroid Build Coastguard Worker // 0-based continuous block ranges) is covered by the SortedRangeSet. If so, returns the offset
288*e7c364b6SAndroid Build Coastguard Worker // within this SortedRangeSet.
289*e7c364b6SAndroid Build Coastguard Worker //
290*e7c364b6SAndroid Build Coastguard Worker // For example, the 4106-th byte of a file is from block 1, assuming a block size of 4096-byte.
291*e7c364b6SAndroid Build Coastguard Worker // The mapped offset within a SortedRangeSet("1-9 15-19") is 10.
292*e7c364b6SAndroid Build Coastguard Worker //
293*e7c364b6SAndroid Build Coastguard Worker // An offset of 65546 falls into the 16-th block in a file. Block 16 is contained as the 10-th
294*e7c364b6SAndroid Build Coastguard Worker // item in SortedRangeSet("1-9 15-19"). So its data can be found at offset 40970 (i.e. 4096 * 10
295*e7c364b6SAndroid Build Coastguard Worker // + 10) in a range represented by this SortedRangeSet.
GetOffsetInRangeSet(size_t old_offset) const296*e7c364b6SAndroid Build Coastguard Worker size_t SortedRangeSet::GetOffsetInRangeSet(size_t old_offset) const {
297*e7c364b6SAndroid Build Coastguard Worker   size_t old_block_start = old_offset / kBlockSize;
298*e7c364b6SAndroid Build Coastguard Worker   size_t new_block_start = 0;
299*e7c364b6SAndroid Build Coastguard Worker   for (const auto& [start, end] : ranges_) {
300*e7c364b6SAndroid Build Coastguard Worker     // Find the index of old_block_start.
301*e7c364b6SAndroid Build Coastguard Worker     if (old_block_start >= end) {
302*e7c364b6SAndroid Build Coastguard Worker       new_block_start += (end - start);
303*e7c364b6SAndroid Build Coastguard Worker     } else if (old_block_start >= start) {
304*e7c364b6SAndroid Build Coastguard Worker       new_block_start += (old_block_start - start);
305*e7c364b6SAndroid Build Coastguard Worker       return (new_block_start * kBlockSize + old_offset % kBlockSize);
306*e7c364b6SAndroid Build Coastguard Worker     } else {
307*e7c364b6SAndroid Build Coastguard Worker       CHECK(false) << "block_start " << old_block_start
308*e7c364b6SAndroid Build Coastguard Worker                    << " is missing between two ranges: " << ToString();
309*e7c364b6SAndroid Build Coastguard Worker       return 0;
310*e7c364b6SAndroid Build Coastguard Worker     }
311*e7c364b6SAndroid Build Coastguard Worker   }
312*e7c364b6SAndroid Build Coastguard Worker   CHECK(false) << "block_start " << old_block_start
313*e7c364b6SAndroid Build Coastguard Worker                << " exceeds the limit of current RangeSet: " << ToString();
314*e7c364b6SAndroid Build Coastguard Worker   return 0;
315*e7c364b6SAndroid Build Coastguard Worker }
316