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