@@ -1841,114 +1841,142 @@ term nif_erlang_make_ref_0(Context *ctx, int argc, term argv[])
18411841 return term_from_ref_ticks (ref_ticks , & ctx -> heap );
18421842}
18431843
1844- term nif_erlang_monotonic_time_1 ( Context * ctx , int argc , term argv [] )
1844+ static bool time_unit_to_parts_per_second ( term unit , avm_int64_t * parts_per_second )
18451845{
1846- UNUSED (ctx );
1847-
1848- term unit ;
1849- if (argc == 0 ) {
1850- unit = NATIVE_ATOM ;
1846+ if (unit == SECOND_ATOM ) {
1847+ * parts_per_second = 1 ;
1848+ } else if (unit == MILLISECOND_ATOM ) {
1849+ * parts_per_second = 1000 ;
1850+ } else if (unit == MICROSECOND_ATOM ) {
1851+ * parts_per_second = INT64_C (1000000 );
1852+ } else if (unit == NANOSECOND_ATOM || unit == NATIVE_ATOM ) {
1853+ * parts_per_second = INT64_C (1000000000 );
1854+ } else if (term_is_int64 (unit )) {
1855+ * parts_per_second = term_maybe_unbox_int64 (unit );
1856+ if (UNLIKELY (* parts_per_second <= 0 )) {
1857+ return false;
1858+ }
18511859 } else {
1852- unit = argv [ 0 ] ;
1860+ return false ;
18531861 }
18541862
1855- struct timespec ts ;
1856- sys_monotonic_time ( & ts );
1863+ return true ;
1864+ }
18571865
1858- if (unit == SECOND_ATOM ) {
1859- return make_maybe_boxed_int64 (ctx , ts .tv_sec );
1866+ // Convert nanoseconds to parts using: parts = nanoseconds * pps / 1e9
1867+ // Splits into high/low to avoid intermediate overflow.
1868+ // Caller must ensure 0 <= nanoseconds < 1e9 and pps > 0.
1869+ static bool nanoseconds_to_parts_per_second (
1870+ avm_int64_t nanoseconds , avm_int64_t parts_per_second , bool round_up , avm_int64_t * parts )
1871+ {
1872+ avm_int64_t quotient = parts_per_second / INT64_C (1000000000 );
1873+ avm_int64_t remainder = parts_per_second % INT64_C (1000000000 );
1874+ avm_int64_t fractional_high = nanoseconds * quotient ;
1875+ avm_int64_t remainder_product = nanoseconds * remainder ;
1876+ avm_int64_t fractional_low = remainder_product / INT64_C (1000000000 );
18601877
1861- } else if (unit == MILLISECOND_ATOM ) {
1862- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000UL + ts .tv_nsec / 1000000UL );
1878+ if (round_up && (remainder_product % INT64_C (1000000000 )) != 0 ) {
1879+ fractional_low += 1 ;
1880+ }
18631881
1864- } else if (unit == MICROSECOND_ATOM ) {
1865- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000000UL + ts .tv_nsec / 1000UL );
1882+ if (UNLIKELY (fractional_high > INT64_MAX - fractional_low )) {
1883+ return false;
1884+ }
18661885
1867- } else if (unit == NANOSECOND_ATOM || unit == NATIVE_ATOM ) {
1868- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * INT64_C (1000000000 ) + ts .tv_nsec );
1886+ * parts = fractional_high + fractional_low ;
1887+ return true;
1888+ }
18691889
1870- } else if (term_is_int64 (unit )) {
1871- avm_int64_t parts_per_second = term_maybe_unbox_int64 (unit );
1872- if (UNLIKELY (parts_per_second <= 0 )) {
1873- RAISE_ERROR (BADARG_ATOM );
1874- }
1890+ // Convert a normalized timespec (0 <= tv_nsec < 1e9) to integer parts.
1891+ // Uses floor semantics for negative timestamps with non-zero tv_nsec.
1892+ static bool timespec_to_parts_per_second (
1893+ const struct timespec * ts , avm_int64_t parts_per_second , avm_int64_t * parts )
1894+ {
1895+ avm_int64_t seconds = (avm_int64_t ) ts -> tv_sec ;
1896+ avm_int64_t fractional_part ;
1897+
1898+ if (ts -> tv_nsec == 0 || seconds >= 0 ) {
18751899 if (UNLIKELY (
1876- ((ts . tv_sec > 0 ) && (( avm_int64_t ) ts . tv_sec > INT64_MAX / parts_per_second ))
1877- || ((ts . tv_sec < 0 ) && (( avm_int64_t ) ts . tv_sec < INT64_MIN / parts_per_second )))) {
1878- RAISE_ERROR ( BADARG_ATOM ) ;
1900+ ((seconds > 0 ) && (seconds > INT64_MAX / parts_per_second ))
1901+ || ((seconds < 0 ) && (seconds < INT64_MIN / parts_per_second )))) {
1902+ return false ;
18791903 }
1880- avm_int64_t second_part = (avm_int64_t ) ts .tv_sec * parts_per_second ;
1881- avm_int64_t quotient = parts_per_second / INT64_C (1000000000 );
1882- avm_int64_t remainder = parts_per_second % INT64_C (1000000000 );
1883- avm_int64_t fractional_high = (avm_int64_t ) ts .tv_nsec * quotient ;
1884- avm_int64_t fractional_low = ((avm_int64_t ) ts .tv_nsec * remainder ) / INT64_C (1000000000 );
1885- if (UNLIKELY (fractional_high > INT64_MAX - fractional_low )) {
1886- RAISE_ERROR (BADARG_ATOM );
1904+
1905+ if (UNLIKELY (!nanoseconds_to_parts_per_second (
1906+ (avm_int64_t ) ts -> tv_nsec , parts_per_second , false, & fractional_part ))) {
1907+ return false;
18871908 }
1888- avm_int64_t fractional_part = fractional_high + fractional_low ;
1909+
1910+ avm_int64_t second_part = seconds * parts_per_second ;
18891911 if (UNLIKELY (second_part > INT64_MAX - fractional_part )) {
1890- RAISE_ERROR ( BADARG_ATOM ) ;
1912+ return false ;
18911913 }
1892- return make_maybe_boxed_int64 (ctx , second_part + fractional_part );
18931914
1894- } else {
1895- RAISE_ERROR (BADARG_ATOM );
1915+ * parts = second_part + fractional_part ;
1916+ return true;
1917+ }
1918+
1919+ // Preserve floor semantics for normalized negative timespecs such as {-2, 999999999}.
1920+ avm_int64_t adjusted_seconds = seconds + 1 ;
1921+ if (UNLIKELY (adjusted_seconds < INT64_MIN / parts_per_second )) {
1922+ return false;
1923+ }
1924+
1925+ if (UNLIKELY (!nanoseconds_to_parts_per_second (
1926+ INT64_C (1000000000 ) - (avm_int64_t ) ts -> tv_nsec , parts_per_second , true,
1927+ & fractional_part ))) {
1928+ return false;
18961929 }
1930+
1931+ avm_int64_t second_part = adjusted_seconds * parts_per_second ;
1932+ if (UNLIKELY (second_part < INT64_MIN + fractional_part )) {
1933+ return false;
1934+ }
1935+
1936+ * parts = second_part - fractional_part ;
1937+ return true;
18971938}
18981939
1899- term nif_erlang_system_time_1 (Context * ctx , int argc , term argv [] )
1940+ static term make_time_in_unit (Context * ctx , term unit , void ( * time_fun )( struct timespec * ) )
19001941{
1901- UNUSED (ctx );
1942+ avm_int64_t parts_per_second ;
1943+ if (UNLIKELY (!time_unit_to_parts_per_second (unit , & parts_per_second ))) {
1944+ RAISE_ERROR (BADARG_ATOM );
1945+ }
19021946
1947+ struct timespec ts ;
1948+ time_fun (& ts );
1949+
1950+ avm_int64_t value ;
1951+ if (UNLIKELY (!timespec_to_parts_per_second (& ts , parts_per_second , & value ))) {
1952+ RAISE_ERROR (BADARG_ATOM );
1953+ }
1954+
1955+ return make_maybe_boxed_int64 (ctx , value );
1956+ }
1957+
1958+ term nif_erlang_monotonic_time_1 (Context * ctx , int argc , term argv [])
1959+ {
19031960 term unit ;
19041961 if (argc == 0 ) {
19051962 unit = NATIVE_ATOM ;
19061963 } else {
19071964 unit = argv [0 ];
19081965 }
19091966
1910- struct timespec ts ;
1911- sys_time (& ts );
1912-
1913- if (unit == SECOND_ATOM ) {
1914- return make_maybe_boxed_int64 (ctx , ts .tv_sec );
1915-
1916- } else if (unit == MILLISECOND_ATOM ) {
1917- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000UL + ts .tv_nsec / 1000000UL );
1918-
1919- } else if (unit == MICROSECOND_ATOM ) {
1920- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000000UL + ts .tv_nsec / 1000UL );
1921-
1922- } else if (unit == NANOSECOND_ATOM || unit == NATIVE_ATOM ) {
1923- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * INT64_C (1000000000 ) + ts .tv_nsec );
1924-
1925- } else if (term_is_int64 (unit )) {
1926- avm_int64_t parts_per_second = term_maybe_unbox_int64 (unit );
1927- if (UNLIKELY (parts_per_second <= 0 )) {
1928- RAISE_ERROR (BADARG_ATOM );
1929- }
1930- if (UNLIKELY (
1931- ((ts .tv_sec > 0 ) && ((avm_int64_t ) ts .tv_sec > INT64_MAX / parts_per_second ))
1932- || ((ts .tv_sec < 0 ) && ((avm_int64_t ) ts .tv_sec < INT64_MIN / parts_per_second )))) {
1933- RAISE_ERROR (BADARG_ATOM );
1934- }
1935- avm_int64_t second_part = (avm_int64_t ) ts .tv_sec * parts_per_second ;
1936- avm_int64_t quotient = parts_per_second / INT64_C (1000000000 );
1937- avm_int64_t remainder = parts_per_second % INT64_C (1000000000 );
1938- avm_int64_t fractional_high = (avm_int64_t ) ts .tv_nsec * quotient ;
1939- avm_int64_t fractional_low = ((avm_int64_t ) ts .tv_nsec * remainder ) / INT64_C (1000000000 );
1940- if (UNLIKELY (fractional_high > INT64_MAX - fractional_low )) {
1941- RAISE_ERROR (BADARG_ATOM );
1942- }
1943- avm_int64_t fractional_part = fractional_high + fractional_low ;
1944- if (UNLIKELY (second_part > INT64_MAX - fractional_part )) {
1945- RAISE_ERROR (BADARG_ATOM );
1946- }
1947- return make_maybe_boxed_int64 (ctx , second_part + fractional_part );
1967+ return make_time_in_unit (ctx , unit , sys_monotonic_time );
1968+ }
19481969
1970+ term nif_erlang_system_time_1 (Context * ctx , int argc , term argv [])
1971+ {
1972+ term unit ;
1973+ if (argc == 0 ) {
1974+ unit = NATIVE_ATOM ;
19491975 } else {
1950- RAISE_ERROR ( BADARG_ATOM ) ;
1976+ unit = argv [ 0 ] ;
19511977 }
1978+
1979+ return make_time_in_unit (ctx , unit , sys_time );
19521980}
19531981
19541982static term build_datetime_from_tm (Context * ctx , struct tm * broken_down_time )
@@ -2062,27 +2090,14 @@ term nif_calendar_system_time_to_universal_time_2(Context *ctx, int argc, term a
20622090 }
20632091 avm_int64_t value = term_maybe_unbox_int64 (argv [0 ]);
20642092
2065- avm_int64_t divisor ;
2066- if (argv [1 ] == SECOND_ATOM ) {
2067- divisor = 1 ;
2068- } else if (argv [1 ] == MILLISECOND_ATOM ) {
2069- divisor = 1000 ;
2070- } else if (argv [1 ] == MICROSECOND_ATOM ) {
2071- divisor = INT64_C (1000000 );
2072- } else if (argv [1 ] == NANOSECOND_ATOM || argv [1 ] == NATIVE_ATOM ) {
2073- divisor = INT64_C (1000000000 );
2074- } else if (term_is_int64 (argv [1 ])) {
2075- divisor = term_maybe_unbox_int64 (argv [1 ]);
2076- if (UNLIKELY (divisor <= 0 )) {
2077- RAISE_ERROR (BADARG_ATOM );
2078- }
2079- } else {
2093+ avm_int64_t parts_per_second ;
2094+ if (UNLIKELY (!time_unit_to_parts_per_second (argv [1 ], & parts_per_second ))) {
20802095 RAISE_ERROR (BADARG_ATOM );
20812096 }
20822097
20832098 // Floor division: round negative fractional seconds toward negative infinity
2084- avm_int64_t quotient = value / divisor ;
2085- avm_int64_t remainder = value % divisor ;
2099+ avm_int64_t quotient = value / parts_per_second ;
2100+ avm_int64_t remainder = value % parts_per_second ;
20862101 struct timespec ts = {
20872102 .tv_sec = (time_t ) (quotient - (remainder < 0 )),
20882103 .tv_nsec = 0
0 commit comments