Loading native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp +37 −0 Original line number Original line Diff line number Diff line Loading @@ -98,6 +98,43 @@ bool TrieMap::put(const int key, const uint64_t value, const int bitmapEntryInde return putInternal(unsignedKey, value, getBitShuffledKey(unsignedKey), bitmapEntryIndex, return putInternal(unsignedKey, value, getBitShuffledKey(unsignedKey), bitmapEntryIndex, readEntry(bitmapEntryIndex), 0 /* level */); readEntry(bitmapEntryIndex), 0 /* level */); } } /** * Iterate next entry in a certain level. * * @param iterationState the iteration state that will be read and updated in this method. * @param outKey the output key * @return Result instance. mIsValid is false when all entries are iterated. */ const TrieMap::Result TrieMap::iterateNext(std::vector<TableIterationState> *const iterationState, int *const outKey) const { while (!iterationState->empty()) { TableIterationState &state = iterationState->back(); if (state.mTableSize <= state.mCurrentIndex) { // Move to parent. iterationState->pop_back(); } else { const int entryIndex = state.mTableIndex + state.mCurrentIndex; state.mCurrentIndex += 1; const Entry entry = readEntry(entryIndex); if (entry.isBitmapEntry()) { // Move to child. iterationState->emplace_back(popCount(entry.getBitmap()), entry.getTableIndex()); } else { if (outKey) { *outKey = entry.getKey(); } if (!entry.hasTerminalLink()) { return Result(entry.getValue(), true, INVALID_INDEX); } const int valueEntryIndex = entry.getValueEntryIndex(); const Entry valueEntry = readEntry(valueEntryIndex); return Result(valueEntry.getValueOfValueEntry(), true, valueEntryIndex + 1); } } } // Visited all entries. return Result(0, false, INVALID_INDEX); } /** /** * Shuffle bits of the key in the fixed order. * Shuffle bits of the key in the fixed order. Loading native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h +121 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,117 @@ class TrieMap { mNextLevelBitmapEntryIndex(nextLevelBitmapEntryIndex) {} mNextLevelBitmapEntryIndex(nextLevelBitmapEntryIndex) {} }; }; /** * Struct to record iteration state in a table. */ struct TableIterationState { int mTableSize; int mTableIndex; int mCurrentIndex; TableIterationState(const int tableSize, const int tableIndex) : mTableSize(tableSize), mTableIndex(tableIndex), mCurrentIndex(0) {} }; class TrieMapRange; class TrieMapIterator { public: class IterationResult { public: IterationResult(const TrieMap *const trieMap, const int key, const uint64_t value, const int nextLeveBitmapEntryIndex) : mTrieMap(trieMap), mKey(key), mValue(value), mNextLevelBitmapEntryIndex(nextLeveBitmapEntryIndex) {} const TrieMapRange getEntriesInNextLevel() const { return TrieMapRange(mTrieMap, mNextLevelBitmapEntryIndex); } bool hasNextLevelMap() const { return mNextLevelBitmapEntryIndex != INVALID_INDEX; } AK_FORCE_INLINE int key() const { return mKey; } AK_FORCE_INLINE uint64_t value() const { return mValue; } private: const TrieMap *const mTrieMap; const int mKey; const uint64_t mValue; const int mNextLevelBitmapEntryIndex; }; TrieMapIterator(const TrieMap *const trieMap, const int bitmapEntryIndex) : mTrieMap(trieMap), mStateStack(), mBaseBitmapEntryIndex(bitmapEntryIndex), mKey(0), mValue(0), mIsValid(false), mNextLevelBitmapEntryIndex(INVALID_INDEX) { if (!trieMap) { return; } const Entry bitmapEntry = mTrieMap->readEntry(mBaseBitmapEntryIndex); mStateStack.emplace_back( mTrieMap->popCount(bitmapEntry.getBitmap()), bitmapEntry.getTableIndex()); this->operator++(); } const IterationResult operator*() const { return IterationResult(mTrieMap, mKey, mValue, mNextLevelBitmapEntryIndex); } bool operator!=(const TrieMapIterator &other) const { // Caveat: This works only for for loops. return mIsValid || other.mIsValid; } const TrieMapIterator &operator++() { const Result result = mTrieMap->iterateNext(&mStateStack, &mKey); mValue = result.mValue; mIsValid = result.mIsValid; mNextLevelBitmapEntryIndex = result.mNextLevelBitmapEntryIndex; return *this; } private: DISALLOW_DEFAULT_CONSTRUCTOR(TrieMapIterator); DISALLOW_ASSIGNMENT_OPERATOR(TrieMapIterator); const TrieMap *const mTrieMap; std::vector<TrieMap::TableIterationState> mStateStack; const int mBaseBitmapEntryIndex; int mKey; uint64_t mValue; bool mIsValid; int mNextLevelBitmapEntryIndex; }; /** * Class to support iterating entries in TrieMap by range base for loops. */ class TrieMapRange { public: TrieMapRange(const TrieMap *const trieMap, const int bitmapEntryIndex) : mTrieMap(trieMap), mBaseBitmapEntryIndex(bitmapEntryIndex) {}; TrieMapIterator begin() const { return TrieMapIterator(mTrieMap, mBaseBitmapEntryIndex); } const TrieMapIterator end() const { return TrieMapIterator(nullptr, INVALID_INDEX); } private: DISALLOW_DEFAULT_CONSTRUCTOR(TrieMapRange); DISALLOW_ASSIGNMENT_OPERATOR(TrieMapRange); const TrieMap *const mTrieMap; const int mBaseBitmapEntryIndex; }; static const int INVALID_INDEX; static const int INVALID_INDEX; static const uint64_t MAX_VALUE; static const uint64_t MAX_VALUE; Loading Loading @@ -73,6 +184,14 @@ class TrieMap { bool put(const int key, const uint64_t value, const int bitmapEntryIndex); bool put(const int key, const uint64_t value, const int bitmapEntryIndex); const TrieMapRange getEntriesInRootLevel() const { return getEntriesInSpecifiedLevel(ROOT_BITMAP_ENTRY_INDEX); } const TrieMapRange getEntriesInSpecifiedLevel(const int bitmapEntryIndex) const { return TrieMapRange(this, bitmapEntryIndex); } private: private: DISALLOW_COPY_AND_ASSIGN(TrieMap); DISALLOW_COPY_AND_ASSIGN(TrieMap); Loading Loading @@ -171,6 +290,8 @@ class TrieMap { bool addNewEntryByExpandingTable(const uint32_t key, const uint64_t value, bool addNewEntryByExpandingTable(const uint32_t key, const uint64_t value, const int tableIndex, const uint32_t bitmap, const int bitmapEntryIndex, const int tableIndex, const uint32_t bitmap, const int bitmapEntryIndex, const int label); const int label); const Result iterateNext(std::vector<TableIterationState> *const iterationState, int *const outKey) const; AK_FORCE_INLINE const Entry readEntry(const int entryIndex) const { AK_FORCE_INLINE const Entry readEntry(const int entryIndex) const { return Entry(readField0(entryIndex), readField1(entryIndex)); return Entry(readField0(entryIndex), readField1(entryIndex)); Loading native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp +57 −2 Original line number Original line Diff line number Diff line Loading @@ -54,7 +54,7 @@ TEST(TrieMapTest, TestSetAndGetLarge) { EXPECT_TRUE(trieMap.putRoot(i, i)); EXPECT_TRUE(trieMap.putRoot(i, i)); } } for (int i = 0; i < ELEMENT_COUNT; ++i) { for (int i = 0; i < ELEMENT_COUNT; ++i) { EXPECT_EQ(trieMap.getRoot(i).mValue, static_cast<uint64_t>(i)); EXPECT_EQ(static_cast<uint64_t>(i), trieMap.getRoot(i).mValue); } } } } Loading @@ -78,7 +78,7 @@ TEST(TrieMapTest, TestRandSetAndGetLarge) { testKeyValuePairs[key] = value; testKeyValuePairs[key] = value; } } for (const auto &v : testKeyValuePairs) { for (const auto &v : testKeyValuePairs) { EXPECT_EQ(trieMap.getRoot(v.first).mValue, v.second); EXPECT_EQ(v.second, trieMap.getRoot(v.first).mValue); } } } } Loading Loading @@ -163,6 +163,61 @@ TEST(TrieMapTest, TestMultiLevel) { } } } } } } // Iteration for (const auto &firstLevelEntry : trieMap.getEntriesInRootLevel()) { EXPECT_EQ(trieMap.getRoot(firstLevelEntry.key()).mValue, firstLevelEntry.value()); EXPECT_EQ(firstLevelEntries[firstLevelEntry.key()], firstLevelEntry.value()); firstLevelEntries.erase(firstLevelEntry.key()); for (const auto &secondLevelEntry : firstLevelEntry.getEntriesInNextLevel()) { EXPECT_EQ(twoLevelMap[firstLevelEntry.key()][secondLevelEntry.key()], secondLevelEntry.value()); twoLevelMap[firstLevelEntry.key()].erase(secondLevelEntry.key()); for (const auto &thirdLevelEntry : secondLevelEntry.getEntriesInNextLevel()) { EXPECT_EQ(threeLevelMap[firstLevelEntry.key()][secondLevelEntry.key()] [thirdLevelEntry.key()], thirdLevelEntry.value()); threeLevelMap[firstLevelEntry.key()][secondLevelEntry.key()].erase( thirdLevelEntry.key()); } } } // Ensure all entries have been traversed. EXPECT_TRUE(firstLevelEntries.empty()); for (const auto &secondLevelEntry : twoLevelMap) { EXPECT_TRUE(secondLevelEntry.second.empty()); } for (const auto &secondLevelEntry : threeLevelMap) { for (const auto &thirdLevelEntry : secondLevelEntry.second) { EXPECT_TRUE(thirdLevelEntry.second.empty()); } } } TEST(TrieMapTest, TestIteration) { static const int ELEMENT_COUNT = 200000; TrieMap trieMap; std::unordered_map<int, uint64_t> testKeyValuePairs; // Use the uniform integer distribution [S_INT_MIN, S_INT_MAX]. std::uniform_int_distribution<int> keyDistribution(S_INT_MIN, S_INT_MAX); auto keyRandomNumberGenerator = std::bind(keyDistribution, std::mt19937()); // Use the uniform distribution [0, TrieMap::MAX_VALUE]. std::uniform_int_distribution<uint64_t> valueDistribution(0, TrieMap::MAX_VALUE); auto valueRandomNumberGenerator = std::bind(valueDistribution, std::mt19937()); for (int i = 0; i < ELEMENT_COUNT; ++i) { const int key = keyRandomNumberGenerator(); const uint64_t value = valueRandomNumberGenerator(); EXPECT_TRUE(trieMap.putRoot(key, value)); testKeyValuePairs[key] = value; } for (const auto &entry : trieMap.getEntriesInRootLevel()) { EXPECT_EQ(trieMap.getRoot(entry.key()).mValue, entry.value()); EXPECT_EQ(testKeyValuePairs[entry.key()], entry.value()); testKeyValuePairs.erase(entry.key()); } EXPECT_TRUE(testKeyValuePairs.empty()); } } } // namespace } // namespace Loading Loading
native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp +37 −0 Original line number Original line Diff line number Diff line Loading @@ -98,6 +98,43 @@ bool TrieMap::put(const int key, const uint64_t value, const int bitmapEntryInde return putInternal(unsignedKey, value, getBitShuffledKey(unsignedKey), bitmapEntryIndex, return putInternal(unsignedKey, value, getBitShuffledKey(unsignedKey), bitmapEntryIndex, readEntry(bitmapEntryIndex), 0 /* level */); readEntry(bitmapEntryIndex), 0 /* level */); } } /** * Iterate next entry in a certain level. * * @param iterationState the iteration state that will be read and updated in this method. * @param outKey the output key * @return Result instance. mIsValid is false when all entries are iterated. */ const TrieMap::Result TrieMap::iterateNext(std::vector<TableIterationState> *const iterationState, int *const outKey) const { while (!iterationState->empty()) { TableIterationState &state = iterationState->back(); if (state.mTableSize <= state.mCurrentIndex) { // Move to parent. iterationState->pop_back(); } else { const int entryIndex = state.mTableIndex + state.mCurrentIndex; state.mCurrentIndex += 1; const Entry entry = readEntry(entryIndex); if (entry.isBitmapEntry()) { // Move to child. iterationState->emplace_back(popCount(entry.getBitmap()), entry.getTableIndex()); } else { if (outKey) { *outKey = entry.getKey(); } if (!entry.hasTerminalLink()) { return Result(entry.getValue(), true, INVALID_INDEX); } const int valueEntryIndex = entry.getValueEntryIndex(); const Entry valueEntry = readEntry(valueEntryIndex); return Result(valueEntry.getValueOfValueEntry(), true, valueEntryIndex + 1); } } } // Visited all entries. return Result(0, false, INVALID_INDEX); } /** /** * Shuffle bits of the key in the fixed order. * Shuffle bits of the key in the fixed order. Loading
native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h +121 −0 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,117 @@ class TrieMap { mNextLevelBitmapEntryIndex(nextLevelBitmapEntryIndex) {} mNextLevelBitmapEntryIndex(nextLevelBitmapEntryIndex) {} }; }; /** * Struct to record iteration state in a table. */ struct TableIterationState { int mTableSize; int mTableIndex; int mCurrentIndex; TableIterationState(const int tableSize, const int tableIndex) : mTableSize(tableSize), mTableIndex(tableIndex), mCurrentIndex(0) {} }; class TrieMapRange; class TrieMapIterator { public: class IterationResult { public: IterationResult(const TrieMap *const trieMap, const int key, const uint64_t value, const int nextLeveBitmapEntryIndex) : mTrieMap(trieMap), mKey(key), mValue(value), mNextLevelBitmapEntryIndex(nextLeveBitmapEntryIndex) {} const TrieMapRange getEntriesInNextLevel() const { return TrieMapRange(mTrieMap, mNextLevelBitmapEntryIndex); } bool hasNextLevelMap() const { return mNextLevelBitmapEntryIndex != INVALID_INDEX; } AK_FORCE_INLINE int key() const { return mKey; } AK_FORCE_INLINE uint64_t value() const { return mValue; } private: const TrieMap *const mTrieMap; const int mKey; const uint64_t mValue; const int mNextLevelBitmapEntryIndex; }; TrieMapIterator(const TrieMap *const trieMap, const int bitmapEntryIndex) : mTrieMap(trieMap), mStateStack(), mBaseBitmapEntryIndex(bitmapEntryIndex), mKey(0), mValue(0), mIsValid(false), mNextLevelBitmapEntryIndex(INVALID_INDEX) { if (!trieMap) { return; } const Entry bitmapEntry = mTrieMap->readEntry(mBaseBitmapEntryIndex); mStateStack.emplace_back( mTrieMap->popCount(bitmapEntry.getBitmap()), bitmapEntry.getTableIndex()); this->operator++(); } const IterationResult operator*() const { return IterationResult(mTrieMap, mKey, mValue, mNextLevelBitmapEntryIndex); } bool operator!=(const TrieMapIterator &other) const { // Caveat: This works only for for loops. return mIsValid || other.mIsValid; } const TrieMapIterator &operator++() { const Result result = mTrieMap->iterateNext(&mStateStack, &mKey); mValue = result.mValue; mIsValid = result.mIsValid; mNextLevelBitmapEntryIndex = result.mNextLevelBitmapEntryIndex; return *this; } private: DISALLOW_DEFAULT_CONSTRUCTOR(TrieMapIterator); DISALLOW_ASSIGNMENT_OPERATOR(TrieMapIterator); const TrieMap *const mTrieMap; std::vector<TrieMap::TableIterationState> mStateStack; const int mBaseBitmapEntryIndex; int mKey; uint64_t mValue; bool mIsValid; int mNextLevelBitmapEntryIndex; }; /** * Class to support iterating entries in TrieMap by range base for loops. */ class TrieMapRange { public: TrieMapRange(const TrieMap *const trieMap, const int bitmapEntryIndex) : mTrieMap(trieMap), mBaseBitmapEntryIndex(bitmapEntryIndex) {}; TrieMapIterator begin() const { return TrieMapIterator(mTrieMap, mBaseBitmapEntryIndex); } const TrieMapIterator end() const { return TrieMapIterator(nullptr, INVALID_INDEX); } private: DISALLOW_DEFAULT_CONSTRUCTOR(TrieMapRange); DISALLOW_ASSIGNMENT_OPERATOR(TrieMapRange); const TrieMap *const mTrieMap; const int mBaseBitmapEntryIndex; }; static const int INVALID_INDEX; static const int INVALID_INDEX; static const uint64_t MAX_VALUE; static const uint64_t MAX_VALUE; Loading Loading @@ -73,6 +184,14 @@ class TrieMap { bool put(const int key, const uint64_t value, const int bitmapEntryIndex); bool put(const int key, const uint64_t value, const int bitmapEntryIndex); const TrieMapRange getEntriesInRootLevel() const { return getEntriesInSpecifiedLevel(ROOT_BITMAP_ENTRY_INDEX); } const TrieMapRange getEntriesInSpecifiedLevel(const int bitmapEntryIndex) const { return TrieMapRange(this, bitmapEntryIndex); } private: private: DISALLOW_COPY_AND_ASSIGN(TrieMap); DISALLOW_COPY_AND_ASSIGN(TrieMap); Loading Loading @@ -171,6 +290,8 @@ class TrieMap { bool addNewEntryByExpandingTable(const uint32_t key, const uint64_t value, bool addNewEntryByExpandingTable(const uint32_t key, const uint64_t value, const int tableIndex, const uint32_t bitmap, const int bitmapEntryIndex, const int tableIndex, const uint32_t bitmap, const int bitmapEntryIndex, const int label); const int label); const Result iterateNext(std::vector<TableIterationState> *const iterationState, int *const outKey) const; AK_FORCE_INLINE const Entry readEntry(const int entryIndex) const { AK_FORCE_INLINE const Entry readEntry(const int entryIndex) const { return Entry(readField0(entryIndex), readField1(entryIndex)); return Entry(readField0(entryIndex), readField1(entryIndex)); Loading
native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp +57 −2 Original line number Original line Diff line number Diff line Loading @@ -54,7 +54,7 @@ TEST(TrieMapTest, TestSetAndGetLarge) { EXPECT_TRUE(trieMap.putRoot(i, i)); EXPECT_TRUE(trieMap.putRoot(i, i)); } } for (int i = 0; i < ELEMENT_COUNT; ++i) { for (int i = 0; i < ELEMENT_COUNT; ++i) { EXPECT_EQ(trieMap.getRoot(i).mValue, static_cast<uint64_t>(i)); EXPECT_EQ(static_cast<uint64_t>(i), trieMap.getRoot(i).mValue); } } } } Loading @@ -78,7 +78,7 @@ TEST(TrieMapTest, TestRandSetAndGetLarge) { testKeyValuePairs[key] = value; testKeyValuePairs[key] = value; } } for (const auto &v : testKeyValuePairs) { for (const auto &v : testKeyValuePairs) { EXPECT_EQ(trieMap.getRoot(v.first).mValue, v.second); EXPECT_EQ(v.second, trieMap.getRoot(v.first).mValue); } } } } Loading Loading @@ -163,6 +163,61 @@ TEST(TrieMapTest, TestMultiLevel) { } } } } } } // Iteration for (const auto &firstLevelEntry : trieMap.getEntriesInRootLevel()) { EXPECT_EQ(trieMap.getRoot(firstLevelEntry.key()).mValue, firstLevelEntry.value()); EXPECT_EQ(firstLevelEntries[firstLevelEntry.key()], firstLevelEntry.value()); firstLevelEntries.erase(firstLevelEntry.key()); for (const auto &secondLevelEntry : firstLevelEntry.getEntriesInNextLevel()) { EXPECT_EQ(twoLevelMap[firstLevelEntry.key()][secondLevelEntry.key()], secondLevelEntry.value()); twoLevelMap[firstLevelEntry.key()].erase(secondLevelEntry.key()); for (const auto &thirdLevelEntry : secondLevelEntry.getEntriesInNextLevel()) { EXPECT_EQ(threeLevelMap[firstLevelEntry.key()][secondLevelEntry.key()] [thirdLevelEntry.key()], thirdLevelEntry.value()); threeLevelMap[firstLevelEntry.key()][secondLevelEntry.key()].erase( thirdLevelEntry.key()); } } } // Ensure all entries have been traversed. EXPECT_TRUE(firstLevelEntries.empty()); for (const auto &secondLevelEntry : twoLevelMap) { EXPECT_TRUE(secondLevelEntry.second.empty()); } for (const auto &secondLevelEntry : threeLevelMap) { for (const auto &thirdLevelEntry : secondLevelEntry.second) { EXPECT_TRUE(thirdLevelEntry.second.empty()); } } } TEST(TrieMapTest, TestIteration) { static const int ELEMENT_COUNT = 200000; TrieMap trieMap; std::unordered_map<int, uint64_t> testKeyValuePairs; // Use the uniform integer distribution [S_INT_MIN, S_INT_MAX]. std::uniform_int_distribution<int> keyDistribution(S_INT_MIN, S_INT_MAX); auto keyRandomNumberGenerator = std::bind(keyDistribution, std::mt19937()); // Use the uniform distribution [0, TrieMap::MAX_VALUE]. std::uniform_int_distribution<uint64_t> valueDistribution(0, TrieMap::MAX_VALUE); auto valueRandomNumberGenerator = std::bind(valueDistribution, std::mt19937()); for (int i = 0; i < ELEMENT_COUNT; ++i) { const int key = keyRandomNumberGenerator(); const uint64_t value = valueRandomNumberGenerator(); EXPECT_TRUE(trieMap.putRoot(key, value)); testKeyValuePairs[key] = value; } for (const auto &entry : trieMap.getEntriesInRootLevel()) { EXPECT_EQ(trieMap.getRoot(entry.key()).mValue, entry.value()); EXPECT_EQ(testKeyValuePairs[entry.key()], entry.value()); testKeyValuePairs.erase(entry.key()); } EXPECT_TRUE(testKeyValuePairs.empty()); } } } // namespace } // namespace Loading