Line data Source code
1 : // Copyright (c) 2011-2024 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 3 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 17 : // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 : #pragma once 19 : 20 : /** \file 21 : * \brief Convert binary to a string in hexadecimal. 22 : * 23 : * C++ offers the std::to_string() function which converts numbers to 24 : * decimal. However, we often want to convert strings to hexadecimal and 25 : * using std::stringstream is not as efficient as using these functions. 26 : * 27 : * Here we can convert a buffer of any length to hexadecimal and vice 28 : * versa. You can also use this implementation to covert just one byte. 29 : */ 30 : 31 : // libexcept 32 : // 33 : #include "libexcept/exception.h" 34 : 35 : 36 : // C++ 37 : // 38 : #include <algorithm> 39 : #include <climits> 40 : #include <cstdint> 41 : #include <iomanip> 42 : 43 : 44 : 45 : namespace snapdev 46 : { 47 : 48 : 49 : 50 311 : DECLARE_MAIN_EXCEPTION(hexadecimal_string_exception); 51 : 52 1001 : DECLARE_OUT_OF_RANGE(hexadecimal_string_out_of_range); 53 : 54 311 : DECLARE_EXCEPTION(hexadecimal_string_exception, hexadecimal_string_invalid_parameter); 55 : 56 : 57 : 58 : /** \brief Check whether character is an hexadecimal digit. 59 : * 60 : * This function checks whether the given \p c character represents 61 : * an hexadecimal digit. 62 : * 63 : * The function accepts upper and lower case characters (A-F or a-f) 64 : * as the same set of digits. 65 : * 66 : * \rparam charT The type of character to be tested. 67 : * \param[in] c The character to be tested. 68 : * 69 : * \return true if the the character represents an hexadecimal digit. 70 : */ 71 : template<class charT> 72 1114408 : bool is_hexdigit(charT c) 73 : { 74 1114360 : return (c >= '0' && c <= '9') 75 1114397 : || (c >= 'a' && c <= 'f') 76 2228816 : || (c >= 'A' && c <= 'F'); 77 : } 78 : 79 : 80 : /** \brief Convert one hexadecimal digit to a character. 81 : * 82 : * This function converts an hexadecimal number from 0 to 15 into 83 : * an hexadecimal character: '0' to '9' or 'a' to 'f'. 84 : * 85 : * To get uppercase, simple set the \p uppercase parameter to true. 86 : * 87 : * \param[in] d The hexadecimal digit convert. 88 : * 89 : * \return An hexadecimal character. 90 : */ 91 : template<class intT> 92 279688 : char to_hex(intT d, bool uppercase = false) 93 : { 94 279688 : if(static_cast<typename std::make_unsigned<intT>::type>(d) >= 16) 95 : { 96 0 : throw hexadecimal_string_out_of_range( 97 : "input number (" 98 : + std::to_string(d) 99 : + ") is negative or too large to represent one hexadecimal digit."); 100 : } 101 279688 : return d < 10 ? d + '0' : d + ((uppercase ? 'A' : 'a') - 10); 102 : } 103 : 104 : 105 : /** \brief Convert an hexadecimal character in a number. 106 : * 107 : * This function converts an hexadecimal character that represents a valid 108 : * digit in an integer. 109 : * 110 : * \exception hexadecimal_string_invalid_parameter 111 : * If the input is not a valid hexadecimal character, then this error is 112 : * raised. 113 : * 114 : * \tparam charT The type of the character (char, char32_t, etc.) 115 : * \param[in] c The character to convert to a number. 116 : * 117 : * \return The converted character as an integer. 118 : */ 119 : template<class charT> 120 1065352 : int hexdigit_to_number(charT c) 121 : { 122 1065352 : if(c >= '0' && c <= '9') 123 : { 124 664303 : return c - '0'; 125 : } 126 401049 : if(c >= 'a' && c <= 'f') 127 : { 128 302450 : return c - ('a' - 10); 129 : } 130 98599 : if(c >= 'A' && c <= 'F') 131 : { 132 98304 : return c - ('A' - 10); 133 : } 134 295 : if(static_cast<typename std::make_unsigned<charT>::type>(c) < 0x80) 135 : { 136 3 : throw hexadecimal_string_invalid_parameter( 137 2 : std::string("input character '") 138 : + static_cast<char>(c) 139 : + "' is not an hexadecimal digit."); 140 : } 141 : // if character represents a UTF-8 charater, we do not have all the 142 : // bytes to convert it so just use a plan error message 143 : // 144 294 : throw hexadecimal_string_invalid_parameter( 145 : "input character is not an hexadecimal digit."); 146 : } 147 : 148 : 149 : /** \brief Transform a binary string to hexadecimal. 150 : * 151 : * This function transforms a string of binary bytes (any value from 0x00 152 : * to 0xFF) to a string of hexadecimal digits. 153 : * 154 : * The output string will be exactly 2x the size of the input string. 155 : * 156 : * You can request uppercase instead of lowercase for the letters a to f. 157 : * The default is to use lowercase characters. 158 : * 159 : * \param[in] binary The input binary string to convert. 160 : * \param[in] uppercase If true, use uppercase letters for a-f. 161 : * 162 : * \return The hexademical representation of the input string. 163 : */ 164 66538 : inline std::string bin_to_hex(std::string const & binary, bool uppercase = false) 165 : { 166 66538 : if(binary.empty()) 167 : { 168 2 : return std::string(); 169 : } 170 : 171 66536 : std::string result; 172 : 173 66536 : result.reserve(binary.length() * 2); 174 : 175 66536 : std::for_each( 176 : binary.begin() 177 : , binary.end() 178 279688 : , [&result,uppercase](char const & c) 179 : { 180 139844 : result.push_back(to_hex((c >> 4) & 15, uppercase)); 181 139844 : result.push_back(to_hex(c & 15, uppercase)); 182 139844 : }); 183 : 184 66536 : return result; 185 66536 : } 186 : 187 : 188 : /** \brief Convert an hexadecimal string to a binary string. 189 : * 190 : * This function is the inverse of the bin_to_hex() function. It 191 : * converts a text string of hexadecimal numbers (exactly 2 digits 192 : * each) into a binary string (a string of any bytes from 0x00 to 193 : * 0xFF.) 194 : * 195 : * The output will be exactly half the size of the input. 196 : * 197 : * \note 198 : * The output is saved in a string in big endian format. 199 : * 200 : * \exception hexadecimal_string_invalid_parameter 201 : * If the input string is not considered valid, then this exception is 202 : * raised. To be valid every single character must be an hexadecimal 203 : * digit (0-9, a-f, A-F) and the length of the string must be even. 204 : * 205 : * \param[in] hex The hexadecimal string of characters. 206 : * 207 : * \return The converted value in a binary string. 208 : */ 209 132383 : inline std::string hex_to_bin(std::string const & hex) 210 : { 211 132383 : if((hex.length() & 1) != 0) 212 : { 213 16 : throw hexadecimal_string_invalid_parameter("the hex parameter must have an even size."); 214 : } 215 : 216 132367 : std::string result; 217 : 218 404178 : for(char const * s(hex.c_str()); *s != '\0'; s += 2) 219 : { 220 272106 : char value = static_cast<char>(hexdigit_to_number(s[0]) * 16 221 271836 : + hexdigit_to_number(s[1])); 222 271811 : result.push_back(value); 223 : } 224 : 225 132072 : return result; 226 295 : } 227 : 228 : 229 : /** \brief Transform an integer to a string of hexadecimal digits. 230 : * 231 : * This function transforms an integer to a string of hexadecimal digits. 232 : * 233 : * The output string is optimized to not include any unnecessary leading 234 : * zeroes. 235 : * 236 : * The input value is considered to be positive or zero. Negative numbers 237 : * are viewed as their unsigned corresponding number (i.e. if the input 238 : * is an int32_t is viewed as uint32_t). 239 : * 240 : * \note 241 : * The function does not add an introducer (so no "0x" at the start of 242 : * the resulting string). 243 : * 244 : * \tparam T The type of integer to convert to a string. 245 : * \param[in] value The input integer to convert. 246 : * \param[in] uppercase Whether to use upper (true) or lower (false) case 247 : * letters for the hexadecimal digits A to F. 248 : * \param[in] width The minimum width (prepend '0' to reach this width). 249 : * 250 : * \return The hexademical representation of the input integer. 251 : */ 252 : template<class T> 253 196864 : inline std::string int_to_hex( 254 : T value 255 : , bool uppercase = false 256 : , std::uint32_t width = 1) 257 : { 258 : typedef typename std::make_unsigned<T>::type unsigned_value_t; 259 : 260 196864 : char buf[sizeof(T) * 2 + 1]; 261 196864 : char * d(buf + sizeof(T) * 2); 262 196864 : char const * const e(d); 263 196864 : *d = '\0'; 264 : 265 196864 : char const letter_digit(uppercase ? 'A' - 10 : 'a' - 10); 266 : 267 196864 : for(unsigned_value_t unsigned_value(value); 268 970684 : unsigned_value != 0; 269 773820 : unsigned_value >>= 4) 270 : { 271 773820 : --d; 272 773820 : int const v(unsigned_value & 15); 273 773820 : *d = v < 10 ? v + '0' : v + letter_digit; 274 : } 275 : 276 196864 : if(*d == '\0') 277 : { 278 4 : --d; 279 4 : *d = '0'; 280 : } 281 : 282 196864 : if(width > sizeof(buf) - 1) 283 : { 284 256 : width = sizeof(buf) - 1; 285 : } 286 204592 : while(e - d < width) 287 : { 288 7728 : --d; 289 7728 : *d = '0'; 290 : } 291 : 292 393728 : return std::string(d, e - d); 293 : } 294 : 295 : 296 : /** \brief Convert a string to an integer. 297 : * 298 : * This function converts a string that represents an hexadecimal number 299 : * in an integer. 300 : * 301 : * \exception out_of_range 302 : * If the number is too large for the integer, then this exception is 303 : * raised. 304 : * 305 : * \exception hexadecimal_string_invalid_parameter 306 : * If the input string is not considered valid, then this exception is 307 : * raised. To be valid every single character must be an hexadecimal 308 : * digit (0-9, a-f, A-F) and the length of the string must be even. 309 : * 310 : * \tparam T The type of integer to return. 311 : * \param[in] hex The hexadecimal string to be converted. 312 : * 313 : * \return The converted \p hex parameter as an integer. 314 : */ 315 : template<class T> 316 132073 : T hex_to_int(std::string const & hex) 317 : { 318 132073 : T value(0); 319 653483 : for(auto const c : hex) 320 : { 321 522411 : if((value >> (sizeof(T) * CHAR_BIT - 4)) != 0) 322 : { 323 1001 : throw hexadecimal_string_out_of_range("input string has an hexadecimal number which is too large for the output integer type."); 324 : } 325 521410 : value *= 16; 326 521410 : value += hexdigit_to_number(c); 327 : } 328 131072 : return value; 329 : } 330 : 331 : 332 : } // namespace snapdev 333 : // vim: ts=4 sw=4 et