forked from ElektraInitiative/libelektra
-
Notifications
You must be signed in to change notification settings - Fork 0
/
coder.cpp
292 lines (257 loc) · 7.65 KB
/
coder.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/**
* @file
*
* @brief Implementation of string encoding and decoding class
*
* @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
*
*/
#include <vector>
#include "coder.hpp"
namespace
{
/**
* @brief This function maps hex characters to integer numbers.
*
* @pre The specified character has to be between
*
* - `'0'`–`'9'`,
* - `'a'`-`'f'`, or
* - `'A'`-`'F'`
*
* .
*
* @param character This argument specifies the (hexadecimal) character this function converts.
*
* @return An integer number between `0` and `15` if the precondition is valid or `0` otherwise
*/
int elektraHexcodeConvFromHex (char const character)
{
if (character >= '0' && character <= '9') return character - '0';
if (character >= 'a' && character <= 'f') return character - 'a' + 10;
if (character >= 'A' && character <= 'F') return character - 'A' + 10;
return 0; /* Unknown escape char */
}
} // end namespace
namespace elektra
{
using CppKeySet = kdb::KeySet;
using CppKey = kdb::Key;
/**
* @brief This function sets default values for the encoding and decoding character mapping.
*/
void Coder::setDefaultConfig ()
{
unsigned char pairs[][2] = { { '\b', 'b' }, { '\t', 't' }, { '\n', 'n' }, { '\v', 'v' }, { '\f', 'f' },
{ '\r', 'r' }, { '\\', '\\' }, { '\'', '\'' }, { '\"', '"' }, { '\0', '0' } };
for (size_t pair = 0; pair < sizeof (pairs) / sizeof (pairs[0]); pair++)
{
unsigned char character = pairs[pair][0];
unsigned char replacement = pairs[pair][1];
encode[character] = replacement;
decode[replacement] = character;
}
}
/**
* @brief This function sets values for the encoding and decoding character mapping.
*
* @param config This key set stores configuration values for the character mapping.
* @param root This key stores the root key for the character mapping stored in `config`.
*/
void Coder::readConfig (CppKeySet const & config, CppKey const & root)
{
for (auto key : config)
{
/* Ignore keys that are not directly below the config root key or have an incorrect size */
if (!key->isDirectBelow (root) || key->getBaseNameSize () != 3 || key->getBinarySize () != 3) continue;
int character = elektraHexcodeConvFromHex (key->getBaseName ()[1]);
character += elektraHexcodeConvFromHex (key->getBaseName ()[0]) * 16;
int replacement = elektraHexcodeConvFromHex (key->get<string> ()[1]);
replacement += elektraHexcodeConvFromHex (key->get<string> ()[0]) * 16;
/* Hexencode this character! */
encode[character & 255] = replacement;
decode[replacement & 255] = character;
}
}
/**
* @brief This function replaces unescaped characters in a string with escaped characters.
*
* @param text This variable stores the string this function escapes.
*
* @return The encoded string
*/
string Coder::encodeString (string const & text)
{
vector<unsigned char> encoded;
for (unsigned char character : text)
{
if (encode[character])
{
encoded.push_back (escapeCharacter);
encoded.push_back (encode[character]);
}
else
{
encoded.push_back (character);
}
}
return { encoded.begin (), encoded.end () };
}
/**
* @brief This function replaces escaped characters in a string with unescaped characters.
*
* @param text This string stores the string this function decodes.
*
* @return The decoded string
*/
string Coder::decodeString (string const & text)
{
vector<char> decoded;
auto character = text.begin ();
while (character != text.end ())
{
if (*character == escapeCharacter)
{
++character;
decoded.push_back (decode[*character]);
}
else
{
decoded.push_back (*character);
}
++character;
}
return { decoded.begin (), decoded.end () };
}
/**
* @brief This function replaces unescaped characters in a key value with escaped characters.
*
* The function only modifies the key value if it has type `string`.
*
* @param key This key stores the value this function encodes.
*/
void Coder::encodeValue (CppKey & key)
{
if (key.isString ()) key.setString (encodeString (key.get<string> ()));
}
/**
* @brief This function replaces escaped characters in a key value with unescaped characters.
*
* The function only modifies the key value if it has type `string`.
*
* @param key This key holds the value this function decodes.
*/
void Coder::decodeValue (CppKey & key)
{
if (key.isString ()) key.setString (decodeString (key.get<string> ()));
}
/**
* @brief This function replaces unescaped characters in a key name with escaped characters.
*
* @param key This `Key` stores a name possibly containing unescaped special characters.
*
* @return A copy of `key` containing an escaped version of the name of `key`
*/
CppKey Coder::encodeName (CppKey const & key)
{
CppKey escaped{ key.dup () };
escaped.setName (key.getNamespace ());
auto keyIterator = key.begin ();
while (++keyIterator != key.end ())
{
escaped.addBaseName (encodeString (*keyIterator));
}
ELEKTRA_LOG_DEBUG ("Encoded name of “%s” is “%s”", key.getName ().c_str (), escaped.getName ().c_str ());
return escaped;
}
/**
* @brief This function replaces escaped characters in a key name with unescaped characters.
*
* @param key This `Key` stores a name possibly containing escaped special characters.
*
* @return A copy of `key` containing an unescaped version of the name of `key`
*/
CppKey Coder::decodeName (CppKey const & key)
{
CppKey unescaped{ key.dup () };
unescaped.setName (key.getNamespace ());
auto keyIterator = key.begin ();
while (++keyIterator != key.end ())
{
unescaped.addBaseName (decodeString (*keyIterator));
}
ELEKTRA_LOG_DEBUG ("Decoded name of “%s” is “%s”", key.getName ().c_str (), unescaped.getName ().c_str ());
return unescaped;
}
/**
* @brief This constructor creates a new object used to decode and encode key sets.
*
* @param config This key set contains configuration values for decoding and encoding such as mapping data and the escape character.
*/
Coder::Coder (CppKeySet config)
{
encode = vector<unsigned char> (256);
decode = vector<unsigned char> (256);
escapeCharacter = '\\';
CppKey const escape = config.lookup ("/escape", 0);
if (escape && escape.getBaseNameSize () && escape.getBinarySize () == 3)
{
int escapeChar = elektraHexcodeConvFromHex (escape.get<string> ()[1]);
escapeChar += elektraHexcodeConvFromHex (escape.get<string> ()[0]) * 16;
escapeCharacter = escapeChar & 255;
}
ELEKTRA_LOG_DEBUG ("Use “%c” as escape character", escapeCharacter);
CppKey const root = CppKey{ "/chars", KEY_END };
CppKeySet mappingConfig{ config.cut (root) };
#ifdef HAVE_LOGGER
for (auto key : mappingConfig)
{
ELEKTRA_LOG_DEBUG ("Decode 0x%s as 0x%s ", key->getBaseName ().c_str (), key.getString ().c_str ());
}
#endif
if (mappingConfig.size () > 0)
{
readConfig (mappingConfig, root);
}
else
{
setDefaultConfig ();
}
}
/**
* @brief This function escapes special characters in key names and values.
*
* @param keys This key set stores keys that possibly contain special characters.
*
* @return A copy of the given KeySet containing only escaped keys
*/
CppKeySet Coder::encodeKeySet (CppKeySet const & keys)
{
CppKeySet escaped{};
for (auto key : keys)
{
CppKey encoded = encodeName (*key);
encodeValue (encoded);
escaped.append (encoded);
}
return escaped;
}
/**
* @brief This function unescapes characters in key names and values.
*
* @param keys This key set stores keys that possibly contain escaped special characters.
*
* @return A copy of the given KeySet containing unescaped keys
*/
CppKeySet Coder::decodeKeySet (CppKeySet const & keys)
{
CppKeySet unescaped{};
for (auto key : keys)
{
CppKey decoded = decodeName (*key);
decodeValue (decoded);
unescaped.append (decoded);
}
return unescaped;
}
} // end namespace elektra