aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2024-07-07 15:42:10 +0200
committerPaul Eggert2024-07-11 16:01:41 +0200
commit0e221d3789a40d22bd4a9489985aebeb86f43e01 (patch)
treeccd43f006ac64fbc99d1566a4c45d0943f9327a3 /src
parent35365620e4c4c2560e13eba1f03332970129d7f8 (diff)
downloademacs-0e221d3789a40d22bd4a9489985aebeb86f43e01.tar.gz
emacs-0e221d3789a40d22bd4a9489985aebeb86f43e01.zip
Refactor timefns order
Move definitions around in timefns.c. This does not affect the implementation; it merely makes future changes easier to follow. * src/timefns.c (frac_to_double, mpz_time, lisp_to_timespec) (enum cform, union c_time, decode_ticks_hz): Move earlier.
Diffstat (limited to 'src')
-rw-r--r--src/timefns.c400
1 files changed, 200 insertions, 200 deletions
diff --git a/src/timefns.c b/src/timefns.c
index 70961c1a560..a7a7d552506 100644
--- a/src/timefns.c
+++ b/src/timefns.c
@@ -400,6 +400,112 @@ enum { flt_radix_power_size = DBL_MANT_DIG - DBL_MIN_EXP + 1 };
400 equals FLT_RADIX**P. */ 400 equals FLT_RADIX**P. */
401static Lisp_Object flt_radix_power; 401static Lisp_Object flt_radix_power;
402 402
403/* Return NUMERATOR / DENOMINATOR, rounded to the nearest double.
404 Arguments must be Lisp integers, and DENOMINATOR must be positive. */
405static double
406frac_to_double (Lisp_Object numerator, Lisp_Object denominator)
407{
408 intmax_t intmax_numerator, intmax_denominator;
409 if (FASTER_TIMEFNS
410 && integer_to_intmax (numerator, &intmax_numerator)
411 && integer_to_intmax (denominator, &intmax_denominator)
412 && intmax_numerator % intmax_denominator == 0)
413 return intmax_numerator / intmax_denominator;
414
415 /* Compute number of base-FLT_RADIX digits in numerator and denominator. */
416 mpz_t const *n = bignum_integer (&mpz[0], numerator);
417 mpz_t const *d = bignum_integer (&mpz[1], denominator);
418 ptrdiff_t ndig = mpz_sizeinbase (*n, FLT_RADIX);
419 ptrdiff_t ddig = mpz_sizeinbase (*d, FLT_RADIX);
420
421 /* Scale with SCALE when doing integer division. That is, compute
422 (N * FLT_RADIX**SCALE) / D [or, if SCALE is negative, N / (D *
423 FLT_RADIX**-SCALE)] as a bignum, convert the bignum to double,
424 then divide the double by FLT_RADIX**SCALE. First scale N
425 (or scale D, if SCALE is negative) ... */
426 ptrdiff_t scale = ddig - ndig + DBL_MANT_DIG;
427 if (scale < 0)
428 {
429 mpz_mul_2exp (mpz[1], *d, - (scale * LOG2_FLT_RADIX));
430 d = &mpz[1];
431 }
432 else
433 {
434 /* min so we don't scale tiny numbers as if they were normalized. */
435 scale = min (scale, flt_radix_power_size - 1);
436
437 mpz_mul_2exp (mpz[0], *n, scale * LOG2_FLT_RADIX);
438 n = &mpz[0];
439 }
440 /* ... and then divide, with quotient Q and remainder R. */
441 mpz_t *q = &mpz[2];
442 mpz_t *r = &mpz[3];
443 mpz_tdiv_qr (*q, *r, *n, *d);
444
445 /* The amount to add to the absolute value of Q so that truncating
446 it to double will round correctly. */
447 int incr;
448
449 /* Round the quotient before converting it to double.
450 If the quotient is less than FLT_RADIX ** DBL_MANT_DIG,
451 round to the nearest integer; otherwise, it is less than
452 FLT_RADIX ** (DBL_MANT_DIG + 1) and round it to the nearest
453 multiple of FLT_RADIX. Break ties to even. */
454 if (mpz_sizeinbase (*q, FLT_RADIX) <= DBL_MANT_DIG)
455 {
456 /* Converting to double will use the whole quotient so add 1 to
457 its absolute value as per round-to-even; i.e., if the doubled
458 remainder exceeds the denominator, or exactly equals the
459 denominator and adding 1 would make the quotient even. */
460 mpz_mul_2exp (*r, *r, 1);
461 int cmp = mpz_cmpabs (*r, *d);
462 incr = cmp > 0 || (cmp == 0 && (FASTER_TIMEFNS && FLT_RADIX == 2
463 ? mpz_odd_p (*q)
464 : mpz_tdiv_ui (*q, FLT_RADIX) & 1));
465 }
466 else
467 {
468 /* Converting to double will discard the quotient's low-order digit,
469 so add FLT_RADIX to its absolute value as per round-to-even. */
470 int lo_2digits = mpz_tdiv_ui (*q, FLT_RADIX * FLT_RADIX);
471 eassume (0 <= lo_2digits && lo_2digits < FLT_RADIX * FLT_RADIX);
472 int lo_digit = lo_2digits % FLT_RADIX;
473 incr = ((lo_digit > FLT_RADIX / 2
474 || (lo_digit == FLT_RADIX / 2 && FLT_RADIX % 2 == 0
475 && ((lo_2digits / FLT_RADIX) & 1
476 || mpz_sgn (*r) != 0)))
477 ? FLT_RADIX : 0);
478 }
479
480 /* Increment the absolute value of the quotient by INCR. */
481 if (!FASTER_TIMEFNS || incr != 0)
482 (mpz_sgn (*n) < 0 ? mpz_sub_ui : mpz_add_ui) (*q, *q, incr);
483
484 /* Rescale the integer Q back to double. This step does not round. */
485 return scalbn (mpz_get_d (*q), -scale);
486}
487
488/* Convert Z to time_t, returning true if it fits. */
489static bool
490mpz_time (mpz_t const z, time_t *t)
491{
492 if (TYPE_SIGNED (time_t))
493 {
494 intmax_t i;
495 if (! (mpz_to_intmax (z, &i) && TIME_T_MIN <= i && i <= TIME_T_MAX))
496 return false;
497 *t = i;
498 }
499 else
500 {
501 uintmax_t i;
502 if (! (mpz_to_uintmax (z, &i) && i <= TIME_T_MAX))
503 return false;
504 *t = i;
505 }
506 return true;
507}
508
403/* Components of a Lisp timestamp (TICKS . HZ). Using this C struct can 509/* Components of a Lisp timestamp (TICKS . HZ). Using this C struct can
404 avoid the consing overhead of creating (TICKS . HZ). */ 510 avoid the consing overhead of creating (TICKS . HZ). */
405struct lisp_time 511struct lisp_time
@@ -411,6 +517,100 @@ struct lisp_time
411 Lisp_Object hz; 517 Lisp_Object hz;
412}; 518};
413 519
520/* Convert T to struct timespec, returning an invalid timespec
521 if T does not fit. */
522static struct timespec
523lisp_to_timespec (struct lisp_time t)
524{
525 struct timespec result = invalid_timespec ();
526 int ns;
527 mpz_t *q = &mpz[0];
528 mpz_t const *qt = q;
529
530 /* Floor-divide (T.ticks * TIMESPEC_HZ) by T.hz,
531 yielding quotient Q (tv_sec) and remainder NS (tv_nsec).
532 Return an invalid timespec if Q does not fit in time_t.
533 For speed, prefer fixnum arithmetic if it works. */
534 if (FASTER_TIMEFNS && BASE_EQ (t.hz, timespec_hz))
535 {
536 if (FIXNUMP (t.ticks))
537 {
538 EMACS_INT s = XFIXNUM (t.ticks) / TIMESPEC_HZ;
539 ns = XFIXNUM (t.ticks) % TIMESPEC_HZ;
540 if (ns < 0)
541 s--, ns += TIMESPEC_HZ;
542 if ((TYPE_SIGNED (time_t) ? TIME_T_MIN <= s : 0 <= s)
543 && s <= TIME_T_MAX)
544 {
545 result.tv_sec = s;
546 result.tv_nsec = ns;
547 }
548 return result;
549 }
550 else
551 ns = mpz_fdiv_q_ui (*q, *xbignum_val (t.ticks), TIMESPEC_HZ);
552 }
553 else if (FASTER_TIMEFNS && BASE_EQ (t.hz, make_fixnum (1)))
554 {
555 ns = 0;
556 if (FIXNUMP (t.ticks))
557 {
558 EMACS_INT s = XFIXNUM (t.ticks);
559 if ((TYPE_SIGNED (time_t) ? TIME_T_MIN <= s : 0 <= s)
560 && s <= TIME_T_MAX)
561 {
562 result.tv_sec = s;
563 result.tv_nsec = ns;
564 }
565 return result;
566 }
567 else
568 qt = xbignum_val (t.ticks);
569 }
570 else
571 {
572 mpz_mul_ui (*q, *bignum_integer (q, t.ticks), TIMESPEC_HZ);
573 mpz_fdiv_q (*q, *q, *bignum_integer (&mpz[1], t.hz));
574 ns = mpz_fdiv_q_ui (*q, *q, TIMESPEC_HZ);
575 }
576
577 /* Check that Q fits in time_t, not merely in T.tv_sec. With some versions
578 of MinGW, tv_sec is a 64-bit type, whereas time_t is a 32-bit type. */
579 time_t sec;
580 if (mpz_time (*qt, &sec))
581 {
582 result.tv_sec = sec;
583 result.tv_nsec = ns;
584 }
585 return result;
586}
587
588/* C timestamp forms. This enum is passed to conversion functions to
589 specify the desired C timestamp form. */
590enum cform
591 {
592 CFORM_TICKS_HZ, /* struct lisp_time */
593 CFORM_SECS_ONLY, /* struct lisp_time but HZ is 1 */
594 CFORM_DOUBLE /* double */
595 };
596
597/* A C timestamp in one of the forms specified by enum cform. */
598union c_time
599{
600 struct lisp_time lt;
601 double d;
602};
603
604/* From a valid timestamp (TICKS . HZ), generate the corresponding
605 time value in CFORM form. */
606static union c_time
607decode_ticks_hz (Lisp_Object ticks, Lisp_Object hz, enum cform cform)
608{
609 return (cform == CFORM_DOUBLE
610 ? (union c_time) { .d = frac_to_double (ticks, hz) }
611 : (union c_time) { .lt = { .ticks = ticks, .hz = hz } });
612}
613
414/* Convert the finite number T into an Emacs time, truncating 614/* Convert the finite number T into an Emacs time, truncating
415 toward minus infinity. Signal an error if unsuccessful. */ 615 toward minus infinity. Signal an error if unsuccessful. */
416static struct lisp_time 616static struct lisp_time
@@ -613,117 +813,6 @@ timespec_to_lisp (struct timespec t)
613 return Fcons (timespec_ticks (t), timespec_hz); 813 return Fcons (timespec_ticks (t), timespec_hz);
614} 814}
615 815
616/* Return NUMERATOR / DENOMINATOR, rounded to the nearest double.
617 Arguments must be Lisp integers, and DENOMINATOR must be positive. */
618static double
619frac_to_double (Lisp_Object numerator, Lisp_Object denominator)
620{
621 intmax_t intmax_numerator, intmax_denominator;
622 if (FASTER_TIMEFNS
623 && integer_to_intmax (numerator, &intmax_numerator)
624 && integer_to_intmax (denominator, &intmax_denominator)
625 && intmax_numerator % intmax_denominator == 0)
626 return intmax_numerator / intmax_denominator;
627
628 /* Compute number of base-FLT_RADIX digits in numerator and denominator. */
629 mpz_t const *n = bignum_integer (&mpz[0], numerator);
630 mpz_t const *d = bignum_integer (&mpz[1], denominator);
631 ptrdiff_t ndig = mpz_sizeinbase (*n, FLT_RADIX);
632 ptrdiff_t ddig = mpz_sizeinbase (*d, FLT_RADIX);
633
634 /* Scale with SCALE when doing integer division. That is, compute
635 (N * FLT_RADIX**SCALE) / D [or, if SCALE is negative, N / (D *
636 FLT_RADIX**-SCALE)] as a bignum, convert the bignum to double,
637 then divide the double by FLT_RADIX**SCALE. First scale N
638 (or scale D, if SCALE is negative) ... */
639 ptrdiff_t scale = ddig - ndig + DBL_MANT_DIG;
640 if (scale < 0)
641 {
642 mpz_mul_2exp (mpz[1], *d, - (scale * LOG2_FLT_RADIX));
643 d = &mpz[1];
644 }
645 else
646 {
647 /* min so we don't scale tiny numbers as if they were normalized. */
648 scale = min (scale, flt_radix_power_size - 1);
649
650 mpz_mul_2exp (mpz[0], *n, scale * LOG2_FLT_RADIX);
651 n = &mpz[0];
652 }
653 /* ... and then divide, with quotient Q and remainder R. */
654 mpz_t *q = &mpz[2];
655 mpz_t *r = &mpz[3];
656 mpz_tdiv_qr (*q, *r, *n, *d);
657
658 /* The amount to add to the absolute value of Q so that truncating
659 it to double will round correctly. */
660 int incr;
661
662 /* Round the quotient before converting it to double.
663 If the quotient is less than FLT_RADIX ** DBL_MANT_DIG,
664 round to the nearest integer; otherwise, it is less than
665 FLT_RADIX ** (DBL_MANT_DIG + 1) and round it to the nearest
666 multiple of FLT_RADIX. Break ties to even. */
667 if (mpz_sizeinbase (*q, FLT_RADIX) <= DBL_MANT_DIG)
668 {
669 /* Converting to double will use the whole quotient so add 1 to
670 its absolute value as per round-to-even; i.e., if the doubled
671 remainder exceeds the denominator, or exactly equals the
672 denominator and adding 1 would make the quotient even. */
673 mpz_mul_2exp (*r, *r, 1);
674 int cmp = mpz_cmpabs (*r, *d);
675 incr = cmp > 0 || (cmp == 0 && (FASTER_TIMEFNS && FLT_RADIX == 2
676 ? mpz_odd_p (*q)
677 : mpz_tdiv_ui (*q, FLT_RADIX) & 1));
678 }
679 else
680 {
681 /* Converting to double will discard the quotient's low-order digit,
682 so add FLT_RADIX to its absolute value as per round-to-even. */
683 int lo_2digits = mpz_tdiv_ui (*q, FLT_RADIX * FLT_RADIX);
684 eassume (0 <= lo_2digits && lo_2digits < FLT_RADIX * FLT_RADIX);
685 int lo_digit = lo_2digits % FLT_RADIX;
686 incr = ((lo_digit > FLT_RADIX / 2
687 || (lo_digit == FLT_RADIX / 2 && FLT_RADIX % 2 == 0
688 && ((lo_2digits / FLT_RADIX) & 1
689 || mpz_sgn (*r) != 0)))
690 ? FLT_RADIX : 0);
691 }
692
693 /* Increment the absolute value of the quotient by INCR. */
694 if (!FASTER_TIMEFNS || incr != 0)
695 (mpz_sgn (*n) < 0 ? mpz_sub_ui : mpz_add_ui) (*q, *q, incr);
696
697 /* Rescale the integer Q back to double. This step does not round. */
698 return scalbn (mpz_get_d (*q), -scale);
699}
700
701/* C timestamp forms. This enum is passed to conversion functions to
702 specify the desired C timestamp form. */
703enum cform
704 {
705 CFORM_TICKS_HZ, /* struct lisp_time */
706 CFORM_SECS_ONLY, /* struct lisp_time but HZ is 1 */
707 CFORM_DOUBLE /* double */
708 };
709
710/* A C timestamp in one of the forms specified by enum cform. */
711union c_time
712{
713 struct lisp_time lt;
714 double d;
715};
716
717/* From a valid timestamp (TICKS . HZ), generate the corresponding
718 time value in CFORM form. */
719static union c_time
720decode_ticks_hz (Lisp_Object ticks, Lisp_Object hz, enum cform cform)
721{
722 return (cform == CFORM_DOUBLE
723 ? (union c_time) { .d = frac_to_double (ticks, hz) }
724 : (union c_time) { .lt = { .ticks = ticks, .hz = hz } });
725}
726
727/* An (error number, C timestamp) pair. */ 816/* An (error number, C timestamp) pair. */
728struct err_time 817struct err_time
729{ 818{
@@ -939,95 +1028,6 @@ float_time (Lisp_Object specified_time)
939 return decode_lisp_time (specified_time, CFORM_DOUBLE).time.d; 1028 return decode_lisp_time (specified_time, CFORM_DOUBLE).time.d;
940} 1029}
941 1030
942/* Convert Z to time_t, returning true if it fits. */
943static bool
944mpz_time (mpz_t const z, time_t *t)
945{
946 if (TYPE_SIGNED (time_t))
947 {
948 intmax_t i;
949 if (! (mpz_to_intmax (z, &i) && TIME_T_MIN <= i && i <= TIME_T_MAX))
950 return false;
951 *t = i;
952 }
953 else
954 {
955 uintmax_t i;
956 if (! (mpz_to_uintmax (z, &i) && i <= TIME_T_MAX))
957 return false;
958 *t = i;
959 }
960 return true;
961}
962
963/* Convert T to struct timespec, returning an invalid timespec
964 if T does not fit. */
965static struct timespec
966lisp_to_timespec (struct lisp_time t)
967{
968 struct timespec result = invalid_timespec ();
969 int ns;
970 mpz_t *q = &mpz[0];
971 mpz_t const *qt = q;
972
973 /* Floor-divide (T.ticks * TIMESPEC_HZ) by T.hz,
974 yielding quotient Q (tv_sec) and remainder NS (tv_nsec).
975 Return an invalid timespec if Q does not fit in time_t.
976 For speed, prefer fixnum arithmetic if it works. */
977 if (FASTER_TIMEFNS && BASE_EQ (t.hz, timespec_hz))
978 {
979 if (FIXNUMP (t.ticks))
980 {
981 EMACS_INT s = XFIXNUM (t.ticks) / TIMESPEC_HZ;
982 ns = XFIXNUM (t.ticks) % TIMESPEC_HZ;
983 if (ns < 0)
984 s--, ns += TIMESPEC_HZ;
985 if ((TYPE_SIGNED (time_t) ? TIME_T_MIN <= s : 0 <= s)
986 && s <= TIME_T_MAX)
987 {
988 result.tv_sec = s;
989 result.tv_nsec = ns;
990 }
991 return result;
992 }
993 else
994 ns = mpz_fdiv_q_ui (*q, *xbignum_val (t.ticks), TIMESPEC_HZ);
995 }
996 else if (FASTER_TIMEFNS && BASE_EQ (t.hz, make_fixnum (1)))
997 {
998 ns = 0;
999 if (FIXNUMP (t.ticks))
1000 {
1001 EMACS_INT s = XFIXNUM (t.ticks);
1002 if ((TYPE_SIGNED (time_t) ? TIME_T_MIN <= s : 0 <= s)
1003 && s <= TIME_T_MAX)
1004 {
1005 result.tv_sec = s;
1006 result.tv_nsec = ns;
1007 }
1008 return result;
1009 }
1010 else
1011 qt = xbignum_val (t.ticks);
1012 }
1013 else
1014 {
1015 mpz_mul_ui (*q, *bignum_integer (q, t.ticks), TIMESPEC_HZ);
1016 mpz_fdiv_q (*q, *q, *bignum_integer (&mpz[1], t.hz));
1017 ns = mpz_fdiv_q_ui (*q, *q, TIMESPEC_HZ);
1018 }
1019
1020 /* Check that Q fits in time_t, not merely in T.tv_sec. With some versions
1021 of MinGW, tv_sec is a 64-bit type, whereas time_t is a 32-bit type. */
1022 time_t sec;
1023 if (mpz_time (*qt, &sec))
1024 {
1025 result.tv_sec = sec;
1026 result.tv_nsec = ns;
1027 }
1028 return result;
1029}
1030
1031/* Convert (HIGH LOW USEC PSEC) to struct timespec. 1031/* Convert (HIGH LOW USEC PSEC) to struct timespec.
1032 Return a valid timestamp if successful, an invalid one otherwise. */ 1032 Return a valid timestamp if successful, an invalid one otherwise. */
1033struct timespec 1033struct timespec