@@ -1851,114 +1851,142 @@ term nif_erlang_make_ref_0(Context *ctx, int argc, term argv[])
18511851 return term_from_ref_ticks (ref_ticks , & ctx -> heap );
18521852}
18531853
1854- term nif_erlang_monotonic_time_1 ( Context * ctx , int argc , term argv [] )
1854+ static bool time_unit_to_parts_per_second ( term unit , avm_int64_t * parts_per_second )
18551855{
1856- UNUSED (ctx );
1857-
1858- term unit ;
1859- if (argc == 0 ) {
1860- unit = NATIVE_ATOM ;
1856+ if (unit == SECOND_ATOM ) {
1857+ * parts_per_second = 1 ;
1858+ } else if (unit == MILLISECOND_ATOM ) {
1859+ * parts_per_second = 1000 ;
1860+ } else if (unit == MICROSECOND_ATOM ) {
1861+ * parts_per_second = INT64_C (1000000 );
1862+ } else if (unit == NANOSECOND_ATOM || unit == NATIVE_ATOM ) {
1863+ * parts_per_second = INT64_C (1000000000 );
1864+ } else if (term_is_int64 (unit )) {
1865+ * parts_per_second = term_maybe_unbox_int64 (unit );
1866+ if (UNLIKELY (* parts_per_second <= 0 )) {
1867+ return false;
1868+ }
18611869 } else {
1862- unit = argv [ 0 ] ;
1870+ return false ;
18631871 }
18641872
1865- struct timespec ts ;
1866- sys_monotonic_time ( & ts );
1873+ return true ;
1874+ }
18671875
1868- if (unit == SECOND_ATOM ) {
1869- return make_maybe_boxed_int64 (ctx , ts .tv_sec );
1876+ // Convert nanoseconds to parts using: parts = nanoseconds * pps / 1e9
1877+ // Splits into high/low to avoid intermediate overflow.
1878+ // Caller must ensure 0 <= nanoseconds < 1e9 and pps > 0.
1879+ static bool nanoseconds_to_parts_per_second (
1880+ avm_int64_t nanoseconds , avm_int64_t parts_per_second , bool round_up , avm_int64_t * parts )
1881+ {
1882+ avm_int64_t quotient = parts_per_second / INT64_C (1000000000 );
1883+ avm_int64_t remainder = parts_per_second % INT64_C (1000000000 );
1884+ avm_int64_t fractional_high = nanoseconds * quotient ;
1885+ avm_int64_t remainder_product = nanoseconds * remainder ;
1886+ avm_int64_t fractional_low = remainder_product / INT64_C (1000000000 );
18701887
1871- } else if (unit == MILLISECOND_ATOM ) {
1872- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000UL + ts .tv_nsec / 1000000UL );
1888+ if (round_up && (remainder_product % INT64_C (1000000000 )) != 0 ) {
1889+ fractional_low += 1 ;
1890+ }
18731891
1874- } else if (unit == MICROSECOND_ATOM ) {
1875- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000000UL + ts .tv_nsec / 1000UL );
1892+ if (UNLIKELY (fractional_high > INT64_MAX - fractional_low )) {
1893+ return false;
1894+ }
18761895
1877- } else if (unit == NANOSECOND_ATOM || unit == NATIVE_ATOM ) {
1878- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * INT64_C (1000000000 ) + ts .tv_nsec );
1896+ * parts = fractional_high + fractional_low ;
1897+ return true;
1898+ }
18791899
1880- } else if (term_is_int64 (unit )) {
1881- avm_int64_t parts_per_second = term_maybe_unbox_int64 (unit );
1882- if (UNLIKELY (parts_per_second <= 0 )) {
1883- RAISE_ERROR (BADARG_ATOM );
1884- }
1900+ // Convert a normalized timespec (0 <= tv_nsec < 1e9) to integer parts.
1901+ // Uses floor semantics for negative timestamps with non-zero tv_nsec.
1902+ static bool timespec_to_parts_per_second (
1903+ const struct timespec * ts , avm_int64_t parts_per_second , avm_int64_t * parts )
1904+ {
1905+ avm_int64_t seconds = (avm_int64_t ) ts -> tv_sec ;
1906+ avm_int64_t fractional_part ;
1907+
1908+ if (ts -> tv_nsec == 0 || seconds >= 0 ) {
18851909 if (UNLIKELY (
1886- ((ts . tv_sec > 0 ) && (( avm_int64_t ) ts . tv_sec > INT64_MAX / parts_per_second ))
1887- || ((ts . tv_sec < 0 ) && (( avm_int64_t ) ts . tv_sec < INT64_MIN / parts_per_second )))) {
1888- RAISE_ERROR ( BADARG_ATOM ) ;
1910+ ((seconds > 0 ) && (seconds > INT64_MAX / parts_per_second ))
1911+ || ((seconds < 0 ) && (seconds < INT64_MIN / parts_per_second )))) {
1912+ return false ;
18891913 }
1890- avm_int64_t second_part = (avm_int64_t ) ts .tv_sec * parts_per_second ;
1891- avm_int64_t quotient = parts_per_second / INT64_C (1000000000 );
1892- avm_int64_t remainder = parts_per_second % INT64_C (1000000000 );
1893- avm_int64_t fractional_high = (avm_int64_t ) ts .tv_nsec * quotient ;
1894- avm_int64_t fractional_low = ((avm_int64_t ) ts .tv_nsec * remainder ) / INT64_C (1000000000 );
1895- if (UNLIKELY (fractional_high > INT64_MAX - fractional_low )) {
1896- RAISE_ERROR (BADARG_ATOM );
1914+
1915+ if (UNLIKELY (!nanoseconds_to_parts_per_second (
1916+ (avm_int64_t ) ts -> tv_nsec , parts_per_second , false, & fractional_part ))) {
1917+ return false;
18971918 }
1898- avm_int64_t fractional_part = fractional_high + fractional_low ;
1919+
1920+ avm_int64_t second_part = seconds * parts_per_second ;
18991921 if (UNLIKELY (second_part > INT64_MAX - fractional_part )) {
1900- RAISE_ERROR ( BADARG_ATOM ) ;
1922+ return false ;
19011923 }
1902- return make_maybe_boxed_int64 (ctx , second_part + fractional_part );
19031924
1904- } else {
1905- RAISE_ERROR (BADARG_ATOM );
1925+ * parts = second_part + fractional_part ;
1926+ return true;
1927+ }
1928+
1929+ // Preserve floor semantics for normalized negative timespecs such as {-2, 999999999}.
1930+ avm_int64_t adjusted_seconds = seconds + 1 ;
1931+ if (UNLIKELY (adjusted_seconds < INT64_MIN / parts_per_second )) {
1932+ return false;
1933+ }
1934+
1935+ if (UNLIKELY (!nanoseconds_to_parts_per_second (
1936+ INT64_C (1000000000 ) - (avm_int64_t ) ts -> tv_nsec , parts_per_second , true,
1937+ & fractional_part ))) {
1938+ return false;
19061939 }
1940+
1941+ avm_int64_t second_part = adjusted_seconds * parts_per_second ;
1942+ if (UNLIKELY (second_part < INT64_MIN + fractional_part )) {
1943+ return false;
1944+ }
1945+
1946+ * parts = second_part - fractional_part ;
1947+ return true;
19071948}
19081949
1909- term nif_erlang_system_time_1 (Context * ctx , int argc , term argv [] )
1950+ static term make_time_in_unit (Context * ctx , term unit , void ( * time_fun )( struct timespec * ) )
19101951{
1911- UNUSED (ctx );
1952+ avm_int64_t parts_per_second ;
1953+ if (UNLIKELY (!time_unit_to_parts_per_second (unit , & parts_per_second ))) {
1954+ RAISE_ERROR (BADARG_ATOM );
1955+ }
19121956
1957+ struct timespec ts ;
1958+ time_fun (& ts );
1959+
1960+ avm_int64_t value ;
1961+ if (UNLIKELY (!timespec_to_parts_per_second (& ts , parts_per_second , & value ))) {
1962+ RAISE_ERROR (BADARG_ATOM );
1963+ }
1964+
1965+ return make_maybe_boxed_int64 (ctx , value );
1966+ }
1967+
1968+ term nif_erlang_monotonic_time_1 (Context * ctx , int argc , term argv [])
1969+ {
19131970 term unit ;
19141971 if (argc == 0 ) {
19151972 unit = NATIVE_ATOM ;
19161973 } else {
19171974 unit = argv [0 ];
19181975 }
19191976
1920- struct timespec ts ;
1921- sys_time (& ts );
1922-
1923- if (unit == SECOND_ATOM ) {
1924- return make_maybe_boxed_int64 (ctx , ts .tv_sec );
1925-
1926- } else if (unit == MILLISECOND_ATOM ) {
1927- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000UL + ts .tv_nsec / 1000000UL );
1928-
1929- } else if (unit == MICROSECOND_ATOM ) {
1930- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * 1000000UL + ts .tv_nsec / 1000UL );
1931-
1932- } else if (unit == NANOSECOND_ATOM || unit == NATIVE_ATOM ) {
1933- return make_maybe_boxed_int64 (ctx , ((int64_t ) ts .tv_sec ) * INT64_C (1000000000 ) + ts .tv_nsec );
1934-
1935- } else if (term_is_int64 (unit )) {
1936- avm_int64_t parts_per_second = term_maybe_unbox_int64 (unit );
1937- if (UNLIKELY (parts_per_second <= 0 )) {
1938- RAISE_ERROR (BADARG_ATOM );
1939- }
1940- if (UNLIKELY (
1941- ((ts .tv_sec > 0 ) && ((avm_int64_t ) ts .tv_sec > INT64_MAX / parts_per_second ))
1942- || ((ts .tv_sec < 0 ) && ((avm_int64_t ) ts .tv_sec < INT64_MIN / parts_per_second )))) {
1943- RAISE_ERROR (BADARG_ATOM );
1944- }
1945- avm_int64_t second_part = (avm_int64_t ) ts .tv_sec * parts_per_second ;
1946- avm_int64_t quotient = parts_per_second / INT64_C (1000000000 );
1947- avm_int64_t remainder = parts_per_second % INT64_C (1000000000 );
1948- avm_int64_t fractional_high = (avm_int64_t ) ts .tv_nsec * quotient ;
1949- avm_int64_t fractional_low = ((avm_int64_t ) ts .tv_nsec * remainder ) / INT64_C (1000000000 );
1950- if (UNLIKELY (fractional_high > INT64_MAX - fractional_low )) {
1951- RAISE_ERROR (BADARG_ATOM );
1952- }
1953- avm_int64_t fractional_part = fractional_high + fractional_low ;
1954- if (UNLIKELY (second_part > INT64_MAX - fractional_part )) {
1955- RAISE_ERROR (BADARG_ATOM );
1956- }
1957- return make_maybe_boxed_int64 (ctx , second_part + fractional_part );
1977+ return make_time_in_unit (ctx , unit , sys_monotonic_time );
1978+ }
19581979
1980+ term nif_erlang_system_time_1 (Context * ctx , int argc , term argv [])
1981+ {
1982+ term unit ;
1983+ if (argc == 0 ) {
1984+ unit = NATIVE_ATOM ;
19591985 } else {
1960- RAISE_ERROR ( BADARG_ATOM ) ;
1986+ unit = argv [ 0 ] ;
19611987 }
1988+
1989+ return make_time_in_unit (ctx , unit , sys_time );
19621990}
19631991
19641992static term build_datetime_from_tm (Context * ctx , struct tm * broken_down_time )
@@ -2150,27 +2178,14 @@ term nif_calendar_system_time_to_universal_time_2(Context *ctx, int argc, term a
21502178 }
21512179 avm_int64_t value = term_maybe_unbox_int64 (argv [0 ]);
21522180
2153- avm_int64_t divisor ;
2154- if (argv [1 ] == SECOND_ATOM ) {
2155- divisor = 1 ;
2156- } else if (argv [1 ] == MILLISECOND_ATOM ) {
2157- divisor = 1000 ;
2158- } else if (argv [1 ] == MICROSECOND_ATOM ) {
2159- divisor = INT64_C (1000000 );
2160- } else if (argv [1 ] == NANOSECOND_ATOM || argv [1 ] == NATIVE_ATOM ) {
2161- divisor = INT64_C (1000000000 );
2162- } else if (term_is_int64 (argv [1 ])) {
2163- divisor = term_maybe_unbox_int64 (argv [1 ]);
2164- if (UNLIKELY (divisor <= 0 )) {
2165- RAISE_ERROR (BADARG_ATOM );
2166- }
2167- } else {
2181+ avm_int64_t parts_per_second ;
2182+ if (UNLIKELY (!time_unit_to_parts_per_second (argv [1 ], & parts_per_second ))) {
21682183 RAISE_ERROR (BADARG_ATOM );
21692184 }
21702185
21712186 // Floor division: round negative fractional seconds toward negative infinity
2172- avm_int64_t quotient = value / divisor ;
2173- avm_int64_t remainder = value % divisor ;
2187+ avm_int64_t quotient = value / parts_per_second ;
2188+ avm_int64_t remainder = value % parts_per_second ;
21742189 struct timespec ts = {
21752190 .tv_sec = (time_t ) (quotient - (remainder < 0 )),
21762191 .tv_nsec = 0
0 commit comments