Skip to content

Commit 2316c36

Browse files
Improve performance and readablility
1 parent 27002c5 commit 2316c36

File tree

2 files changed

+90
-53
lines changed

2 files changed

+90
-53
lines changed

Flow.Launcher.Infrastructure/PinyinAlphabet.cs

Lines changed: 80 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,8 @@ namespace Flow.Launcher.Infrastructure
1414
{
1515
public class PinyinAlphabet : IAlphabet
1616
{
17-
private ConcurrentDictionary<string, (string translation, TranslationMapping map)> _pinyinCache =
18-
new();
19-
17+
private readonly ConcurrentDictionary<string, (string translation, TranslationMapping map)> _pinyinCache = new();
2018
private readonly Settings _settings;
21-
2219
private ReadOnlyDictionary<string, string> currentDoublePinyinTable;
2320

2421
public PinyinAlphabet()
@@ -44,105 +41,145 @@ public void Reload()
4441

4542
private void CreateDoublePinyinTableFromStream(Stream jsonStream)
4643
{
47-
Dictionary<string, Dictionary<string, string>> table = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(jsonStream);
48-
string schemaKey = _settings.DoublePinyinSchema.ToString(); // Convert enum to string
49-
if (!table.TryGetValue(schemaKey, out var value))
44+
var table = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(jsonStream);
45+
if (table == null)
46+
{
47+
throw new InvalidOperationException("Failed to deserialize double pinyin table: result is null");
48+
}
49+
50+
var schemaKey = _settings.DoublePinyinSchema.ToString();
51+
if (!table.TryGetValue(schemaKey, out var schemaDict))
5052
{
51-
throw new ArgumentException("DoublePinyinSchema is invalid or double pinyin table is broken.");
53+
throw new ArgumentException($"DoublePinyinSchema '{schemaKey}' is invalid or double pinyin table is broken.");
5254
}
53-
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(value);
55+
56+
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(schemaDict);
5457
}
5558

5659
private void LoadDoublePinyinTable()
5760
{
58-
if (_settings.UseDoublePinyin)
61+
if (!_settings.UseDoublePinyin)
5962
{
60-
var tablePath = Path.Join(AppContext.BaseDirectory, "Resources", "double_pinyin.json");
61-
try
62-
{
63-
using var fs = File.OpenRead(tablePath);
64-
CreateDoublePinyinTableFromStream(fs);
65-
}
66-
catch (System.Exception e)
67-
{
68-
Log.Exception(nameof(PinyinAlphabet), "Failed to load double pinyin table from file: " + tablePath, e);
69-
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>());
70-
}
63+
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>());
64+
return;
65+
}
66+
67+
var tablePath = Path.Combine(AppContext.BaseDirectory, "Resources", "double_pinyin.json");
68+
try
69+
{
70+
using var fs = File.OpenRead(tablePath);
71+
CreateDoublePinyinTableFromStream(fs);
7172
}
72-
else
73+
catch (FileNotFoundException e)
7374
{
75+
Log.Exception(nameof(PinyinAlphabet), $"Double pinyin table file not found: {tablePath}", e);
76+
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>());
77+
}
78+
catch (DirectoryNotFoundException e)
79+
{
80+
Log.Exception(nameof(PinyinAlphabet), $"Directory not found for double pinyin table: {tablePath}", e);
81+
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>());
82+
}
83+
catch (UnauthorizedAccessException e)
84+
{
85+
Log.Exception(nameof(PinyinAlphabet), $"Access denied to double pinyin table: {tablePath}", e);
86+
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>());
87+
}
88+
catch (System.Exception e)
89+
{
90+
Log.Exception(nameof(PinyinAlphabet), $"Failed to load double pinyin table from file: {tablePath}", e);
7491
currentDoublePinyinTable = new ReadOnlyDictionary<string, string>(new Dictionary<string, string>());
7592
}
7693
}
7794

7895
public bool ShouldTranslate(string stringToTranslate)
7996
{
80-
// If a string has Chinese characters, we don't need to translate it to pinyin.
81-
return _settings.ShouldUsePinyin && !WordsHelper.HasChinese(stringToTranslate);
97+
// If the query (stringToTranslate) does NOT contain Chinese characters,
98+
// we should translate the target string to pinyin for matching
99+
return _settings.ShouldUsePinyin && !ContainsChinese(stringToTranslate);
82100
}
83101

84102
public (string translation, TranslationMapping map) Translate(string content)
85103
{
86-
if (!_settings.ShouldUsePinyin || !WordsHelper.HasChinese(content))
104+
if (!_settings.ShouldUsePinyin || !ContainsChinese(content))
87105
return (content, null);
88106

89-
return _pinyinCache.TryGetValue(content, out var value)
90-
? value
91-
: BuildCacheFromContent(content);
107+
return _pinyinCache.TryGetValue(content, out var cached) ? cached : BuildCacheFromContent(content);
92108
}
93109

94110
private (string translation, TranslationMapping map) BuildCacheFromContent(string content)
95111
{
96112
var resultList = WordsHelper.GetPinyinList(content);
97-
98-
var resultBuilder = new StringBuilder();
113+
var resultBuilder = new StringBuilder(_settings.UseDoublePinyin ? 3 : 4); // Pre-allocate with estimated capacity
99114
var map = new TranslationMapping();
100115

101116
var previousIsChinese = false;
102117

103118
for (var i = 0; i < resultList.Length; i++)
104119
{
105-
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
120+
if (IsChineseCharacter(content[i]))
106121
{
107-
string translated = _settings.UseDoublePinyin ? ToDoublePin(resultList[i]) : resultList[i];
122+
var translated = _settings.UseDoublePinyin ? ToDoublePinyin(resultList[i]) : resultList[i];
123+
108124
if (i > 0)
109125
{
110126
resultBuilder.Append(' ');
111127
}
128+
112129
map.AddNewIndex(resultBuilder.Length, translated.Length);
113130
resultBuilder.Append(translated);
114131
previousIsChinese = true;
115132
}
116133
else
117134
{
135+
// Add space after Chinese characters before non-Chinese characters
118136
if (previousIsChinese)
119137
{
120138
previousIsChinese = false;
121139
resultBuilder.Append(' ');
122140
}
141+
123142
map.AddNewIndex(resultBuilder.Length, resultList[i].Length);
124143
resultBuilder.Append(resultList[i]);
125144
}
126145
}
127146

128-
map.endConstruct();
147+
map.EndConstruct();
129148

130-
var key = resultBuilder.ToString();
131-
132-
return _pinyinCache[content] = (key, map);
149+
var translation = resultBuilder.ToString();
150+
var result = (translation, map);
151+
152+
return _pinyinCache[content] = result;
133153
}
134154

135-
#region Double Pinyin
136-
137-
private string ToDoublePin(string fullPinyin)
155+
/// <summary>
156+
/// Optimized Chinese character detection using the comprehensive CJK Unicode ranges
157+
/// </summary>
158+
private static bool ContainsChinese(ReadOnlySpan<char> text)
138159
{
139-
if (currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue))
160+
foreach (var c in text)
140161
{
141-
return doublePinyinValue;
162+
if (IsChineseCharacter(c))
163+
return true;
142164
}
143-
return fullPinyin;
165+
return false;
144166
}
145167

146-
#endregion
168+
/// <summary>
169+
/// Check if a character is a Chinese character using comprehensive Unicode ranges
170+
/// Covers CJK Unified Ideographs, Extension A
171+
/// </summary>
172+
private static bool IsChineseCharacter(char c)
173+
{
174+
return (c >= 0x4E00 && c <= 0x9FFF) || // CJK Unified Ideographs
175+
(c >= 0x3400 && c <= 0x4DBF); // CJK Extension A
176+
}
177+
178+
private string ToDoublePinyin(string fullPinyin)
179+
{
180+
return currentDoublePinyinTable.TryGetValue(fullPinyin, out var doublePinyinValue)
181+
? doublePinyinValue
182+
: fullPinyin;
183+
}
147184
}
148185
}

Flow.Launcher.Infrastructure/TranslationMapping.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,31 @@ namespace Flow.Launcher.Infrastructure
66
{
77
public class TranslationMapping
88
{
9-
private bool constructed;
9+
private bool _isConstructed;
1010

1111
// Assuming one original item maps to multi translated items
1212
// list[i] is the last translated index + 1 of original index i
13-
private readonly List<int> originalToTranslated = new List<int>();
13+
private readonly List<int> _originalToTranslated = new();
1414

1515
public void AddNewIndex(int translatedIndex, int length)
1616
{
17-
if (constructed)
18-
throw new InvalidOperationException("Mapping shouldn't be changed after constructed");
17+
if (_isConstructed)
18+
throw new InvalidOperationException("Mapping shouldn't be changed after construction");
1919

20-
originalToTranslated.Add(translatedIndex + length);
20+
_originalToTranslated.Add(translatedIndex + length);
2121
}
2222

2323
public int MapToOriginalIndex(int translatedIndex)
2424
{
25-
int loc = originalToTranslated.BinarySearch(translatedIndex);
26-
return loc >= 0 ? loc : ~loc;
25+
var searchResult = _originalToTranslated.BinarySearch(translatedIndex);
26+
return searchResult >= 0 ? searchResult : ~searchResult;
2727
}
2828

29-
public void endConstruct()
29+
public void EndConstruct()
3030
{
31-
if (constructed)
31+
if (_isConstructed)
3232
throw new InvalidOperationException("Mapping has already been constructed");
33-
constructed = true;
33+
_isConstructed = true;
3434
}
3535
}
3636
}

0 commit comments

Comments
 (0)