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:
@@ -68,6 +68,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void initFromRanges(const uint32_t* ranges, size_t nRanges);
|
void initFromRanges(const uint32_t* ranges, size_t nRanges);
|
||||||
|
|
||||||
|
static const uint32_t kMaximumCapacity = 0xFFFFFF;
|
||||||
static const int kLogValuesPerPage = 8;
|
static const int kLogValuesPerPage = 8;
|
||||||
static const int kPageMask = (1 << kLogValuesPerPage) - 1;
|
static const int kPageMask = (1 << kLogValuesPerPage) - 1;
|
||||||
static const int kLogBytesPerEl = 2;
|
static const int kLogBytesPerEl = 2;
|
||||||
@@ -77,16 +78,16 @@ private:
|
|||||||
typedef uint32_t element;
|
typedef uint32_t element;
|
||||||
static const element kElAllOnes = ~((element)0);
|
static const element kElAllOnes = ~((element)0);
|
||||||
static const element kElFirst = ((element)1) << kElMask;
|
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 uint32_t calcNumPages(const uint32_t* ranges, size_t nRanges);
|
||||||
static int CountLeadingZeros(element x);
|
static int CountLeadingZeros(element x);
|
||||||
|
|
||||||
uint32_t mMaxVal;
|
uint32_t mMaxVal;
|
||||||
|
|
||||||
std::unique_ptr<uint32_t[]> mIndices;
|
std::unique_ptr<uint16_t[]> mIndices;
|
||||||
std::unique_ptr<element[]> mBitmaps;
|
std::unique_ptr<element[]> mBitmaps;
|
||||||
uint32_t mZeroPageIndex;
|
uint16_t mZeroPageIndex;
|
||||||
|
|
||||||
// Forbid copy and assign.
|
// Forbid copy and assign.
|
||||||
SparseBitSet(const SparseBitSet&) = delete;
|
SparseBitSet(const SparseBitSet&) = delete;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ using std::vector;
|
|||||||
|
|
||||||
#include <minikin/SparseBitSet.h>
|
#include <minikin/SparseBitSet.h>
|
||||||
#include <minikin/CmapCoverage.h>
|
#include <minikin/CmapCoverage.h>
|
||||||
|
#include "MinikinInternal.h"
|
||||||
|
|
||||||
namespace minikin {
|
namespace minikin {
|
||||||
|
|
||||||
@@ -127,6 +128,16 @@ static bool getCoverageFormat12(vector<uint32_t>& coverage, const uint8_t* data,
|
|||||||
android_errorWriteLog(0x534e4554, "26413177");
|
android_errorWriteLog(0x534e4554, "26413177");
|
||||||
return false;
|
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
|
addRange(coverage, start, end + 1); // file is inclusive, vector is exclusive
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ void assertMinikinLocked();
|
|||||||
|
|
||||||
hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag);
|
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
|
// An RAII wrapper for hb_blob_t
|
||||||
class HbBlob {
|
class HbBlob {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -55,8 +55,12 @@ void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
|
|||||||
if (nRanges == 0) {
|
if (nRanges == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mMaxVal = ranges[nRanges * 2 - 1];
|
const uint32_t maxVal = ranges[nRanges * 2 - 1];
|
||||||
mIndices.reset(new uint32_t[(mMaxVal + kPageMask) >> kLogValuesPerPage]);
|
if (maxVal >= kMaximumCapacity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mMaxVal = maxVal;
|
||||||
|
mIndices.reset(new uint16_t[(mMaxVal + kPageMask) >> kLogValuesPerPage]);
|
||||||
uint32_t nPages = calcNumPages(ranges, nRanges);
|
uint32_t nPages = calcNumPages(ranges, nRanges);
|
||||||
mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]());
|
mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]());
|
||||||
mZeroPageIndex = noZeroPage;
|
mZeroPageIndex = noZeroPage;
|
||||||
@@ -124,7 +128,7 @@ uint32_t SparseBitSet::nextSetBit(uint32_t fromIndex) const {
|
|||||||
}
|
}
|
||||||
uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage;
|
uint32_t maxPage = (mMaxVal + kPageMask) >> kLogValuesPerPage;
|
||||||
for (uint32_t page = fromPage + 1; page < maxPage; page++) {
|
for (uint32_t page = fromPage + 1; page < maxPage; page++) {
|
||||||
uint32_t index = mIndices[page];
|
uint16_t index = mIndices[page];
|
||||||
if (index == mZeroPageIndex) {
|
if (index == mZeroPageIndex) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
TEST(CmapCoverageTest, notSupportedEncodings) {
|
||||||
bool has_cmap_format_14_subtable = false;
|
bool has_cmap_format_14_subtable = false;
|
||||||
|
|
||||||
@@ -398,6 +426,16 @@ TEST(CmapCoverageTest, brokenFormat12Table) {
|
|||||||
builder.appendTable(0, 0, table);
|
builder.appendTable(0, 0, table);
|
||||||
std::vector<uint8_t> cmap = builder.build();
|
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(
|
SparseBitSet coverage = CmapCoverage::getCoverage(
|
||||||
cmap.data(), cmap.size(), &has_cmap_format_14_subtable);
|
cmap.data(), cmap.size(), &has_cmap_format_14_subtable);
|
||||||
EXPECT_EQ(0U, coverage.length());
|
EXPECT_EQ(0U, coverage.length());
|
||||||
|
|||||||
Reference in New Issue
Block a user