flutter_flutter/libs/minikin/SparseBitSet.cpp
Seigo Nonaka fbbc5a6b36 Make SparseBitSet serializable.
To share the calculated coverage information across the processes, make
SparseBitSet serializable.

Bug: 34042446
Test: minikin_tests passes
Change-Id: I0463138adcf234739bb3ce1cdadf382021921f3e
2017-03-13 14:07:55 -07:00

228 lines
8.0 KiB
C++

/*
* 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 <stddef.h>
#include <string.h>
#include <log/log.h>
#include <minikin/SparseBitSet.h>
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<const SparseBitSetHeader*>(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<decltype(mIndices)>(data);
data += indicesSizeInBytes;
mBitmaps = reinterpret_cast<decltype(mBitmaps)>(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<SparseBitSetHeader*>(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