1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <bitmap/rle-bitmap.h>
6
7 #include <stddef.h>
8
9 #include <zircon/errors.h>
10 #include <zircon/types.h>
11 #include <fbl/algorithm.h>
12 #include <fbl/alloc_checker.h>
13
14 #include <utility>
15
16 namespace bitmap {
17
18 namespace {
19
20 // Allocate a new bitmap element. If *free_list* is null, allocate one using
21 // new. If *free_list* is not null, take one from *free_list*.
AllocateElement(RleBitmap::FreeList * free_list)22 RleBitmapElementPtr AllocateElement(RleBitmap::FreeList* free_list) {
23 if (!free_list) {
24 fbl::AllocChecker ac;
25 RleBitmapElementPtr new_elem(new (&ac) RleBitmapElement());
26 if (!ac.check()) {
27 return RleBitmapElementPtr();
28 }
29 return new_elem;
30 } else {
31 return free_list->pop_front();
32 }
33 }
34
35 // Release the element *elem*. If *free_list* is null, release the element
36 // with delete. If *free_list* is not null, append it to *free_list*.
ReleaseElement(RleBitmap::FreeList * free_list,RleBitmapElementPtr && elem)37 void ReleaseElement(RleBitmap::FreeList* free_list, RleBitmapElementPtr&& elem) {
38 if (free_list) {
39 free_list->push_back(std::move(elem));
40 }
41 }
42
43 } // namespace
44
Find(bool is_set,size_t bitoff,size_t bitmax,size_t run_len,size_t * out) const45 zx_status_t RleBitmap::Find(bool is_set, size_t bitoff, size_t bitmax, size_t run_len, size_t* out)
46 const {
47 *out = bitmax;
48
49 // Loop through all existing elems to try to find a |run_len| length range of |is_set| bits.
50 // On each loop, |bitoff| is guaranteed to be either within the current elem, or in the range
51 // of unset bits leading up to it.
52 // Therefore, we can check whether |run_len| bits between |bitmax| and |bitoff| exist before
53 // the start of the elem (for unset runs), or within the current elem (for set runs).
54 for (const auto& elem : elems_) {
55 if (bitoff >= elem.end()) {
56 continue;
57 } else if (bitmax - bitoff < run_len) {
58 return ZX_ERR_NO_RESOURCES;
59 }
60
61 size_t elem_min = fbl::max(bitoff, elem.bitoff); // Minimum valid bit within elem.
62 size_t elem_max = fbl::min(bitmax, elem.end()); // Maximum valid bit within elem.
63
64 if (is_set && elem_max > elem_min && elem_max - elem_min >= run_len) {
65 // This element contains at least |run_len| bits
66 // which are between |bitoff| and |bitmax|.
67 *out = elem_min;
68 return ZX_OK;
69 }
70
71 if (!is_set && bitoff < elem.bitoff && elem.bitoff - bitoff >= run_len) {
72 // There are at least |run_len| bits between |bitoff| and the beginning of this element.
73 *out = bitoff;
74 return ZX_OK;
75 }
76
77 if (bitmax < elem.end()) {
78 // We have not found a valid run, and the specified range
79 // does not extend past this element.
80 return ZX_ERR_NO_RESOURCES;
81 }
82
83 // Update bitoff to the next value we want to check within the range.
84 bitoff = elem.end();
85 }
86
87
88 if (!is_set && bitmax - bitoff >= run_len) {
89 // We have not found an element with bits > bitoff, which means there is an infinite unset
90 // range starting at bitoff.
91 *out = bitoff;
92 return ZX_OK;
93 }
94
95 return ZX_ERR_NO_RESOURCES;
96 }
97
98
Get(size_t bitoff,size_t bitmax,size_t * first_unset) const99 bool RleBitmap::Get(size_t bitoff, size_t bitmax, size_t* first_unset) const {
100 for (const auto& elem : elems_) {
101 if (bitoff < elem.bitoff) {
102 break;
103 }
104 if (bitoff < elem.bitoff + elem.bitlen) {
105 bitoff = elem.bitoff + elem.bitlen;
106 break;
107 }
108 }
109 if (bitoff > bitmax) {
110 bitoff = bitmax;
111 }
112 if (first_unset) {
113 *first_unset = bitoff;
114 }
115
116 return bitoff == bitmax;
117 }
118
ClearAll()119 void RleBitmap::ClearAll() {
120 elems_.clear();
121 num_elems_ = 0;
122 num_bits_ = 0;
123 }
124
Set(size_t bitoff,size_t bitmax)125 zx_status_t RleBitmap::Set(size_t bitoff, size_t bitmax) {
126 return SetInternal(bitoff, bitmax, nullptr);
127 }
128
SetNoAlloc(size_t bitoff,size_t bitmax,FreeList * free_list)129 zx_status_t RleBitmap::SetNoAlloc(size_t bitoff, size_t bitmax, FreeList* free_list) {
130 if (free_list == nullptr) {
131 return ZX_ERR_INVALID_ARGS;
132 }
133
134 return SetInternal(bitoff, bitmax, free_list);
135 }
136
Clear(size_t bitoff,size_t bitmax)137 zx_status_t RleBitmap::Clear(size_t bitoff, size_t bitmax) {
138 return ClearInternal(bitoff, bitmax, nullptr);
139 }
140
ClearNoAlloc(size_t bitoff,size_t bitmax,FreeList * free_list)141 zx_status_t RleBitmap::ClearNoAlloc(size_t bitoff, size_t bitmax, FreeList* free_list) {
142 if (free_list == nullptr) {
143 return ZX_ERR_INVALID_ARGS;
144 }
145
146 return ClearInternal(bitoff, bitmax, free_list);
147 }
148
SetInternal(size_t bitoff,size_t bitmax,FreeList * free_list)149 zx_status_t RleBitmap::SetInternal(size_t bitoff, size_t bitmax, FreeList* free_list) {
150 if (bitmax < bitoff) {
151 return ZX_ERR_INVALID_ARGS;
152 }
153
154 const size_t bitlen = bitmax - bitoff;
155 if (bitlen == 0) {
156 return ZX_OK;
157 }
158
159 RleBitmapElementPtr new_elem = AllocateElement(free_list);
160 if (!new_elem) {
161 return ZX_ERR_NO_MEMORY;
162 }
163 ++num_elems_;
164 new_elem->bitoff = bitoff;
165 new_elem->bitlen = bitlen;
166
167 auto ends_after = elems_.find_if([bitoff](const RleBitmapElement& elem) -> bool {
168 return elem.bitoff + elem.bitlen >= bitoff;
169 });
170
171 // Insert the new element before the first node that ends at a point >=
172 // when we begin.
173 elems_.insert(ends_after, std::move(new_elem));
174 num_bits_ += bitlen;
175
176 // If ends_after was the end of the list, there is no merging to do.
177 if (ends_after == elems_.end()) {
178 return ZX_OK;
179 }
180
181 auto itr = ends_after;
182 RleBitmapElement& elem = *--ends_after;
183
184 if (elem.bitoff >= itr->bitoff) {
185 // Our range either starts before or in the middle/end of *elem*.
186 // Adjust it so it starts at the same place as *elem*, to allow
187 // the merge logic to not consider this overlap case.
188 elem.bitlen += elem.bitoff - itr->bitoff;
189 num_bits_ += elem.bitoff - itr->bitoff;
190 elem.bitoff = itr->bitoff;
191 }
192
193 // Walk forwards and remove/merge any overlaps
194 size_t max = elem.bitoff + elem.bitlen;
195 while (itr != elems_.end()) {
196 if (itr->bitoff > max) {
197 break;
198 }
199
200 max = fbl::max(max, itr->bitoff + itr->bitlen);
201 num_bits_ += max - elem.bitoff - itr->bitlen - elem.bitlen;
202 elem.bitlen = max - elem.bitoff;
203 auto to_erase = itr;
204 ++itr;
205 ReleaseElement(free_list, elems_.erase(to_erase));
206 --num_elems_;
207 }
208
209 return ZX_OK;
210 }
211
ClearInternal(size_t bitoff,size_t bitmax,FreeList * free_list)212 zx_status_t RleBitmap::ClearInternal(size_t bitoff, size_t bitmax, FreeList* free_list) {
213 if (bitmax < bitoff) {
214 return ZX_ERR_INVALID_ARGS;
215 }
216
217 if (bitmax - bitoff == 0) {
218 return ZX_OK;
219 }
220
221 auto itr = elems_.begin();
222 while (itr != elems_.end()) {
223 if (itr->bitoff + itr->bitlen < bitoff) {
224 ++itr;
225 continue;
226 }
227 if (bitmax < itr->bitoff) {
228 break;
229 }
230 if (itr->bitoff < bitoff) {
231 if (itr->bitoff + itr->bitlen <= bitmax) {
232 // '*itr' contains 'bitoff'.
233 num_bits_ -= (itr->bitlen - (bitoff - itr->bitoff));
234 itr->bitlen = bitoff - itr->bitoff;
235 ++itr;
236 continue;
237 } else {
238 // '*itr' contains [bitoff, bitmax), and we need to split it.
239 RleBitmapElementPtr new_elem = AllocateElement(free_list);
240 if (!new_elem) {
241 return ZX_ERR_NO_MEMORY;
242 }
243 ++num_elems_;
244 new_elem->bitoff = bitmax;
245 new_elem->bitlen = itr->bitoff + itr->bitlen - bitmax;
246
247 elems_.insert_after(itr, std::move(new_elem));
248 itr->bitlen = bitoff - itr->bitoff;
249 num_bits_ -= (bitmax - bitoff);
250 break;
251 }
252 } else {
253 if (bitmax < itr->bitoff + itr->bitlen) {
254 // 'elem' contains 'bitmax'
255 num_bits_ -= (bitmax - itr->bitoff);
256 itr->bitlen = itr->bitoff + itr->bitlen - bitmax;
257 itr->bitoff = bitmax;
258 break;
259 } else {
260 // [bitoff, bitmax) fully contains '*itr'.
261 num_bits_ -= itr->bitlen;
262 auto to_erase = itr++;
263 ReleaseElement(free_list, elems_.erase(to_erase));
264 --num_elems_;
265 }
266 }
267 }
268
269 return ZX_OK;
270 }
271
272 } // namespace bitmap
273