Line data Source code
1 : // Copyright (c) 2022-2023 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 a numeric or string literal to a 128 bit value. 22 : * 23 : * These functions transform string literals or numeric literals to 128 bit 24 : * values as supported by g++. We support signed and unsigned in decimal, 25 : * hexadecimal, octal, and binary. 26 : */ 27 : 28 : // C++ 29 : // 30 : #include <cstring> 31 : #include <limits> 32 : 33 : 34 : namespace snapdev 35 : { 36 : namespace literals 37 : { 38 : 39 : /** \brief Convert a literal to a signed __int128. 40 : * 41 : * This function converts a literal string to a signed __int128 integer 42 : * number. 43 : * 44 : * \code 45 : * using namespace snapdev::literals; 46 : * 47 : * __int128 value = "0xabcd000000000000"_int128; 48 : * \endcode 49 : * 50 : * \todo 51 : * Make sure that the first '\0' represents the end of the string. 52 : * 53 : * \todo 54 : * Support the negative (and positive) sign at the start of the string 55 : * (with number literal, it is viewed as an operator so it works as is, 56 : * with with string literals, we have to handle those ourselves). 57 : * 58 : * \param[in] literal A 128 bit literal number. 59 : * 60 : * \return The __int128 number. 61 : * 62 : * \sa operator ""_uint128() 63 : */ 64 : #pragma GCC diagnostic push 65 : #pragma GCC diagnostic ignored "-Wpedantic" 66 1408 : constexpr __int128 operator ""_int128(char const * literal, std::size_t len) 67 : { 68 1408 : __int128 result(0); 69 1408 : __int128 base(10); 70 : 71 1408 : if(len == 0) 72 : { 73 0 : throw std::invalid_argument("A string literal of a int128 must be at least one cahracter."); 74 : } 75 : 76 1408 : if(*literal == '0') 77 : { 78 0 : ++literal; 79 0 : if(*literal == '\0') 80 : { 81 0 : return result; 82 : } 83 0 : if(*literal == 'x' 84 0 : || *literal == 'X') 85 : { 86 : // parse an hexadecimal number 87 : // 88 0 : base = 16; 89 0 : ++literal; 90 0 : if(*literal == '\0') 91 : { 92 0 : throw std::invalid_argument("0x must be followed by at least one hexadecimal digit."); 93 : } 94 : } 95 0 : else if(*literal == 'b' 96 0 : || *literal == 'B') 97 : { 98 : // parse a binary number 99 : // 100 0 : base = 2; 101 0 : ++literal; 102 0 : if(*literal == '\0') 103 : { 104 0 : throw std::invalid_argument("0b must be followed by at least one binary digit."); 105 : } 106 : } 107 : else 108 : { 109 : // parse an octal number 110 : // 111 0 : base = 8; 112 : } 113 : } 114 : // else -- parse a decimal number 115 : 116 : for(;;) 117 : { 118 2816 : char const c(*literal++); 119 2816 : if(c == '\0') 120 : { 121 : // valid 122 : // 123 1408 : return result; 124 : } 125 1408 : int digit(std::numeric_limits<int>::max()); 126 1408 : if(c >= '0' && c <= '9') 127 : { 128 1408 : digit = c - '0'; 129 : } 130 0 : else if(c >= 'a' && c <= 'f') 131 : { 132 0 : digit = c - ('a' - 10); 133 : } 134 0 : else if(c >= 'A' && c <= 'F') 135 : { 136 0 : digit = c - ('A' - 10); 137 : } 138 1408 : if(digit >= base) 139 : { 140 0 : throw std::domain_error( 141 0 : std::string("digit '") 142 0 : + c 143 0 : + "' too large for the selected base"); 144 : } 145 : 146 1408 : __int128 const old(result); 147 1408 : result = result * base + digit; 148 1408 : if(result < old) 149 : { 150 : // overflow 151 : // 152 : // IMPORTANT NOTE: This test works because we do not deal with 153 : // the '-' sign which the C++ compiler handles for us 154 : // 155 0 : throw std::domain_error("signed __int128 literal too large."); 156 : } 157 1408 : } 158 : 159 : return result; 160 : } 161 : #pragma GCC diagnostic pop 162 : 163 : 164 : /** \brief Convert a literal number to an __int128. 165 : * 166 : * This function converts a literal number to an __int128. For example: 167 : * 168 : * \code 169 : * using namespace snapdev::literals; 170 : * 171 : * __int128 value = 123_int128; 172 : * \endcode 173 : * 174 : * \param[in] literal A literal number. 175 : * 176 : * \return The literal number converted to an __int128 value. 177 : */ 178 : #pragma GCC diagnostic push 179 : #pragma GCC diagnostic ignored "-Wpedantic" 180 1408 : constexpr __int128 operator ""_int128(char const * literal) 181 : { 182 1408 : return operator ""_int128(literal, strlen(literal)); 183 : } 184 : #pragma GCC diagnostic pop 185 : 186 : 187 : /** \brief Convert a literal to an unsigned __int128. 188 : * 189 : * This function converts a literal string to an unsigned __int128 integer 190 : * number. 191 : * 192 : * \todo 193 : * Make sure that the first '\0' represents the end of the string. 194 : * 195 : * \param[in] literal A 128 bit literal number. 196 : * 197 : * \return The unsigned __int128 number. 198 : * 199 : * \sa operator ""_int128() 200 : */ 201 : #pragma GCC diagnostic push 202 : #pragma GCC diagnostic ignored "-Wpedantic" 203 768 : constexpr unsigned __int128 operator ""_uint128(char const * literal, std::size_t len) 204 : { 205 768 : unsigned __int128 result(0); 206 768 : unsigned __int128 base(10); 207 : 208 768 : if(len == 0) 209 : { 210 0 : throw std::invalid_argument("A string literal of a uint128 must be at least one cahracter."); 211 : } 212 : 213 768 : if(*literal == '0') 214 : { 215 0 : ++literal; 216 0 : if(*literal == '\0') 217 : { 218 0 : return result; 219 : } 220 0 : if(*literal == 'x' 221 0 : || *literal == 'X') 222 : { 223 : // parse an hexadecimal number 224 : // 225 0 : base = 16; 226 0 : ++literal; 227 0 : if(*literal == '\0') 228 : { 229 0 : throw std::invalid_argument("0x must be followed by at least one hexadecimal digit."); 230 : } 231 : } 232 0 : else if(*literal == 'b' 233 0 : || *literal == 'B') 234 : { 235 : // parse a binary number 236 : // 237 0 : base = 2; 238 0 : ++literal; 239 0 : if(*literal == '\0') 240 : { 241 0 : throw std::invalid_argument("0b must be followed by at least one binary digit."); 242 : } 243 : } 244 : else 245 : { 246 : // parse an octal number 247 : // 248 0 : base = 8; 249 : } 250 : } 251 : // else -- parse a decimal number 252 : 253 : for(;;) 254 : { 255 1536 : char const c(*literal++); 256 1536 : if(c == '\0') 257 : { 258 : // valid 259 : // 260 768 : return result; 261 : } 262 768 : unsigned int digit(std::numeric_limits<unsigned int>::max()); 263 768 : if(c >= '0' && c <= '9') 264 : { 265 768 : digit = c - '0'; 266 : } 267 0 : else if(c >= 'a' && c <= 'f') 268 : { 269 0 : digit = c - ('a' - 10); 270 : } 271 0 : else if(c >= 'A' && c <= 'F') 272 : { 273 0 : digit = c - ('A' - 10); 274 : } 275 768 : if(digit >= base) 276 : { 277 0 : throw std::domain_error( 278 0 : std::string("digit '") 279 0 : + c 280 0 : + "' too large for the selected base"); 281 : } 282 : 283 768 : unsigned __int128 const old(result); 284 768 : result = result * base + digit; 285 768 : if(result < old) 286 : { 287 : // overflow 288 : // 289 0 : throw std::domain_error("unsigned __int128 literal too large."); 290 : } 291 768 : } 292 : 293 : return result; 294 : } 295 : #pragma GCC diagnostic pop 296 : 297 : 298 : /** \brief Convert a literal number to an unsigned __int128 value. 299 : * 300 : * This function converts a literal number to an unsigned __int128. 301 : * For example: 302 : * 303 : * \code 304 : * using namespace snapdev::literals; 305 : * 306 : * unsigned __int128 value = 123_uint128; 307 : * \endcode 308 : * 309 : * \param[in] literal A literal number. 310 : * 311 : * \return The literal number converted to an unsigned __int128 value. 312 : */ 313 : #pragma GCC diagnostic push 314 : #pragma GCC diagnostic ignored "-Wpedantic" 315 768 : constexpr unsigned __int128 operator ""_uint128(char const * literal) 316 : { 317 768 : return operator ""_uint128(literal, strlen(literal)); 318 : } 319 : #pragma GCC diagnostic pop 320 : 321 : 322 : 323 : } // namespace literals 324 : } // namespace snapdev 325 : // vim: ts=4 sw=4 et