/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "SparseBitSet" #include #include #include #include namespace minikin { const uint32_t SparseBitSet::kNotFound; void SparseBitSet::clear() { mMaxVal = 0; if (mOwnIndicesAndBitmaps) { delete[] mIndices; delete[] mBitmaps; mIndexSize = 0; mBitmapSize = 0; mOwnIndicesAndBitmaps = false; } } uint32_t SparseBitSet::calcNumPages(const uint32_t* ranges, size_t nRanges) { bool haveZeroPage = false; uint32_t nonzeroPageEnd = 0; uint32_t nPages = 0; for (size_t i = 0; i < nRanges; i++) { uint32_t start = ranges[i * 2]; uint32_t end = ranges[i * 2 + 1]; uint32_t startPage = start >> kLogValuesPerPage; uint32_t endPage = (end - 1) >> kLogValuesPerPage; if (startPage >= nonzeroPageEnd) { if (startPage > nonzeroPageEnd) { if (!haveZeroPage) { haveZeroPage = true; nPages++; } } nPages++; } nPages += endPage - startPage; nonzeroPageEnd = endPage + 1; } return nPages; } void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) { if (nRanges == 0) { clear(); return; } mMaxVal = ranges[nRanges * 2 - 1]; mIndexSize = (mMaxVal + kPageMask) >> kLogValuesPerPage; uint32_t* indices = new uint32_t[mIndexSize]; uint32_t nPages = calcNumPages(ranges, nRanges); mBitmapSize = nPages << (kLogValuesPerPage - kLogBitsPerEl); element* bitmaps = new element[mBitmapSize]; mOwnIndicesAndBitmaps = true; memset(bitmaps, 0, nPages << (kLogValuesPerPage - 3)); mZeroPageIndex = noZeroPage; uint32_t nonzeroPageEnd = 0; uint32_t currentPage = 0; for (size_t i = 0; i < nRanges; i++) { uint32_t start = ranges[i * 2]; uint32_t end = ranges[i * 2 + 1]; LOG_ALWAYS_FATAL_IF(end < start); // make sure range size is nonnegative uint32_t startPage = start >> kLogValuesPerPage; uint32_t endPage = (end - 1) >> kLogValuesPerPage; if (startPage >= nonzeroPageEnd) { if (startPage > nonzeroPageEnd) { if (mZeroPageIndex == noZeroPage) { mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl); } for (uint32_t j = nonzeroPageEnd; j < startPage; j++) { indices[j] = mZeroPageIndex; } } indices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl); } size_t index = ((currentPage - 1) << (kLogValuesPerPage - kLogBitsPerEl)) + ((start & kPageMask) >> kLogBitsPerEl); size_t nElements = (end - (start & ~kElMask) + kElMask) >> kLogBitsPerEl; if (nElements == 1) { bitmaps[index] |= (kElAllOnes >> (start & kElMask)) & (kElAllOnes << ((~end + 1) & kElMask)); } else { bitmaps[index] |= kElAllOnes >> (start & kElMask); for (size_t j = 1; j < nElements - 1; j++) { bitmaps[index + j] = kElAllOnes; } bitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask); } for (size_t j = startPage + 1; j < endPage + 1; j++) { indices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl); } nonzeroPageEnd = endPage + 1; } mBitmaps = bitmaps; mIndices = indices; } struct SparseBitSetHeader { uint32_t maxValue; uint32_t zeroPageIndex; uint32_t indexSize; uint32_t bitmapSize; }; bool SparseBitSet::initFromBuffer(const uint8_t* data, size_t size) { // No need to be concerned about endianness here since Intel x86 CPUs are little-endian. ARM // CPUs are bi-endian but the endianness is only changeable at reset time and is impossible to // change at runtime. Thus incoming data is guaranteed to have the same endianness as when it // was created. if (data == nullptr || size < sizeof(SparseBitSetHeader)) { clear(); return false; } // The serialized data starts with SparseBitSetHeader. const SparseBitSetHeader* header = reinterpret_cast(data); mMaxVal = header->maxValue; mZeroPageIndex = header->zeroPageIndex; mIndexSize = header->indexSize; mBitmapSize = header->bitmapSize; mOwnIndicesAndBitmaps = false; if (mIndexSize == 0 || mBitmapSize == 0 || mMaxVal == 0) { const bool isValidEmptyBitSet = (mIndexSize == 0 && mBitmapSize == 0 && mMaxVal == 0); if (!isValidEmptyBitSet) { clear(); } return isValidEmptyBitSet; } const size_t indicesSizeInBytes = sizeof(mIndices[0]) * mIndexSize; const size_t bitmapsSizeInBytes = sizeof(mBitmaps[0]) * mBitmapSize; if (size != sizeof(SparseBitSetHeader) + indicesSizeInBytes + bitmapsSizeInBytes) { clear(); return false; } data += sizeof(SparseBitSetHeader); mIndices = reinterpret_cast(data); data += indicesSizeInBytes; mBitmaps = reinterpret_cast(data); return true; } size_t SparseBitSet::writeToBuffer(uint8_t* out) const{ // See comments in SparseBitSet::initFromBuffer for the data structure. const size_t indicesSizeInBytes = sizeof(mIndices[0]) * mIndexSize; const size_t bitmapsSizeInBytes = sizeof(mBitmaps[0]) * mBitmapSize; size_t necessarySize = sizeof(SparseBitSetHeader) + indicesSizeInBytes + bitmapsSizeInBytes; if (out != nullptr) { SparseBitSetHeader* header = reinterpret_cast(out); header->maxValue = mMaxVal; header->zeroPageIndex = mZeroPageIndex; header->indexSize = mIndexSize; header->bitmapSize = mBitmapSize; out += sizeof(SparseBitSetHeader); memcpy(out, mIndices, indicesSizeInBytes); out += indicesSizeInBytes; memcpy(out, mBitmaps, bitmapsSizeInBytes); } return necessarySize; } int SparseBitSet::CountLeadingZeros(element x) { // Note: GCC / clang builtin return sizeof(element) <= sizeof(int) ? __builtin_clz(x) : __builtin_clzl(x); } uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const { if (fromIndex >= mMaxVal) { return kNotFound; } uint32_t fromPage = fromIndex >> kLogValuesPerPage; const element* bitmap = &mBitmaps[mIndices[fromPage]]; uint32_t offset = (fromIndex & kPageMask) >> kLogBitsPerEl; element e = bitmap[offset] & (kElAllOnes >> (fromIndex & kElMask)); if (e != 0) { return (fromIndex & ~kElMask) + CountLeadingZeros(e); } for (uint32_t j = offset + 1; j < (1 << (kLogValuesPerPage - kLogBitsPerEl)); j++) { e = bitmap[j]; if (e != 0) { return (fromIndex & ~kPageMask) + (j << kLogBitsPerEl) + CountLeadingZeros(e); } } uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage; for (uint32_t page = fromPage + 1; page < maxPage; page++) { uint32_t index = mIndices[page]; if (index == mZeroPageIndex) { continue; } bitmap = &mBitmaps[index]; for (uint32_t j = 0; j < (1 << (kLogValuesPerPage - kLogBitsPerEl)); j++) { e = bitmap[j]; if (e != 0) { return (page << kLogValuesPerPage) + (j << kLogBitsPerEl) + CountLeadingZeros(e); } } } return kNotFound; } } // namespace minikin