11#include <gmp.h>
2- #include <mpfr.h>
32#include <langinfo.h>
43#include <stdarg.h>
54#include <stdio.h>
5+ #include <stdlib.h>
66#include <inttypes.h>
77#include <string.h>
88#include <ctype.h>
@@ -224,53 +224,58 @@ static void strstrip(char *str) {
224224 str [i - begin ] = '\0' ;
225225}
226226
227- static bool multiply_size_by_unit (mpfr_t size , char * unit_str ) {
227+ static bool multiply_size_by_unit (mpq_t size , char * unit_str ) {
228228 BSBunit bunit = BS_BUNIT_UNDEF ;
229229 BSDunit dunit = BS_DUNIT_UNDEF ;
230230 uint64_t pwr = 0 ;
231- mpfr_t dec_mul ;
231+ mpq_t dec_mul ;
232+ mpz_t pow_1000 ;
232233 size_t unit_str_len = 0 ;
233234
234235 unit_str_len = strlen (unit_str );
235236
236237 for (bunit = BS_BUNIT_B ; bunit < BS_BUNIT_UNDEF ; bunit ++ )
237238 if (strncasecmp (unit_str , b_units [bunit - BS_BUNIT_B ], unit_str_len ) == 0 ) {
238239 pwr = (uint64_t ) bunit - BS_BUNIT_B ;
239- mpfr_mul_2exp ( size , size , 10 * pwr , MPFR_RNDN );
240+ mpz_mul_2exp ( mpq_numref ( size ), mpq_numref ( size ) , 10 * pwr );
240241 return true;
241242 }
242243
243- mpfr_init2 (dec_mul , BS_FLOAT_PREC_BITS );
244- mpfr_set_ui ( dec_mul , 1000 , MPFR_RNDN );
244+ mpq_init (dec_mul );
245+ mpz_init ( pow_1000 );
245246 for (dunit = BS_DUNIT_B ; dunit < BS_DUNIT_UNDEF ; dunit ++ )
246247 if (strncasecmp (unit_str , d_units [dunit - BS_DUNIT_B ], unit_str_len ) == 0 ) {
247248 pwr = (uint64_t ) (dunit - BS_DUNIT_B );
248- mpfr_pow_ui (dec_mul , dec_mul , pwr , MPFR_RNDN );
249- mpfr_mul (size , size , dec_mul , MPFR_RNDN );
250- mpfr_clear (dec_mul );
249+ mpz_ui_pow_ui (pow_1000 , 1000 , pwr );
250+ mpq_set_z (dec_mul , pow_1000 );
251+ mpq_mul (size , size , dec_mul );
252+ mpz_clear (pow_1000 );
253+ mpq_clear (dec_mul );
251254 return true;
252255 }
253256
254- /* not found among the binary and decimal units, let's try their translated
255- versions */
256257 for (bunit = BS_BUNIT_B ; bunit < BS_BUNIT_UNDEF ; bunit ++ )
257258 if (strncasecmp (unit_str , _ (b_units [bunit - BS_BUNIT_B ]), unit_str_len ) == 0 ) {
258259 pwr = (uint64_t ) bunit - BS_BUNIT_B ;
259- mpfr_mul_2exp (size , size , 10 * pwr , MPFR_RNDN );
260+ mpz_mul_2exp (mpq_numref (size ), mpq_numref (size ), 10 * pwr );
261+ mpz_clear (pow_1000 );
262+ mpq_clear (dec_mul );
260263 return true;
261264 }
262265
263- mpfr_init2 (dec_mul , BS_FLOAT_PREC_BITS );
264- mpfr_set_ui (dec_mul , 1000 , MPFR_RNDN );
265266 for (dunit = BS_DUNIT_B ; dunit < BS_DUNIT_UNDEF ; dunit ++ )
266267 if (strncasecmp (unit_str , _ (d_units [dunit - BS_DUNIT_B ]), unit_str_len ) == 0 ) {
267268 pwr = (uint64_t ) (dunit - BS_DUNIT_B );
268- mpfr_pow_ui (dec_mul , dec_mul , pwr , MPFR_RNDN );
269- mpfr_mul (size , size , dec_mul , MPFR_RNDN );
270- mpfr_clear (dec_mul );
269+ mpz_ui_pow_ui (pow_1000 , 1000 , pwr );
270+ mpq_set_z (dec_mul , pow_1000 );
271+ mpq_mul (size , size , dec_mul );
272+ mpz_clear (pow_1000 );
273+ mpq_clear (dec_mul );
271274 return true;
272275 }
273276
277+ mpz_clear (pow_1000 );
278+ mpq_clear (dec_mul );
274279 return false;
275280}
276281
@@ -436,10 +441,10 @@ BSSize bs_size_new_from_bytes (uint64_t bytes, int sgn) {
436441 */
437442BSSize bs_size_new_from_str (const char * size_str , BSError * * error ) {
438443 char const * const pattern = "^\\s* # white space \n" \
439- "(?P<numeric> # the numeric part consists of three parts, below \n" \
440- " (-|\\+)? # optional sign character \n" \
441- " (?P<base>( [0-9\\.%s]+)) # base \n" \
442- " (?P<exp>(e|E)( -|\\+)?[0-9]+)?) # exponent \n" \
444+ "(?P<sign>(-|\\+)?) # optional sign character \n" \
445+ "(?P<int_part>[0-9]*) # integer part \n" \
446+ "(?:%s(?P<frac_part> [0-9]*))? # optional fractional part \n" \
447+ "(?:(?P<exp_sep>[eE])(?P<exp_sign>( -|\\+)?)(?P<exp_val> [0-9]+))? # optional exponent \n" \
443448 "\\s* # white space \n" \
444449 "(?P<rest>[^\\s]*)\\s*$ # unit specification" ;
445450 char * real_pattern = NULL ;
@@ -451,19 +456,25 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
451456 int str_count = 0 ;
452457 const char * radix_char = NULL ;
453458 char * loc_size_str = NULL ;
454- mpf_t parsed_size ;
455- mpfr_t size ;
459+ mpq_t size ;
456460 int status = 0 ;
457461 BSSize ret = NULL ;
458462 PCRE2_UCHAR * substring = NULL ;
459463 PCRE2_SIZE substring_len = 0 ;
460464 PCRE2_UCHAR error_buffer [ERROR_BUFFER_LEN ];
465+ int sign = 1 ;
466+ long exp_val = 0 ;
467+ int exp_sign = 1 ;
468+ mpz_t numerator , denominator , int_part , frac_part , pow_10 ;
469+ size_t frac_digits = 0 ;
470+ bool has_int_digits = false;
471+ bool has_frac_digits = false;
461472
462473 radix_char = nl_langinfo (RADIXCHAR );
463474 if (strncmp (radix_char , "." , 1 ) != 0 )
464475 real_pattern = strdup_printf (pattern , radix_char );
465476 else
466- real_pattern = strdup_printf (pattern , "" );
477+ real_pattern = strdup_printf (pattern , "\\. " );
467478
468479 regex = pcre2_compile ((PCRE2_SPTR ) real_pattern , PCRE2_ZERO_TERMINATED , PCRE2_EXTENDED , & errorcode , & erroffset , NULL );
469480 free (real_pattern );
@@ -509,32 +520,111 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
509520 return NULL ;
510521 }
511522
512- status = pcre2_substring_get_byname (match_data , (PCRE2_SPTR ) "numeric" , & substring , & substring_len );
513- if (status < 0 || substring_len < 1 ) {
514- set_error (error , BS_ERROR_INVALID_SPEC , strdup_printf ("Failed to parse size spec: %s" , size_str ));
515- pcre2_match_data_free (match_data );
516- pcre2_code_free (regex );
517- free (loc_size_str );
518- return NULL ;
523+ mpq_init (size );
524+ mpz_init (numerator );
525+ mpz_init (denominator );
526+ mpz_init (int_part );
527+ mpz_init (frac_part );
528+ mpz_init (pow_10 );
529+
530+ status = pcre2_substring_get_byname (match_data , (PCRE2_SPTR ) "sign" , & substring , & substring_len );
531+ if (status >= 0 && substring_len > 0 && substring [0 ] == '-' )
532+ sign = -1 ;
533+ if (status >= 0 )
534+ pcre2_substring_free (substring );
535+
536+ status = pcre2_substring_get_byname (match_data , (PCRE2_SPTR ) "int_part" , & substring , & substring_len );
537+ if (status >= 0 && substring_len > 0 ) {
538+ has_int_digits = true;
539+ status = mpz_set_str (int_part , (const char * ) substring , 10 );
540+ pcre2_substring_free (substring );
541+ if (status != 0 ) {
542+ set_error (error , BS_ERROR_INVALID_SPEC , strdup_printf ("Failed to parse size spec: %s" , size_str ));
543+ mpz_clears (numerator , denominator , int_part , frac_part , pow_10 , NULL );
544+ mpq_clear (size );
545+ pcre2_match_data_free (match_data );
546+ pcre2_code_free (regex );
547+ free (loc_size_str );
548+ return NULL ;
549+ }
550+ } else {
551+ mpz_set_ui (int_part , 0 );
552+ if (status >= 0 )
553+ pcre2_substring_free (substring );
519554 }
520555
521- /* parse the number using GMP because it knows how to handle localization
522- much better than MPFR */
523- mpf_init2 (parsed_size , BS_FLOAT_PREC_BITS );
524- status = mpf_set_str (parsed_size , * substring == '+' ? (const char * ) substring + 1 : (const char * ) substring , 10 );
525- pcre2_substring_free (substring );
526- if (status != 0 ) {
556+ status = pcre2_substring_get_byname (match_data , (PCRE2_SPTR ) "frac_part" , & substring , & substring_len );
557+ if (status >= 0 && substring_len > 0 ) {
558+ has_frac_digits = true;
559+ frac_digits = substring_len ;
560+ status = mpz_set_str (frac_part , (const char * ) substring , 10 );
561+ pcre2_substring_free (substring );
562+ if (status != 0 ) {
563+ set_error (error , BS_ERROR_INVALID_SPEC , strdup_printf ("Failed to parse size spec: %s" , size_str ));
564+ mpz_clears (numerator , denominator , int_part , frac_part , pow_10 , NULL );
565+ mpq_clear (size );
566+ pcre2_match_data_free (match_data );
567+ pcre2_code_free (regex );
568+ free (loc_size_str );
569+ return NULL ;
570+ }
571+ } else {
572+ mpz_set_ui (frac_part , 0 );
573+ frac_digits = 0 ;
574+ if (status >= 0 )
575+ pcre2_substring_free (substring );
576+ }
577+
578+ /* Validate: we must have at least one digit in int_part or frac_part */
579+ if (!has_int_digits && !has_frac_digits ) {
527580 set_error (error , BS_ERROR_INVALID_SPEC , strdup_printf ("Failed to parse size spec: %s" , size_str ));
581+ mpz_clears (numerator , denominator , int_part , frac_part , pow_10 , NULL );
582+ mpq_clear (size );
528583 pcre2_match_data_free (match_data );
529584 pcre2_code_free (regex );
530585 free (loc_size_str );
531- mpf_clear (parsed_size );
532586 return NULL ;
533587 }
534- /* but use MPFR from now on because GMP thinks 0.1*1000 = 99 */
535- mpfr_init2 (size , BS_FLOAT_PREC_BITS );
536- mpfr_set_f (size , parsed_size , MPFR_RNDN );
537- mpf_clear (parsed_size );
588+
589+ status = pcre2_substring_get_byname (match_data , (PCRE2_SPTR ) "exp_val" , & substring , & substring_len );
590+ if (status >= 0 && substring_len > 0 ) {
591+ exp_val = strtol ((const char * ) substring , NULL , 10 );
592+ pcre2_substring_free (substring );
593+
594+ status = pcre2_substring_get_byname (match_data , (PCRE2_SPTR ) "exp_sign" , & substring , & substring_len );
595+ if (status >= 0 && substring_len > 0 && substring [0 ] == '-' )
596+ exp_sign = -1 ;
597+ if (status >= 0 )
598+ pcre2_substring_free (substring );
599+ }
600+
601+ mpz_ui_pow_ui (pow_10 , 10 , frac_digits );
602+ mpz_set (denominator , pow_10 );
603+ mpz_mul (numerator , int_part , pow_10 );
604+ mpz_add (numerator , numerator , frac_part );
605+
606+ if (exp_val != 0 ) {
607+ long adjusted_exp = exp_val ;
608+ if (exp_sign == -1 )
609+ adjusted_exp = - adjusted_exp ;
610+
611+ if (adjusted_exp > 0 ) {
612+ mpz_ui_pow_ui (pow_10 , 10 , adjusted_exp );
613+ mpz_mul (numerator , numerator , pow_10 );
614+ } else if (adjusted_exp < 0 ) {
615+ mpz_ui_pow_ui (pow_10 , 10 , - adjusted_exp );
616+ mpz_mul (denominator , denominator , pow_10 );
617+ }
618+ }
619+
620+ if (sign == -1 )
621+ mpz_neg (numerator , numerator );
622+
623+ mpq_set_num (size , numerator );
624+ mpq_set_den (size , denominator );
625+ mpq_canonicalize (size );
626+
627+ mpz_clears (numerator , denominator , int_part , frac_part , pow_10 , NULL );
538628
539629 status = pcre2_substring_get_byname (match_data , (PCRE2_SPTR ) "rest" , & substring , & substring_len );
540630 if ((status >= 0 ) && strncmp ((const char * ) substring , "" , 1 ) != 0 ) {
@@ -545,7 +635,7 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
545635 pcre2_match_data_free (match_data );
546636 pcre2_code_free (regex );
547637 free (loc_size_str );
548- mpfr_clear (size );
638+ mpq_clear (size );
549639 return NULL ;
550640 }
551641 }
@@ -554,10 +644,11 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
554644 pcre2_code_free (regex );
555645
556646 ret = bs_size_new ();
557- mpfr_get_z (ret -> bytes , size , MPFR_RNDZ );
647+ /* Rational to int, round towards zero for preserving previous behaviour */
648+ mpz_tdiv_q (ret -> bytes , mpq_numref (size ), mpq_denref (size ));
558649
559650 free (loc_size_str );
560- mpfr_clear (size );
651+ mpq_clear (size );
561652
562653 return ret ;
563654}
0 commit comments