Line data Source code
1 : // Copyright (c) 2011-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 St, Fifth Floor, Boston, MA 02110-1301 USA
19 : #pragma once
20 :
21 :
22 : // libexcept lib
23 : //
24 : #include "libexcept/exception.h"
25 :
26 :
27 : // C++ lib
28 : //
29 : #include <algorithm>
30 : #include <climits>
31 :
32 :
33 :
34 : namespace snapdev
35 : {
36 :
37 :
38 92 : DECLARE_MAIN_EXCEPTION(hexadecimal_string_exception);
39 :
40 2002 : DECLARE_OUT_OF_RANGE(hexadecimal_string_out_of_range);
41 :
42 92 : DECLARE_EXCEPTION(hexadecimal_string_exception, hexadecimal_string_invalid_parameter);
43 :
44 :
45 :
46 : /** \brief Check whether character is an hexadecimal digit.
47 : *
48 : * This function checks whether the given \p c character represents
49 : * an hexadecimal digit.
50 : *
51 : * The function accepts upper and lower case characters (A-F or a-f)
52 : * as the same set of digits.
53 : *
54 : * \rparam charT The type of character to be tested.
55 : * \param[in] c The character to be tested.
56 : *
57 : * \return true if the the character represents an hexadecimal digit.
58 : */
59 : template<class charT>
60 1114142 : bool is_hexdigit(charT c)
61 : {
62 1114094 : return (c >= '0' && c <= '9')
63 1114132 : || (c >= 'a' && c <= 'f')
64 2228268 : || (c >= 'A' && c <= 'F');
65 : }
66 :
67 :
68 : /** \brief Convert an hexadecimal character in a number.
69 : *
70 : * This function converts an hexadecimal character that represents a valid
71 : * digit in an integer.
72 : *
73 : * \exception hexadecimal_string_invalid_parameter
74 : * If the input is not a valid hexadecimal character, then this error is
75 : * raised.
76 : *
77 : * \tparam charT The type of the character (char, char32_t, etc.)
78 : * \param[in] c The character to convert to a number.
79 : *
80 : * \return The converted character as an integer.
81 : */
82 : template<class charT>
83 1062847 : int hexdigit_to_number(charT c)
84 : {
85 1062847 : if(c >= '0' && c <= '9')
86 : {
87 662832 : return c - '0';
88 : }
89 400015 : if(c >= 'a' && c <= 'f')
90 : {
91 301681 : return c - ('a' - 10);
92 : }
93 98334 : if(c >= 'A' && c <= 'F')
94 : {
95 98304 : return c - ('A' - 10);
96 : }
97 30 : throw hexadecimal_string_invalid_parameter("the input character is not an hexadecimal digit.");
98 : }
99 :
100 :
101 : /** \brief Transform a binary string to hexadecimal.
102 : *
103 : * This function transforms a string of binary bytes (any value from 0x00
104 : * to 0xFF) to a string of hexadecimal digits.
105 : *
106 : * The output string will be exactly 2x the size of the input string.
107 : *
108 : * \param[in] binary The input binary string to convert.
109 : *
110 : * \return The hexademical representation of the input string.
111 : */
112 66538 : inline std::string bin_to_hex(std::string const & binary)
113 : {
114 66538 : if(binary.empty())
115 : {
116 2 : return std::string();
117 : }
118 :
119 133072 : std::string result;
120 :
121 66536 : result.reserve(binary.length() * 2);
122 :
123 66536 : std::for_each(
124 : binary.begin()
125 : , binary.end()
126 139770 : , [&result](char const & c)
127 279540 : {
128 419310 : auto to_hex([](char d)
129 : {
130 279540 : return d < 10 ? d + '0' : d + ('a' - 10);
131 279540 : });
132 :
133 139770 : result.push_back(to_hex((c >> 4) & 15));
134 139770 : result.push_back(to_hex(c & 15));
135 206306 : });
136 :
137 66536 : return result;
138 : }
139 :
140 :
141 : /** \brief Convert an hexadecimal string to a binary string.
142 : *
143 : * This function is the inverse of the bin_to_hex() function. It
144 : * converts a text string of hexadecimal numbers (exactly 2 digits
145 : * each) into a binary string (a string of any bytes from 0x00 to
146 : * 0xFF.)
147 : *
148 : * The output will be exactly half the size of the input.
149 : *
150 : * \note
151 : * The output is saved in a string in big endian format.
152 : *
153 : * \exception hexadecimal_string_invalid_parameter
154 : * If the input string is not considered valid, then this exception is
155 : * raised. To be valid every single character must be an hexadecimal
156 : * digit (0-9, a-f, A-F) and the length of the string must be even.
157 : *
158 : * \param[in] hex The salt as an hexadecimal string of characters.
159 : *
160 : * \return The converted value in a binary string.
161 : */
162 132118 : inline std::string hex_to_bin(std::string const & hex)
163 : {
164 132118 : std::string result;
165 :
166 132118 : if((hex.length() & 1) != 0)
167 : {
168 16 : throw hexadecimal_string_invalid_parameter("the hex parameter must have an even size.");
169 : }
170 :
171 402803 : for(char const * s(hex.c_str()); *s != '\0'; s += 2)
172 : {
173 270731 : char value = static_cast<char>(hexdigit_to_number(s[0]) * 16
174 270706 : + hexdigit_to_number(s[1]));
175 270701 : result.push_back(value);
176 : }
177 :
178 132072 : return result;
179 : }
180 :
181 :
182 : /** \brief Transform an integer to a string of hexadecimal digits.
183 : *
184 : * This function transforms an integer to a string of hexadecimal digits.
185 : *
186 : * The output string is optimized to not include any unnecessary leading
187 : * zeroes.
188 : *
189 : * The input value is considered to be positive or zero. Negative numbers
190 : * are viewed as their unsigned corresponding number (i.e. if the input
191 : * is an int32_t is viewed as uint32_t).
192 : *
193 : * \note
194 : * The function does not add an introducer (so no "0x" at the start of
195 : * the resulting string).
196 : *
197 : * \tparam T The type of integer to convert to a string.
198 : * \param[in] value The input integer to convert.
199 : * \param[in] uppercase Whether to use upper (true) or lower (false) case
200 : * letters for the hexadecimal digits A to F.
201 : * \param[in] width The minimum width (prepend '0' to reach this width).
202 : *
203 : * \return The hexademical representation of the input integer.
204 : */
205 : template<class T>
206 196864 : inline std::string int_to_hex(
207 : T value
208 : , bool uppercase = false
209 : , std::uint32_t width = 1)
210 : {
211 : typedef typename std::make_unsigned<T>::type unsigned_value_t;
212 :
213 196864 : char buf[sizeof(T) * 2 + 1];
214 196864 : char * d(buf + sizeof(T) * 2);
215 196864 : char const * const e(d);
216 196864 : *d = '\0';
217 :
218 196864 : char const letter_digit(uppercase ? 'A' - 10 : 'a' - 10);
219 :
220 970684 : for(unsigned_value_t unsigned_value(value);
221 970684 : unsigned_value != 0;
222 495 : unsigned_value >>= 4)
223 : {
224 773820 : --d;
225 773820 : int const v(unsigned_value & 15);
226 773820 : *d = v < 10 ? v + '0' : v + letter_digit;
227 : }
228 :
229 196864 : if(*d == '\0')
230 : {
231 4 : --d;
232 4 : *d = '0';
233 : }
234 :
235 196864 : if(width > sizeof(buf) - 1)
236 : {
237 256 : width = sizeof(buf) - 1;
238 : }
239 212320 : while(e - d < width)
240 : {
241 7728 : --d;
242 7728 : *d = '0';
243 : }
244 :
245 196864 : return std::string(d);
246 : }
247 :
248 :
249 : /** \brief Convert a string to an integer.
250 : *
251 : * This function converts a string that represents an hexadecimal number
252 : * in an integer.
253 : *
254 : * \exception out_of_range
255 : * If the number is too large for the integer, then this exception is
256 : * raised.
257 : *
258 : * \exception hexadecimal_string_invalid_parameter
259 : * If the input string is not considered valid, then this exception is
260 : * raised. To be valid every single character must be an hexadecimal
261 : * digit (0-9, a-f, A-F) and the length of the string must be even.
262 : *
263 : * \tparam T The type of integer to return.
264 : * \param[in] hex The hexadecimal string to be converted.
265 : *
266 : * \return The converted \p hex parameter as an integer.
267 : */
268 : template<class T>
269 132073 : T hex_to_int(std::string const & hex)
270 : {
271 132073 : T value(0);
272 653483 : for(auto const c : hex)
273 : {
274 522411 : if((value >> (sizeof(T) * CHAR_BIT - 4)) != 0)
275 : {
276 1001 : throw hexadecimal_string_out_of_range("input string has an hexadecimal number which is too large for the output integer type.");
277 : }
278 521410 : value *= 16;
279 521410 : value += hexdigit_to_number(c);
280 : }
281 131072 : return value;
282 : }
283 :
284 :
285 : } // namespace snapdev
286 : // vim: ts=4 sw=4 et
|