SparseBitSet.cpp revision 77baca2bda0f8d313f48af2df0b397d5de94a132
1/*
2 * Copyright (C) 2012 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#define LOG_TAG "SparseBitSet"
18
19#include <stddef.h>
20#include <string.h>
21
22#include <log/log.h>
23
24#include <minikin/SparseBitSet.h>
25
26namespace minikin {
27
28const uint32_t SparseBitSet::kNotFound;
29
30void SparseBitSet::clear() {
31    mMaxVal = 0;
32    mIndices.reset();
33    mBitmaps.reset();
34}
35
36uint32_t SparseBitSet::calcNumPages(const uint32_t* ranges, size_t nRanges) {
37    bool haveZeroPage = false;
38    uint32_t nonzeroPageEnd = 0;
39    uint32_t nPages = 0;
40    for (size_t i = 0; i < nRanges; i++) {
41        uint32_t start = ranges[i * 2];
42        uint32_t end = ranges[i * 2 + 1];
43        uint32_t startPage = start >> kLogValuesPerPage;
44        uint32_t endPage = (end - 1) >> kLogValuesPerPage;
45        if (startPage >= nonzeroPageEnd) {
46            if (startPage > nonzeroPageEnd) {
47                if (!haveZeroPage) {
48                    haveZeroPage = true;
49                    nPages++;
50                }
51            }
52            nPages++;
53        }
54        nPages += endPage - startPage;
55        nonzeroPageEnd = endPage + 1;
56    }
57    return nPages;
58}
59
60void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
61    if (nRanges == 0) {
62        mMaxVal = 0;
63        mIndices.reset();
64        mBitmaps.reset();
65        return;
66    }
67    mMaxVal = ranges[nRanges * 2 - 1];
68    size_t indexSize = (mMaxVal + kPageMask) >> kLogValuesPerPage;
69    mIndices.reset(new uint32_t[indexSize]);
70    uint32_t nPages = calcNumPages(ranges, nRanges);
71    mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]);
72    memset(mBitmaps.get(), 0, nPages << (kLogValuesPerPage - 3));
73    mZeroPageIndex = noZeroPage;
74    uint32_t nonzeroPageEnd = 0;
75    uint32_t currentPage = 0;
76    for (size_t i = 0; i < nRanges; i++) {
77        uint32_t start = ranges[i * 2];
78        uint32_t end = ranges[i * 2 + 1];
79        LOG_ALWAYS_FATAL_IF(end < start);  // make sure range size is nonnegative
80        uint32_t startPage = start >> kLogValuesPerPage;
81        uint32_t endPage = (end - 1) >> kLogValuesPerPage;
82        if (startPage >= nonzeroPageEnd) {
83            if (startPage > nonzeroPageEnd) {
84                if (mZeroPageIndex == noZeroPage) {
85                    mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
86                }
87                for (uint32_t j = nonzeroPageEnd; j < startPage; j++) {
88                    mIndices[j] = mZeroPageIndex;
89                }
90            }
91            mIndices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
92        }
93
94        size_t index = ((currentPage - 1) << (kLogValuesPerPage - kLogBitsPerEl)) +
95            ((start & kPageMask) >> kLogBitsPerEl);
96        size_t nElements = (end - (start & ~kElMask) + kElMask) >> kLogBitsPerEl;
97        if (nElements == 1) {
98            mBitmaps[index] |= (kElAllOnes >> (start & kElMask)) &
99                (kElAllOnes << ((~end + 1) & kElMask));
100        } else {
101            mBitmaps[index] |= kElAllOnes >> (start & kElMask);
102            for (size_t j = 1; j < nElements - 1; j++) {
103                mBitmaps[index + j] = kElAllOnes;
104            }
105            mBitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
106        }
107        for (size_t j = startPage + 1; j < endPage + 1; j++) {
108            mIndices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
109        }
110        nonzeroPageEnd = endPage + 1;
111    }
112}
113
114int SparseBitSet::CountLeadingZeros(element x) {
115    // Note: GCC / clang builtin
116    return sizeof(element) <= sizeof(int) ? __builtin_clz(x) : __builtin_clzl(x);
117}
118
119uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const {
120    if (fromIndex >= mMaxVal) {
121        return kNotFound;
122    }
123    uint32_t fromPage = fromIndex >> kLogValuesPerPage;
124    const element* bitmap = &mBitmaps[mIndices[fromPage]];
125    uint32_t offset = (fromIndex & kPageMask) >> kLogBitsPerEl;
126    element e = bitmap[offset] & (kElAllOnes >> (fromIndex & kElMask));
127    if (e != 0) {
128        return (fromIndex & ~kElMask) + CountLeadingZeros(e);
129    }
130    for (uint32_t j = offset + 1; j < (1 << (kLogValuesPerPage - kLogBitsPerEl)); j++) {
131        e = bitmap[j];
132        if (e != 0) {
133            return (fromIndex & ~kPageMask) + (j << kLogBitsPerEl) + CountLeadingZeros(e);
134        }
135    }
136    uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage;
137    for (uint32_t page = fromPage + 1; page < maxPage; page++) {
138        uint32_t index = mIndices[page];
139        if (index == mZeroPageIndex) {
140            continue;
141        }
142        bitmap = &mBitmaps[index];
143        for (uint32_t j = 0; j < (1 << (kLogValuesPerPage - kLogBitsPerEl)); j++) {
144            e = bitmap[j];
145            if (e != 0) {
146                return (page << kLogValuesPerPage) + (j << kLogBitsPerEl) + CountLeadingZeros(e);
147            }
148        }
149    }
150    return kNotFound;
151}
152
153}  // namespace minikin
154