0.00% Lines (0/162) 0.00% Functions (0/4)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // Copyright 2023 Matt Borland 1   // Copyright 2023 Matt Borland
2   // Distributed under the Boost Software License, Version 1.0. 2   // Distributed under the Boost Software License, Version 1.0.
3   // https://www.boost.org/LICENSE_1_0.txt 3   // https://www.boost.org/LICENSE_1_0.txt
4   4  
5   #ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP 5   #ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP
6   #define BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP 6   #define BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP
7   7  
8   #include <boost/json/detail/charconv/detail/config.hpp> 8   #include <boost/json/detail/charconv/detail/config.hpp>
9   #include <boost/json/detail/charconv/detail/from_chars_result.hpp> 9   #include <boost/json/detail/charconv/detail/from_chars_result.hpp>
10   #include <boost/json/detail/charconv/detail/from_chars_integer_impl.hpp> 10   #include <boost/json/detail/charconv/detail/from_chars_integer_impl.hpp>
11   #include <boost/json/detail/charconv/detail/integer_search_trees.hpp> 11   #include <boost/json/detail/charconv/detail/integer_search_trees.hpp>
12   #include <boost/json/detail/charconv/limits.hpp> 12   #include <boost/json/detail/charconv/limits.hpp>
13   #include <boost/json/detail/charconv/chars_format.hpp> 13   #include <boost/json/detail/charconv/chars_format.hpp>
14   #include <system_error> 14   #include <system_error>
15   #include <type_traits> 15   #include <type_traits>
16   #include <limits> 16   #include <limits>
17   #include <cerrno> 17   #include <cerrno>
18   #include <cstdint> 18   #include <cstdint>
19   #include <cstring> 19   #include <cstring>
20   20  
21   #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) 21   #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
22   # pragma GCC diagnostic push 22   # pragma GCC diagnostic push
23   # pragma GCC diagnostic ignored "-Wmissing-field-initializers" 23   # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
24   #endif 24   #endif
25   25  
26   namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { 26   namespace boost { namespace json { namespace detail { namespace charconv { namespace detail {
27   27  
MISUBC 28   inline bool is_integer_char(char c) noexcept 28   inline bool is_integer_char(char c) noexcept
29   { 29   {
MISUBC 30   return (c >= '0') && (c <= '9'); 30   return (c >= '0') && (c <= '9');
31   } 31   }
32   32  
MISUBC 33   inline bool is_hex_char(char c) noexcept 33   inline bool is_hex_char(char c) noexcept
34   { 34   {
MISUBC 35   return is_integer_char(c) || (((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F'))); 35   return is_integer_char(c) || (((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')));
36   } 36   }
37   37  
MISUBC 38   inline bool is_delimiter(char c, chars_format fmt) noexcept 38   inline bool is_delimiter(char c, chars_format fmt) noexcept
39   { 39   {
MISUBC 40   if (fmt != chars_format::hex) 40   if (fmt != chars_format::hex)
41   { 41   {
MISUBC 42   return !is_integer_char(c) && c != 'e' && c != 'E'; 42   return !is_integer_char(c) && c != 'e' && c != 'E';
43   } 43   }
44   44  
MISUBC 45   return !is_hex_char(c) && c != 'p' && c != 'P'; 45   return !is_hex_char(c) && c != 'p' && c != 'P';
46   } 46   }
47   47  
48   template <typename Unsigned_Integer, typename Integer> 48   template <typename Unsigned_Integer, typename Integer>
MISUBC 49   inline from_chars_result parser(const char* first, const char* last, bool& sign, Unsigned_Integer& significand, Integer& exponent, chars_format fmt = chars_format::general) noexcept 49   inline from_chars_result parser(const char* first, const char* last, bool& sign, Unsigned_Integer& significand, Integer& exponent, chars_format fmt = chars_format::general) noexcept
50   { 50   {
MISUBC 51   if (first > last) 51   if (first > last)
52   { 52   {
MISUBC 53   return {first, std::errc::invalid_argument}; 53   return {first, std::errc::invalid_argument};
54   } 54   }
55   55  
MISUBC 56   auto next = first; 56   auto next = first;
MISUBC 57   bool all_zeros = true; 57   bool all_zeros = true;
58   58  
59   // First extract the sign 59   // First extract the sign
MISUBC 60   if (*next == '-') 60   if (*next == '-')
61   { 61   {
MISUBC 62   sign = true; 62   sign = true;
MISUBC 63   ++next; 63   ++next;
64   } 64   }
MISUBC 65   else if (*next == '+') 65   else if (*next == '+')
66   { 66   {
MISUBC 67   return {next, std::errc::invalid_argument}; 67   return {next, std::errc::invalid_argument};
68   } 68   }
69   else 69   else
70   { 70   {
MISUBC 71   sign = false; 71   sign = false;
72   } 72   }
73   73  
74   // Ignore leading zeros (e.g. 00005 or -002.3e+5) 74   // Ignore leading zeros (e.g. 00005 or -002.3e+5)
MISUBC 75   while (*next == '0' && next != last) 75   while (*next == '0' && next != last)
76   { 76   {
MISUBC 77   ++next; 77   ++next;
78   } 78   }
79   79  
80   // If the number is 0 we can abort now 80   // If the number is 0 we can abort now
81   char exp_char; 81   char exp_char;
82   char capital_exp_char; 82   char capital_exp_char;
MISUBC 83   if (fmt != chars_format::hex) 83   if (fmt != chars_format::hex)
84   { 84   {
MISUBC 85   exp_char = 'e'; 85   exp_char = 'e';
MISUBC 86   capital_exp_char = 'E'; 86   capital_exp_char = 'E';
87   } 87   }
88   else 88   else
89   { 89   {
MISUBC 90   exp_char = 'p'; 90   exp_char = 'p';
MISUBC 91   capital_exp_char = 'P'; 91   capital_exp_char = 'P';
92   } 92   }
93   93  
MISUBC 94   if (next == last || *next == exp_char || *next == -capital_exp_char) 94   if (next == last || *next == exp_char || *next == -capital_exp_char)
95   { 95   {
MISUBC 96   significand = 0; 96   significand = 0;
MISUBC 97   exponent = 0; 97   exponent = 0;
MISUBC 98   return {next, std::errc()}; 98   return {next, std::errc()};
99   } 99   }
100   100  
101   // Next we get the significand 101   // Next we get the significand
MISUBC 102   constexpr std::size_t significand_buffer_size = limits<Unsigned_Integer>::max_chars10 - 1; // Base 10 or 16 102   constexpr std::size_t significand_buffer_size = limits<Unsigned_Integer>::max_chars10 - 1; // Base 10 or 16
MISUBC 103   char significand_buffer[significand_buffer_size] {}; 103   char significand_buffer[significand_buffer_size] {};
MISUBC 104   std::size_t i = 0; 104   std::size_t i = 0;
MISUBC 105   std::size_t dot_position = 0; 105   std::size_t dot_position = 0;
MISUBC 106   Integer extra_zeros = 0; 106   Integer extra_zeros = 0;
MISUBC 107   Integer leading_zero_powers = 0; 107   Integer leading_zero_powers = 0;
MISUBC 108   const auto char_validation_func = (fmt != charconv::chars_format::hex) ? is_integer_char : is_hex_char; 108   const auto char_validation_func = (fmt != charconv::chars_format::hex) ? is_integer_char : is_hex_char;
MISUBC 109   const int base = (fmt != charconv::chars_format::hex) ? 10 : 16; 109   const int base = (fmt != charconv::chars_format::hex) ? 10 : 16;
110   110  
MISUBC 111   while (char_validation_func(*next) && next != last && i < significand_buffer_size) 111   while (char_validation_func(*next) && next != last && i < significand_buffer_size)
112   { 112   {
MISUBC 113   all_zeros = false; 113   all_zeros = false;
MISUBC 114   significand_buffer[i] = *next; 114   significand_buffer[i] = *next;
MISUBC 115   ++next; 115   ++next;
MISUBC 116   ++i; 116   ++i;
117   } 117   }
118   118  
MISUBC 119   bool fractional = false; 119   bool fractional = false;
MISUBC 120   if (next == last) 120   if (next == last)
121   { 121   {
122   // if fmt is chars_format::scientific the e is required 122   // if fmt is chars_format::scientific the e is required
MISUBC 123   if (fmt == chars_format::scientific) 123   if (fmt == chars_format::scientific)
124   { 124   {
MISUBC 125   return {first, std::errc::invalid_argument}; 125   return {first, std::errc::invalid_argument};
126   } 126   }
127   127  
MISUBC 128   exponent = 0; 128   exponent = 0;
MISUBC 129   std::size_t offset = i; 129   std::size_t offset = i;
130   130  
MISUBC 131   from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base); 131   from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base);
MISUBC 132   switch (r.ec) 132   switch (r.ec)
133   { 133   {
MISUBC 134   case std::errc::invalid_argument: 134   case std::errc::invalid_argument:
MISUBC 135   return {first, std::errc::invalid_argument}; 135   return {first, std::errc::invalid_argument};
MISUBC 136   case std::errc::result_out_of_range: 136   case std::errc::result_out_of_range:
MISUBC 137   return {next, std::errc::result_out_of_range}; 137   return {next, std::errc::result_out_of_range};
MISUBC 138   default: 138   default:
MISUBC 139   return {next, std::errc()}; 139   return {next, std::errc()};
140   } 140   }
141   } 141   }
MISUBC 142   else if (*next == '.') 142   else if (*next == '.')
143   { 143   {
MISUBC 144   ++next; 144   ++next;
MISUBC 145   fractional = true; 145   fractional = true;
MISUBC 146   dot_position = i; 146   dot_position = i;
147   147  
148   // Process the fractional part if we have it 148   // Process the fractional part if we have it
149   // 149   //
150   // if fmt is chars_format::scientific the e is required 150   // if fmt is chars_format::scientific the e is required
151   // if fmt is chars_format::fixed and not scientific the e is disallowed 151   // if fmt is chars_format::fixed and not scientific the e is disallowed
152   // if fmt is chars_format::general (which is scientific and fixed) the e is optional 152   // if fmt is chars_format::general (which is scientific and fixed) the e is optional
153   153  
154   // If we have the value 0.00001 we can continue to chop zeros and adjust the exponent 154   // If we have the value 0.00001 we can continue to chop zeros and adjust the exponent
155   // so that we get the useful parts of the fraction 155   // so that we get the useful parts of the fraction
MISUBC 156   if (all_zeros) 156   if (all_zeros)
157   { 157   {
MISUBC 158   while (*next == '0' && next != last) 158   while (*next == '0' && next != last)
159   { 159   {
MISUBC 160   ++next; 160   ++next;
MISUBC 161   --leading_zero_powers; 161   --leading_zero_powers;
162   } 162   }
163   163  
MISUBC 164   if (next == last) 164   if (next == last)
165   { 165   {
MISUBC 166   return {last, std::errc()}; 166   return {last, std::errc()};
167   } 167   }
168   } 168   }
169   169  
MISUBC 170   while (char_validation_func(*next) && next != last && i < significand_buffer_size) 170   while (char_validation_func(*next) && next != last && i < significand_buffer_size)
171   { 171   {
MISUBC 172   significand_buffer[i] = *next; 172   significand_buffer[i] = *next;
MISUBC 173   ++next; 173   ++next;
MISUBC 174   ++i; 174   ++i;
175   } 175   }
176   } 176   }
177   177  
MISUBC 178   if (i == significand_buffer_size) 178   if (i == significand_buffer_size)
179   { 179   {
180   // We can not process any more significant figures into the significand so skip to the end 180   // We can not process any more significant figures into the significand so skip to the end
181   // or the exponent part and capture the additional orders of magnitude for the exponent 181   // or the exponent part and capture the additional orders of magnitude for the exponent
MISUBC 182   bool found_dot = false; 182   bool found_dot = false;
MISUBC 183   while ((char_validation_func(*next) || *next == '.') && next != last) 183   while ((char_validation_func(*next) || *next == '.') && next != last)
184   { 184   {
MISUBC 185   ++next; 185   ++next;
MISUBC 186   if (!fractional && !found_dot) 186   if (!fractional && !found_dot)
187   { 187   {
MISUBC 188   ++extra_zeros; 188   ++extra_zeros;
189   } 189   }
MISUBC 190   if (*next == '.') 190   if (*next == '.')
191   { 191   {
MISUBC 192   found_dot = true; 192   found_dot = true;
193   } 193   }
194   } 194   }
195   } 195   }
196   196  
MISUBC 197   if (next == last || is_delimiter(*next, fmt)) 197   if (next == last || is_delimiter(*next, fmt))
198   { 198   {
MISUBC 199   if (fmt == chars_format::scientific) 199   if (fmt == chars_format::scientific)
200   { 200   {
MISUBC 201   return {first, std::errc::invalid_argument}; 201   return {first, std::errc::invalid_argument};
202   } 202   }
MISUBC 203   if (dot_position != 0 || fractional) 203   if (dot_position != 0 || fractional)
204   { 204   {
MISUBC 205   exponent = static_cast<Integer>(dot_position) - i + extra_zeros + leading_zero_powers; 205   exponent = static_cast<Integer>(dot_position) - i + extra_zeros + leading_zero_powers;
206   } 206   }
207   else 207   else
208   { 208   {
MISUBC 209   exponent = extra_zeros + leading_zero_powers; 209   exponent = extra_zeros + leading_zero_powers;
210   } 210   }
MISUBC 211   std::size_t offset = i; 211   std::size_t offset = i;
212   212  
MISUBC 213   from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base); 213   from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base);
MISUBC 214   switch (r.ec) 214   switch (r.ec)
215   { 215   {
MISUBC 216   case std::errc::invalid_argument: 216   case std::errc::invalid_argument:
MISUBC 217   return {first, std::errc::invalid_argument}; 217   return {first, std::errc::invalid_argument};
MISUBC 218   case std::errc::result_out_of_range: 218   case std::errc::result_out_of_range:
MISUBC 219   return {next, std::errc::result_out_of_range}; 219   return {next, std::errc::result_out_of_range};
MISUBC 220   default: 220   default:
MISUBC 221   return {next, std::errc()}; 221   return {next, std::errc()};
222   } 222   }
223   } 223   }
MISUBC 224   else if (*next == exp_char || *next == capital_exp_char) 224   else if (*next == exp_char || *next == capital_exp_char)
225   { 225   {
226   // Would be a number without a significand e.g. e+03 226   // Would be a number without a significand e.g. e+03
MISUBC 227   if (next == first) 227   if (next == first)
228   { 228   {
MISUBC 229   return {next, std::errc::invalid_argument}; 229   return {next, std::errc::invalid_argument};
230   } 230   }
231   231  
MISUBC 232   ++next; 232   ++next;
MISUBC 233   if (fmt == chars_format::fixed) 233   if (fmt == chars_format::fixed)
234   { 234   {
MISUBC 235   return {first, std::errc::invalid_argument}; 235   return {first, std::errc::invalid_argument};
236   } 236   }
237   237  
MISUBC 238   exponent = i - 1; 238   exponent = i - 1;
MISUBC 239   std::size_t offset = i; 239   std::size_t offset = i;
MISUBC 240   bool round = false; 240   bool round = false;
241   // If more digits are present than representable in the significand of the target type 241   // If more digits are present than representable in the significand of the target type
242   // we set the maximum 242   // we set the maximum
MISUBC 243   if (offset > significand_buffer_size) 243   if (offset > significand_buffer_size)
244   { 244   {
MISUBC 245   offset = significand_buffer_size - 1; 245   offset = significand_buffer_size - 1;
MISUBC 246   i = significand_buffer_size; 246   i = significand_buffer_size;
MISUBC 247   if (significand_buffer[offset] == '5' || 247   if (significand_buffer[offset] == '5' ||
MISUBC 248   significand_buffer[offset] == '6' || 248   significand_buffer[offset] == '6' ||
MISUBC 249   significand_buffer[offset] == '7' || 249   significand_buffer[offset] == '7' ||
MISUBC 250   significand_buffer[offset] == '8' || 250   significand_buffer[offset] == '8' ||
MISUBC 251   significand_buffer[offset] == '9') 251   significand_buffer[offset] == '9')
252   { 252   {
MISUBC 253   round = true; 253   round = true;
254   } 254   }
255   } 255   }
256   256  
257   // If the significand is 0 from chars will return std::errc::invalid_argument because there is nothing in the buffer, 257   // If the significand is 0 from chars will return std::errc::invalid_argument because there is nothing in the buffer,
258   // but it is a valid value. We need to continue parsing to get the correct value of ptr even 258   // but it is a valid value. We need to continue parsing to get the correct value of ptr even
259   // though we know we could bail now. 259   // though we know we could bail now.
260   // 260   //
261   // See GitHub issue #29: https://github.com/cppalliance/charconv/issues/29 261   // See GitHub issue #29: https://github.com/cppalliance/charconv/issues/29
MISUBC 262   if (offset != 0) 262   if (offset != 0)
263   { 263   {
MISUBC 264   from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base); 264   from_chars_result r = from_chars(significand_buffer, significand_buffer + offset, significand, base);
MISUBC 265   switch (r.ec) 265   switch (r.ec)
266   { 266   {
MISUBC 267   case std::errc::invalid_argument: 267   case std::errc::invalid_argument:
MISUBC 268   return {first, std::errc::invalid_argument}; 268   return {first, std::errc::invalid_argument};
MISUBC 269   case std::errc::result_out_of_range: 269   case std::errc::result_out_of_range:
MISUBC 270   return {next, std::errc::result_out_of_range}; 270   return {next, std::errc::result_out_of_range};
MISUBC 271   default: 271   default:
MISUBC 272   break; 272   break;
273   } 273   }
274   274  
MISUBC 275   if (round) 275   if (round)
276   { 276   {
MISUBC 277   significand += 1; 277   significand += 1;
278   } 278   }
279   } 279   }
MISUBC 280   } 280   }
281   else 281   else
282   { 282   {
MISUBC 283   return {first, std::errc::invalid_argument}; 283   return {first, std::errc::invalid_argument};
284   } 284   }
285   285  
286   // Finally we get the exponent 286   // Finally we get the exponent
MISUBC 287   constexpr std::size_t exponent_buffer_size = 6; // Float128 min exp is −16382 287   constexpr std::size_t exponent_buffer_size = 6; // Float128 min exp is −16382
MISUBC 288   char exponent_buffer[exponent_buffer_size] {}; 288   char exponent_buffer[exponent_buffer_size] {};
MISUBC 289   Integer significand_digits = i; 289   Integer significand_digits = i;
MISUBC 290   i = 0; 290   i = 0;
291   291  
292   // Get the sign first 292   // Get the sign first
MISUBC 293   if (*next == '-') 293   if (*next == '-')
294   { 294   {
MISUBC 295   exponent_buffer[i] = *next; 295   exponent_buffer[i] = *next;
MISUBC 296   ++next; 296   ++next;
MISUBC 297   ++i; 297   ++i;
298   } 298   }
MISUBC 299   else if (*next == '+') 299   else if (*next == '+')
300   { 300   {
MISUBC 301   ++next; 301   ++next;
302   } 302   }
303   303  
304   // Next strip any leading zeros 304   // Next strip any leading zeros
MISUBC 305   while (*next == '0') 305   while (*next == '0')
306   { 306   {
MISUBC 307   ++next; 307   ++next;
308   } 308   }
309   309  
310   // Process the significant values 310   // Process the significant values
MISUBC 311   while (is_integer_char(*next) && next != last && i < exponent_buffer_size) 311   while (is_integer_char(*next) && next != last && i < exponent_buffer_size)
312   { 312   {
MISUBC 313   exponent_buffer[i] = *next; 313   exponent_buffer[i] = *next;
MISUBC 314   ++next; 314   ++next;
MISUBC 315   ++i; 315   ++i;
316   } 316   }
317   317  
318   // If the exponent can't fit in the buffer the number is not representable 318   // If the exponent can't fit in the buffer the number is not representable
MISUBC 319   if (next != last && i == exponent_buffer_size) 319   if (next != last && i == exponent_buffer_size)
320   { 320   {
MISUBC 321   return {next, std::errc::result_out_of_range}; 321   return {next, std::errc::result_out_of_range};
322   } 322   }
323   323  
324   // If the exponent was e+00 or e-00 324   // If the exponent was e+00 or e-00
MISUBC 325   if (i == 0 || (i == 1 && exponent_buffer[0] == '-')) 325   if (i == 0 || (i == 1 && exponent_buffer[0] == '-'))
326   { 326   {
MISUBC 327   if (fractional) 327   if (fractional)
328   { 328   {
MISUBC 329   exponent = static_cast<Integer>(dot_position) - significand_digits; 329   exponent = static_cast<Integer>(dot_position) - significand_digits;
330   } 330   }
331   else 331   else
332   { 332   {
MISUBC 333   exponent = extra_zeros; 333   exponent = extra_zeros;
334   } 334   }
335   335  
MISUBC 336   return {next, std::errc()}; 336   return {next, std::errc()};
337   } 337   }
338   338  
MISUBC 339   const auto r = from_chars(exponent_buffer, exponent_buffer + i, exponent); 339   const auto r = from_chars(exponent_buffer, exponent_buffer + i, exponent);
340   340  
MISUBC 341   exponent += leading_zero_powers; 341   exponent += leading_zero_powers;
342   342  
MISUBC 343   switch (r.ec) 343   switch (r.ec)
344   { 344   {
MISUBC 345   case std::errc::invalid_argument: 345   case std::errc::invalid_argument:
MISUBC 346   return {first, std::errc::invalid_argument}; 346   return {first, std::errc::invalid_argument};
MISUBC 347   case std::errc::result_out_of_range: 347   case std::errc::result_out_of_range:
MISUBC 348   return {next, std::errc::result_out_of_range}; 348   return {next, std::errc::result_out_of_range};
MISUBC 349   default: 349   default:
MISUBC 350   if (fractional) 350   if (fractional)
351   { 351   {
352   // Need to take the offset from 1.xxx because compute_floatXXX assumes the significand is an integer 352   // Need to take the offset from 1.xxx because compute_floatXXX assumes the significand is an integer
353   // so the exponent is off by the number of digits in the significand - 1 353   // so the exponent is off by the number of digits in the significand - 1
MISUBC 354   if (fmt == chars_format::hex) 354   if (fmt == chars_format::hex)
355   { 355   {
356   // In hex the number of digits parsed is possibly less than the number of digits in base10 356   // In hex the number of digits parsed is possibly less than the number of digits in base10
MISUBC 357   exponent -= num_digits(significand) - dot_position; 357   exponent -= num_digits(significand) - dot_position;
358   } 358   }
359   else 359   else
360   { 360   {
MISUBC 361   exponent -= significand_digits - dot_position; 361   exponent -= significand_digits - dot_position;
362   } 362   }
363   } 363   }
364   else 364   else
365   { 365   {
MISUBC 366   exponent += extra_zeros; 366   exponent += extra_zeros;
367   } 367   }
MISUBC 368   return {next, std::errc()}; 368   return {next, std::errc()};
369   } 369   }
370   } 370   }
371   371  
372   }}}}} // Namespaces 372   }}}}} // Namespaces
373   373  
374   #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) 374   #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
375   # pragma GCC diagnostic pop 375   # pragma GCC diagnostic pop
376   #endif 376   #endif
377   377  
378   #endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP 378   #endif // BOOST_JSON_DETAIL_CHARCONV_DETAIL_PARSER_HPP