Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions src/libime/core/lrucache.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,32 @@
#ifndef _FCITX_LIBIME_CORE_LRU_H_
#define _FCITX_LIBIME_CORE_LRU_H_

#include <concepts>
#include <cstddef>
#include <functional>
#include <list>
#include <unordered_map>
#include <utility>
#include <boost/container_hash/hash.hpp>
#include <boost/unordered_map.hpp>

namespace libime {

// A simple LRU cache.
template <typename K, typename V, typename H = boost::hash<K>>
template <typename K, typename V, typename H = std::hash<K>,
typename E = std::equal_to<K>>
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<K, std::pair<V, typename std::list<K>::iterator>,
H>;
std::unordered_map<K, std::pair<V, typename std::list<K>::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<H> && std::default_initializable<E>)
: LRUCache(H(), E(), sz) {}

size_t size() const { return dict_.size(); }

Expand Down Expand Up @@ -71,11 +76,9 @@ class LRUCache {
return find_helper(i);
}

template <class CompatibleKey, class CompatibleHash,
class CompatiblePredicate>
value_type *find(CompatibleKey const &k, CompatibleHash const &h,
CompatiblePredicate const &p) {
return find_helper(dict_.find(k, h, p));
template <typename CompatibleKey>
value_type *find(const CompatibleKey &key) {
return find_helper(dict_.find(key));
}

void clear() {
Expand Down
119 changes: 16 additions & 103 deletions src/libime/pinyin/pinyindictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> 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 {
Expand Down Expand Up @@ -326,7 +233,7 @@ class PinyinMatchContext {
PinyinMatchContext(const PinyinMatchContext &) = delete;

const SegmentGraph &graph_;
PinyinSegmentGraphPathHasher hasher_;
PinyinStringHasher hasher_;

const GraphMatchCallback &callback_;
const std::unordered_set<const SegmentGraphNode *> &ignore_;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<MatchedPinyinTrieNodes> result;
if (!p) {
result = std::make_shared<MatchedPinyinTrieNodes>(
path.trie(), path.size() + 1);
nodeCache.insert(context.hasher_.pathToPinyins(segmentPath),
nodeCache.insert(context.hasher_.stringFor(segmentPath),
result);
result->triePositions_ =
traverseAlongPathOneStepBySyllables(path, syls);
Expand Down
117 changes: 112 additions & 5 deletions src/libime/pinyin/pinyinmatchstate_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -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<char> hasher;

Expand All @@ -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<char> 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
Expand All @@ -107,12 +213,13 @@ using NodeToMatchedPinyinPathsMap =
using PinyinTrieNodeCache = std::unordered_map<
const PinyinTrie *,
LRUCache<std::string, std::shared_ptr<MatchedPinyinTrieNodes>,
PinyinStringHasher>>;
PinyinStringHasher, PinyinStringEqual>>;

// A cache for PinyinMatchResult.
using PinyinMatchResultCache = std::unordered_map<
const PinyinTrie *,
LRUCache<std::string, std::vector<PinyinMatchResult>, PinyinStringHasher>>;
using PinyinMatchResultCache =
std::unordered_map<const PinyinTrie *,
LRUCache<std::string, std::vector<PinyinMatchResult>,
PinyinStringHasher, PinyinStringEqual>>;

class PinyinMatchStatePrivate {
public:
Expand Down
Loading