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