Line data Source code
1 : // Copyright (c) 2021-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : /** \file
21 : * \brief Verify that the hexadecimal convertions work.
22 : *
23 : * This file implements tests for the hexadecimal to binary and vice
24 : * versa functions.
25 : */
26 :
27 : // self
28 : //
29 : #include <snapdev/hexadecimal_string.h>
30 :
31 : #include "catch_main.h"
32 :
33 :
34 : // C++ lib
35 : //
36 : #include <iomanip>
37 : #include <set>
38 :
39 :
40 : // last include
41 : //
42 : #include <snapdev/poison.h>
43 :
44 :
45 :
46 : namespace
47 : {
48 :
49 :
50 :
51 : // function stolen from the libutf8 library
52 : // see https://snapwebsites.org/project/libutf8
53 31 : int wctombs(char * mb, char32_t wc, size_t len)
54 : {
55 62 : auto verify_length = [&len](size_t required_len)
56 31 : {
57 31 : if(len < required_len)
58 : {
59 0 : throw std::logic_error("wctombs() called with an output buffer which is too small.");
60 : }
61 62 : };
62 :
63 31 : if(wc < 0x80)
64 : {
65 0 : verify_length(2);
66 :
67 : /* this will also encode '\0'... */
68 0 : mb[0] = static_cast<char>(wc);
69 0 : mb[1] = '\0';
70 0 : return 1;
71 : }
72 31 : if(wc < 0x800)
73 : {
74 0 : verify_length(3);
75 :
76 0 : mb[0] = static_cast<char>((wc >> 6) | 0xC0);
77 0 : mb[1] = (wc & 0x3F) | 0x80;
78 0 : mb[2] = '\0';
79 0 : return 2;
80 : }
81 :
82 : // avoid encoding the UTF-16 surrogate because those code points do not
83 : // represent characters
84 : //
85 31 : if(wc < 0xD800 || wc > 0xDFFF)
86 : {
87 31 : if(wc < 0x10000)
88 : {
89 1 : verify_length(4);
90 :
91 1 : mb[0] = static_cast<char>((wc >> 12) | 0xE0);
92 1 : mb[1] = ((wc >> 6) & 0x3F) | 0x80;
93 1 : mb[2] = (wc & 0x3F) | 0x80;
94 1 : mb[3] = '\0';
95 1 : return 3;
96 : }
97 30 : if(wc < 0x110000)
98 : {
99 30 : verify_length(5);
100 :
101 30 : mb[0] = static_cast<char>((wc >> 18) | 0xF0);
102 30 : mb[1] = ((wc >> 12) & 0x3F) | 0x80;
103 30 : mb[2] = ((wc >> 6) & 0x3F) | 0x80;
104 30 : mb[3] = (wc & 0x3F) | 0x80;
105 30 : mb[4] = '\0';
106 30 : return 4;
107 : }
108 : }
109 :
110 0 : verify_length(1);
111 :
112 : /* an invalid wide character */
113 0 : mb[0] = '\0';
114 0 : return -1;
115 : }
116 :
117 :
118 : } // no name namespace
119 :
120 :
121 :
122 3 : CATCH_TEST_CASE("hexadecimal_string_hex_digits", "[hexadecimal_string]")
123 : {
124 2 : CATCH_START_SECTION("hexadecimal_string: verify hexadecimal digit detection")
125 : {
126 1114113 : for(int i(0); i < 0x110000; ++i) // all of Unicode
127 : {
128 1114112 : if((i >= '0' && i <= '9')
129 1114102 : || (i >= 'a' && i <= 'f')
130 1114096 : || (i >= 'A' && i <= 'F'))
131 : {
132 22 : CATCH_REQUIRE(snapdev::is_hexdigit(i));
133 : }
134 : else
135 : {
136 1114090 : CATCH_REQUIRE_FALSE(snapdev::is_hexdigit(i));
137 : }
138 : }
139 : }
140 : CATCH_END_SECTION()
141 1 : }
142 :
143 :
144 6 : CATCH_TEST_CASE("hexadecimal_string_16_bit_values", "[hexadecimal_string]")
145 : {
146 8 : CATCH_START_SECTION("hexadecimal_string: all 16 bit values")
147 : {
148 65537 : for(int i(0); i < 65'536; ++i)
149 : {
150 131072 : std::stringstream ss;
151 65536 : ss << std::hex << i;
152 131072 : std::string value(ss.str());
153 65536 : if((value.length() & 1) != 0)
154 : {
155 3856 : value = "0" + value;
156 : }
157 131072 : std::string bin(snapdev::hex_to_bin(value));
158 131072 : std::string upper;
159 65536 : if(bin.length() == 1)
160 : {
161 256 : CATCH_REQUIRE(i < 256);
162 256 : CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i));
163 :
164 256 : CATCH_REQUIRE(snapdev::int_to_hex(i, false) == ss.str());
165 256 : CATCH_REQUIRE(snapdev::int_to_hex(i, false, 2) == value);
166 :
167 256 : CATCH_REQUIRE(snapdev::hex_to_int<int>(ss.str()) == i);
168 256 : CATCH_REQUIRE(snapdev::hex_to_int<int>(value) == i);
169 :
170 256 : upper = snapdev::int_to_hex(i, true, 2);
171 : }
172 : else
173 : {
174 65280 : CATCH_REQUIRE(bin.length() == 2);
175 65280 : CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i >> 8));
176 65280 : CATCH_REQUIRE(static_cast<unsigned char>(bin[1]) == static_cast<unsigned char>(i));
177 :
178 65280 : CATCH_REQUIRE(snapdev::int_to_hex(i, false) == ss.str());
179 65280 : CATCH_REQUIRE(snapdev::int_to_hex(i, false, 4) == value);
180 65280 : CATCH_REQUIRE(snapdev::hex_to_int<int>(ss.str()) == i);
181 65280 : CATCH_REQUIRE(snapdev::hex_to_int<int>(value) == i);
182 :
183 65280 : upper = snapdev::int_to_hex(i, true, 4);
184 : }
185 :
186 65536 : CATCH_REQUIRE(snapdev::bin_to_hex(bin) == value);
187 :
188 131072 : std::string bin_from_upper(snapdev::hex_to_bin(upper));
189 65536 : if(bin_from_upper.length() == 1)
190 : {
191 256 : CATCH_REQUIRE(i < 256);
192 256 : CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i));
193 : }
194 : else
195 : {
196 65280 : CATCH_REQUIRE(bin.length() == 2);
197 65280 : CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i >> 8));
198 65280 : CATCH_REQUIRE(static_cast<unsigned char>(bin[1]) == static_cast<unsigned char>(i));
199 : }
200 : }
201 : }
202 : CATCH_END_SECTION()
203 :
204 8 : CATCH_START_SECTION("hexadecimal_string: hex_to_bin & bin_to_hex, large (and small) random numbers")
205 : {
206 1001 : for(int i(0); i < 1'000; ++i)
207 : {
208 2000 : std::stringstream ss;
209 2000 : std::string result;
210 1000 : int const length((rand() % 16 + 2) & -2);
211 9910 : for(int j(0); j < length; ++j)
212 : {
213 8910 : int value(rand() % 256);
214 8910 : ss << std::setw(2) << std::setfill('0') << std::hex << value;
215 8910 : result.push_back(value);
216 : }
217 1000 : CATCH_REQUIRE(snapdev::hex_to_bin(ss.str()) == result);
218 1000 : CATCH_REQUIRE(snapdev::bin_to_hex(result) == ss.str());
219 : }
220 : }
221 : CATCH_END_SECTION()
222 :
223 8 : CATCH_START_SECTION("hexadecimal_string: bin_to_hex with empty")
224 : {
225 1 : CATCH_REQUIRE(snapdev::bin_to_hex(std::string()) == std::string());
226 1 : CATCH_REQUIRE(snapdev::bin_to_hex("") == "");
227 : }
228 : CATCH_END_SECTION()
229 :
230 8 : CATCH_START_SECTION("hexadecimal_string: large width for int_to_hex()")
231 : {
232 257 : for(int i(0); i < 256; ++i)
233 : {
234 256 : std::uint8_t c(i & 255);
235 512 : std::string const value(snapdev::int_to_hex(c, true, 4)); // 4 when type is at most 2 digits, return 2 digits...
236 512 : std::stringstream ss;
237 256 : ss << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << i;
238 256 : CATCH_REQUIRE(ss.str() == value);
239 : }
240 : }
241 : CATCH_END_SECTION()
242 4 : }
243 :
244 :
245 5 : CATCH_TEST_CASE("hexadecimal_string_invalid_input", "[hexadecimal_string][error]")
246 : {
247 6 : CATCH_START_SECTION("hexadecimal_string: invalid length")
248 : {
249 17 : for(int i(1); i <= 31; i += 2)
250 : {
251 32 : std::stringstream ss;
252 272 : for(int j(0); j < i; ++j)
253 : {
254 256 : int const value(rand() % 16);
255 256 : ss << std::hex << value;
256 : }
257 16 : CATCH_REQUIRE_THROWS_MATCHES(
258 : snapdev::hex_to_bin(ss.str())
259 : , snapdev::hexadecimal_string_invalid_parameter
260 : , Catch::Matchers::ExceptionMessage(
261 : "hexadecimal_string_exception: the hex parameter must have an even size."));
262 : }
263 : }
264 : CATCH_END_SECTION()
265 :
266 6 : CATCH_START_SECTION("hexadecimal_string: invalid digits")
267 : {
268 1 : int left(0);
269 1 : int right(0);
270 32 : while(left < 25 && right < 25)
271 : {
272 62 : std::stringstream ss;
273 31 : ss << std::hex << rand();
274 31 : if((ss.str().length() & 1) != 0)
275 : {
276 6 : ++right;
277 : }
278 : else
279 : {
280 25 : ++left;
281 : }
282 :
283 : // all of our std::string are considered UTF-8 so add a Unicode
284 : // character even though the hex_to_bin() function really
285 : // only expects ASCII
286 : //
287 : for(;;)
288 : {
289 31 : char32_t const invalid(rand() % (0x110000 - 1) + 1);
290 62 : if((invalid < 0xD800 || invalid >= 0xE000) // don't try with UTF-16 surrogates
291 62 : && !snapdev::is_hexdigit(invalid))
292 : {
293 31 : char buf[16];
294 31 : wctombs(buf, invalid, sizeof(buf));
295 31 : ss << buf;
296 31 : break;
297 : }
298 0 : }
299 :
300 : // make sure the length is even
301 : //
302 62 : if((ss.str().length() & 1) != 0)
303 : {
304 5 : ss << std::hex << rand() % 16;
305 : }
306 :
307 31 : CATCH_REQUIRE_THROWS_MATCHES(
308 : snapdev::hex_to_bin(ss.str())
309 : , snapdev::hexadecimal_string_invalid_parameter
310 : , Catch::Matchers::ExceptionMessage(
311 : "hexadecimal_string_exception: the input character is not an hexadecimal digit."));
312 : }
313 : }
314 : CATCH_END_SECTION()
315 :
316 6 : CATCH_START_SECTION("hexadecimal_string: integer too small (overflow)")
317 : {
318 1002 : for(int i(0); i <= 1'000; ++i)
319 : {
320 1001 : std::uint32_t value(rand());
321 1001 : while(value < 256)
322 : {
323 0 : value = rand();
324 : }
325 2002 : std::stringstream ss;
326 1001 : ss << value;
327 1001 : CATCH_REQUIRE_THROWS_MATCHES(
328 : snapdev::hex_to_int<std::uint8_t>(ss.str())
329 : , snapdev::hexadecimal_string_out_of_range
330 : , Catch::Matchers::ExceptionMessage(
331 : "hexadecimal_string_out_of_range: input string has an hexadecimal number which is too large for the output integer type."));
332 : }
333 : }
334 : CATCH_END_SECTION()
335 9 : }
336 :
337 :
338 :
339 : // vim: ts=4 sw=4 et
|