Reduce heap memory in minikin.

This patch reduces about 73 kB memory.
The original SparseBitSet could contain full 32bit integers, but all of
that is not necessary for Unicode code points. By reducing the supported
range to up to Unicode maximum, U+10FFFF, we can save extra memory.

SparseBitSet holds 256-bit sliced pages and indices of them.
Previously, we needed to hold up to 2^24-1 pages for keeping 32-bit
integers.

This CL limits the number of pages to 2^16-1 (65535), so that
SparseBitSet only supports 24-bit integers now, but this is sufficient
for keeping all Unicode code points. With this change, we can change the
index integer type from uint32_t to uint16_t.

Bug: 37357593
Test: minikin_tests passes
Change-Id: I462cc27927752c942ac5da0bf303a5afb81b87a3
This commit is contained in:
Seigo Nonaka
2017-04-14 11:08:12 -07:00
parent cc8f7117d3
commit 97ee89d605
5 changed files with 62 additions and 6 deletions

View File

@@ -68,6 +68,7 @@ public:
private:
void initFromRanges(const uint32_t* ranges, size_t nRanges);
static const uint32_t kMaximumCapacity = 0xFFFFFF;
static const int kLogValuesPerPage = 8;
static const int kPageMask = (1 << kLogValuesPerPage) - 1;
static const int kLogBytesPerEl = 2;
@@ -77,16 +78,16 @@ private:
typedef uint32_t element;
static const element kElAllOnes = ~((element)0);
static const element kElFirst = ((element)1) << kElMask;
static const uint32_t noZeroPage = ~0u;
static const uint16_t noZeroPage = 0xFFFF;
static uint32_t calcNumPages(const uint32_t* ranges, size_t nRanges);
static int CountLeadingZeros(element x);
uint32_t mMaxVal;
std::unique_ptr<uint32_t[]> mIndices;
std::unique_ptr<uint16_t[]> mIndices;
std::unique_ptr<element[]> mBitmaps;
uint32_t mZeroPageIndex;
uint16_t mZeroPageIndex;
// Forbid copy and assign.
SparseBitSet(const SparseBitSet&) = delete;

View File

@@ -25,6 +25,7 @@ using std::vector;
#include <minikin/SparseBitSet.h>
#include <minikin/CmapCoverage.h>
#include "MinikinInternal.h"
namespace minikin {
@@ -127,6 +128,16 @@ static bool getCoverageFormat12(vector<uint32_t>& coverage, const uint8_t* data,
android_errorWriteLog(0x534e4554, "26413177");
return false;
}
// No need to read outside of Unicode code point range.
if (start > MAX_UNICODE_CODE_POINT) {
return true;
}
if (end > MAX_UNICODE_CODE_POINT) {
// file is inclusive, vector is exclusive
addRange(coverage, start, MAX_UNICODE_CODE_POINT + 1);
return true;
}
addRange(coverage, start, end + 1); // file is inclusive, vector is exclusive
}
return true;

View File

@@ -38,6 +38,8 @@ void assertMinikinLocked();
hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag);
constexpr uint32_t MAX_UNICODE_CODE_POINT = 0x10FFFF;
// An RAII wrapper for hb_blob_t
class HbBlob {
public:

View File

@@ -55,8 +55,12 @@ void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
if (nRanges == 0) {
return;
}
mMaxVal = ranges[nRanges * 2 - 1];
mIndices.reset(new uint32_t[(mMaxVal + kPageMask) >> kLogValuesPerPage]);
const uint32_t maxVal = ranges[nRanges * 2 - 1];
if (maxVal >= kMaximumCapacity) {
return;
}
mMaxVal = maxVal;
mIndices.reset(new uint16_t[(mMaxVal + kPageMask) >> kLogValuesPerPage]);
uint32_t nPages = calcNumPages(ranges, nRanges);
mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]());
mZeroPageIndex = noZeroPage;
@@ -124,7 +128,7 @@ uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const {
}
uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage;
for (uint32_t page = fromPage + 1; page < maxPage; page++) {
uint32_t index = mIndices[page];
uint16_t index = mIndices[page];
if (index == mZeroPageIndex) {
continue;
}

View File

@@ -271,6 +271,34 @@ TEST(CmapCoverageTest, SingleFormat12) {
}
}
TEST(CmapCoverageTest, Format12_beyondTheUnicodeLimit) {
bool has_cmap_format_14_subtable = false;
{
SCOPED_TRACE("Starting range is out of Unicode code point. Should be ignored.");
std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
0, 0, std::vector<uint32_t>({'a', 'a', 0x110000, 0x110000}));
SparseBitSet coverage = CmapCoverage::getCoverage(
cmap.data(), cmap.size(), &has_cmap_format_14_subtable);
EXPECT_TRUE(coverage.get('a'));
EXPECT_FALSE(coverage.get(0x110000));
EXPECT_FALSE(has_cmap_format_14_subtable);
}
{
SCOPED_TRACE("Ending range is out of Unicode code point. Should be ignored.");
std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
0, 0, std::vector<uint32_t>({'a', 'a', 0x10FF00, 0x110000}));
SparseBitSet coverage = CmapCoverage::getCoverage(
cmap.data(), cmap.size(), &has_cmap_format_14_subtable);
EXPECT_TRUE(coverage.get('a'));
EXPECT_TRUE(coverage.get(0x10FF00));
EXPECT_TRUE(coverage.get(0x10FFFF));
EXPECT_FALSE(coverage.get(0x110000));
EXPECT_FALSE(has_cmap_format_14_subtable);
}
}
TEST(CmapCoverageTest, notSupportedEncodings) {
bool has_cmap_format_14_subtable = false;
@@ -398,6 +426,16 @@ TEST(CmapCoverageTest, brokenFormat12Table) {
builder.appendTable(0, 0, table);
std::vector<uint8_t> cmap = builder.build();
SparseBitSet coverage = CmapCoverage::getCoverage(
cmap.data(), cmap.size(), &has_cmap_format_14_subtable);
EXPECT_EQ(0U, coverage.length());
EXPECT_FALSE(has_cmap_format_14_subtable);
}
{
SCOPED_TRACE("Too large code point");
std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
0, 0, std::vector<uint32_t>({0x110000, 0x110000}));
SparseBitSet coverage = CmapCoverage::getCoverage(
cmap.data(), cmap.size(), &has_cmap_format_14_subtable);
EXPECT_EQ(0U, coverage.length());