提交 fbbc5a6b 编写于 作者: S Seigo Nonaka

Make SparseBitSet serializable.

To share the calculated coverage information across the processes, make
SparseBitSet serializable.

Bug: 34042446
Test: minikin_tests passes
Change-Id: I0463138adcf234739bb3ce1cdadf382021921f3e
上级 d78f260a
......@@ -128,8 +128,23 @@ public:
FontFamily(int variant, std::vector<Font>&& fonts);
FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts);
// The accelerator table won't be copied. Do not release the memory until the created FontFamily
// is destructed.
FontFamily(std::vector<Font>&& fonts, const uint8_t* acceleratorTable, size_t tableSize);
FontFamily(int variant, std::vector<Font>&& fonts, const uint8_t* acceleratorTable,
size_t tableSize);
FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts,
const uint8_t* acceleratorTable, size_t tableSize);
~FontFamily();
// Writes internal accelerator tables into the 'out' buffer.
//
// This method returns the number of bytes written to the buffer. By calling the method with
// 'out' set to nullptr, the method just returns the size needed, which the caller can then use
// for allocating a buffer for a second call.
size_t writeAcceleratorTable(uint8_t* out) const;
// TODO: Good to expose FontUtil.h.
static bool analyzeStyle(const std::shared_ptr<MinikinFont>& typeface, int* weight,
bool* italic);
......@@ -164,6 +179,8 @@ public:
private:
void computeCoverage();
void readAcceleratorTable(const uint8_t* data, size_t size);
uint32_t mLangId;
int mVariant;
std::vector<Font> mFonts;
......
......@@ -34,7 +34,7 @@ namespace minikin {
class SparseBitSet {
public:
SparseBitSet(): mMaxVal(0) {
SparseBitSet(): mMaxVal(0), mOwnIndicesAndBitmaps(false) {
}
// Clear the set
......@@ -45,10 +45,21 @@ public:
// inclusive of start, exclusive of end, laid out in a uint32 array.
void initFromRanges(const uint32_t* ranges, size_t nRanges);
// Initializes the set with pre-calculted data. Returns false if the serialized data is invalid.
// Even if this function returns false, the internal data is cleared.
bool initFromBuffer(const uint8_t* data, size_t size);
// Serialize the set and write into out.
//
// This method returns the number of bytes written to the buffer. By calling the method with
// 'out' set to nullptr, the method just returns the size needed, which the caller can then use
// for allocating a buffer for a second call.
size_t writeToBuffer(uint8_t* out) const;
// Determine whether the value is included in the set
bool get(uint32_t ch) const {
if (ch >= mMaxVal) return false;
uint32_t *bitmap = &mBitmaps[mIndices[ch >> kLogValuesPerPage]];
const uint32_t *bitmap = &mBitmaps[mIndices[ch >> kLogValuesPerPage]];
uint32_t index = ch & kPageMask;
return (bitmap[index >> kLogBitsPerEl] & (kElFirst >> (index & kElMask))) != 0;
}
......@@ -80,8 +91,14 @@ private:
static int CountLeadingZeros(element x);
uint32_t mMaxVal;
std::unique_ptr<uint32_t[]> mIndices;
std::unique_ptr<element[]> mBitmaps;
// True if this SparseBitSet is responsible for freeing mIndices and mBitamps.
bool mOwnIndicesAndBitmaps;
uint32_t mIndexSize;
const uint32_t* mIndices;
uint32_t mBitmapSize;
const element* mBitmaps;
uint32_t mZeroPageIndex;
};
......
......@@ -112,6 +112,22 @@ FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts)
computeCoverage();
}
FontFamily::FontFamily(std::vector<Font>&& fonts, const uint8_t* acceleratorTable, size_t tableSize)
: FontFamily(0 /* variant */, std::move(fonts), acceleratorTable, tableSize) {
}
FontFamily::FontFamily(int variant, std::vector<Font>&& fonts, const uint8_t* acceleratorTable,
size_t tableSize)
: FontFamily(FontLanguageListCache::kEmptyListId, variant, std::move(fonts), acceleratorTable,
tableSize) {
}
FontFamily::FontFamily(uint32_t langId, int variant, std::vector<Font>&& fonts,
const uint8_t* acceleratorTable, size_t tableSize)
: mLangId(langId), mVariant(variant), mFonts(std::move(fonts)), mHasVSTable(false) {
readAcceleratorTable(acceleratorTable, tableSize);
}
FontFamily::~FontFamily() {
}
......@@ -247,4 +263,12 @@ std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
return std::shared_ptr<FontFamily>(new FontFamily(mLangId, mVariant, std::move(fonts)));
}
size_t FontFamily::writeAcceleratorTable(uint8_t* out) const {
return mCoverage.writeToBuffer(out);
}
void FontFamily::readAcceleratorTable(const uint8_t* data, size_t size) {
bool result = mCoverage.initFromBuffer(data, size);
LOG_ALWAYS_FATAL_IF(!result, "Failed to reconstruct accelerator table from buffer");
}
} // namespace minikin
......@@ -29,8 +29,13 @@ const uint32_t SparseBitSet::kNotFound;
void SparseBitSet::clear() {
mMaxVal = 0;
mIndices.reset();
mBitmaps.reset();
if (mOwnIndicesAndBitmaps) {
delete[] mIndices;
delete[] mBitmaps;
mIndexSize = 0;
mBitmapSize = 0;
mOwnIndicesAndBitmaps = false;
}
}
uint32_t SparseBitSet::calcNumPages(const uint32_t* ranges, size_t nRanges) {
......@@ -59,17 +64,17 @@ uint32_t SparseBitSet::calcNumPages(const uint32_t* ranges, size_t nRanges) {
void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
if (nRanges == 0) {
mMaxVal = 0;
mIndices.reset();
mBitmaps.reset();
clear();
return;
}
mMaxVal = ranges[nRanges * 2 - 1];
size_t indexSize = (mMaxVal + kPageMask) >> kLogValuesPerPage;
mIndices.reset(new uint32_t[indexSize]);
mIndexSize = (mMaxVal + kPageMask) >> kLogValuesPerPage;
uint32_t* indices = new uint32_t[mIndexSize];
uint32_t nPages = calcNumPages(ranges, nRanges);
mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]);
memset(mBitmaps.get(), 0, nPages << (kLogValuesPerPage - 3));
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;
......@@ -85,30 +90,99 @@ void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
}
for (uint32_t j = nonzeroPageEnd; j < startPage; j++) {
mIndices[j] = mZeroPageIndex;
indices[j] = mZeroPageIndex;
}
}
mIndices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
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) {
mBitmaps[index] |= (kElAllOnes >> (start & kElMask)) &
bitmaps[index] |= (kElAllOnes >> (start & kElMask)) &
(kElAllOnes << ((~end + 1) & kElMask));
} else {
mBitmaps[index] |= kElAllOnes >> (start & kElMask);
bitmaps[index] |= kElAllOnes >> (start & kElMask);
for (size_t j = 1; j < nElements - 1; j++) {
mBitmaps[index + j] = kElAllOnes;
bitmaps[index + j] = kElAllOnes;
}
mBitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
bitmaps[index + nElements - 1] |= kElAllOnes << ((~end + 1) & kElMask);
}
for (size_t j = startPage + 1; j < endPage + 1; j++) {
mIndices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);
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) {
......
......@@ -22,6 +22,7 @@ perftest_src_files := \
../util/MinikinFontForTest.cpp \
../util/UnicodeUtils.cpp \
FontCollection.cpp \
FontFamily.cpp \
FontLanguage.cpp \
GraphemeBreak.cpp \
Hyphenator.cpp \
......
/*
* Copyright (C) 2017 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.
*/
#include <benchmark/benchmark.h>
#include <minikin/FontFamily.h>
#include "../util/MinikinFontForTest.h"
namespace minikin {
static void BM_FontFamily_create(benchmark::State& state) {
std::shared_ptr<MinikinFontForTest> minikinFont =
std::make_shared<MinikinFontForTest>("/system/fonts/NotoSansCJK-Regular.ttc", 0);
while (state.KeepRunning()) {
std::shared_ptr<FontFamily> family = std::make_shared<FontFamily>(
std::vector<Font>({Font(minikinFont, FontStyle())}));
}
}
BENCHMARK(BM_FontFamily_create);
static void BM_FontFamily_create_fromBuffer(benchmark::State& state) {
std::shared_ptr<MinikinFontForTest> minikinFont =
std::make_shared<MinikinFontForTest>("/system/fonts/NotoSansCJK-Regular.ttc", 0);
std::shared_ptr<FontFamily> family = std::make_shared<FontFamily>(
std::vector<Font>({Font(minikinFont, FontStyle())}));
size_t bufSize = family->writeAcceleratorTable(nullptr);
std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufSize]);
family->writeAcceleratorTable(buffer.get());
while (state.KeepRunning()) {
std::shared_ptr<FontFamily> family = std::make_shared<FontFamily>(
std::vector<Font>({Font(minikinFont, FontStyle())}), buffer.get(), bufSize);
}
}
BENCHMARK(BM_FontFamily_create_fromBuffer);
} // namespace minikin
......@@ -76,6 +76,7 @@ LOCAL_SRC_FILES += \
GraphemeBreakTests.cpp \
LayoutTest.cpp \
LayoutUtilsTest.cpp \
SparseBitSetTest.cpp \
UnicodeUtilsTest.cpp \
WordBreakerTests.cpp
......
/*
* Copyright (C) 2017 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.
*/
#include <random>
#include <gtest/gtest.h>
#include <minikin/SparseBitSet.h>
namespace minikin {
TEST(SparseBitSetTest, randomTest) {
const uint32_t kTestRangeNum = 4096;
std::mt19937 mt; // Fix seeds to be able to reproduce the result.
std::uniform_int_distribution<uint16_t> distribution(1, 512);
std::vector<uint32_t> range { distribution(mt) };
for (size_t i = 1; i < kTestRangeNum * 2; ++i) {
range.push_back((range.back() - 1) + distribution(mt));
}
SparseBitSet bitset;
bitset.initFromRanges(range.data(), range.size() / 2);
uint32_t ch = 0;
for (size_t i = 0; i < range.size() / 2; ++i) {
uint32_t start = range[i * 2];
uint32_t end = range[i * 2 + 1];
for (; ch < start; ch++) {
ASSERT_FALSE(bitset.get(ch)) << std::hex << ch;
}
for (; ch < end; ch++) {
ASSERT_TRUE(bitset.get(ch)) << std::hex << ch;
}
}
for (; ch < 0x1FFFFFF; ++ch) {
ASSERT_FALSE(bitset.get(ch)) << std::hex << ch;
}
}
TEST(SparseBitSetTest, randomTest_restoredFromBuffer) {
const uint32_t kTestRangeNum = 4096;
std::mt19937 mt; // Fix seeds to be able to reproduce the result.
std::uniform_int_distribution<uint16_t> distribution(1, 512);
std::vector<uint32_t> range { distribution(mt) };
for (size_t i = 1; i < kTestRangeNum * 2; ++i) {
range.push_back((range.back() - 1) + distribution(mt));
}
SparseBitSet tmpBitset;
tmpBitset.initFromRanges(range.data(), range.size() / 2);
size_t bufSize = tmpBitset.writeToBuffer(nullptr);
ASSERT_NE(0U, bufSize);
std::vector<uint8_t> buffer(bufSize);
tmpBitset.writeToBuffer(buffer.data());
SparseBitSet bitset;
bitset.initFromBuffer(buffer.data(), buffer.size());
uint32_t ch = 0;
for (size_t i = 0; i < range.size() / 2; ++i) {
uint32_t start = range[i * 2];
uint32_t end = range[i * 2 + 1];
for (; ch < start; ch++) {
ASSERT_FALSE(bitset.get(ch)) << std::hex << ch;
}
for (; ch < end; ch++) {
ASSERT_TRUE(bitset.get(ch)) << std::hex << ch;
}
}
for (; ch < 0x1FFFFFF; ++ch) {
ASSERT_FALSE(bitset.get(ch)) << std::hex << ch;
}
}
TEST(SparseBitSetTest, emptyBitSet) {
SparseBitSet bitset;
uint32_t empty_bitset[4] = {
0 /* max value */, 0 /* zero page index */, 0 /* index size */, 0 /* bitmap size */
};
EXPECT_TRUE(bitset.initFromBuffer(
reinterpret_cast<uint8_t*>(empty_bitset), sizeof(empty_bitset)));
}
TEST(SparseBitSetTest, invalidData) {
SparseBitSet bitset;
EXPECT_FALSE(bitset.initFromBuffer(nullptr, 0));
// Buffer is too small.
uint32_t small_buffer[3] = { 0, 0, 0 };
EXPECT_FALSE(bitset.initFromBuffer(
reinterpret_cast<uint8_t*>(small_buffer), sizeof(small_buffer)));
// Buffer size does not match with necessary size.
uint32_t invalid_size_buffer[4] = {
0x12345678 /* max value */, 0 /* zero page index */, 0x50 /* index size*/,
0x80 /* bitmap size */
};
EXPECT_FALSE(bitset.initFromBuffer(
reinterpret_cast<uint8_t*>(invalid_size_buffer), sizeof(invalid_size_buffer)));
// max value, index size, bitmap size must be zero if the bitset is empty.
uint32_t invalid_empty_bitset1[4] = {
1 /* max value */, 0 /* zero page index */, 0 /* index size */, 0 /* bitmap size */
};
EXPECT_FALSE(bitset.initFromBuffer(
reinterpret_cast<uint8_t*>(invalid_empty_bitset1), sizeof(invalid_empty_bitset1)));
uint32_t invalid_empty_bitset2[4] = {
0 /* max value */, 0 /* zero page index */, 1 /* index size */, 0 /* bitmap size */
};
EXPECT_FALSE(bitset.initFromBuffer(
reinterpret_cast<uint8_t*>(invalid_empty_bitset2), sizeof(invalid_empty_bitset2)));
uint32_t invalid_empty_bitset3[4] = {
0 /* max value */, 0 /* zero page index */, 0 /* index size */, 1 /* bitmap size */
};
EXPECT_FALSE(bitset.initFromBuffer(
reinterpret_cast<uint8_t*>(invalid_empty_bitset3), sizeof(invalid_empty_bitset3)));
}
} // namespace minikin
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册