diff --git a/src/libime/core/lrucache.h b/src/libime/core/lrucache.h index ce8d447..fe3a26a 100644 --- a/src/libime/core/lrucache.h +++ b/src/libime/core/lrucache.h @@ -6,27 +6,32 @@ #ifndef _FCITX_LIBIME_CORE_LRU_H_ #define _FCITX_LIBIME_CORE_LRU_H_ +#include #include +#include #include +#include #include -#include -#include namespace libime { // A simple LRU cache. -template > +template , + typename E = std::equal_to> class LRUCache { public: using key_type = K; using value_type = V; - // we use boost's unordered_map is for the heterogeneous lookup - // functionality. using dict_type = - boost::unordered_map::iterator>, - H>; + std::unordered_map::iterator>, H, + E>; - LRUCache(size_t sz = 80) : sz_(sz) {} + LRUCache(const H &hash, const E &equal, size_t sz = 80) + : dict_(0, hash, equal), sz_(sz) {} + + LRUCache(size_t sz = 80) + requires(std::default_initializable && std::default_initializable) + : LRUCache(H(), E(), sz) {} size_t size() const { return dict_.size(); } @@ -71,11 +76,9 @@ class LRUCache { return find_helper(i); } - template - value_type *find(CompatibleKey const &k, CompatibleHash const &h, - CompatiblePredicate const &p) { - return find_helper(dict_.find(k, h, p)); + template + value_type *find(const CompatibleKey &key) { + return find_helper(dict_.find(key)); } void clear() { diff --git a/src/libime/pinyin/pinyindictionary.cpp b/src/libime/pinyin/pinyindictionary.cpp index 047ee3c..71da023 100644 --- a/src/libime/pinyin/pinyindictionary.cpp +++ b/src/libime/pinyin/pinyindictionary.cpp @@ -58,99 +58,6 @@ const char pinyinHanziSep = '!'; constexpr uint32_t pinyinBinaryFormatMagic = 0x000fc613; constexpr uint32_t pinyinBinaryFormatVersion = 0x2; -struct PinyinSegmentGraphPathHasher { - PinyinSegmentGraphPathHasher(const SegmentGraph &graph) : graph_(graph) {} - - // Generate a "|" separated raw pinyin string from given path, skip all - // separator. - std::string pathToPinyins(const SegmentGraphPath &path) const { - std::string result; - result.reserve(path.size() + path.back()->index() - - path.front()->index() + 1); - const auto &data = graph_.data(); - auto iter = path.begin(); - while (iter + 1 < path.end()) { - auto begin = (*iter)->index(); - auto end = (*std::next(iter))->index(); - iter++; - if (data[begin] == '\'') { - continue; - } - while (begin < end) { - result.push_back(data[begin]); - begin++; - } - result.push_back('|'); - } - return result; - } - - // Generate hash for path but avoid allocate the string. - size_t operator()(const SegmentGraphPath &path) const { - if (path.size() <= 1) { - return 0; - } - boost::hash hasher; - - size_t seed = 0; - const auto &data = graph_.data(); - auto iter = path.begin(); - while (iter + 1 < path.end()) { - auto begin = (*iter)->index(); - auto end = (*std::next(iter))->index(); - iter++; - if (data[begin] == '\'') { - continue; - } - while (begin < end) { - boost::hash_combine(seed, hasher(data[begin])); - begin++; - } - boost::hash_combine(seed, hasher('|')); - } - return seed; - } - - // Check equality of pinyin string and the path. The string s should be - // equal to pathToPinyins(path), but this function just try to avoid - // allocate a string for comparison. - bool operator()(const SegmentGraphPath &path, const std::string &s) const { - if (path.size() <= 1) { - return false; - } - auto is = s.begin(); - const auto &data = graph_.data(); - auto iter = path.begin(); - while (iter + 1 < path.end() && is != s.end()) { - auto begin = (*iter)->index(); - auto end = (*std::next(iter))->index(); - iter++; - if (data[begin] == '\'') { - continue; - } - while (begin < end && is != s.end()) { - if (*is != data[begin]) { - return false; - } - is++; - begin++; - } - if (begin != end) { - return false; - } - - if (is == s.end() || *is != '|') { - return false; - } - is++; - } - return iter + 1 == path.end() && is == s.end(); - } - -private: - const SegmentGraph &graph_; -}; - struct SegmentGraphNodeGreater { bool operator()(const SegmentGraphNode *lhs, const SegmentGraphNode *rhs) const { @@ -326,7 +233,7 @@ class PinyinMatchContext { PinyinMatchContext(const PinyinMatchContext &) = delete; const SegmentGraph &graph_; - PinyinSegmentGraphPathHasher hasher_; + PinyinStringHasher hasher_; const GraphMatchCallback &callback_; const std::unordered_set &ignore_; @@ -541,12 +448,14 @@ bool PinyinDictionaryPrivate::matchWordsForOnePath( }; if (context.matchCacheMap_) { - auto &matchCache = (*context.matchCacheMap_)[path.trie()]; - auto *result = - matchCache.find(path.path_, context.hasher_, context.hasher_); + auto &matchCache = + context.matchCacheMap_ + ->try_emplace(path.trie(), PinyinStringHasher(context.graph_), + PinyinStringEqual(context.graph_)) + .first->second; + auto *result = matchCache.find(path.path_); if (!result) { - result = - matchCache.insert(context.hasher_.pathToPinyins(path.path_)); + result = matchCache.insert(context.hasher_.stringFor(path.path_)); result->clear(); auto &items = *result; @@ -632,14 +541,18 @@ void PinyinDictionaryPrivate::findMatchesBetween( // A map from trie (dict) to a lru cache. if (context.nodeCacheMap_) { - auto &nodeCache = (*context.nodeCacheMap_)[path.trie()]; - auto *p = - nodeCache.find(segmentPath, context.hasher_, context.hasher_); + auto &nodeCache = + context.nodeCacheMap_ + ->try_emplace(path.trie(), + PinyinStringHasher(context.graph_), + PinyinStringEqual(context.graph_)) + .first->second; + auto *p = nodeCache.find(segmentPath); std::shared_ptr result; if (!p) { result = std::make_shared( path.trie(), path.size() + 1); - nodeCache.insert(context.hasher_.pathToPinyins(segmentPath), + nodeCache.insert(context.hasher_.stringFor(segmentPath), result); result->triePositions_ = traverseAlongPathOneStepBySyllables(path, syls); diff --git a/src/libime/pinyin/pinyinmatchstate_p.h b/src/libime/pinyin/pinyinmatchstate_p.h index 6a0c51f..8db647f 100644 --- a/src/libime/pinyin/pinyinmatchstate_p.h +++ b/src/libime/pinyin/pinyinmatchstate_p.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -81,9 +82,12 @@ struct MatchedPinyinPath { PinyinDictFlags flags_; }; -// This need to be keep sync with PinyinSegmentGraphPathHasher class PinyinStringHasher { public: + explicit PinyinStringHasher(const SegmentGraph &graph) : graph_(graph) {} + + using is_transparent = void; + size_t operator()(const std::string &s) const { boost::hash hasher; @@ -93,6 +97,108 @@ class PinyinStringHasher { } return seed; } + + std::string stringFor(const SegmentGraphPath &path) const { + std::string result; + result.reserve(path.size() + path.back()->index() - + path.front()->index() + 1); + const auto &data = graph_.data(); + auto iter = path.begin(); + while (iter + 1 < path.end()) { + auto begin = (*iter)->index(); + auto end = (*std::next(iter))->index(); + iter++; + if (data[begin] == '\'') { + continue; + } + while (begin < end) { + result.push_back(data[begin]); + begin++; + } + result.push_back('|'); + } + return result; + } + + size_t operator()(const SegmentGraphPath &path) const { + if (path.size() <= 1) { + return 0; + } + + boost::hash hasher; + size_t seed = 0; + const auto &data = graph_.data(); + auto iter = path.begin(); + while (iter + 1 < path.end()) { + auto begin = (*iter)->index(); + auto end = (*std::next(iter))->index(); + iter++; + if (data[begin] == '\'') { + continue; + } + while (begin < end) { + boost::hash_combine(seed, hasher(data[begin])); + begin++; + } + boost::hash_combine(seed, hasher('|')); + } + return seed; + } + +private: + const SegmentGraph &graph_; +}; + +class PinyinStringEqual { +public: + explicit PinyinStringEqual(const SegmentGraph &graph) : graph_(graph) {} + + using is_transparent = void; + + bool operator()(const std::string &lhs, const std::string &rhs) const { + return lhs == rhs; + } + + bool operator()(const SegmentGraphPath &path, const std::string &s) const { + if (path.size() <= 1) { + return false; + } + + auto is = s.begin(); + const auto &data = graph_.data(); + auto iter = path.begin(); + while (iter + 1 < path.end() && is != s.end()) { + auto begin = (*iter)->index(); + auto end = (*std::next(iter))->index(); + iter++; + if (data[begin] == '\'') { + continue; + } + while (begin < end && is != s.end()) { + if (*is != data[begin]) { + return false; + } + is++; + begin++; + } + if (begin != end) { + return false; + } + + if (is == s.end() || *is != '|') { + return false; + } + is++; + } + return iter + 1 == path.end() && is == s.end(); + } + + bool operator()(const std::string &s, const SegmentGraphPath &path) const { + return (*this)(path, s); + } + +private: + const SegmentGraph &graph_; }; // A list of all search paths @@ -107,12 +213,13 @@ using NodeToMatchedPinyinPathsMap = using PinyinTrieNodeCache = std::unordered_map< const PinyinTrie *, LRUCache, - PinyinStringHasher>>; + PinyinStringHasher, PinyinStringEqual>>; // A cache for PinyinMatchResult. -using PinyinMatchResultCache = std::unordered_map< - const PinyinTrie *, - LRUCache, PinyinStringHasher>>; +using PinyinMatchResultCache = + std::unordered_map, + PinyinStringHasher, PinyinStringEqual>>; class PinyinMatchStatePrivate { public: