Merge "Make SparseBitSet memory mappable."
diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h
index 00f8486..f002cd6 100644
--- a/include/minikin/FontFamily.h
+++ b/include/minikin/FontFamily.h
@@ -47,13 +47,12 @@
     FamilyVariant variant() const { return mVariant; }
 
     // API's for enumerating the fonts in a family. These don't guarantee any particular order
-    size_t getNumFonts() const { return mFontsCount; }
+    size_t getNumFonts() const { return mFonts.size(); }
     const Font* getFont(size_t index) const { return mFonts[index].get(); }
     const std::shared_ptr<Font>& getFontRef(size_t index) const { return mFonts[index]; }
     FontStyle getStyle(size_t index) const { return mFonts[index]->style(); }
     bool isColorEmojiFamily() const { return mIsColorEmoji; }
-    size_t getSupportedAxesCount() const { return mSupportedAxesCount; }
-    AxisTag getSupportedAxisAt(size_t index) const { return mSupportedAxes[index]; }
+    const std::unordered_set<AxisTag>& supportedAxes() const { return mSupportedAxes; }
     bool isCustomFallback() const { return mIsCustomFallback; }
 
     // Get Unicode coverage.
@@ -64,7 +63,7 @@
     bool hasGlyph(uint32_t codepoint, uint32_t variationSelector) const;
 
     // Returns true if this font family has a variaion sequence table (cmap format 14 subtable).
-    bool hasVSTable() const { return mCmapFmt14CoverageCount != 0; }
+    bool hasVSTable() const { return !mCmapFmt14Coverage.empty(); }
 
     // Creates new FontFamily based on this family while applying font variations. Returns nullptr
     // if none of variations apply to this family.
@@ -73,30 +72,22 @@
 
 private:
     FontFamily(uint32_t localeListId, FamilyVariant variant,
-               std::unique_ptr<std::shared_ptr<Font>[]>&& fonts, uint32_t fontsCount,
-               std::unique_ptr<AxisTag[]>&& supportedAxes, uint32_t supportedAxesCount,
-               bool isColorEmoji, bool isCustomFallback, SparseBitSet&& coverage,
-               std::unique_ptr<std::unique_ptr<SparseBitSet>[]>&& cmapFmt14Coverage,
-               uint16_t cmapFmt14CoverageCount);
+               std::vector<std::shared_ptr<Font>>&& fonts,
+               std::unordered_set<AxisTag>&& supportedAxes, bool isColorEmoji,
+               bool isCustomFallback, SparseBitSet&& coverage,
+               std::vector<std::unique_ptr<SparseBitSet>>&& cmapFmt14Coverage);
 
     void computeCoverage();
 
-public:
-    // Note: to minimize padding, small member fields are grouped at the end.
-    std::unique_ptr<std::shared_ptr<Font>[]> mFonts;
-    // mSupportedAxes is sorted.
-    std::unique_ptr<AxisTag[]> mSupportedAxes;
+    uint32_t mLocaleListId;
+    FamilyVariant mVariant;
+    std::vector<std::shared_ptr<Font>> mFonts;
+    std::unordered_set<AxisTag> mSupportedAxes;
+    bool mIsColorEmoji;
+    bool mIsCustomFallback;
+
     SparseBitSet mCoverage;
-    std::unique_ptr<std::unique_ptr<SparseBitSet>[]> mCmapFmt14Coverage;
-    uint32_t mLocaleListId;  // 4 bytes
-    uint32_t mFontsCount;    // 4 bytes
-    // OpenType supports up to 2^16-1 (uint16) axes.
-    // https://docs.microsoft.com/en-us/typography/opentype/spec/fvar
-    uint16_t mSupportedAxesCount;      // 2 bytes
-    uint16_t mCmapFmt14CoverageCount;  // 2 bytes
-    FamilyVariant mVariant;            // 1 byte
-    bool mIsColorEmoji;                // 1 byte
-    bool mIsCustomFallback;            // 1 byte
+    std::vector<std::unique_ptr<SparseBitSet>> mCmapFmt14Coverage;
 
     MINIKIN_PREVENT_COPY_AND_ASSIGN(FontFamily);
 };
diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp
index 99242b1..563fb77 100644
--- a/libs/minikin/FontCollection.cpp
+++ b/libs/minikin/FontCollection.cpp
@@ -150,9 +150,8 @@
         mMaxChar = max(mMaxChar, coverage.length());
         lastChar.push_back(coverage.nextSetBit(0));
 
-        for (size_t i = 0; i < family->getSupportedAxesCount(); i++) {
-            mSupportedAxes.insert(family->getSupportedAxisAt(i));
-        }
+        const std::unordered_set<AxisTag>& supportedAxes = family->supportedAxes();
+        mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
     }
     // mMaybeSharedFamilies is not shared.
     mMaybeSharedFamilies = families;
diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp
index 7938780..91bcaef 100644
--- a/libs/minikin/FontFamily.cpp
+++ b/libs/minikin/FontFamily.cpp
@@ -43,47 +43,38 @@
 
 FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
                        std::vector<std::shared_ptr<Font>>&& fonts, bool isCustomFallback)
-        : mFonts(std::make_unique<std::shared_ptr<Font>[]>(fonts.size())),
-          mLocaleListId(localeListId),
-          mFontsCount(static_cast<uint32_t>(fonts.size())),
+        : mLocaleListId(localeListId),
           mVariant(variant),
+          mFonts(std::move(fonts)),
           mIsColorEmoji(LocaleListCache::getById(localeListId).getEmojiStyle() ==
                         EmojiStyle::EMOJI),
           mIsCustomFallback(isCustomFallback) {
-    MINIKIN_ASSERT(!fonts.empty(), "FontFamily must contain at least one font.");
-    MINIKIN_ASSERT(fonts.size() <= std::numeric_limits<uint32_t>::max(),
-                   "Number of fonts must be less than 2^32.");
-    for (size_t i = 0; i < mFontsCount; i++) {
-        mFonts[i] = std::move(fonts[i]);
-    }
+    MINIKIN_ASSERT(!mFonts.empty(), "FontFamily must contain at least one font.");
     computeCoverage();
 }
 
 FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant,
-                       std::unique_ptr<std::shared_ptr<Font>[]>&& fonts, uint32_t fontsCount,
-                       std::unique_ptr<AxisTag[]>&& supportedAxes, uint32_t supportedAxesCount,
-                       bool isColorEmoji, bool isCustomFallback, SparseBitSet&& coverage,
-                       std::unique_ptr<std::unique_ptr<SparseBitSet>[]>&& cmapFmt14Coverage,
-                       uint16_t cmapFmt14CoverageCount)
-        : mFonts(std::move(fonts)),
-          mSupportedAxes(std::move(supportedAxes)),
-          mCoverage(std::move(coverage)),
-          mCmapFmt14Coverage(std::move(cmapFmt14Coverage)),
-          mLocaleListId(localeListId),
-          mFontsCount(fontsCount),
-          mSupportedAxesCount(supportedAxesCount),
-          mCmapFmt14CoverageCount(cmapFmt14CoverageCount),
+                       std::vector<std::shared_ptr<Font>>&& fonts,
+                       std::unordered_set<AxisTag>&& supportedAxes, bool isColorEmoji,
+                       bool isCustomFallback, SparseBitSet&& coverage,
+                       std::vector<std::unique_ptr<SparseBitSet>>&& cmapFmt14Coverage)
+        : mLocaleListId(localeListId),
           mVariant(variant),
+          mFonts(std::move(fonts)),
+          mSupportedAxes(std::move(supportedAxes)),
           mIsColorEmoji(isColorEmoji),
-          mIsCustomFallback(isCustomFallback) {}
+          mIsCustomFallback(isCustomFallback),
+          mCoverage(std::move(coverage)),
+          mCmapFmt14Coverage(std::move(cmapFmt14Coverage)) {}
 
 // static
 std::shared_ptr<FontFamily> FontFamily::readFrom(BufferReader* reader) {
     uint32_t localeListId = LocaleListCache::readFrom(reader);
     uint32_t fontsCount = reader->read<uint32_t>();
-    auto fonts = std::make_unique<std::shared_ptr<Font>[]>(fontsCount);
-    for (size_t i = 0; i < fontsCount; i++) {
-        fonts[i] = Font::readFrom(reader, localeListId);
+    std::vector<std::shared_ptr<Font>> fonts;
+    fonts.reserve(fontsCount);
+    for (uint32_t i = 0; i < fontsCount; i++) {
+        fonts.emplace_back(Font::readFrom(reader, localeListId));
     }
     // FamilyVariant is uint8_t
     static_assert(sizeof(FamilyVariant) == 1);
@@ -91,48 +82,48 @@
     // AxisTag is uint32_t
     static_assert(sizeof(AxisTag) == 4);
     const auto& [axesPtr, axesCount] = reader->readArray<AxisTag>();
-    auto supportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[axesCount]);
-    std::copy(axesPtr, axesPtr + axesCount, supportedAxes.get());
+    std::unordered_set<AxisTag> supportedAxes(axesPtr, axesPtr + axesCount);
     bool isColorEmoji = static_cast<bool>(reader->read<uint8_t>());
     bool isCustomFallback = static_cast<bool>(reader->read<uint8_t>());
     SparseBitSet coverage(reader);
     // Read mCmapFmt14Coverage. As it can have null entries, it is stored in the buffer as a sparse
     // array (size, non-null entry count, array of (index, entry)).
     uint32_t cmapFmt14CoverageSize = reader->read<uint32_t>();
-    auto cmapFmt14Coverage =
-            std::make_unique<std::unique_ptr<SparseBitSet>[]>(cmapFmt14CoverageSize);
+    std::vector<std::unique_ptr<SparseBitSet>> cmapFmt14Coverage(cmapFmt14CoverageSize);
     uint32_t cmapFmt14CoverageEntryCount = reader->read<uint32_t>();
     for (uint32_t i = 0; i < cmapFmt14CoverageEntryCount; i++) {
         uint32_t index = reader->read<uint32_t>();
         cmapFmt14Coverage[index] = std::make_unique<SparseBitSet>(reader);
     }
     return std::shared_ptr<FontFamily>(new FontFamily(
-            localeListId, variant, std::move(fonts), fontsCount, std::move(supportedAxes),
-            axesCount, isColorEmoji, isCustomFallback, std::move(coverage),
-            std::move(cmapFmt14Coverage), cmapFmt14CoverageSize));
+            localeListId, variant, std::move(fonts), std::move(supportedAxes), isColorEmoji,
+            isCustomFallback, std::move(coverage), std::move(cmapFmt14Coverage)));
 }
 
 void FontFamily::writeTo(BufferWriter* writer) const {
     LocaleListCache::writeTo(writer, mLocaleListId);
-    writer->write<uint32_t>(mFontsCount);
-    for (size_t i = 0; i < mFontsCount; i++) {
-        mFonts[i]->writeTo(writer);
+    writer->write<uint32_t>(mFonts.size());
+    for (const std::shared_ptr<Font>& font : mFonts) {
+        font->writeTo(writer);
     }
     writer->write<FamilyVariant>(mVariant);
-    writer->writeArray<AxisTag>(mSupportedAxes.get(), mSupportedAxesCount);
+    std::vector<AxisTag> axes(mSupportedAxes.begin(), mSupportedAxes.end());
+    // Sort axes to be deterministic.
+    std::sort(axes.begin(), axes.end());
+    writer->writeArray<AxisTag>(axes.data(), axes.size());
     writer->write<uint8_t>(mIsColorEmoji);
     writer->write<uint8_t>(mIsCustomFallback);
     mCoverage.writeTo(writer);
     // Write mCmapFmt14Coverage as a sparse array (size, non-null entry count,
     // array of (index, entry))
-    writer->write<uint32_t>(mCmapFmt14CoverageCount);
+    writer->write<uint32_t>(mCmapFmt14Coverage.size());
     uint32_t cmapFmt14CoverageEntryCount = 0;
-    for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
-        if (mCmapFmt14Coverage[i]) cmapFmt14CoverageEntryCount++;
+    for (const std::unique_ptr<SparseBitSet>& coverage : mCmapFmt14Coverage) {
+        if (coverage != nullptr) cmapFmt14CoverageEntryCount++;
     }
     writer->write<uint32_t>(cmapFmt14CoverageEntryCount);
-    for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
-        if (mCmapFmt14Coverage[i]) {
+    for (size_t i = 0; i < mCmapFmt14Coverage.size(); i++) {
+        if (mCmapFmt14Coverage[i] != nullptr) {
             writer->write<uint32_t>(i);
             mCmapFmt14Coverage[i]->writeTo(writer);
         }
@@ -163,7 +154,7 @@
     int bestIndex = 0;
     Font* bestFont = mFonts[bestIndex].get();
     int bestMatch = computeMatch(bestFont->style(), style);
-    for (size_t i = 1; i < mFontsCount; i++) {
+    for (size_t i = 1; i < mFonts.size(); i++) {
         Font* font = mFonts[i].get();
         int match = computeMatch(font->style(), style);
         if (i == 0 || match < bestMatch) {
@@ -183,30 +174,12 @@
         return;
     }
 
-    std::vector<std::unique_ptr<SparseBitSet>> cmapFmt14Coverage;
-    mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &cmapFmt14Coverage);
-    static_assert(INVALID_VS_INDEX <= std::numeric_limits<uint16_t>::max());
-    // cmapFmt14Coverage maps VS index to coverage.
-    // cmapFmt14Coverage's size cannot exceed INVALID_VS_INDEX.
-    MINIKIN_ASSERT(cmapFmt14Coverage.size() <= INVALID_VS_INDEX,
-                   "cmapFmt14Coverage's size must not exceed INVALID_VS_INDEX.");
-    mCmapFmt14CoverageCount = static_cast<uint16_t>(cmapFmt14Coverage.size());
-    mCmapFmt14Coverage = std::make_unique<std::unique_ptr<SparseBitSet>[]>(mCmapFmt14CoverageCount);
-    for (size_t i = 0; i < mCmapFmt14CoverageCount; i++) {
-        mCmapFmt14Coverage[i] = std::move(cmapFmt14Coverage[i]);
-    }
+    mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage);
 
-    std::unordered_set<AxisTag> supportedAxesSet;
-    for (size_t i = 0; i < mFontsCount; ++i) {
+    for (size_t i = 0; i < mFonts.size(); ++i) {
         std::unordered_set<AxisTag> supportedAxes = mFonts[i]->getSupportedAxes();
-        supportedAxesSet.insert(supportedAxes.begin(), supportedAxes.end());
+        mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end());
     }
-    MINIKIN_ASSERT(supportedAxesSet.size() <= std::numeric_limits<uint32_t>::max(),
-                   "Number of supported axes must be less than 2^16.");
-    mSupportedAxesCount = static_cast<uint16_t>(supportedAxesSet.size());
-    mSupportedAxes = std::unique_ptr<AxisTag[]>(new AxisTag[mSupportedAxesCount]);
-    std::copy(supportedAxesSet.begin(), supportedAxesSet.end(), mSupportedAxes.get());
-    std::sort(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount);
 }
 
 bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const {
@@ -214,13 +187,13 @@
         return mCoverage.get(codepoint);
     }
 
-    if (mCmapFmt14CoverageCount == 0) {
+    if (mCmapFmt14Coverage.empty()) {
         return false;
     }
 
     const uint16_t vsIndex = getVsIndex(variationSelector);
 
-    if (vsIndex >= mCmapFmt14CoverageCount) {
+    if (vsIndex >= mCmapFmt14Coverage.size()) {
         // Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to
         // be at the maximum end of the range.
         return false;
@@ -236,14 +209,13 @@
 
 std::shared_ptr<FontFamily> FontFamily::createFamilyWithVariation(
         const std::vector<FontVariation>& variations) const {
-    if (variations.empty() || mSupportedAxesCount == 0) {
+    if (variations.empty() || mSupportedAxes.empty()) {
         return nullptr;
     }
 
     bool hasSupportedAxis = false;
     for (const FontVariation& variation : variations) {
-        if (std::binary_search(mSupportedAxes.get(), mSupportedAxes.get() + mSupportedAxesCount,
-                               variation.axisTag)) {
+        if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) {
             hasSupportedAxis = true;
             break;
         }
@@ -254,8 +226,7 @@
     }
 
     std::vector<std::shared_ptr<Font>> fonts;
-    for (size_t i = 0; i < mFontsCount; i++) {
-        const std::shared_ptr<Font>& font = mFonts[i];
+    for (const auto& font : mFonts) {
         bool supportedVariations = false;
         std::unordered_set<AxisTag> supportedAxes = font->getSupportedAxes();
         if (!supportedAxes.empty()) {
diff --git a/tests/unittest/FontFamilyTest.cpp b/tests/unittest/FontFamilyTest.cpp
index d658cdd..53803fd 100644
--- a/tests/unittest/FontFamilyTest.cpp
+++ b/tests/unittest/FontFamilyTest.cpp
@@ -832,10 +832,7 @@
         ASSERT_EQ(original->localeListId(), copied->localeListId());
         ASSERT_EQ(original->variant(), copied->variant());
         ASSERT_EQ(original->getNumFonts(), copied->getNumFonts());
-        ASSERT_EQ(original->getSupportedAxesCount(), copied->getSupportedAxesCount());
-        for (size_t i = 0; i < original->getSupportedAxesCount(); i++) {
-            ASSERT_EQ(original->getSupportedAxisAt(i), copied->getSupportedAxisAt(i));
-        }
+        ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
         ASSERT_EQ(original->isColorEmojiFamily(), copied->isColorEmojiFamily());
         ASSERT_EQ(original->isCustomFallback(), copied->isCustomFallback());
         ASSERT_EQ(original->hasVSTable(), copied->hasVSTable());
@@ -850,10 +847,7 @@
         std::vector<uint8_t> buffer = writeToBuffer<FontFamily>(*original);
         BufferReader reader(buffer.data());
         std::shared_ptr<FontFamily> copied = FontFamily::readFrom(&reader);
-        ASSERT_EQ(original->getSupportedAxesCount(), copied->getSupportedAxesCount());
-        for (size_t i = 0; i < original->getSupportedAxesCount(); i++) {
-            ASSERT_EQ(original->getSupportedAxisAt(i), copied->getSupportedAxisAt(i));
-        }
+        ASSERT_EQ(original->supportedAxes(), copied->supportedAxes());
         std::vector<uint8_t> newBuffer = writeToBuffer<FontFamily>(*copied);
         ASSERT_EQ(buffer, newBuffer);
     }