Merge "Add TrieMap::remove()."
commit
9481d088d7
|
@ -26,6 +26,7 @@ const int TrieMap::FIELD1_SIZE = 3;
|
||||||
const int TrieMap::ENTRY_SIZE = FIELD0_SIZE + FIELD1_SIZE;
|
const int TrieMap::ENTRY_SIZE = FIELD0_SIZE + FIELD1_SIZE;
|
||||||
const uint32_t TrieMap::VALUE_FLAG = 0x400000;
|
const uint32_t TrieMap::VALUE_FLAG = 0x400000;
|
||||||
const uint32_t TrieMap::VALUE_MASK = 0x3FFFFF;
|
const uint32_t TrieMap::VALUE_MASK = 0x3FFFFF;
|
||||||
|
const uint32_t TrieMap::INVALID_VALUE_IN_KEY_VALUE_ENTRY = VALUE_MASK;
|
||||||
const uint32_t TrieMap::TERMINAL_LINK_FLAG = 0x800000;
|
const uint32_t TrieMap::TERMINAL_LINK_FLAG = 0x800000;
|
||||||
const uint32_t TrieMap::TERMINAL_LINK_MASK = 0x7FFFFF;
|
const uint32_t TrieMap::TERMINAL_LINK_MASK = 0x7FFFFF;
|
||||||
const int TrieMap::NUM_OF_BITS_USED_FOR_ONE_LEVEL = 5;
|
const int TrieMap::NUM_OF_BITS_USED_FOR_ONE_LEVEL = 5;
|
||||||
|
@ -34,6 +35,7 @@ const int TrieMap::MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL = 1 << NUM_OF_BITS_USED_FOR_O
|
||||||
const int TrieMap::ROOT_BITMAP_ENTRY_INDEX = 0;
|
const int TrieMap::ROOT_BITMAP_ENTRY_INDEX = 0;
|
||||||
const int TrieMap::ROOT_BITMAP_ENTRY_POS = MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL * FIELD0_SIZE;
|
const int TrieMap::ROOT_BITMAP_ENTRY_POS = MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL * FIELD0_SIZE;
|
||||||
const TrieMap::Entry TrieMap::EMPTY_BITMAP_ENTRY = TrieMap::Entry(0, 0);
|
const TrieMap::Entry TrieMap::EMPTY_BITMAP_ENTRY = TrieMap::Entry(0, 0);
|
||||||
|
const int TrieMap::TERMINAL_LINKED_ENTRY_COUNT = 2; // Value entry and bitmap entry.
|
||||||
const uint64_t TrieMap::MAX_VALUE =
|
const uint64_t TrieMap::MAX_VALUE =
|
||||||
(static_cast<uint64_t>(1) << ((FIELD0_SIZE + FIELD1_SIZE) * CHAR_BIT)) - 1;
|
(static_cast<uint64_t>(1) << ((FIELD0_SIZE + FIELD1_SIZE) * CHAR_BIT)) - 1;
|
||||||
const int TrieMap::MAX_BUFFER_SIZE = TERMINAL_LINK_MASK * ENTRY_SIZE;
|
const int TrieMap::MAX_BUFFER_SIZE = TERMINAL_LINK_MASK * ENTRY_SIZE;
|
||||||
|
@ -76,7 +78,7 @@ int TrieMap::getNextLevelBitmapEntryIndex(const int key, const int bitmapEntryIn
|
||||||
return terminalEntry.getValueEntryIndex() + 1;
|
return terminalEntry.getValueEntryIndex() + 1;
|
||||||
}
|
}
|
||||||
// Create a value entry and a bitmap entry.
|
// Create a value entry and a bitmap entry.
|
||||||
const int valueEntryIndex = allocateTable(2 /* entryCount */);
|
const int valueEntryIndex = allocateTable(TERMINAL_LINKED_ENTRY_COUNT);
|
||||||
if (!writeEntry(Entry(0, terminalEntry.getValue()), valueEntryIndex)) {
|
if (!writeEntry(Entry(0, terminalEntry.getValue()), valueEntryIndex)) {
|
||||||
return INVALID_INDEX;
|
return INVALID_INDEX;
|
||||||
}
|
}
|
||||||
|
@ -108,6 +110,31 @@ bool TrieMap::save(FILE *const file) const {
|
||||||
return DictFileWritingUtils::writeBufferToFileTail(file, &mBuffer);
|
return DictFileWritingUtils::writeBufferToFileTail(file, &mBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TrieMap::remove(const int key, const int bitmapEntryIndex) {
|
||||||
|
const Entry bitmapEntry = readEntry(bitmapEntryIndex);
|
||||||
|
const uint32_t unsignedKey = static_cast<uint32_t>(key);
|
||||||
|
const int terminalEntryIndex = getTerminalEntryIndex(
|
||||||
|
unsignedKey, getBitShuffledKey(unsignedKey), bitmapEntry, 0 /* level */);
|
||||||
|
if (terminalEntryIndex == INVALID_INDEX) {
|
||||||
|
// Not found.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const Entry terminalEntry = readEntry(terminalEntryIndex);
|
||||||
|
if (!writeField1(VALUE_FLAG ^ INVALID_VALUE_IN_KEY_VALUE_ENTRY , terminalEntryIndex)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (terminalEntry.hasTerminalLink()) {
|
||||||
|
const Entry nextLevelBitmapEntry = readEntry(terminalEntry.getValueEntryIndex() + 1);
|
||||||
|
if (!freeTable(terminalEntry.getValueEntryIndex(), TERMINAL_LINKED_ENTRY_COUNT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!removeInner(nextLevelBitmapEntry)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate next entry in a certain level.
|
* Iterate next entry in a certain level.
|
||||||
*
|
*
|
||||||
|
@ -129,7 +156,7 @@ const TrieMap::Result TrieMap::iterateNext(std::vector<TableIterationState> *con
|
||||||
if (entry.isBitmapEntry()) {
|
if (entry.isBitmapEntry()) {
|
||||||
// Move to child.
|
// Move to child.
|
||||||
iterationState->emplace_back(popCount(entry.getBitmap()), entry.getTableIndex());
|
iterationState->emplace_back(popCount(entry.getBitmap()), entry.getTableIndex());
|
||||||
} else {
|
} else if (entry.isValidTerminalEntry()) {
|
||||||
if (outKey) {
|
if (outKey) {
|
||||||
*outKey = entry.getKey();
|
*outKey = entry.getKey();
|
||||||
}
|
}
|
||||||
|
@ -162,12 +189,12 @@ uint32_t TrieMap::getBitShuffledKey(const uint32_t key) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TrieMap::writeValue(const uint64_t value, const int terminalEntryIndex) {
|
bool TrieMap::writeValue(const uint64_t value, const int terminalEntryIndex) {
|
||||||
if (value <= VALUE_MASK) {
|
if (value < VALUE_MASK) {
|
||||||
// Write value into the terminal entry.
|
// Write value into the terminal entry.
|
||||||
return writeField1(value | VALUE_FLAG, terminalEntryIndex);
|
return writeField1(value | VALUE_FLAG, terminalEntryIndex);
|
||||||
}
|
}
|
||||||
// Create value entry and write value.
|
// Create value entry and write value.
|
||||||
const int valueEntryIndex = allocateTable(2 /* entryCount */);
|
const int valueEntryIndex = allocateTable(TERMINAL_LINKED_ENTRY_COUNT);
|
||||||
if (!writeEntry(Entry(value >> (FIELD1_SIZE * CHAR_BIT), value), valueEntryIndex)) {
|
if (!writeEntry(Entry(value >> (FIELD1_SIZE * CHAR_BIT), value), valueEntryIndex)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -227,6 +254,9 @@ int TrieMap::getTerminalEntryIndex(const uint32_t key, const uint32_t hashedKey,
|
||||||
// Move to the next level.
|
// Move to the next level.
|
||||||
return getTerminalEntryIndex(key, hashedKey, entry, level + 1);
|
return getTerminalEntryIndex(key, hashedKey, entry, level + 1);
|
||||||
}
|
}
|
||||||
|
if (!entry.isValidTerminalEntry()) {
|
||||||
|
return INVALID_INDEX;
|
||||||
|
}
|
||||||
if (entry.getKey() == key) {
|
if (entry.getKey() == key) {
|
||||||
// Terminal entry is found.
|
// Terminal entry is found.
|
||||||
return entryIndex;
|
return entryIndex;
|
||||||
|
@ -287,6 +317,10 @@ bool TrieMap::putInternal(const uint32_t key, const uint64_t value, const uint32
|
||||||
// Bitmap entry is found. Go to the next level.
|
// Bitmap entry is found. Go to the next level.
|
||||||
return putInternal(key, value, hashedKey, entryIndex, entry, level + 1);
|
return putInternal(key, value, hashedKey, entryIndex, entry, level + 1);
|
||||||
}
|
}
|
||||||
|
if (!entry.isValidTerminalEntry()) {
|
||||||
|
// Overwrite invalid terminal entry.
|
||||||
|
return writeTerminalEntry(key, value, entryIndex);
|
||||||
|
}
|
||||||
if (entry.getKey() == key) {
|
if (entry.getKey() == key) {
|
||||||
// Terminal entry for the key is found. Update the value.
|
// Terminal entry for the key is found. Update the value.
|
||||||
return updateValue(entry, value, entryIndex);
|
return updateValue(entry, value, entryIndex);
|
||||||
|
@ -384,4 +418,33 @@ bool TrieMap::addNewEntryByExpandingTable(const uint32_t key, const uint64_t val
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TrieMap::removeInner(const Entry &bitmapEntry) {
|
||||||
|
const int tableSize = popCount(bitmapEntry.getBitmap());
|
||||||
|
for (int i = 0; i < tableSize; ++i) {
|
||||||
|
const int entryIndex = bitmapEntry.getTableIndex() + i;
|
||||||
|
const Entry entry = readEntry(entryIndex);
|
||||||
|
if (entry.isBitmapEntry()) {
|
||||||
|
// Delete next bitmap entry recursively.
|
||||||
|
if (!removeInner(entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Invalidate terminal entry just in case.
|
||||||
|
if (!writeField1(VALUE_FLAG ^ INVALID_VALUE_IN_KEY_VALUE_ENTRY , entryIndex)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (entry.hasTerminalLink()) {
|
||||||
|
const Entry nextLevelBitmapEntry = readEntry(entry.getValueEntryIndex() + 1);
|
||||||
|
if (!freeTable(entry.getValueEntryIndex(), TERMINAL_LINKED_ENTRY_COUNT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!removeInner(nextLevelBitmapEntry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return freeTable(bitmapEntry.getTableIndex(), tableSize);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
|
|
|
@ -202,6 +202,8 @@ class TrieMap {
|
||||||
|
|
||||||
bool save(FILE *const file) const;
|
bool save(FILE *const file) const;
|
||||||
|
|
||||||
|
bool remove(const int key, const int bitmapEntryIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(TrieMap);
|
DISALLOW_COPY_AND_ASSIGN(TrieMap);
|
||||||
|
|
||||||
|
@ -244,6 +246,11 @@ class TrieMap {
|
||||||
return mData1 & VALUE_MASK;
|
return mData1 & VALUE_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For terminal entry.
|
||||||
|
AK_FORCE_INLINE bool isValidTerminalEntry() const {
|
||||||
|
return hasTerminalLink() || ((mData1 & VALUE_MASK) != INVALID_VALUE_IN_KEY_VALUE_ENTRY);
|
||||||
|
}
|
||||||
|
|
||||||
// For terminal entry.
|
// For terminal entry.
|
||||||
AK_FORCE_INLINE uint32_t getValueEntryIndex() const {
|
AK_FORCE_INLINE uint32_t getValueEntryIndex() const {
|
||||||
return mData1 & TERMINAL_LINK_MASK;
|
return mData1 & TERMINAL_LINK_MASK;
|
||||||
|
@ -272,6 +279,7 @@ class TrieMap {
|
||||||
static const int ENTRY_SIZE;
|
static const int ENTRY_SIZE;
|
||||||
static const uint32_t VALUE_FLAG;
|
static const uint32_t VALUE_FLAG;
|
||||||
static const uint32_t VALUE_MASK;
|
static const uint32_t VALUE_MASK;
|
||||||
|
static const uint32_t INVALID_VALUE_IN_KEY_VALUE_ENTRY;
|
||||||
static const uint32_t TERMINAL_LINK_FLAG;
|
static const uint32_t TERMINAL_LINK_FLAG;
|
||||||
static const uint32_t TERMINAL_LINK_MASK;
|
static const uint32_t TERMINAL_LINK_MASK;
|
||||||
static const int NUM_OF_BITS_USED_FOR_ONE_LEVEL;
|
static const int NUM_OF_BITS_USED_FOR_ONE_LEVEL;
|
||||||
|
@ -280,6 +288,7 @@ class TrieMap {
|
||||||
static const int ROOT_BITMAP_ENTRY_INDEX;
|
static const int ROOT_BITMAP_ENTRY_INDEX;
|
||||||
static const int ROOT_BITMAP_ENTRY_POS;
|
static const int ROOT_BITMAP_ENTRY_POS;
|
||||||
static const Entry EMPTY_BITMAP_ENTRY;
|
static const Entry EMPTY_BITMAP_ENTRY;
|
||||||
|
static const int TERMINAL_LINKED_ENTRY_COUNT;
|
||||||
static const int MAX_BUFFER_SIZE;
|
static const int MAX_BUFFER_SIZE;
|
||||||
|
|
||||||
uint32_t getBitShuffledKey(const uint32_t key) const;
|
uint32_t getBitShuffledKey(const uint32_t key) const;
|
||||||
|
@ -378,6 +387,8 @@ class TrieMap {
|
||||||
AK_FORCE_INLINE int getTailEntryIndex() const {
|
AK_FORCE_INLINE int getTailEntryIndex() const {
|
||||||
return (mBuffer.getTailPosition() - ROOT_BITMAP_ENTRY_POS) / ENTRY_SIZE;
|
return (mBuffer.getTailPosition() - ROOT_BITMAP_ENTRY_POS) / ENTRY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool removeInner(const Entry &bitmapEntry);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace latinime
|
} // namespace latinime
|
||||||
|
|
|
@ -47,6 +47,31 @@ TEST(TrieMapTest, TestSetAndGet) {
|
||||||
EXPECT_EQ(0xFFFFFFFFFull, trieMap.getRoot(0).mValue);
|
EXPECT_EQ(0xFFFFFFFFFull, trieMap.getRoot(0).mValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(TrieMapTest, TestRemove) {
|
||||||
|
TrieMap trieMap;
|
||||||
|
trieMap.putRoot(10, 10);
|
||||||
|
EXPECT_EQ(10ull, trieMap.getRoot(10).mValue);
|
||||||
|
EXPECT_TRUE(trieMap.remove(10, trieMap.getRootBitmapEntryIndex()));
|
||||||
|
EXPECT_FALSE(trieMap.getRoot(10).mIsValid);
|
||||||
|
for (const auto &element : trieMap.getEntriesInRootLevel()) {
|
||||||
|
EXPECT_TRUE(false);
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(trieMap.putRoot(10, 0x3FFFFF));
|
||||||
|
EXPECT_FALSE(trieMap.remove(11, trieMap.getRootBitmapEntryIndex()))
|
||||||
|
<< "Should fail if the key does not exist.";
|
||||||
|
EXPECT_EQ(0x3FFFFFull, trieMap.getRoot(10).mValue);
|
||||||
|
trieMap.putRoot(12, 11);
|
||||||
|
const int nextLevel = trieMap.getNextLevelBitmapEntryIndex(10);
|
||||||
|
trieMap.put(10, 10, nextLevel);
|
||||||
|
EXPECT_EQ(0x3FFFFFull, trieMap.getRoot(10).mValue);
|
||||||
|
EXPECT_EQ(10ull, trieMap.get(10, nextLevel).mValue);
|
||||||
|
EXPECT_TRUE(trieMap.remove(10, trieMap.getRootBitmapEntryIndex()));
|
||||||
|
const TrieMap::Result result = trieMap.getRoot(10);
|
||||||
|
EXPECT_FALSE(result.mIsValid);
|
||||||
|
EXPECT_EQ(TrieMap::INVALID_INDEX, result.mNextLevelBitmapEntryIndex);
|
||||||
|
EXPECT_EQ(11ull, trieMap.getRoot(12).mValue);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(TrieMapTest, TestSetAndGetLarge) {
|
TEST(TrieMapTest, TestSetAndGetLarge) {
|
||||||
static const int ELEMENT_COUNT = 200000;
|
static const int ELEMENT_COUNT = 200000;
|
||||||
TrieMap trieMap;
|
TrieMap trieMap;
|
||||||
|
|
Loading…
Reference in New Issue