1 /*
2  * SPDX-FileCopyrightText: 2015-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  */
6 
7 #ifndef _FCITX_LIBIME_TABLE_TABLERULE_H_
8 #define _FCITX_LIBIME_TABLE_TABLERULE_H_
9 
10 #include "libimetable_export.h"
11 
12 #include <boost/algorithm/string.hpp>
13 #include <cstdint>
14 #include <iostream>
15 #include <libime/core/utils.h>
16 #include <sstream>
17 #include <stdexcept>
18 #include <string>
19 #include <string_view>
20 #include <vector>
21 
22 namespace libime {
23 enum class TableRuleEntryFlag : std::uint32_t { FromFront, FromBack };
24 
25 enum class TableRuleFlag : std::uint32_t { LengthLongerThan, LengthEqual };
26 
27 class LIBIMETABLE_EXPORT TableRuleEntry {
28 public:
29     TableRuleEntry(TableRuleEntryFlag _flag = TableRuleEntryFlag::FromFront,
30                    uint8_t _character = 0, uint8_t _encodingIndex = 0)
flag_(_flag)31         : flag_(_flag), character_(_character), encodingIndex_(_encodingIndex) {
32     }
33 
TableRuleEntry(std::istream & in)34     explicit TableRuleEntry(std::istream &in) {
35         throw_if_io_fail(unmarshall(in, flag_));
36         throw_if_io_fail(unmarshall(in, character_));
37         throw_if_io_fail(unmarshall(in, encodingIndex_));
38     }
39 
40     FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(TableRuleEntry);
41 
42     friend std::ostream &operator<<(std::ostream &out,
43                                     const TableRuleEntry &r) {
44         marshall(out, r.flag_) && marshall(out, r.character_) &&
45             marshall(out, r.encodingIndex_);
46         return out;
47     }
48 
isPlaceHolder()49     bool isPlaceHolder() const {
50         return character_ == 0 || encodingIndex_ == 0;
51     }
52 
flag()53     TableRuleEntryFlag flag() const { return flag_; }
character()54     uint8_t character() const { return character_; }
encodingIndex()55     uint8_t encodingIndex() const { return encodingIndex_; }
56 
57 private:
58     TableRuleEntryFlag flag_;
59     uint8_t character_;
60     uint8_t encodingIndex_;
61 };
62 
63 class TableRule {
64 public:
TableRule(const std::string & ruleString,unsigned int maxLength)65     TableRule(const std::string &ruleString, unsigned int maxLength) {
66         if (!ruleString[0]) {
67             throw std::invalid_argument("invalid rule string");
68         }
69 
70         switch (ruleString[0]) {
71         case 'e':
72         case 'E':
73             flag_ = TableRuleFlag::LengthEqual;
74             break;
75 
76         case 'a':
77         case 'A':
78             flag_ = TableRuleFlag::LengthLongerThan;
79             break;
80 
81         default:
82             throw std::invalid_argument("invalid rule string");
83         }
84 
85         auto equalSignPos = ruleString.find('=', 1);
86         if (equalSignPos == std::string::npos) {
87             throw std::invalid_argument("invalid rule string");
88         }
89 
90         auto afterEqualSign =
91             std::string_view(ruleString).substr(equalSignPos + 1);
92         std::vector<std::string> entryStrings;
93         boost::split(entryStrings, afterEqualSign, boost::is_any_of("+"));
94         if (entryStrings.empty() || entryStrings.size() > maxLength) {
95             throw std::invalid_argument("invalid rule string");
96         }
97 
98         auto beforeEqualSign =
99             std::string_view(ruleString).substr(0, equalSignPos);
100         if (beforeEqualSign.size() != 2 || !isdigit(beforeEqualSign[1])) {
101             throw std::invalid_argument("invalid rule string");
102         }
103 
104         phraseLength_ = beforeEqualSign[1] - '0';
105         if (phraseLength_ <= 0 || phraseLength_ > maxLength) {
106             throw std::invalid_argument("Invalid phrase length");
107         }
108 
109         unsigned int idx = 0;
110         for (const auto &entryString : entryStrings) {
111             idx++;
112             TableRuleEntryFlag entryFlag;
113             switch (entryString[0]) {
114             case 'p':
115             case 'P':
116                 entryFlag = TableRuleEntryFlag::FromFront;
117                 break;
118             case 'n':
119             case 'N':
120                 entryFlag = TableRuleEntryFlag::FromBack;
121                 break;
122             default:
123                 throw std::invalid_argument("invalid rule entry flag");
124             }
125 
126             if (entryString.size() != 3 || !isdigit(entryString[1]) ||
127                 !isdigit(entryString[2])) {
128                 throw std::invalid_argument("invalid rule entry");
129             }
130 
131             int8_t character = entryString[1] - '0';     // 0 ~ maxLength
132             int8_t encodingIndex = entryString[2] - '0'; // 0 ~ maxLength
133             if (character < 0 || character > static_cast<int>(maxLength) ||
134                 encodingIndex < 0 ||
135                 encodingIndex > static_cast<int>(maxLength) ||
136                 ((character == 0) ^ (encodingIndex == 0))) {
137                 throw std::invalid_argument("invalid rule entry");
138             }
139 
140             entries_.push_back(
141                 TableRuleEntry(entryFlag, character, encodingIndex));
142         }
143     }
144 
145     TableRule(TableRuleFlag _flag = TableRuleFlag::LengthEqual,
146               int _phraseLength = 0, std::vector<TableRuleEntry> _entries = {})
flag_(_flag)147         : flag_(_flag), phraseLength_(_phraseLength),
148           entries_(std::move(_entries)) {}
149 
TableRule(std::istream & in)150     explicit TableRule(std::istream &in) {
151         uint32_t size;
152         throw_if_io_fail(unmarshall(in, flag_));
153         throw_if_io_fail(unmarshall(in, phraseLength_));
154         throw_if_io_fail(unmarshall(in, size));
155         entries_.reserve(size);
156         for (auto i = 0U; i < size; i++) {
157             entries_.emplace_back(in);
158         }
159     }
160 
161     FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(TableRule)
162 
163     friend std::ostream &operator<<(std::ostream &out, const TableRule &r) {
164         if (marshall(out, r.flag_) && marshall(out, r.phraseLength_) &&
165             marshall(out, static_cast<uint32_t>(r.entries_.size()))) {
166             for (const auto &entry : r.entries_) {
167                 if (!(out << entry)) {
168                     break;
169                 }
170             }
171         }
172         return out;
173     }
174 
name()175     std::string name() const {
176         std::string result;
177         result += ((flag_ == TableRuleFlag::LengthEqual) ? 'e' : 'a');
178         result += std::to_string(phraseLength_);
179 
180         return result;
181     }
182 
toString()183     std::string toString() const {
184         std::string result;
185 
186         result += name();
187         result += '=';
188         bool first = true;
189         for (const auto &entry : entries_) {
190             if (first) {
191                 first = false;
192             } else {
193                 result += '+';
194             }
195             result +=
196                 ((entry.flag() == TableRuleEntryFlag::FromFront) ? 'p' : 'n');
197             result += static_cast<char>('0' + entry.character());
198             result += static_cast<char>('0' + entry.encodingIndex());
199         }
200         return result;
201     }
202 
flag()203     TableRuleFlag flag() const { return flag_; }
phraseLength()204     uint8_t phraseLength() const { return phraseLength_; }
entries()205     const std::vector<TableRuleEntry> &entries() const { return entries_; }
codeLength()206     size_t codeLength() const {
207         size_t sum = 0;
208         for (const auto &entry : entries_) {
209             if (entry.isPlaceHolder()) {
210                 continue;
211             }
212             sum += 1;
213         }
214         return sum;
215     }
216 
217 private:
218     TableRuleFlag flag_ = TableRuleFlag::LengthLongerThan;
219     uint8_t phraseLength_ = 0;
220     std::vector<TableRuleEntry> entries_;
221 };
222 } // namespace libime
223 
224 #endif // _FCITX_LIBIME_TABLE_TABLERULE_H_
225