Compare commits
21
Commits
ddcf8f39be
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d07a914a1
|
||
|
|
49968bdbfc
|
||
|
|
1f39e217d4
|
||
|
|
b731a02db4
|
||
|
|
c4c64a4eb5
|
||
|
|
9eb32c4010
|
||
|
|
523045b5ae
|
||
|
|
9b576757cf
|
||
|
|
fefd17241f
|
||
|
|
3bf875030a
|
||
|
|
f9a055f547
|
||
|
|
9ffe86fc55
|
||
|
|
8c0f23fbd0
|
||
|
|
e9aa002453
|
||
|
|
3ab43c7f10
|
||
|
|
c4156efeff
|
||
|
|
ea0482e9ff
|
||
|
|
c67e890b17
|
||
|
|
d495d3eb89
|
||
|
|
ed996ff4c4
|
||
|
|
3eff4c97fd
|
+337
-11
@@ -15,9 +15,23 @@ AM_CFLAGS = @VLIBC_CFLAGS@
|
||||
# appended to libvlibc_la_SOURCES WHEN ITS TODO LANDS — the list grows
|
||||
# incrementally and never references a directory that does not exist yet:
|
||||
#
|
||||
# W1 (wired now): src/internal, src/start, src/errno,
|
||||
# src/setjmp/x86_64, src/string, crt/ (crt1.o)
|
||||
# todo 7 src/malloc todo 39-42 src/math todo 50 src/multibyte
|
||||
# Wired so far (W1-W3 + W5/W6 slices, each wired when its todo landed):
|
||||
# src/internal, src/start, src/errno,
|
||||
# src/setjmp/x86_64, src/string, crt/ (crt1.o),
|
||||
# src/malloc (todo 7), src/ctype (todo 9,51),
|
||||
# src/stdlib (todo 11-14, incl. the 13 env slice),
|
||||
# src/unistd (todo 19), src/process (todo 20,23),
|
||||
# src/fcntl (todo 21), src/stat (todo 22),
|
||||
# src/dirent (todo 24), src/select (todo 25),
|
||||
# src/time (todo 26 + todo 27's sys/time
|
||||
# slice — gettimeofday/settimeofday/
|
||||
# getitimer/setitimer/utimes.c + sys/time.h —
|
||||
# all wired), src/stdio (todo 15-18 FILE core
|
||||
# + popen; vfprintf.c/vfprintf_float.c now
|
||||
# wired with the printf test),
|
||||
# src/math (todo 39-42 basic set — all L1,
|
||||
# wired with the consolidated math test)
|
||||
# todo 39-42 src/math todo 50 src/multibyte
|
||||
# todo 11-14 src/stdlib todo 43 src/complex todo 53-54 src/regex
|
||||
# todo 15-18 src/stdio todo 9,51 src/ctype todo 57-58 src/network
|
||||
# todo 19,38 src/unistd todo 55 src/search todo 59 src/mq
|
||||
@@ -41,6 +55,15 @@ AM_CFLAGS = @VLIBC_CFLAGS@
|
||||
# standalone crt1.o below (the sole crt owner; todo 64 reuses, never rebuilds).
|
||||
|
||||
# Level-independent L1 core (wired today + the pre-plan string slice).
|
||||
# W5/W6 additions follow the established split rule: whole-content-L2 TUs
|
||||
# (lockf, mknod, readdir_r/scandir/alphasort, ppoll, strptime/timer) go to
|
||||
# VLIBC_LEVEL2_SRCS; L1 bodies with L2 sections self-gated inside the .c
|
||||
# (fork.c vfork, groups.c setgroups, session.c tail, wait.c wait3/wait4,
|
||||
# time ctime/mktime/nanosleep/strftime/tzset L2 tails, stdio.c L2 tail,
|
||||
# unistd lseek64/dup3/pipe2/truncate) stay in CORE. task-19's unistd dir is
|
||||
# CORE throughout (self-gated L2 sections, no whole-file gate). T27's
|
||||
# sys/time slice (gettimeofday/settimeofday/getitimer/setitimer/utimes) is
|
||||
# whole-file L2, so it lives in VLIBC_LEVEL2_SRCS.
|
||||
VLIBC_CORE_SRCS = \
|
||||
src/vlibc.c \
|
||||
src/internal/assert_fail.c \
|
||||
@@ -50,6 +73,10 @@ VLIBC_CORE_SRCS = \
|
||||
src/start/exit.c \
|
||||
src/start/environ.c \
|
||||
src/start/tls.c \
|
||||
src/thread/futex.c \
|
||||
src/thread/pthread_self.c \
|
||||
src/thread/tls.c \
|
||||
src/thread/x86_64/__set_thread_area.s \
|
||||
src/errno/strerror.c \
|
||||
src/setjmp/x86_64/setjmp.s \
|
||||
src/setjmp/x86_64/longjmp.s \
|
||||
@@ -85,7 +112,116 @@ VLIBC_CORE_SRCS = \
|
||||
src/stdlib/inttypes.c \
|
||||
src/stdlib/env.c \
|
||||
src/stdlib/multibyte.c \
|
||||
src/stdlib/mkstemp.c
|
||||
src/stdlib/mkstemp.c \
|
||||
src/stdio/stdio.c \
|
||||
src/stdio/fmemopen.c \
|
||||
src/stdio/getline.c \
|
||||
src/stdio/locking.c \
|
||||
src/stdio/popen.c \
|
||||
src/stdio/vfscanf.c \
|
||||
src/stdio/vfprintf.c \
|
||||
src/unistd/access.c \
|
||||
src/unistd/close.c \
|
||||
src/unistd/dup.c \
|
||||
src/unistd/fsync.c \
|
||||
src/unistd/lseek.c \
|
||||
src/unistd/open.c \
|
||||
src/unistd/pipe.c \
|
||||
src/unistd/pread.c \
|
||||
src/unistd/rw.c \
|
||||
src/unistd/sync.c \
|
||||
src/unistd/truncate.c \
|
||||
src/unistd/chdir.c \
|
||||
src/unistd/getcwd.c \
|
||||
src/unistd/getlogin.c \
|
||||
src/unistd/isatty.c \
|
||||
src/unistd/link.c \
|
||||
src/unistd/readlink.c \
|
||||
src/unistd/renameat.c \
|
||||
src/unistd/rmdir.c \
|
||||
src/unistd/symlink.c \
|
||||
src/unistd/ttyname.c \
|
||||
src/unistd/unlink.c \
|
||||
src/unistd/getopt.c \
|
||||
src/process/atfork.c \
|
||||
src/process/execl.c \
|
||||
src/process/execle.c \
|
||||
src/process/execlp.c \
|
||||
src/process/execv.c \
|
||||
src/process/execve.c \
|
||||
src/process/execvp.c \
|
||||
src/process/fork.c \
|
||||
src/process/groups.c \
|
||||
src/process/pid.c \
|
||||
src/process/session.c \
|
||||
src/process/system.c \
|
||||
src/process/uid.c \
|
||||
src/process/wait.c \
|
||||
src/fcntl/creat.c \
|
||||
src/fcntl/fcntl.c \
|
||||
src/fcntl/posix_fadvise.c \
|
||||
src/fcntl/posix_fallocate.c \
|
||||
src/stat/chmod.c \
|
||||
src/stat/chown.c \
|
||||
src/stat/fstatat.c \
|
||||
src/stat/mkdir.c \
|
||||
src/stat/mkfifo.c \
|
||||
src/stat/umask.c \
|
||||
src/stat/utimensat.c \
|
||||
src/dirent/closedir.c \
|
||||
src/dirent/dirfd.c \
|
||||
src/dirent/fdopendir.c \
|
||||
src/dirent/opendir.c \
|
||||
src/dirent/readdir.c \
|
||||
src/dirent/rewinddir.c \
|
||||
src/dirent/seekdir.c \
|
||||
src/dirent/telldir.c \
|
||||
src/select/poll.c \
|
||||
src/select/pselect.c \
|
||||
src/select/select.c \
|
||||
src/time/time.c \
|
||||
src/time/clock.c \
|
||||
src/time/timespec_get.c \
|
||||
src/time/sleep.c \
|
||||
src/time/nanosleep.c \
|
||||
src/time/mktime.c \
|
||||
src/time/ctime.c \
|
||||
src/time/tzset.c \
|
||||
src/time/strftime.c \
|
||||
src/mman/mmap.c \
|
||||
src/passwd/passwd.c \
|
||||
src/group/group.c \
|
||||
src/math/cbrt.c \
|
||||
src/math/ceil.c \
|
||||
src/math/copysign.c \
|
||||
src/math/exp.c \
|
||||
src/math/fabs.c \
|
||||
src/math/fdim.c \
|
||||
src/math/floor.c \
|
||||
src/math/fmax.c \
|
||||
src/math/fmin.c \
|
||||
src/math/fmod.c \
|
||||
src/math/frexp.c \
|
||||
src/math/hypot.c \
|
||||
src/math/ilogb.c \
|
||||
src/math/ldexp.c \
|
||||
src/math/llrint.c \
|
||||
src/math/llround.c \
|
||||
src/math/log.c \
|
||||
src/math/log1p.c \
|
||||
src/math/logb.c \
|
||||
src/math/lrint.c \
|
||||
src/math/lround.c \
|
||||
src/math/modf.c \
|
||||
src/math/nearbyint.c \
|
||||
src/math/pow.c \
|
||||
src/math/remainder.c \
|
||||
src/math/remquo.c \
|
||||
src/math/rint.c \
|
||||
src/math/round.c \
|
||||
src/math/scalbn.c \
|
||||
src/math/sqrt.c \
|
||||
src/math/trunc.c
|
||||
|
||||
# Profile-gated sources, EXISTING gates preserved (strlcpy/strlcat stay L2
|
||||
# BSD, strcasestr stays L3 GNU — NOT promoted). Task 8/9 additions: stpcpy.c
|
||||
@@ -100,11 +236,29 @@ VLIBC_CORE_SRCS = \
|
||||
# and self-gate at level 1. Likewise rand.c (its L2 rand_r/drand48 section)
|
||||
# and the strtox/strtod/inttypes/div/qsort C files (their L2 gates live in
|
||||
# include/stdlib.h / include/inttypes.h declarations) stay in VLIBC_CORE_SRCS.
|
||||
# W5/W6 whole-file-L2 joins (verified by reading each .c — the #if
|
||||
# VLIBC_LEVEL_GE(2) wraps the whole body after the includes): lockf.c (XSI),
|
||||
# mknod.c (XSI), readdir_r.c/scandir.c/alphasort.c (XSI dirent), ppoll.c
|
||||
# (Linux), strptime.c/timer.c (POSIX timers), plus task 27's sys/time slice
|
||||
# (gettimeofday.c/settimeofday.c/getitimer.c/setitimer.c/utimes.c — each
|
||||
# whole-file L2, wired with the systime test), plus the earlier strings
|
||||
# slice.
|
||||
VLIBC_LEVEL2_SRCS = src/string/strlcpy.c src/string/strlcat.c \
|
||||
src/string/stpcpy.c src/string/memccpy.c \
|
||||
src/ctype/isascii.c src/ctype/toascii.c \
|
||||
src/string/strings_impl.c \
|
||||
src/stdlib/pty.c src/stdlib/getsubopt.c
|
||||
src/stdlib/pty.c src/stdlib/getsubopt.c \
|
||||
src/fcntl/lockf.c \
|
||||
src/stat/mknod.c \
|
||||
src/dirent/readdir_r.c src/dirent/scandir.c src/dirent/alphasort.c \
|
||||
src/select/ppoll.c \
|
||||
src/time/strptime.c src/time/timer.c \
|
||||
src/time/gettimeofday.c src/time/settimeofday.c \
|
||||
src/time/getitimer.c src/time/setitimer.c src/time/utimes.c \
|
||||
src/stat/statvfs.c src/sys/times.c src/misc/syslog.c \
|
||||
src/unistd/basename.c src/unistd/dirname.c src/unistd/realpath.c \
|
||||
src/unistd/chroot.c src/unistd/swab.c src/unistd/confstr.c \
|
||||
src/unistd/getwd.c
|
||||
VLIBC_LEVEL3_SRCS = src/string/strcasestr.c
|
||||
|
||||
vlibc_lib_LTLIBRARIES = libvlibc.la
|
||||
@@ -146,10 +300,34 @@ vlibc_include_HEADERS = \
|
||||
include/stdlib.h \
|
||||
include/stdnoreturn.h \
|
||||
include/tar.h \
|
||||
include/math.h \
|
||||
include/tgmath.h \
|
||||
include/threads.h \
|
||||
include/uchar.h \
|
||||
include/sys/types.h
|
||||
include/dirent.h \
|
||||
include/fcntl.h \
|
||||
include/poll.h \
|
||||
include/stdio.h \
|
||||
include/time.h \
|
||||
include/unistd.h \
|
||||
include/syslog.h \
|
||||
include/pwd.h \
|
||||
include/grp.h \
|
||||
include/libgen.h
|
||||
|
||||
# sys/* headers use their own primary so the sys/ subpath survives install:
|
||||
# automake's flat _HEADERS install strips directory components, which would
|
||||
# both drop the sys/ prefix for <sys/*.h> consumers AND collide on shared
|
||||
# basenames (time.h vs sys/time.h, GNU install refuses the second copy).
|
||||
vlibc_include_sys_HEADERS = \
|
||||
include/sys/select.h \
|
||||
include/sys/stat.h \
|
||||
include/sys/time.h \
|
||||
include/sys/types.h \
|
||||
include/sys/wait.h \
|
||||
include/sys/mman.h \
|
||||
include/sys/statvfs.h \
|
||||
include/sys/times.h
|
||||
|
||||
# Install location depends on the install method (see configure.ac).
|
||||
if INSTALL_OVERWRITE
|
||||
@@ -160,6 +338,11 @@ vlibc_includedir = $(prefix)/lib/vlibc/include
|
||||
vlibc_libdir = $(prefix)/lib/vlibc/lib
|
||||
endif
|
||||
|
||||
# sys/* headers live under $(vlibc_includedir)/sys (see the sys primary
|
||||
# above; must follow the INSTALL_OVERWRITE conditional to inherit the
|
||||
# resolved vlibc_includedir).
|
||||
vlibc_include_sysdir = $(vlibc_includedir)/sys
|
||||
|
||||
# Generated per-profile feature header (see configure.ac); installed under
|
||||
# $(vlibc_includedir)/vlibc/. A dedicated primary is used because
|
||||
# nodist_nobase_vlibc_include_HEADERS is rejected by automake (the "nobase"
|
||||
@@ -278,7 +461,12 @@ VLIBC_TEST_CFLAGS = -DVLIBC_LEVEL=$(VLIBC_TEST_LEVEL) -fno-stack-protector \
|
||||
|
||||
check_PROGRAMS = test_syscall test_strerror test_setjmp test_headers \
|
||||
test_startup test_malloc test_string test_ctype test_strings \
|
||||
test_strtol test_qsort test_inttypes test_env
|
||||
test_strtol test_qsort test_inttypes test_env \
|
||||
test_process test_fcntl test_stat test_wait test_dirent \
|
||||
test_poll test_time test_scanf test_getline \
|
||||
test_systime test_printf test_stdio test_unistd_file \
|
||||
test_mman test_statvfs test_times test_syslog test_pwdgrp \
|
||||
test_unistd_misc test_math_basic
|
||||
|
||||
test_syscall_SOURCES = tests/syscall_test.c
|
||||
test_syscall_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
@@ -358,15 +546,143 @@ test_env_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_env_LINK = $(VLIBC_TEST_LINK)
|
||||
test_env_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_process_SOURCES = tests/test_process.c
|
||||
test_process_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_process_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_process_LINK = $(VLIBC_TEST_LINK)
|
||||
test_process_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_fcntl_SOURCES = tests/test_fcntl.c
|
||||
test_fcntl_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_fcntl_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_fcntl_LINK = $(VLIBC_TEST_LINK)
|
||||
test_fcntl_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_stat_SOURCES = tests/test_stat.c
|
||||
test_stat_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_stat_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_stat_LINK = $(VLIBC_TEST_LINK)
|
||||
test_stat_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_wait_SOURCES = tests/test_wait.c
|
||||
test_wait_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_wait_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_wait_LINK = $(VLIBC_TEST_LINK)
|
||||
test_wait_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_dirent_SOURCES = tests/test_dirent.c
|
||||
test_dirent_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_dirent_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_dirent_LINK = $(VLIBC_TEST_LINK)
|
||||
test_dirent_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_poll_SOURCES = tests/test_poll.c
|
||||
test_poll_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_poll_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_poll_LINK = $(VLIBC_TEST_LINK)
|
||||
test_poll_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_time_SOURCES = tests/test_time.c
|
||||
test_time_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_time_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_time_LINK = $(VLIBC_TEST_LINK)
|
||||
test_time_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_scanf_SOURCES = tests/test_scanf.c
|
||||
test_scanf_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_scanf_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_scanf_LINK = $(VLIBC_TEST_LINK)
|
||||
test_scanf_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_getline_SOURCES = tests/test_getline.c
|
||||
test_getline_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_getline_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_getline_LINK = $(VLIBC_TEST_LINK)
|
||||
test_getline_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_systime_SOURCES = tests/test_systime.c
|
||||
test_systime_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_systime_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_systime_LINK = $(VLIBC_TEST_LINK)
|
||||
test_systime_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_printf_SOURCES = tests/test_printf.c
|
||||
test_printf_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_printf_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_printf_LINK = $(VLIBC_TEST_LINK)
|
||||
test_printf_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_stdio_SOURCES = tests/test_stdio.c
|
||||
test_stdio_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_stdio_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_stdio_LINK = $(VLIBC_TEST_LINK)
|
||||
test_stdio_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_unistd_file_SOURCES = tests/test_unistd_file.c
|
||||
test_unistd_file_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_unistd_file_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_unistd_file_LINK = $(VLIBC_TEST_LINK)
|
||||
test_unistd_file_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_mman_SOURCES = tests/test_mman.c
|
||||
test_mman_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_mman_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_mman_LINK = $(VLIBC_TEST_LINK)
|
||||
test_mman_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_statvfs_SOURCES = tests/test_statvfs.c
|
||||
test_statvfs_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_statvfs_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_statvfs_LINK = $(VLIBC_TEST_LINK)
|
||||
test_statvfs_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_times_SOURCES = tests/test_times.c
|
||||
test_times_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_times_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_times_LINK = $(VLIBC_TEST_LINK)
|
||||
test_times_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_syslog_SOURCES = tests/test_syslog.c
|
||||
test_syslog_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_syslog_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_syslog_LINK = $(VLIBC_TEST_LINK)
|
||||
test_syslog_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_pwdgrp_SOURCES = tests/test_pwdgrp.c
|
||||
test_pwdgrp_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_pwdgrp_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_pwdgrp_LINK = $(VLIBC_TEST_LINK)
|
||||
test_pwdgrp_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_unistd_misc_SOURCES = tests/test_unistd_misc.c
|
||||
test_unistd_misc_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_unistd_misc_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_unistd_misc_LINK = $(VLIBC_TEST_LINK)
|
||||
test_unistd_misc_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_math_basic_SOURCES = tests/test_math_basic.c
|
||||
test_math_basic_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_math_basic_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_math_basic_LINK = $(VLIBC_TEST_LINK)
|
||||
test_math_basic_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
# test_startup's default mode exits 42 by design, so it is NOT a bare TESTS
|
||||
# entry; tests/startup_modes.sh runs its full mode matrix and the -f failure
|
||||
# scenarios of the other binaries. test_malloc/test_string/test_ctype/
|
||||
# test_strings/test_strtol/test_qsort/test_inttypes/test_env default to the
|
||||
# happy mode (exit 0 on all-pass) and are bare TESTS entries; their -f/-p
|
||||
# modes stay runnable by hand.
|
||||
# test_strings/test_strtol/test_qsort/test_inttypes/test_env and the W5/W6
|
||||
# wave tests (test_process/test_fcntl/test_stat/test_wait/test_dirent/
|
||||
# test_poll/test_time/test_scanf/test_getline) default to the happy mode
|
||||
# (exit 0 on all-pass) and are bare TESTS entries; their -f/-p modes stay
|
||||
# runnable by hand.
|
||||
TESTS = test_syscall test_strerror test_setjmp test_headers \
|
||||
test_malloc test_string test_ctype test_strings \
|
||||
test_strtol test_qsort test_inttypes test_env \
|
||||
test_process test_fcntl test_stat test_wait test_dirent \
|
||||
test_poll test_time test_scanf test_getline \
|
||||
test_systime test_printf test_stdio test_unistd_file \
|
||||
test_mman test_statvfs test_times test_syslog test_pwdgrp \
|
||||
test_unistd_misc \
|
||||
test_math_basic \
|
||||
tests/startup_modes.sh
|
||||
|
||||
CLEANFILES = crt1.o libvlibc-check.a $(VLIBC_CHECK_OBJECTS)
|
||||
@@ -381,10 +697,20 @@ EXTRA_DIST = \
|
||||
arch/x86_64/syscall_arch.h \
|
||||
src/internal/atomic.h \
|
||||
src/internal/errno.h \
|
||||
src/internal/futex.h \
|
||||
src/internal/libc.h \
|
||||
src/internal/malloc.h \
|
||||
src/internal/strtox.h \
|
||||
src/internal/syscall.h \
|
||||
src/internal/types.h \
|
||||
src/start/start.h \
|
||||
src/start/tcb.h
|
||||
src/start/tcb.h \
|
||||
src/thread/pthread_impl.h \
|
||||
src/dirent/dirent_impl.h \
|
||||
src/process/atfork_impl.h \
|
||||
src/stat/stat_impl.h \
|
||||
src/stdio/stdio_impl.h \
|
||||
src/stdio/vfprintf_float.c \
|
||||
src/time/time_impl.h \
|
||||
src/unistd/unistd_impl.h \
|
||||
src/math/math_impl.h
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#ifndef VLIBC_GRP_H
|
||||
#define VLIBC_GRP_H
|
||||
|
||||
/*
|
||||
* vlibc — <grp.h>.
|
||||
*
|
||||
* The group database (todo 37). struct group mirrors /etc/group, whose
|
||||
* records are name:passwd:gid:member,member,... — the member list is a
|
||||
* comma-separated tail, empty when the group has no members. Everything
|
||||
* here is POSIX.1-2008 base (Level 1).
|
||||
*
|
||||
* The non-reentrant forms getgrnam/getgrgid/getgrent return a pointer to a
|
||||
* static structure whose contents — including the strings and the member
|
||||
* array it points at — are overwritten by the next call to any of the
|
||||
* three. getgrnam/getgrgid open and scan the whole file per call; getgrent
|
||||
* walks a cursor that setgrent rewinds and endgrent closes.
|
||||
*
|
||||
* The _r forms behave like their <pwd.h> counterparts: the entry is written
|
||||
* into caller storage (the gr_mem pointer array and the member name strings
|
||||
* both live inside the supplied buffer) and the return value is 0 on
|
||||
* success, ERANGE when the buffer is too small, or 0 with *result NULL when
|
||||
* no entry matches. errno is never touched: the error number IS the return
|
||||
* value.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* One group database entry (/etc/group record). */
|
||||
struct group
|
||||
{
|
||||
char *gr_name; /* group name */
|
||||
char *gr_passwd; /* encrypted password; usually empty */
|
||||
gid_t gr_gid; /* numeric group id */
|
||||
char **gr_mem; /* NULL-terminated member-name array */
|
||||
};
|
||||
|
||||
/*
|
||||
* Look up the first entry whose name matches; NULL when absent. The result
|
||||
* points into shared static storage valid only until the next grp call.
|
||||
*/
|
||||
struct group *
|
||||
getgrnam(const char *name);
|
||||
|
||||
/*
|
||||
* Look up the first entry whose gid matches; NULL when absent. Result
|
||||
* storage is shared with getgrnam/getgrent as described above.
|
||||
*/
|
||||
struct group *
|
||||
getgrgid(gid_t gid);
|
||||
|
||||
/*
|
||||
* Return the next entry from the group stream, opening it on the first
|
||||
* call; NULL at end of file. The stream cursor advances per call.
|
||||
*/
|
||||
struct group *
|
||||
getgrent(void);
|
||||
|
||||
/* Rewind the group stream to its first entry. */
|
||||
void
|
||||
setgrent(void);
|
||||
|
||||
/* Close the group stream; a later getgrent reopens from the start. */
|
||||
void
|
||||
endgrent(void);
|
||||
|
||||
int
|
||||
getgrnam_r(const char *name, struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
int
|
||||
getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
int
|
||||
getgrent_r(struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
#include <stdio.h> /* FILE */
|
||||
|
||||
/*
|
||||
* Return the next entry read from the given open stream, or NULL at end of
|
||||
* file or on a malformed/unopenable record; the entry is parsed as an
|
||||
* /etc/group record. Result storage is shared with getgrnam/getgrgid/
|
||||
* getgrent as described above, so the stream must not be closed until the
|
||||
* caller has copied the result. XSI.
|
||||
*/
|
||||
struct group *
|
||||
fgetgrent(FILE *stream);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_GRP_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef VLIBC_LIBGEN_H
|
||||
#define VLIBC_LIBGEN_H
|
||||
|
||||
/*
|
||||
* vlibc — <libgen.h>.
|
||||
*
|
||||
* basename() and dirname() split a pathname into its final component and
|
||||
* the directory that contains it (XSI, declared when _XOPEN_SOURCE is
|
||||
* enabled in glibc — level 2 here). They are the XSI forms: the input
|
||||
* path is modified in place (callers must pass a writable buffer), and
|
||||
* both are allowed to return pointers into a shared static area, so
|
||||
* repeated calls overwrite earlier results.
|
||||
*
|
||||
* The two functions are deliberately declared here and nowhere else:
|
||||
* POSIX places them in <libgen.h> only.
|
||||
*
|
||||
* Neither declaration carries an intent attribute: both functions write
|
||||
* to and return pointers into their argument or a shared static buffer,
|
||||
* so const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Return a pointer to the final component of path, modifying path in
|
||||
* place: trailing '/' characters (other than a leading root '/') are
|
||||
* overwritten with NUL so basename("/usr/lib") == "lib", basename("/")
|
||||
* == "/" and basename("") == ".". The result may be a suffix of path or
|
||||
* point into a shared static buffer (POSIX permits either); the special
|
||||
* cases "/" and "" return static strings.
|
||||
*/
|
||||
char *
|
||||
basename(char *path);
|
||||
|
||||
/*
|
||||
* Return a pointer to the directory portion of path, modifying path in
|
||||
* place: the last '/' that is not the final character is overwritten
|
||||
* with NUL so dirname("/usr/lib") == "/usr", dirname("/") == "/" and
|
||||
* dirname("") == ".". The result may be a suffix of path or point into
|
||||
* a shared static buffer (POSIX permits either); the special cases
|
||||
* return static strings.
|
||||
*/
|
||||
char *
|
||||
dirname(char *path);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LIBGEN_H */
|
||||
+634
@@ -0,0 +1,634 @@
|
||||
#ifndef VLIBC_MATH_H
|
||||
#define VLIBC_MATH_H
|
||||
|
||||
/*
|
||||
* vlibc — <math.h>.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile. It carries the
|
||||
* whole todo-39 arithmetic inventory: the classification macros
|
||||
* (fpclassify/isnan/isinf/isfinite/isnormal/signbit), the constants
|
||||
* (HUGE_VAL/INFINITY/NAN, FP_*, math_errhandling), and the 25 basic
|
||||
* real-function families (fabs, copysign, floor, ceil, trunc, round, rint,
|
||||
* nearbyint, lrint, llrint, frexp, ldexp, modf, scalbn, scalbln, fmin,
|
||||
* fmax, fdim, fmod, remainder, remquo, ilogb, logb, lround, llround), each
|
||||
* spelled in all three precisions (float `...f`, double, long double
|
||||
* `...l`).
|
||||
*
|
||||
* Every declaration below lands NOW so that the header is stable across the
|
||||
* todo-39 implementation slices; the slices (wip commits) fill in the
|
||||
* src/math/ definitions family by family and never touch this header again.
|
||||
* Later math todos extend this header in place the same way stdlib.h is
|
||||
* extended: todo 40 adds the exp/log/pow families, todo 41 the trig and
|
||||
* hyperbolic families, todo 42 erf/erfc/lgamma/tgamma/fma/nextafter/nan,
|
||||
* and todo 43 provides <complex.h> separately.
|
||||
*
|
||||
* Edge-case behavior (IEEE 754-2008): floor/ceil/trunc round exact results
|
||||
* with correct signed-zero and NaN/Inf handling; fabs/copysign/fmin/fmax/
|
||||
* fdim never raise an exception on their own and preserve NaN payloads
|
||||
* where the standard allows; the classification macros are
|
||||
* exception-free. Functions that per POSIX can set errno (ldexp, scalbn,
|
||||
* scalbln, fmod, remainder, remquo, and the fraction-splitters frexp/modf
|
||||
* with their pointer outputs) carry no const attribute below so the
|
||||
* compiler never hoists or elides an errno-setting call. Functions that
|
||||
* are pure (never set errno in their defined domain — fabs, copysign,
|
||||
* floor, ceil, trunc, round, rint, nearbyint, fmin, fmax, fdim, ilogb,
|
||||
* logb) are declared const and fold away in static links.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Constants ---- */
|
||||
|
||||
/*
|
||||
* Positive infinity, as double/float/long double (C23 7.12.1p4). HUGE_VAL
|
||||
* is also the "overflowed" return value of the strto* conversions.
|
||||
*/
|
||||
#define HUGE_VAL __builtin_huge_val()
|
||||
#define HUGE_VALF __builtin_huge_valf()
|
||||
#define HUGE_VALL __builtin_huge_vall()
|
||||
|
||||
/* Positive infinity as a float (C23 7.12.1p5). */
|
||||
#define INFINITY __builtin_inff()
|
||||
|
||||
/* A quiet NaN as a float (C23 7.12.1p6); the payload is implementation
|
||||
* defined but always a NaN of the quiet kind. */
|
||||
#define NAN __builtin_nanf("")
|
||||
|
||||
/*
|
||||
* Return values of fpclassify (C23 7.12.3.1): the numbers need only be
|
||||
* distinct positive values; the classification macros below hand these
|
||||
* exact constants to __builtin_fpclassify so the two always agree.
|
||||
*/
|
||||
#define FP_NAN 0
|
||||
#define FP_INFINITE 1
|
||||
#define FP_ZERO 2
|
||||
#define FP_SUBNORMAL 3
|
||||
#define FP_NORMAL 4
|
||||
|
||||
/*
|
||||
* Return values of ilogb (C23 7.12.6.5p4): FP_ILOGB0 for a zero argument
|
||||
* and FP_ILOGBNAN for a NaN or infinite argument, each the indicated
|
||||
* <limits.h> sentinel.
|
||||
*/
|
||||
#define FP_ILOGB0 INT_MIN
|
||||
#define FP_ILOGBNAN INT_MAX
|
||||
|
||||
/*
|
||||
* Which error mechanisms the library reports through (C23 7.12.1p3):
|
||||
* MATH_ERRNO and MATH_ERREXCEPT are both in effect (the domain/range
|
||||
* errno values are set where POSIX requires and the corresponding
|
||||
* floating-point exceptions are raised by the hardware).
|
||||
*/
|
||||
#define MATH_ERRNO 1
|
||||
#define MATH_ERREXCEPT 2
|
||||
#define math_errhandling 3
|
||||
|
||||
/* ---- Classification macros (type-generic, exception-free) ---- */
|
||||
|
||||
/*
|
||||
* Classify x as NaN, infinite, zero, subnormal, or normal, returning the
|
||||
* matching FP_* constant. __builtin_fpclassify is a GCC type-generic
|
||||
* builtin that evaluates its floating argument exactly once and never
|
||||
* traps, so no _Generic dispatch is needed here.
|
||||
*/
|
||||
#define fpclassify(x) \
|
||||
__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, (x))
|
||||
|
||||
/*
|
||||
* The predicate macros below are thin wrappers over the corresponding
|
||||
* GCC type-generic builtins. Each builtin evaluates its argument once and
|
||||
* returns an int; integer arguments classify as finite, non-zero, and
|
||||
* normal (they convert exactly), so isnan(3) is 0 and isinf(3) is 0.
|
||||
*/
|
||||
#define isnan(x) __builtin_isnan(x)
|
||||
#define isinf(x) __builtin_isinf(x)
|
||||
#define isfinite(x) __builtin_isfinite(x)
|
||||
#define isnormal(x) __builtin_isnormal(x)
|
||||
#define signbit(x) __builtin_signbit(x)
|
||||
|
||||
/* ---- Basic arithmetic families (todo 39) ---- */
|
||||
|
||||
/*
|
||||
* Absolute value of x (C23 7.12.7.2). fabs(±0) is +0, fabs(±Inf) is +Inf,
|
||||
* and fabs(NaN) is a NaN. Pure: no domain, no exception.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fabsf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
fabs(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fabsl(long double x);
|
||||
|
||||
/*
|
||||
* A value with the magnitude of x and the sign of y (C23 7.12.7.3).
|
||||
* copysign(±0, y) carries y's sign; a NaN x keeps its payload but takes
|
||||
* y's sign bit. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
copysignf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
copysign(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
copysignl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The largest integral value not greater than x (C23 7.12.9.2).
|
||||
* floor(±0) is ±0, floor(-0.5) is -1.0, floor(±Inf) and floor(NaN)
|
||||
* return their argument unchanged. Pure and exact (no inexact
|
||||
* exception), so it folds.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
floorf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
floor(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
floorl(long double x);
|
||||
|
||||
/*
|
||||
* The smallest integral value not less than x (C23 7.12.9.1).
|
||||
* ceil(±0) is ±0, ceil(-0.5) is -0.0 (a real negative zero), ceil(±Inf)
|
||||
* and ceil(NaN) return their argument unchanged. Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
ceilf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
ceil(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
ceill(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x in the direction of zero (C23 7.12.9.3).
|
||||
* trunc(-0.7) is -0.0, trunc(0.7) is 0.0, and ±Inf/NaN pass through.
|
||||
* Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
truncf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
trunc(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
truncl(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x, with halfway cases rounded away from
|
||||
* zero (C23 7.12.9.6). round(±0) is ±0, round(-0.5) is -1.0, and ±Inf/
|
||||
* NaN pass through. Pure: round never raises the inexact exception.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
roundf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
round(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
roundl(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x in the current rounding direction
|
||||
* (C23 7.12.9.4). rint may raise the inexact exception; it is still pure
|
||||
* in the const sense because it never sets errno and reads no memory.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
rintf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
rint(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
rintl(long double x);
|
||||
|
||||
/*
|
||||
* As rint, but guaranteed never to raise the inexact exception (C23
|
||||
* 7.12.9.5). Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
nearbyintf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
nearbyint(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
nearbyintl(long double x);
|
||||
|
||||
/*
|
||||
* The nearest integral value to x in the current rounding direction,
|
||||
* returned as long (C23 7.12.9.7). A result outside the range of long is
|
||||
* a range error (the return value is unspecified and errno may be set),
|
||||
* so no const attribute.
|
||||
*/
|
||||
long
|
||||
lrintf(float x);
|
||||
|
||||
long
|
||||
lrint(double x);
|
||||
|
||||
long
|
||||
lrintl(long double x);
|
||||
|
||||
/*
|
||||
* As lrint, returned as long long (C23 7.12.9.8). Range errors as for
|
||||
* lrint; no const attribute.
|
||||
*/
|
||||
long long
|
||||
llrintf(float x);
|
||||
|
||||
long long
|
||||
llrint(double x);
|
||||
|
||||
long long
|
||||
llrintl(long double x);
|
||||
|
||||
/*
|
||||
* Split x into a fraction f in [1/2, 1) (or 0) and an integer exponent
|
||||
* *exp such that x == f * 2^(*exp) (C23 7.12.6.4). Zero returns ±0 with
|
||||
* *exp 0; Inf/NaN return x with an unspecified *exp. Writes *exp, so no
|
||||
* const attribute.
|
||||
*/
|
||||
float
|
||||
frexpf(float x, int *exp);
|
||||
|
||||
double
|
||||
frexp(double x, int *exp);
|
||||
|
||||
long double
|
||||
frexpl(long double x, int *exp);
|
||||
|
||||
/*
|
||||
* x times 2^n (C23 7.12.6.3): the inverse of frexp. A result too large
|
||||
* to represent is a range error returning ±HUGE_VAL with errno ERANGE;
|
||||
* no const attribute.
|
||||
*/
|
||||
float
|
||||
ldexpf(float x, int n);
|
||||
|
||||
double
|
||||
ldexp(double x, int n);
|
||||
|
||||
long double
|
||||
ldexpl(long double x, int n);
|
||||
|
||||
/*
|
||||
* Split x into an integral part stored in *iptr and a fractional part
|
||||
* returned (C23 7.12.6.5); both have x's sign, so modf(-1.5, &i) puts
|
||||
* -1.0 in i and returns -0.5. Writes *iptr, so no const attribute.
|
||||
*/
|
||||
float
|
||||
modff(float x, float *iptr);
|
||||
|
||||
double
|
||||
modf(double x, double *iptr);
|
||||
|
||||
long double
|
||||
modfl(long double x, long double *iptr);
|
||||
|
||||
/*
|
||||
* x * FLT_RADIX^n with FLT_RADIX 2 (C23 7.12.6.6): the scalb* functions
|
||||
* differ from ldexp only in the exponent argument's type. A result too
|
||||
* large to represent is a range error returning ±HUGE_VAL* with errno
|
||||
* ERANGE; no const attribute.
|
||||
*/
|
||||
float
|
||||
scalbnf(float x, int n);
|
||||
|
||||
double
|
||||
scalbn(double x, int n);
|
||||
|
||||
long double
|
||||
scalbnl(long double x, int n);
|
||||
|
||||
float
|
||||
scalblnf(float x, long n);
|
||||
|
||||
double
|
||||
scalbln(double x, long n);
|
||||
|
||||
long double
|
||||
scalblnl(long double x, long n);
|
||||
|
||||
/*
|
||||
* The smaller of x and y (C23 7.12.12.4), returning -0.0 when the
|
||||
* arguments are +0.0 and -0.0. A NaN argument is ignored in favor of the
|
||||
* numeric one; two NaNs return a NaN. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fminf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fmin(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fminl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The larger of x and y (C23 7.12.12.3), returning +0.0 when the
|
||||
* arguments are +0.0 and -0.0. NaN handling as fmin. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fmaxf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fmax(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fmaxl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The positive difference x - y when x > y and +0.0 otherwise (C23
|
||||
* 7.12.12.2); fdim(x, NaN) and fdim(NaN, x) return a NaN. Pure in the
|
||||
* domain-error-free sense (an overflow may still raise an exception
|
||||
* through the hardware, which const does not model).
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fdimf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fdim(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fdiml(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The floating-point remainder x - n*y, where n is x/y truncated toward
|
||||
* zero (C23 7.12.10.1); the result therefore has x's sign. fmod(x, ±0) is
|
||||
* a domain error returning NaN with errno EDOM; ±Inf/x is likewise a
|
||||
* domain error. errno can be set, so no const attribute.
|
||||
*/
|
||||
float
|
||||
fmodf(float x, float y);
|
||||
|
||||
double
|
||||
fmod(double x, double y);
|
||||
|
||||
long double
|
||||
fmodl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y, where n is x/y rounded to the nearest
|
||||
* integer (ties to even) (C23 7.12.10.2); |result| <= |y|/2. Domain
|
||||
* errors as fmod; errno can be set, so no const attribute.
|
||||
*/
|
||||
float
|
||||
remainderf(float x, float y);
|
||||
|
||||
double
|
||||
remainder(double x, double y);
|
||||
|
||||
long double
|
||||
remainderl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* As remainder, additionally storing the low bits of the integer
|
||||
* quotient n in *quo (C23 7.12.10.3). Writes *quo, so no const
|
||||
* attribute.
|
||||
*/
|
||||
float
|
||||
remquof(float x, float y, int *quo);
|
||||
|
||||
double
|
||||
remquo(double x, double y, int *quo);
|
||||
|
||||
long double
|
||||
remquol(long double x, long double y, int *quo);
|
||||
|
||||
/*
|
||||
* The signed exponent of x as an int (C23 7.12.6.5p1): ilogb(x) is
|
||||
* floor(log2 |x|) for a nonzero finite x. ilogb(±0) returns FP_ILOGB0,
|
||||
* ilogb(±Inf) and ilogb(NaN) return FP_ILOGBNAN. Pure: the sentinels are
|
||||
* returned without touching errno.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
ilogbf(float x);
|
||||
|
||||
__attribute__((const)) int
|
||||
ilogb(double x);
|
||||
|
||||
__attribute__((const)) int
|
||||
ilogbl(long double x);
|
||||
|
||||
/*
|
||||
* The signed exponent of x as a floating-point value (C23 7.12.6.6):
|
||||
* logb(±0) is -Inf and logb(±Inf) is +Inf, each raising the division-
|
||||
* by-zero/invalid exception through the hardware but without an errno
|
||||
* path in the representable domain. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
logbf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
logb(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
logbl(long double x);
|
||||
|
||||
/*
|
||||
* The nearest integral value to x, with halfway cases rounded away from
|
||||
* zero, returned as long (C23 7.12.9.9). A result outside the range of
|
||||
* long is a range error; no const attribute.
|
||||
*/
|
||||
long
|
||||
lroundf(float x);
|
||||
|
||||
long
|
||||
lround(double x);
|
||||
|
||||
long
|
||||
lroundl(long double x);
|
||||
|
||||
/*
|
||||
* As lround, returned as long long (C23 7.12.9.10). Range errors as for
|
||||
* lround; no const attribute.
|
||||
*/
|
||||
long long
|
||||
llroundf(float x);
|
||||
|
||||
long long
|
||||
llround(double x);
|
||||
|
||||
long long
|
||||
llroundl(long double x);
|
||||
|
||||
/*
|
||||
* e raised to the power x (C23 7.12.6.1). exp(+0) == exp(-0) == 1;
|
||||
* exp(-Inf) == +0; exp(+Inf) == +Inf; exp(NaN) == NaN. A result too
|
||||
* large or too small is a range error and sets errno to ERANGE, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
exp(double x);
|
||||
|
||||
float
|
||||
expf(float x);
|
||||
|
||||
long double
|
||||
expl(long double x);
|
||||
|
||||
/*
|
||||
* 2 raised to the power x (C23 7.12.6.2). exp2(+0) == exp2(-0) == 1;
|
||||
* exp2(-Inf) == +0; exp2(+Inf) == +Inf; exp2(NaN) == NaN. A result too
|
||||
* large or too small is a range error and sets errno to ERANGE, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
exp2(double x);
|
||||
|
||||
float
|
||||
exp2f(float x);
|
||||
|
||||
long double
|
||||
exp2l(long double x);
|
||||
|
||||
/*
|
||||
* e raised to the power x minus 1 (C23 7.12.6.3). expm1(+0) == +0;
|
||||
* expm1(-0) == -0; expm1(-Inf) == -1; expm1(+Inf) == +Inf;
|
||||
* expm1(NaN) == NaN. A result too large is a range error and sets
|
||||
* errno to ERANGE, so no const attribute.
|
||||
*/
|
||||
double
|
||||
expm1(double x);
|
||||
|
||||
float
|
||||
expm1f(float x);
|
||||
|
||||
long double
|
||||
expm1l(long double x);
|
||||
|
||||
/*
|
||||
* Natural logarithm of x (C23 7.12.6.7). log(+0) == log(-0) == -Inf;
|
||||
* log(1) == 0; log(-x) == NaN (domain error); log(+Inf) == +Inf;
|
||||
* log(NaN) == NaN. A negative argument is a domain error setting errno
|
||||
* to EDOM and a zero argument is a range error setting errno to ERANGE,
|
||||
* so no const attribute.
|
||||
*/
|
||||
double
|
||||
log(double x);
|
||||
|
||||
float
|
||||
logf(float x);
|
||||
|
||||
long double
|
||||
logl(long double x);
|
||||
|
||||
/*
|
||||
* Base-2 logarithm of x (C23 7.12.6.8). log2(1) == 0; log2(2) == 1.
|
||||
* Special values, domain errors, and range errors as for log, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
log2(double x);
|
||||
|
||||
float
|
||||
log2f(float x);
|
||||
|
||||
long double
|
||||
log2l(long double x);
|
||||
|
||||
/*
|
||||
* Base-10 logarithm of x (C23 7.12.6.9). log10(1) == 0; log10(10) == 1.
|
||||
* Special values, domain errors, and range errors as for log, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
log10(double x);
|
||||
|
||||
float
|
||||
log10f(float x);
|
||||
|
||||
long double
|
||||
log10l(long double x);
|
||||
|
||||
/*
|
||||
* Natural logarithm of 1 + x (C23 7.12.6.13). log1p(+-0) == +-0;
|
||||
* log1p(-1) == -Inf (a pole error setting errno to ERANGE); log1p(x) for
|
||||
* x < -1 is a domain error setting errno to EDOM; log1p(+Inf) == +Inf;
|
||||
* log1p(NaN) == NaN. Accurate for x close to 0, so no const attribute.
|
||||
*/
|
||||
double
|
||||
log1p(double x);
|
||||
|
||||
float
|
||||
log1pf(float x);
|
||||
|
||||
long double
|
||||
log1pl(long double x);
|
||||
|
||||
/*
|
||||
* Principal square root of x (C23 7.12.5.4). sqrt(+-0) == +-0; sqrt(x)
|
||||
* for x < 0 is a domain error returning NaN and setting errno to EDOM;
|
||||
* sqrt(+Inf) == +Inf; sqrt(NaN) == NaN. Correctly rounded; no const
|
||||
* attribute because of the errno path.
|
||||
*/
|
||||
double
|
||||
sqrt(double x);
|
||||
|
||||
float
|
||||
sqrtf(float x);
|
||||
|
||||
long double
|
||||
sqrtl(long double x);
|
||||
|
||||
/*
|
||||
* Cube root of x (C23 7.12.5.5). cbrt(+-0) == +-0; cbrt(+-Inf) == +-Inf;
|
||||
* cbrt(NaN) == NaN; cbrt(-x) == -cbrt(x). Defined for all reals and never
|
||||
* sets errno.
|
||||
*/
|
||||
double
|
||||
cbrt(double x);
|
||||
|
||||
float
|
||||
cbrtf(float x);
|
||||
|
||||
long double
|
||||
cbrtl(long double x);
|
||||
|
||||
/*
|
||||
* Square root of x*x + y*y without undue overflow or underflow
|
||||
* (C23 7.12.5.6). hypot(+-0, +-0) == +0; hypot(+-Inf, y) == +Inf even for
|
||||
* NaN y; hypot(x, NaN) == NaN for finite x. The internal scaling avoids
|
||||
* spurious overflow, so no errno path exists.
|
||||
*/
|
||||
double
|
||||
hypot(double x, double y);
|
||||
|
||||
float
|
||||
hypotf(float x, float y);
|
||||
|
||||
long double
|
||||
hypotl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* x raised to the power y (C23 7.12.6.11). pow(x, 0) == 1; pow(1, y) == 1
|
||||
* even for NaN y; pow(NaN, 0) == 1; pow(+-0, negative y) == +-Inf (a pole
|
||||
* error setting errno to ERANGE); pow(negative finite x, non-integer y) is
|
||||
* a domain error setting errno to EDOM; overflow and underflow set errno to
|
||||
* ERANGE. No const attribute.
|
||||
*/
|
||||
double
|
||||
pow(double x, double y);
|
||||
|
||||
float
|
||||
powf(float x, float y);
|
||||
|
||||
long double
|
||||
powl(long double x, long double y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_MATH_H */
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef VLIBC_PWD_H
|
||||
#define VLIBC_PWD_H
|
||||
|
||||
/*
|
||||
* vlibc — <pwd.h>.
|
||||
*
|
||||
* The password database (todo 37). struct passwd mirrors /etc/passwd, whose
|
||||
* records are name:passwd:uid:gid:gecos:dir:shell — seven ':'-separated
|
||||
* fields. Everything here is POSIX.1-2008 base (Level 1).
|
||||
*
|
||||
* The non-reentrant forms getpwnam/getpwuid/getpwent return a pointer to a
|
||||
* static structure whose contents — including the strings it points at —
|
||||
* are overwritten by the next call to any of the three, so the result must
|
||||
* be copied out before the next database call. getpwnam/getpwuid open and
|
||||
* scan the whole file per call; getpwent walks a cursor that setpwent
|
||||
* rewinds and endpwent closes.
|
||||
*
|
||||
* The getpwnam_r/getpwuid_r/getpwent_r forms write the entry into
|
||||
* caller-supplied storage (struct, char buffer of bufsize bytes) and
|
||||
* return 0 on success with *result pointing at the filled struct, or an
|
||||
* error number as the return value with *result left NULL: ERANGE when the
|
||||
* buffer is too small for the entry, 0 with *result NULL when no entry
|
||||
* matches (or the stream is exhausted). errno is never touched by any _r
|
||||
* function: the error number IS the return value.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* One password database entry (/etc/passwd record). */
|
||||
struct passwd
|
||||
{
|
||||
char *pw_name; /* user's login name */
|
||||
char *pw_passwd; /* encrypted password; often "x" with shadow files */
|
||||
uid_t pw_uid; /* numeric user id */
|
||||
gid_t pw_gid; /* numeric group id */
|
||||
char *pw_gecos; /* user information (comment) field */
|
||||
char *pw_dir; /* home directory */
|
||||
char *pw_shell; /* login shell */
|
||||
};
|
||||
|
||||
/*
|
||||
* Look up the first entry whose name matches; NULL when absent. The result
|
||||
* points into shared static storage valid only until the next pwd call.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwnam(const char *name);
|
||||
|
||||
/*
|
||||
* Look up the first entry whose uid matches; NULL when absent. Result
|
||||
* storage is shared with getpwnam/getpwent as described above.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwuid(uid_t uid);
|
||||
|
||||
/*
|
||||
* Return the next entry from the password stream, opening it on the first
|
||||
* call; NULL at end of file. The stream cursor advances per call.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwent(void);
|
||||
|
||||
/* Rewind the password stream to its first entry. */
|
||||
void
|
||||
setpwent(void);
|
||||
|
||||
/* Close the password stream; a later getpwent reopens from the start. */
|
||||
void
|
||||
endpwent(void);
|
||||
|
||||
int
|
||||
getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
int
|
||||
getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
int
|
||||
getpwent_r(struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_PWD_H */
|
||||
@@ -0,0 +1,436 @@
|
||||
#ifndef VLIBC_SIGNAL_H
|
||||
#define VLIBC_SIGNAL_H
|
||||
|
||||
/*
|
||||
* vlibc — <signal.h>.
|
||||
*
|
||||
* Signal handling (POSIX.1-2008 base) plus the XSI/obsolete conveniences.
|
||||
* The core calls ride the kernel's rt_* signal ABI (SYS_rt_sigaction,
|
||||
* SYS_rt_sigprocmask, SYS_rt_sigpending, SYS_rt_sigsuspend,
|
||||
* SYS_rt_sigtimedwait, SYS_rt_sigqueueinfo) with the x86_64 kernel
|
||||
* sigset size of one 64-bit word.
|
||||
*
|
||||
* Level 1 (onlyposix): signal numbers, SIG_DFL/SIG_IGN/SIG_ERR, the
|
||||
* sigset_t manipulation and query calls, sigaction,
|
||||
* sigprocmask, sigpending, sigsuspend, sigwait,
|
||||
* sigwaitinfo, sigtimedwait, kill, killpg, raise,
|
||||
* pause, alarm, abort, sigqueue, and the signal()
|
||||
* convenience.
|
||||
* Level 2 (muslmimic): sigaltstack (and stack_t / SS_* / SIGSTKSZ),
|
||||
* ualarm, siginterrupt, psignal, psiginfo (XSI /
|
||||
* obsolete — sigaction/sigprocmask/alarm are POSIX
|
||||
* base and stay at level 1).
|
||||
*
|
||||
* The layout facts below are x86_64 kernel-ABI:
|
||||
*
|
||||
* - The kernel sigset_t is 8 bytes (one word); signal N occupies bit
|
||||
* N-1, signals 1..64 are usable (32..64 are the realtime signals).
|
||||
* The same single-word sigset_t is declared by <sys/select.h> and
|
||||
* <poll.h> under the shared VLIBC_SIGSET_T_DEFINED guard so pselect/
|
||||
* ppoll and the sig* API agree on the type no matter the include order.
|
||||
* - SIG_DFL/SIG_IGN/SIG_ERR are the magic handler values 0/1/-1 cast to
|
||||
* the handler type; the kernel dispatches on the raw value.
|
||||
* - The PUBLIC struct sigaction is the POSIX layout (handler, mask,
|
||||
* int flags, restorer). The kernel's rt_sigaction ABI layout is
|
||||
* DIFFERENT (handler, flags, restorer, mask — flags is a full word);
|
||||
* src/signal/sigaction.c converts between the two.
|
||||
* - SA_RESTORER (0x04000000) is the kernel-private bit that makes the
|
||||
* kernel jump to the user-supplied sa_restorer trampoline when the
|
||||
* handler returns; every real (non-DFL/IGN) handler install sets it
|
||||
* (see sigaction.c). It is masked out of the flags reported back.
|
||||
*
|
||||
* siginfo_t is the minimal 128-byte layout shared with <sys/wait.h>
|
||||
* under the VLIBC_INTERNAL_SIGINFO_DEFINED guard (see the block below);
|
||||
* either header may be included first. No SIGEV_* / SI_* constants are
|
||||
* defined: the signal-handling details behind them are owned by a later
|
||||
* todo and nothing here needs the names.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct timespec is defined by <time.h>; a forward declaration is enough
|
||||
* for sigtimedwait()'s pointer parameter, and the two headers may be
|
||||
* included in either order.
|
||||
*/
|
||||
struct timespec;
|
||||
|
||||
/* Signal numbers (x86_64 asm-generic values; kernel-ABI facts). */
|
||||
#define SIGHUP 1 /* hangup detected on controlling terminal */
|
||||
#define SIGINT 2 /* interactive attention (^C) */
|
||||
#define SIGQUIT 3 /* interactive quit (^\) */
|
||||
#define SIGILL 4 /* illegal instruction */
|
||||
#define SIGTRAP 5 /* trace/breakpoint trap */
|
||||
#define SIGABRT 6 /* abnormal termination (abort()) */
|
||||
#define SIGBUS 7 /* bus error */
|
||||
#define SIGFPE 8 /* floating-point exception */
|
||||
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
|
||||
#define SIGUSR1 10 /* user-defined signal 1 */
|
||||
#define SIGSEGV 11 /* invalid memory reference */
|
||||
#define SIGUSR2 12 /* user-defined signal 2 */
|
||||
#define SIGPIPE 13 /* write on a pipe with no reader */
|
||||
#define SIGALRM 14 /* real-time timer expired (alarm/ualarm/setitimer) */
|
||||
#define SIGTERM 15 /* termination request */
|
||||
#define SIGSTKFLT 16 /* stack fault (unused) */
|
||||
#define SIGCHLD 17 /* child stopped or terminated */
|
||||
#define SIGCONT 18 /* continue if stopped */
|
||||
#define SIGSTOP 19 /* stop (cannot be caught or ignored) */
|
||||
#define SIGTSTP 20 /* interactive stop (^Z) */
|
||||
#define SIGTTIN 21 /* background process read from terminal */
|
||||
#define SIGTTOU 22 /* background process write to terminal */
|
||||
#define SIGURG 23 /* urgent condition on a socket */
|
||||
#define SIGXCPU 24 /* CPU time limit exceeded */
|
||||
#define SIGXFSZ 25 /* file size limit exceeded */
|
||||
#define SIGVTALRM 26 /* virtual timer expired */
|
||||
#define SIGPROF 27 /* profiling timer expired */
|
||||
#define SIGWINCH 28 /* window size change */
|
||||
#define SIGIO 29 /* I/O now possible */
|
||||
#define SIGPWR 30 /* power failure */
|
||||
#define SIGSYS 31 /* bad system call */
|
||||
|
||||
/* Realtime signals: the 32..64 range, usable for user-defined purposes. */
|
||||
#define SIGRTMIN 32
|
||||
#define SIGRTMAX 64
|
||||
|
||||
/* One more than the highest signal number; signals 1..64 are usable. */
|
||||
#define NSIG 65
|
||||
|
||||
/* The magic handler values. The kernel dispatches on the raw value. */
|
||||
#define SIG_DFL ((void (*)(int))0) /* default action */
|
||||
#define SIG_IGN ((void (*)(int))1) /* ignore the signal */
|
||||
#define SIG_ERR ((void (*)(int)) - 1) /* signal() error return */
|
||||
|
||||
/*
|
||||
* Integer type that can be accessed as an atomic entity even when an
|
||||
* asynchronous signal interrupts the access (volatile sig_atomic_t is the
|
||||
* conventional spelling). int is atomic on x86_64.
|
||||
*/
|
||||
typedef int sig_atomic_t;
|
||||
|
||||
/* sigprocmask()/pthread_sigmask() how values (kernel-identical). */
|
||||
#define SIG_BLOCK 0 /* add the given set to the blocked mask */
|
||||
#define SIG_UNBLOCK 1 /* remove the given set from the blocked mask */
|
||||
#define SIG_SETMASK 2 /* replace the blocked mask with the given set */
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t,
|
||||
* where signal N is bit N-1 (see setjmp.h, whose sigsetjmp stores this
|
||||
* same word). The typedef is shared with <sys/select.h> and <poll.h>
|
||||
* under this guard, so pselect()/ppoll() and the sig* API agree on the
|
||||
* type regardless of include order.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Value carried with a queued (realtime) signal. sival_int and sival_ptr
|
||||
* are alternative views of the same 64-bit payload.
|
||||
*/
|
||||
union sigval
|
||||
{
|
||||
int sival_int; /* integer payload */
|
||||
void *sival_ptr; /* pointer payload */
|
||||
};
|
||||
|
||||
/* POSIX spelling of the payload type. */
|
||||
typedef union sigval sigval_t;
|
||||
|
||||
/*
|
||||
* Description of a delivered signal: number, errno, code and one of the
|
||||
* per-code unions. This is the minimal x86_64 kernel layout (128 bytes)
|
||||
* shared with <sys/wait.h>, which fills si_pid/si_uid/si_status/si_utime/
|
||||
* si_stime for waitid(); the two headers define this exact block under the
|
||||
* common VLIBC_INTERNAL_SIGINFO_DEFINED guard, so whichever is included
|
||||
* first wins and the other skips — the type is identical either way. The
|
||||
* kernel copies the full 128 bytes on signal delivery (sigaction with
|
||||
* SA_SIGINFO, sigwaitinfo, sigtimedwait), so the size must stay 128 and
|
||||
* the pad member guarantees it; the fields behind each si_code value
|
||||
* (si_addr, si_value, timers, ...) are owned by a later todo.
|
||||
*/
|
||||
#ifndef VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
#define VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
typedef struct
|
||||
{
|
||||
int si_signo;
|
||||
int si_errno;
|
||||
int si_code;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
int si_status; /* 24 */
|
||||
long si_utime; /* 32 — 8-byte clock_t, matches the kernel/glibc ABI */
|
||||
long si_stime; /* 40 */
|
||||
};
|
||||
int vlibc_siginfo_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
} siginfo_t;
|
||||
|
||||
_Static_assert(sizeof(siginfo_t) == 128, "siginfo_t must match the kernel size");
|
||||
_Static_assert(offsetof(siginfo_t, si_pid) == 16, "si_pid must sit at offset 16");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Action taken when signal sig is delivered. sa_handler and sa_sigaction
|
||||
* are two views of the same slot (POSIX: sa_sigaction is used when
|
||||
* SA_SIGINFO is set and receives (sig, siginfo_t *, void *) instead of
|
||||
* just sig). The layout — handler, mask, flags, restorer — is the PUBLIC
|
||||
* POSIX order; the kernel ABI uses a different order (see the file-top
|
||||
* note) that sigaction() translates.
|
||||
*/
|
||||
struct sigaction
|
||||
{
|
||||
union
|
||||
{
|
||||
void (*sa_handler)(int); /* SIG_DFL/SIG_IGN/a handler */
|
||||
void (*sa_sigaction)(int, siginfo_t *, void *); /* SA_SIGINFO form */
|
||||
};
|
||||
sigset_t sa_mask; /* signals additionally blocked in the handler */
|
||||
int sa_flags; /* SA_* bits below */
|
||||
void (*sa_restorer)(void); /* kernel-private; do not set (see top) */
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct sigaction) == 32, "struct sigaction must be 32 bytes");
|
||||
_Static_assert(offsetof(struct sigaction, sa_mask) == 8, "sa_mask must sit at offset 8");
|
||||
_Static_assert(offsetof(struct sigaction, sa_flags) == 16, "sa_flags must sit at offset 16");
|
||||
_Static_assert(offsetof(struct sigaction, sa_restorer) == 24, "sa_restorer must sit at offset 24");
|
||||
|
||||
/* sa_flags bits (x86_64 kernel values; SA_RESTORER is kernel-private). */
|
||||
#define SA_NOCLDSTOP 1 /* do not generate SIGCHLD when children stop */
|
||||
#define SA_NOCLDWAIT 2 /* do not leave zombies on child exit */
|
||||
#define SA_SIGINFO 4 /* call the handler with the siginfo form */
|
||||
#define SA_RESTORER 0x04000000 /* handler returns via sa_restorer (kernel) */
|
||||
#define SA_ONSTACK 0x08000000 /* run the handler on the alternate stack */
|
||||
#define SA_RESTART 0x10000000 /* restart interrupted syscalls after the handler */
|
||||
#define SA_NODEFER 0x40000000 /* do not block the delivered signal in its handler */
|
||||
#define SA_RESETHAND 0x80000000 /* reset the disposition to SIG_DFL on entry */
|
||||
|
||||
/*
|
||||
* Install the action act for signal sig (NULL leaves it unchanged) and
|
||||
* store the previous action through oact (NULL skips the store). Return
|
||||
* 0, or -1 with errno set. sig must be in 1..64 and (for a catching
|
||||
* action) not SIGKILL/SIGSTOP; the kernel rejects invalid values with
|
||||
* EINVAL. Real handler pointers are installed with SA_RESTORER and the
|
||||
* internal return trampoline so the handler may return normally.
|
||||
*/
|
||||
int
|
||||
sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
|
||||
|
||||
/*
|
||||
* Inspect or change the calling thread's blocked-signal mask. how is
|
||||
* SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK; set is the mask operand (NULL
|
||||
* queries without changing: how is then ignored) and the previous mask is
|
||||
* stored through oldset (NULL skips the store). Return 0, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);
|
||||
|
||||
/* Store the set of blocked-and-pending signals through set. 0 or -1. */
|
||||
int
|
||||
sigpending(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Atomically install mask as the blocked mask and wait until a signal
|
||||
* whose delivery is not blocked by mask is caught; when its handler
|
||||
* returns, the previous mask is restored and sigsuspend returns -1 with
|
||||
* errno EINTR.
|
||||
*/
|
||||
int
|
||||
sigsuspend(const sigset_t *mask);
|
||||
|
||||
/* Empty set: no signal blocked, and no member set. Always 0. */
|
||||
int
|
||||
sigemptyset(sigset_t *set);
|
||||
|
||||
/* Set every signal 1..64 blocked/member. Always 0. */
|
||||
int
|
||||
sigfillset(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Add/remove signal sig to/from set. Return 0, or -1 with errno EINVAL
|
||||
* when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigaddset(sigset_t *set, int sig);
|
||||
int
|
||||
sigdelset(sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Nonzero when sig is a member of set, 0 otherwise, or -1 with errno
|
||||
* EINVAL when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigismember(const sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Synchronously wait for one of the signals in set (which should be
|
||||
* blocked in the calling thread) and store the delivered signal number
|
||||
* through sig. Return 0 on success, or the error number (EINTR is
|
||||
* retried internally). The signal is consumed and never delivered to a
|
||||
* handler.
|
||||
*/
|
||||
int
|
||||
sigwait(const sigset_t *restrict set, int *restrict sig);
|
||||
|
||||
/*
|
||||
* Like sigwait, but return the delivered signal number directly (or -1
|
||||
* with errno set) and, when info is not NULL, store the full 128-byte
|
||||
* kernel siginfo through it.
|
||||
*/
|
||||
int
|
||||
sigwaitinfo(const sigset_t *restrict set, siginfo_t *restrict info);
|
||||
|
||||
/*
|
||||
* sigwaitinfo bounded by timeout (relative; NULL waits indefinitely).
|
||||
* Returns the signal number, 0 is never returned for a successful wait,
|
||||
* or -1 with errno set (EAGAIN on timeout, EINTR if a handler ran).
|
||||
*/
|
||||
int
|
||||
sigtimedwait(const sigset_t *restrict set, siginfo_t *restrict info,
|
||||
const struct timespec *restrict timeout);
|
||||
|
||||
/*
|
||||
* Send signal sig to the process pid (negative pid targets a process
|
||||
* group; 0 targets the caller's process group; see killpg). Return 0, or
|
||||
* -1 with errno set. Permission, existence and signal validity are
|
||||
* checked by the kernel.
|
||||
*/
|
||||
int
|
||||
kill(pid_t pid, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to every process in the process group pgrp (0 selects
|
||||
* the caller's process group). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
killpg(pid_t pgrp, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to the calling thread (thread-directed, so it is
|
||||
* delivered even when another thread of the process has it blocked).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
raise(int sig);
|
||||
|
||||
/*
|
||||
* Wait until a signal is caught, then return -1 with errno EINTR. If the
|
||||
* process is terminated by the signal instead, pause never returns.
|
||||
*/
|
||||
int
|
||||
pause(void);
|
||||
|
||||
/*
|
||||
* Schedule delivery of SIGALRM to the calling process after seconds
|
||||
* seconds (0 cancels any pending alarm). Return the number of seconds
|
||||
* remaining on any previously scheduled alarm, or 0.
|
||||
*/
|
||||
unsigned
|
||||
alarm(unsigned seconds);
|
||||
|
||||
/*
|
||||
* Abnormally terminate the calling process: raise SIGABRT with default
|
||||
* disposition — if SIGABRT is blocked it is unblocked first, and if it
|
||||
* is caught or ignored the disposition is reset to SIG_DFL and the raise
|
||||
* repeated. If that still returns, terminate with exit status 134 as a
|
||||
* last resort. abort never returns.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
abort(void);
|
||||
|
||||
/*
|
||||
* Send signal sig with payload value to process pid, as if by a kernel
|
||||
* queue operation (the kernel sees a negative si_code, SI_QUEUE). Return
|
||||
* 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigqueue(pid_t pid, int sig, const union sigval value);
|
||||
|
||||
/*
|
||||
* Install handler as the action for sig with SA_RESTART (BSD semantics:
|
||||
* syscalls interrupted by the signal are restarted). Return the previous
|
||||
* handler, or SIG_ERR with errno set.
|
||||
*/
|
||||
void (*signal(int sig, void (*handler)(int)))(int);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and obsolete conveniences. */
|
||||
|
||||
/*
|
||||
* Alternate signal stack descriptor. ss_sp/ss_size name a region the
|
||||
* kernel switches to when delivering a handler installed with SA_ONSTACK
|
||||
* (see sigaltstack below). stack_t is the POSIX spelling.
|
||||
*/
|
||||
typedef struct sigaltstack
|
||||
{
|
||||
void *ss_sp; /* stack base or current stack base */
|
||||
int ss_flags; /* SS_ONSTACK/SS_DISABLE */
|
||||
size_t ss_size; /* stack bytes */
|
||||
} stack_t;
|
||||
|
||||
/* ss_flags values. */
|
||||
#define SS_ONSTACK 1 /* the process is currently executing on this stack */
|
||||
#define SS_DISABLE 2 /* the alternate stack is currently disabled */
|
||||
|
||||
/* Minimum / default alternate stack sizes (x86_64 values). */
|
||||
#define MINSIGSTKSZ 2048
|
||||
#define SIGSTKSZ 8192
|
||||
|
||||
/*
|
||||
* Install ss as the alternate signal stack (NULL leaves it unchanged)
|
||||
* and store the previous descriptor through oss (NULL skips the store).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigaltstack(const stack_t *restrict ss, stack_t *restrict oss);
|
||||
|
||||
/*
|
||||
* Schedule SIGALRM after usecs microseconds, repeating every interval
|
||||
* microseconds (0 fires once). Return the number of microseconds
|
||||
* remaining on any previously scheduled alarm, or -1 on error.
|
||||
*/
|
||||
useconds_t
|
||||
ualarm(useconds_t usecs, useconds_t interval);
|
||||
|
||||
/*
|
||||
* Toggle the SA_RESTART bit of sig's disposition: flag nonzero removes
|
||||
* it (interrupted syscalls return EINTR, System V semantics), flag zero
|
||||
* restores it. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
siginterrupt(int sig, int flag);
|
||||
|
||||
/*
|
||||
* Write s (when non-NULL and nonempty), ": " and strsignal(sig) to
|
||||
* stderr. The output is a diagnostic only.
|
||||
*/
|
||||
void
|
||||
psignal(int sig, const char *s);
|
||||
|
||||
/*
|
||||
* Like psignal, but the signal number and details are taken from the
|
||||
* siginfo si (si_signo names the signal). si may be NULL, in which case
|
||||
* only the message prefix is written.
|
||||
*/
|
||||
void
|
||||
psiginfo(const siginfo_t *si, const char *s);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SIGNAL_H */
|
||||
@@ -429,4 +429,35 @@ fmemopen(void *buf, size_t size, const char *mode);
|
||||
FILE *
|
||||
open_memstream(char **ptr, size_t *sizeloc);
|
||||
|
||||
/* formatted input (todo 17) */
|
||||
|
||||
/*
|
||||
* Read formatted input. scanf reads from stdin, fscanf from the stream,
|
||||
* sscanf from the string. The conversion directives are %d %i %u %o %x %X
|
||||
* %f %F %e %E %g %G %a %A %c %s %[ %p %n and %% (with assignment
|
||||
* suppression '*', a decimal field width, and the length modifiers hh h l
|
||||
* ll j z t, plus L for the float conversions). A conversion fails to match
|
||||
* without consuming its offending character; the return value is the
|
||||
* number of assigned (non-suppressed) input items, or EOF if an input
|
||||
* failure occurs before the first one. Integer overflow stores a saturated
|
||||
* value and sets errno = ERANGE (nothing else touches errno).
|
||||
*/
|
||||
int
|
||||
scanf(const char *restrict format, ...);
|
||||
|
||||
int
|
||||
fscanf(FILE *restrict stream, const char *restrict format, ...);
|
||||
|
||||
int
|
||||
sscanf(const char *restrict s, const char *restrict format, ...);
|
||||
|
||||
int
|
||||
vscanf(const char *restrict format, va_list ap);
|
||||
|
||||
int
|
||||
vfscanf(FILE *restrict stream, const char *restrict format, va_list ap);
|
||||
|
||||
int
|
||||
vsscanf(const char *restrict s, const char *restrict format, va_list ap);
|
||||
|
||||
#endif /* VLIBC_STDIO_H */
|
||||
|
||||
@@ -544,6 +544,21 @@ getsubopt(char **optionp, char *const *tokens, char **valuep);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Resolve path to an absolute, NUL-terminated canonical pathname with no
|
||||
* symbolic links, "." or ".." components (todo 38). resolved_path may be
|
||||
* NULL, in which case the canonical path is returned in a malloc'd
|
||||
* buffer the caller must free; otherwise it must hold at least PATH_MAX
|
||||
* bytes. Returns resolved_path (or the malloc'd buffer), or NULL with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
char *
|
||||
realpath(const char *restrict path, char *restrict resolved_path);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#ifndef VLIBC_SYS_MMAN_H
|
||||
#define VLIBC_SYS_MMAN_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/mman.h>.
|
||||
*
|
||||
* Memory mapping, protection, and synchronization (POSIX.1-2008). Every
|
||||
* function here is an unbuffered pass-through to the kernel: failures are
|
||||
* reported as -1 (or MAP_FAILED for mmap) with errno set by the syscall
|
||||
* layer.
|
||||
*
|
||||
* Level 1 (onlyposix): mmap, munmap, mprotect, msync, mlock, munlock,
|
||||
* mlockall, munlockall, posix_madvise.
|
||||
* Level 2 (muslmimic): madvise (XSI — posix_madvise is the POSIX base
|
||||
* form), shm_open, shm_unlink.
|
||||
*
|
||||
* All constant values are Linux x86_64 kernel-UAPI facts (asm-generic/
|
||||
* mman-common.h and mman.h), transcribed, not invented. PROT_*, MAP_*,
|
||||
* MAP_FAILED, MS_* and MCL_* exist at level 1. The POSIX_MADV_* advice
|
||||
* values match the Linux MADV_* values for the same advice (0-4), which is
|
||||
* what lets posix_madvise pass its advice straight to SYS_madvise; the
|
||||
* Linux MADV_* names themselves are gated at level 2 with madvise, the only
|
||||
* function that takes them. MAP_ANONYMOUS is provided as a source
|
||||
* compatibility alias of the primary spelling MAP_ANON (the BSD/POSIX
|
||||
* draft name glibc also accepts).
|
||||
*
|
||||
* POSIX shared memory (shm_open/shm_unlink, level 2) is backed by a real
|
||||
* named file under /dev/shm (tmpfs), NOT by memfd_create: the object is a
|
||||
* path in the shared-memory filesystem, visible there and re-openable by
|
||||
* name until shm_unlink removes it, which matches POSIX's named-object
|
||||
* semantics. vlibc renders name as "/dev/shm/" + name and requires that
|
||||
* name contain no '/' — the object is a single directory entry, so a slash
|
||||
* would escape the namespace (EINVAL). This differs from the POSIX wording
|
||||
* that a name begin with a slash: vlibc names carry no leading slash and
|
||||
* the prefix is added internally. Otherwise shm_open behaves as open(2) on
|
||||
* the rendered path (the oflag access modes plus O_CREAT/O_EXCL/O_TRUNC and
|
||||
* mode bits are honored; O_WRONLY alone is undefined by POSIX and passed
|
||||
* through to the kernel) and shm_unlink as unlink(2) on it.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* changes the caller's address space or performs I/O with side effects and
|
||||
* reports failures through errno, so const/pure would be unsound (the same
|
||||
* rationale unistd.h documents for its I/O family).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- memory protection ---- */
|
||||
|
||||
#define PROT_NONE 0x0 /* page cannot be accessed */
|
||||
#define PROT_READ 0x1 /* page can be read */
|
||||
#define PROT_WRITE 0x2 /* page can be written */
|
||||
#define PROT_EXEC 0x4 /* page can be executed */
|
||||
|
||||
/* ---- mapping types and flags ---- */
|
||||
|
||||
#define MAP_SHARED 0x01 /* share changes with the file and other mappers */
|
||||
#define MAP_PRIVATE 0x02 /* private copy-on-write mapping */
|
||||
#define MAP_FIXED 0x10 /* interpret addr exactly; fail if it cannot be used */
|
||||
#define MAP_ANON 0x20 /* anonymous mapping, not file-backed (primary name) */
|
||||
#define MAP_ANONYMOUS MAP_ANON /* source-compatibility alias of MAP_ANON */
|
||||
|
||||
/* Value returned by mmap on failure (never a valid mapping address). */
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
/* ---- msync synchronization flags ---- */
|
||||
|
||||
#define MS_ASYNC 0x1 /* return before the writes take effect */
|
||||
#define MS_INVALIDATE 0x2 /* invalidate other mappings of the file */
|
||||
#define MS_SYNC 0x4 /* perform synchronous writes */
|
||||
|
||||
/* ---- mlockall flags ---- */
|
||||
|
||||
#define MCL_CURRENT 0x1 /* lock all currently mapped pages */
|
||||
#define MCL_FUTURE 0x2 /* lock all pages mapped in the future */
|
||||
|
||||
/* ---- posix_madvise advice (Linux MADV_* values, see the header note) ---- */
|
||||
|
||||
#define POSIX_MADV_NORMAL 0 /* no special treatment */
|
||||
#define POSIX_MADV_RANDOM 1 /* pages will be accessed randomly */
|
||||
#define POSIX_MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
|
||||
#define POSIX_MADV_WILLNEED 3 /* pages will be needed soon */
|
||||
#define POSIX_MADV_DONTNEED 4 /* pages are not needed soon */
|
||||
|
||||
/*
|
||||
* Map len bytes starting at offset off of the object open on fildes into
|
||||
* the process address space and return the mapping address, or MAP_FAILED
|
||||
* with errno set. addr is a hint for the placement (0 for "anywhere")
|
||||
* unless flags contains MAP_FIXED; prot is a combination of PROT_* (PROT_NONE
|
||||
* alone forbids all access). With MAP_ANON fildes is ignored and must be -1
|
||||
* and off 0; the mapping is zero-filled. The mapping is shared or private
|
||||
* per MAP_SHARED/MAP_PRIVATE. len is rounded up to whole pages.
|
||||
*/
|
||||
void *
|
||||
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
|
||||
|
||||
/*
|
||||
* Remove the mapping at addr (a page boundary) covering len bytes; return
|
||||
* 0, or -1 with errno set. The address range becomes invalid.
|
||||
*/
|
||||
int
|
||||
munmap(void *addr, size_t len);
|
||||
|
||||
/*
|
||||
* Change the protection of the mapped pages at addr covering len bytes to
|
||||
* prot; return 0, or -1 with errno set. addr must be page-aligned.
|
||||
*/
|
||||
int
|
||||
mprotect(void *addr, size_t len, int prot);
|
||||
|
||||
/*
|
||||
* Flush the mapped pages at addr covering len bytes of a MAP_SHARED mapping
|
||||
* to (or invalidate them from) the underlying object per flags (MS_ASYNC,
|
||||
* MS_SYNC, MS_INVALIDATE); return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
msync(void *addr, size_t len, int flags);
|
||||
|
||||
/*
|
||||
* Lock the pages at addr covering len bytes into memory so they are never
|
||||
* paged out; return 0, or -1 with errno set. munlock unlocks them again.
|
||||
*/
|
||||
int
|
||||
mlock(const void *addr, size_t len);
|
||||
|
||||
int
|
||||
munlock(const void *addr, size_t len);
|
||||
|
||||
/*
|
||||
* Lock all pages mapped by the process into memory per flags (MCL_CURRENT,
|
||||
* MCL_FUTURE); return 0, or -1 with errno set. munlockall unlocks them.
|
||||
*/
|
||||
int
|
||||
mlockall(int flags);
|
||||
|
||||
int
|
||||
munlockall(void);
|
||||
|
||||
/*
|
||||
* Give the kernel advice (one of the POSIX_MADV_* values) about how the
|
||||
* pages at addr covering len bytes will be used; return 0, or -1 with errno
|
||||
* set. The advice may be ignored; the mapping is unchanged.
|
||||
*/
|
||||
int
|
||||
posix_madvise(void *addr, size_t len, int advice);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI + POSIX shared memory. */
|
||||
|
||||
/* ---- madvise advice (Linux x86_64 kernel-UAPI values) ---- */
|
||||
|
||||
#define MADV_NORMAL 0 /* no special treatment */
|
||||
#define MADV_RANDOM 1 /* pages will be accessed randomly */
|
||||
#define MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
|
||||
#define MADV_WILLNEED 3 /* pages will be needed soon */
|
||||
#define MADV_DONTNEED 4 /* pages are not needed soon; free them */
|
||||
#define MADV_FREE 8 /* free pages; contents lost on later write */
|
||||
#define MADV_REMOVE 9 /* free the pages and punch a hole in the file */
|
||||
#define MADV_DONTFORK 10 /* do not inherit the pages across fork */
|
||||
#define MADV_DOFORK 11 /* revert MADV_DONTFORK */
|
||||
#define MADV_MERGEABLE 12 /* enable KSM merging of the pages */
|
||||
#define MADV_UNMERGEABLE 13 /* revert MADV_MERGEABLE */
|
||||
#define MADV_HUGEPAGE 14 /* prefer transparent huge pages */
|
||||
#define MADV_NOHUGEPAGE 15 /* revert MADV_HUGEPAGE */
|
||||
#define MADV_DONTDUMP 16 /* exclude the pages from core dumps */
|
||||
#define MADV_DODUMP 17 /* revert MADV_DONTDUMP */
|
||||
#define MADV_WIPEONFORK 18 /* zero the pages in the child after fork */
|
||||
#define MADV_KEEPONFORK 19 /* revert MADV_WIPEONFORK */
|
||||
#define MADV_COLD 20 /* pages will be accessed less soon */
|
||||
#define MADV_PAGEOUT 21 /* reclaim the pages immediately */
|
||||
#define MADV_POPULATE_READ 22 /* fault the pages in for reading */
|
||||
#define MADV_POPULATE_WRITE 23 /* fault the pages in for writing */
|
||||
#define MADV_DONTNEED_LOCKED 24 /* like MADV_DONTNEED on locked pages */
|
||||
#define MADV_COLLAPSE 25 /* collapse the range into a THP */
|
||||
|
||||
/*
|
||||
* Linux madvise: give the kernel advice (one of the MADV_* values above)
|
||||
* about the pages at addr covering len bytes; return 0, or -1 with errno
|
||||
* set. XSI — posix_madvise (level 1) is the POSIX base form; this is the
|
||||
* raw Linux interface exposing the full MADV_* set.
|
||||
*/
|
||||
int
|
||||
madvise(void *addr, size_t len, int advice);
|
||||
|
||||
/*
|
||||
* Open (creating with mode if oflag contains O_CREAT) the POSIX shared
|
||||
* memory object name and return a descriptor, or -1 with errno set. The
|
||||
* object is a file under /dev/shm; name must not contain '/', and the
|
||||
* object persists (re-openable by name) until shm_unlink removes it. See
|
||||
* the header note on the naming deviation. oflag behaves as for open(2).
|
||||
*/
|
||||
int
|
||||
shm_open(const char *name, int oflag, mode_t mode);
|
||||
|
||||
/*
|
||||
* Remove the shared memory object name from /dev/shm; return 0, or -1 with
|
||||
* errno set. Open descriptors of the object stay valid until closed. See
|
||||
* the header note on the naming deviation.
|
||||
*/
|
||||
int
|
||||
shm_unlink(const char *name);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_MMAN_H */
|
||||
@@ -0,0 +1,203 @@
|
||||
#ifndef VLIBC_SYS_RESOURCE_H
|
||||
#define VLIBC_SYS_RESOURCE_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/resource.h>.
|
||||
*
|
||||
* Per-process resource limits and usage accounting. Nothing in this header
|
||||
* is POSIX.1-2008 base — getrlimit/setrlimit/getrusage/getpriority/
|
||||
* setpriority and nice are all XSI [CX] — so the entire surface is gated at
|
||||
* level 2:
|
||||
*
|
||||
* Level 2 (muslmimic): getrlimit, setrlimit, getrusage, getpriority,
|
||||
* setpriority, nice, getrlimit64, setrlimit64,
|
||||
* struct rlimit, struct rusage, rlim_t, RLIMIT_*,
|
||||
* RLIM_INFINITY, PRIO_*, RUSAGE_*.
|
||||
*
|
||||
* struct rusage matches the kernel layout on x86_64 verbatim: two struct
|
||||
* timevals (16 bytes each — the kernel record uses the same two-long
|
||||
* __kernel_old_timeval) followed by fourteen longs, and the kernel
|
||||
* getrusage copies it out without translation. struct rlimit is likewise
|
||||
* the kernel's struct rlimit64 (two 64-bit words), which is why
|
||||
* getrlimit/setrlimit pass the record straight to SYS_prlimit64 and why
|
||||
* getrlimit64/setrlimit64 are name-only aliases on this architecture.
|
||||
* Both shapes are pinned by the static asserts below.
|
||||
*
|
||||
* Every function here performs kernel I/O with side effects and reports
|
||||
* failures through errno, so no declaration carries an intent attribute
|
||||
* (const/pure would be unsound).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
#include <stddef.h> /* offsetof */
|
||||
|
||||
#include <sys/time.h> /* struct timeval (shared VLIBC_TIMEVAL_DEFINED guard) */
|
||||
|
||||
#include <sys/types.h> /* id_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Resource limit values: the 64-bit width of the kernel's rlimit64 record
|
||||
* (unsigned long long on x86_64, where the classic long is also 64-bit but
|
||||
* the LFS spelling is authoritative).
|
||||
*/
|
||||
typedef unsigned long long rlim_t;
|
||||
|
||||
/*
|
||||
* A resource limit pair: rlim_cur is the soft limit (enforced), rlim_max
|
||||
* the hard limit (ceiling a soft limit may be raised to without
|
||||
* privilege). RLIM_INFINITY in either field means "unlimited".
|
||||
*/
|
||||
struct rlimit
|
||||
{
|
||||
rlim_t rlim_cur; /* soft limit (current) */
|
||||
rlim_t rlim_max; /* hard limit (ceiling) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-process resource usage. The field order is the kernel's own
|
||||
* (getrusage copies the record out untouched): the two CPU-time timevals
|
||||
* first, then the fourteen accounting longs. ru_maxrss is in kilobytes.
|
||||
* The fields a kernel does not maintain stay zero.
|
||||
*/
|
||||
struct rusage
|
||||
{
|
||||
struct timeval ru_utime; /* user CPU time used */
|
||||
struct timeval ru_stime; /* system CPU time used */
|
||||
long ru_maxrss; /* maximum resident set size (KiB) */
|
||||
long ru_ixrss; /* integral shared memory size */
|
||||
long ru_idrss; /* integral unshared data size */
|
||||
long ru_isrss; /* integral unshared stack size */
|
||||
long ru_minflt; /* page reclaims (soft page faults) */
|
||||
long ru_majflt; /* page faults (hard page faults) */
|
||||
long ru_nswap; /* swaps */
|
||||
long ru_inblock; /* block input operations */
|
||||
long ru_oublock; /* block output operations */
|
||||
long ru_msgsnd; /* IPC messages sent */
|
||||
long ru_msgrcv; /* IPC messages received */
|
||||
long ru_nsignals; /* signals received */
|
||||
long ru_nvcsw; /* voluntary context switches */
|
||||
long ru_nivcsw; /* involuntary context switches */
|
||||
};
|
||||
|
||||
/* x86_64 kernel ABI: 2 x struct timeval (16 B) + 14 longs (8 B) = 144 B. */
|
||||
_Static_assert(sizeof(struct rusage) == 144, "struct rusage must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct rusage, ru_maxrss) == 32, "ru_maxrss must follow the two timevals");
|
||||
_Static_assert(sizeof(struct rlimit) == 2 * sizeof(rlim_t),
|
||||
"struct rlimit is a pair of rlim_t words");
|
||||
|
||||
/* ---- Resource limit identifiers (kernel asm-generic values) ---- */
|
||||
|
||||
#define RLIMIT_CPU 0 /* CPU time in seconds */
|
||||
#define RLIMIT_FSIZE 1 /* maximum file size */
|
||||
#define RLIMIT_DATA 2 /* data segment size */
|
||||
#define RLIMIT_STACK 3 /* stack size */
|
||||
#define RLIMIT_CORE 4 /* core file size */
|
||||
#define RLIMIT_RSS 5 /* resident set size */
|
||||
#define RLIMIT_NPROC 6 /* number of processes */
|
||||
#define RLIMIT_NOFILE 7 /* number of open files */
|
||||
#define RLIMIT_MEMLOCK 8 /* locked-in-memory address space */
|
||||
#define RLIMIT_AS 9 /* address space size */
|
||||
#define RLIMIT_LOCKS 10 /* number of file locks held */
|
||||
#define RLIMIT_SIGPENDING 11 /* number of pending signals */
|
||||
#define RLIMIT_MSGQUEUE 12 /* bytes in POSIX message queues */
|
||||
#define RLIMIT_NICE 13 /* ceiling for the nice value */
|
||||
#define RLIMIT_RTPRIO 14 /* maximum realtime priority */
|
||||
#define RLIMIT_RTTIME 15 /* realtime CPU time (us) */
|
||||
#define RLIMIT_NLIMITS 16 /* number of resource kinds */
|
||||
|
||||
/* "No limit" value for either field of struct rlimit. */
|
||||
#define RLIM_INFINITY (~0UL)
|
||||
|
||||
/* Saved-limit markers (legacy; equal to RLIM_INFINITY on Linux). */
|
||||
#define RLIM_SAVED_CUR RLIM_INFINITY
|
||||
#define RLIM_SAVED_MAX RLIM_INFINITY
|
||||
|
||||
/* ---- getpriority/setpriority target selectors ---- */
|
||||
|
||||
#define PRIO_PROCESS 0 /* a single process */
|
||||
#define PRIO_PGRP 1 /* a process group */
|
||||
#define PRIO_USER 2 /* every process of a user */
|
||||
|
||||
/* The valid nice-value range (PRIO_MIN is the highest scheduling priority). */
|
||||
#define PRIO_MIN (-20)
|
||||
#define PRIO_MAX 19
|
||||
|
||||
/* ---- getrusage `who` selectors ---- */
|
||||
|
||||
#define RUSAGE_SELF 0 /* the calling process */
|
||||
#define RUSAGE_CHILDREN (-1) /* terminated and waited-for children */
|
||||
#define RUSAGE_THREAD 1 /* the calling thread */
|
||||
|
||||
/*
|
||||
* Return the current soft and hard limits of resource through rlim. The
|
||||
* call never fails for a valid resource. Return 0, or -1 with errno set
|
||||
* when resource is out of range or rlim points outside the address space.
|
||||
*/
|
||||
int
|
||||
getrlimit(int resource, struct rlimit *rlim);
|
||||
|
||||
/*
|
||||
* Set the soft and hard limits of resource from rlim. Raising the hard
|
||||
* limit (or the soft limit above the hard limit) requires privilege.
|
||||
* Return 0, or -1 with errno set for an out-of-range resource, a soft
|
||||
* limit above the hard limit, or a denied raise.
|
||||
*/
|
||||
int
|
||||
setrlimit(int resource, const struct rlimit *rlim);
|
||||
|
||||
/*
|
||||
* Fill usage with the resource usage of who (RUSAGE_SELF, RUSAGE_CHILDREN
|
||||
* or RUSAGE_THREAD). Return 0, or -1 with errno set for an unknown who or
|
||||
* when usage points outside the address space.
|
||||
*/
|
||||
int
|
||||
getrusage(int who, struct rusage *usage);
|
||||
|
||||
/*
|
||||
* Return the nice value of the target described by (which, who), in the
|
||||
* range PRIO_MIN..PRIO_MAX, or -1 with errno set on error. Because -1 is
|
||||
* also a valid priority, callers conventionally clear errno before the
|
||||
* call and treat a -1 return with a nonzero errno as an error.
|
||||
*/
|
||||
int
|
||||
getpriority(int which, id_t who);
|
||||
|
||||
/*
|
||||
* Set the nice value of every process named by (which, who) to prio,
|
||||
* clamping out-of-range values to PRIO_MIN..PRIO_MAX. Lowering the value
|
||||
* (raising priority) requires privilege. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
setpriority(int which, id_t who, int prio);
|
||||
|
||||
/*
|
||||
* Add inc to the calling process's nice value and return the new value,
|
||||
* or -1 with errno set on error (see getpriority for the -1 convention).
|
||||
*/
|
||||
int
|
||||
nice(int inc);
|
||||
|
||||
/*
|
||||
* Large-file aliases. rlim_t is already 64-bit on x86_64, so these behave
|
||||
* exactly as getrlimit/setrlimit and exist as separate exported symbols for
|
||||
* LFS callers.
|
||||
*/
|
||||
int
|
||||
getrlimit64(int resource, struct rlimit *rlim);
|
||||
int
|
||||
setrlimit64(int resource, const struct rlimit *rlim);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#endif /* VLIBC_SYS_RESOURCE_H */
|
||||
@@ -86,12 +86,20 @@ typedef struct
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/* Elapsed time in seconds and microseconds (see select()'s timeout). */
|
||||
/*
|
||||
* Elapsed time in seconds and microseconds (see select()'s timeout).
|
||||
* Shared guard with <sys/time.h>, the canonical POSIX home of struct
|
||||
* timeval: both headers define the same layout under
|
||||
* VLIBC_TIMEVAL_DEFINED, so either may be included first at any level.
|
||||
*/
|
||||
#ifndef VLIBC_TIMEVAL_DEFINED
|
||||
#define VLIBC_TIMEVAL_DEFINED
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Wait for readiness on the descriptors marked in readfds, writefds and
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef VLIBC_SYS_STATVFS_H
|
||||
#define VLIBC_SYS_STATVFS_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/statvfs.h>.
|
||||
*
|
||||
* Filesystem statistics in an ABI-independent POSIX shape: block and file
|
||||
* counts as unsigned counts over a reported block size, plus the read-only
|
||||
* and setuid-disabling mount flags and the longest file name. This header
|
||||
* carries only the portable view; the Linux kernel's private <linux/statfs.h>
|
||||
* layout (signed longs, an int-pair f_fsid, a f_spare tail) never leaks
|
||||
* into it.
|
||||
*
|
||||
* Level 2 (muslmimic): the whole header (statvfs and fstatvfs are XSI
|
||||
* [CX] in POSIX.1-2008, not base; the base filesystem
|
||||
* queries are stat()/fstat() in <sys/stat.h>).
|
||||
*
|
||||
* f_flag holds ST_RDONLY and/or ST_NOSUID, and f_fsid is a best-effort
|
||||
* filesystem identifier (the kernel does not populate it for every
|
||||
* filesystem). The block counts are measured in f_frsize units (which is
|
||||
* f_bsize when the filesystem does not distinguish a transfer size).
|
||||
*
|
||||
* None of these declarations carries an intent attribute: both functions
|
||||
* perform I/O with side effects and report failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* fsblkcnt_t, fsfilcnt_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* Filesystem statistics. f_flag is a bitwise OR of ST_RDONLY and
|
||||
* ST_NOSUID; f_frsize is the fundamental block size the count fields are
|
||||
* expressed in. f_fsid is 0 when the filesystem provides no identifier.
|
||||
*/
|
||||
struct statvfs
|
||||
{
|
||||
unsigned long f_bsize; /* preferred I/O block size, bytes */
|
||||
unsigned long f_frsize; /* fundamental block size, bytes */
|
||||
fsblkcnt_t f_blocks; /* total blocks, in f_frsize units */
|
||||
fsblkcnt_t f_bfree; /* free blocks */
|
||||
fsblkcnt_t f_bavail; /* free blocks available to an unprivileged process */
|
||||
fsfilcnt_t f_files; /* total file serial numbers (inodes) */
|
||||
fsfilcnt_t f_ffree; /* free file serial numbers */
|
||||
fsfilcnt_t f_favail; /* free serial numbers available to an unprivileged process */
|
||||
unsigned long f_fsid; /* filesystem ID (best effort) */
|
||||
unsigned long f_flag; /* mount flags: ST_RDONLY | ST_NOSUID */
|
||||
unsigned long f_namemax; /* maximum file name length */
|
||||
};
|
||||
|
||||
/* f_flag values (also the kernel's f_flags bits on Linux). */
|
||||
#define ST_RDONLY 1 /* read-only filesystem */
|
||||
#define ST_NOSUID 2 /* set-user-ID/set-group-ID bits ignored on exec */
|
||||
|
||||
/*
|
||||
* Store statistics for the filesystem holding path into buf; return 0, or
|
||||
* -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
statvfs(const char *restrict path, struct statvfs *restrict buf);
|
||||
|
||||
/*
|
||||
* Like statvfs(), for the filesystem holding the open descriptor fildes;
|
||||
* return 0, or -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
fstatvfs(int fildes, struct statvfs *buf);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_STATVFS_H */
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef VLIBC_SYS_TIME_H
|
||||
#define VLIBC_SYS_TIME_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/time.h>.
|
||||
*
|
||||
* Wall-clock access and per-process interval timers expressed in
|
||||
* microseconds. Everything here is XSI or BSD legacy — POSIX.1-2008 base
|
||||
* leaves only timerisset in this header — so the whole surface is gated at
|
||||
* level 2:
|
||||
*
|
||||
* Level 2 (muslmimic): gettimeofday, settimeofday, getitimer,
|
||||
* setitimer, utimes, futimes, lutimes,
|
||||
* struct timeval, struct itimerval,
|
||||
* struct timezone, the timer* macros.
|
||||
*
|
||||
* struct timeval is the same type <sys/select.h> uses for select()'s
|
||||
* timeout; that header defines it under the shared guard below so the two
|
||||
* headers agree and either may be included first. The layout is the kernel
|
||||
* ABI on x86_64 (two longs), which is why gettimeofday/setitimer pass it
|
||||
* through unmodified.
|
||||
*
|
||||
* ITIMER_REAL/ITIMER_VIRTUAL/ITIMER_PROF are kernel ABI values (the
|
||||
* getitimer/setitimer `which` argument). None of these declarations carries
|
||||
* an intent attribute: every function performs I/O with side effects and
|
||||
* reports failures through errno, so const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* time_t, suseconds_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI/BSD date-and-time interfaces. */
|
||||
|
||||
/* Interval-timer kinds (kernel ABI; the `which` argument). */
|
||||
#define ITIMER_REAL 0 /* count down in real time */
|
||||
#define ITIMER_VIRTUAL 1 /* count down in process virtual time */
|
||||
#define ITIMER_PROF 2 /* count down in process virtual + system time */
|
||||
|
||||
/*
|
||||
* Elapsed time in seconds and microseconds. Shared guard with
|
||||
* <sys/select.h>, which needs struct timeval for select()'s timeout at
|
||||
* level 1 and defines it under the same macro; either header may therefore
|
||||
* be included first. tv_usec holds 0..999999.
|
||||
*/
|
||||
#ifndef VLIBC_TIMEVAL_DEFINED
|
||||
#define VLIBC_TIMEVAL_DEFINED
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
suseconds_t tv_usec; /* microseconds */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An interval timer setting: it_value is the time to the next expiry,
|
||||
* it_interval the period between expiries after the first (both zero for a
|
||||
* one-shot or a disarmed timer). Kernel ABI layout (x86_64): two timevals.
|
||||
*/
|
||||
struct itimerval
|
||||
{
|
||||
struct timeval it_interval; /* interval between periodic expiries */
|
||||
struct timeval it_value; /* time to the next expiry */
|
||||
};
|
||||
|
||||
/*
|
||||
* Historic timezone record for settimeofday(). POSIX.1-2008 removed struct
|
||||
* timezone; it is kept for the legacy gettimeofday/settimeofday signatures,
|
||||
* whose tz argument is ignored (see src/time/gettimeofday.c).
|
||||
*/
|
||||
struct timezone
|
||||
{
|
||||
int tz_minuteswest; /* minutes west of Greenwich */
|
||||
int tz_dsttime; /* type of daylight-saving correction */
|
||||
};
|
||||
|
||||
/* True when the timer described by tp is armed. */
|
||||
#define timerisset(tp) ((tp)->tv_sec != 0 || (tp)->tv_usec != 0)
|
||||
|
||||
/* Disarm the timer described by tp (zero both fields). */
|
||||
#define timerclear(tp) ((tp)->tv_sec = (tp)->tv_usec = 0)
|
||||
|
||||
/*
|
||||
* Set *result to *a plus *b, normalizing the carry into tv_sec. The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
#define timeradd(a, b, result) \
|
||||
do \
|
||||
{ \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
(result)->tv_sec++; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Set *result to *a minus *b, normalizing the borrow out of tv_sec. The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
#define timersub(a, b, result) \
|
||||
do \
|
||||
{ \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) \
|
||||
{ \
|
||||
(result)->tv_sec--; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* True when *a op *b, compared first by tv_sec and, on equality, by
|
||||
* tv_usec. op is a comparison operator (timercmp(x, y, <=)). The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
/* clang-format off */
|
||||
#define timercmp(a, b, op) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? ((a)->tv_usec op (b)->tv_usec) : ((a)->tv_sec op (b)->tv_sec))
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* Read the current wall-clock time into tv (seconds since the Epoch and
|
||||
* microseconds 0..999999). tz is ignored for compatibility with the
|
||||
* historic two-argument form and may be anything, including NULL; tv may
|
||||
* also be NULL, in which case nothing is written. Return 0, or -1 with
|
||||
* errno set when tv points outside the address space.
|
||||
*/
|
||||
int
|
||||
gettimeofday(struct timeval *restrict tv, void *restrict tz);
|
||||
|
||||
/*
|
||||
* Set the kernel's idea of the current time from tv. tz, when not NULL, is
|
||||
* honored only together with tv; passing tz alone is unsupported (ENOTSUP).
|
||||
* Requires privilege. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
settimeofday(const struct timeval *tv, const struct timezone *tz);
|
||||
|
||||
/*
|
||||
* Store the current setting of interval timer which (ITIMER_REAL,
|
||||
* ITIMER_VIRTUAL or ITIMER_PROF) through value. Return 0, or -1 with errno
|
||||
* set for an unknown which or when value points outside the address space.
|
||||
*/
|
||||
int
|
||||
getitimer(int which, struct itimerval *value);
|
||||
|
||||
/*
|
||||
* Arm interval timer which from value (when value is not NULL) and store
|
||||
* the previous setting through ovalue (when not NULL). A value whose
|
||||
* it_value is zero disarms the timer. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue);
|
||||
|
||||
/*
|
||||
* Set the access (times[0]) and modification (times[1]) timestamps of path.
|
||||
* A NULL times array sets both to the current time. Return 0, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
utimes(const char *path, const struct timeval times[2]);
|
||||
|
||||
/* utimes() on the open file fd. */
|
||||
int
|
||||
futimes(int fd, const struct timeval times[2]);
|
||||
|
||||
/* utimes() on the symbolic link path itself, never following it. */
|
||||
int
|
||||
lutimes(const char *path, const struct timeval times[2]);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_TIME_H */
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef VLIBC_SYS_TIMES_H
|
||||
#define VLIBC_SYS_TIMES_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/times.h>.
|
||||
*
|
||||
* Process CPU-time accounting in clock ticks. times() and struct tms are
|
||||
* XSI [CX] in POSIX.1-2008 (glibc gates them behind _XOPEN_SOURCE, not
|
||||
* _POSIX_C_SOURCE), so the whole surface sits at level 2 — nothing here is
|
||||
* POSIX.1-2008 base:
|
||||
*
|
||||
* Level 2 (muslmimic): times, struct tms.
|
||||
*
|
||||
* clock_t is the scalar tick type owned by <sys/types.h> (typedef long
|
||||
* clock_t; <time.h> pulls it from the same place for clock()); this header
|
||||
* includes that one rather than re-typedefing clock_t. The unit of every
|
||||
* times() figure is the tick: CLK_TCK per second, the fixed x86_64 USER_HZ
|
||||
* constant of 100 that todo 32's sysconf(_SC_CLK_TCK) reports (defined
|
||||
* once, in src/misc/sysconf.c).
|
||||
*
|
||||
* struct tms mirrors the x86_64 kernel's __kernel_tms exactly: four longs
|
||||
* in the same order (tms_utime, tms_stime, tms_cutime, tms_cstime), which
|
||||
* is why src/sys/times.c hands the struct straight to SYS_times. The kernel
|
||||
* writes all four words on every successful call, so no field is optional.
|
||||
*
|
||||
* The declaration carries no intent attribute: times() performs a syscall
|
||||
* with side effects and reports failures through errno, so const/pure
|
||||
* would be unsound (see the rationale in <sys/time.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* clock_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI process-CPU-time accounting. */
|
||||
|
||||
/*
|
||||
* CPU-time accounting for the calling process and its waited-for children,
|
||||
* measured in clock ticks (CLK_TCK per second). tms_utime and tms_stime
|
||||
* are the user and system CPU time of the calling process; tms_cutime and
|
||||
* tms_cstime are the user and system CPU time consumed by children that
|
||||
* have been waited for (all four zeroed before any child is waited on).
|
||||
*/
|
||||
struct tms
|
||||
{
|
||||
clock_t tms_utime; /* user CPU time of the calling process */
|
||||
clock_t tms_stime; /* system CPU time of the calling process */
|
||||
clock_t tms_cutime; /* user CPU time of waited-for children */
|
||||
clock_t tms_cstime; /* system CPU time of waited-for children */
|
||||
};
|
||||
|
||||
/*
|
||||
* Store the process CPU-time accounting through buf and return the number
|
||||
* of clock ticks since an arbitrary fixed point in the past (the Linux
|
||||
* kernel reports ticks since boot). Return (clock_t)-1 with errno set when
|
||||
* buf points outside the address space.
|
||||
*/
|
||||
clock_t
|
||||
times(struct tms *buf);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_TIMES_H */
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef VLIBC_SYS_UIO_H
|
||||
#define VLIBC_SYS_UIO_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/uio.h>.
|
||||
*
|
||||
* Scatter/gather I/O: readv() and writev() transfer data between a file
|
||||
* descriptor and a vector of memory buffers (POSIX.1-2008 base). Both are
|
||||
* unbuffered pass-throughs to the kernel: the iovec array is handed to
|
||||
* SYS_readv/SYS_writev as-is, and the kernel applies its own validation
|
||||
* (iovcnt in 0..IOV_MAX, iov NULL with iovcnt > 0 faults as EFAULT). Failures
|
||||
* are reported as -1 with errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): readv, writev.
|
||||
*
|
||||
* The Linux extensions that build on this ABI (preadv/pwritev,
|
||||
* process_vm_readv/process_vm_writev, ...) are deliberately absent — they are
|
||||
* not POSIX and belong to a later compatibility profile.
|
||||
*
|
||||
* struct iovec matches the kernel's struct iovec (x86_64: a pointer and a
|
||||
* size_t, no padding). IOV_MAX is 1024, the Linux UIO_MAXIOV; the kernel
|
||||
* rejects any iovcnt above it with EINVAL.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Maximum number of iovec entries a single readv()/writev() call accepts. */
|
||||
#define IOV_MAX 1024
|
||||
|
||||
/*
|
||||
* A scatter/gather segment: iov_base points at the buffer (or, for readv,
|
||||
* the buffer to fill) and iov_len is its size in bytes. Kernel layout, as
|
||||
* above.
|
||||
*/
|
||||
struct iovec
|
||||
{
|
||||
void *iov_base; /* starting address of the segment */
|
||||
size_t iov_len; /* number of bytes in the segment */
|
||||
};
|
||||
|
||||
/*
|
||||
* Read up to the sum of iov_len bytes from fd into the iovcnt buffers of
|
||||
* iov, in order; return the number of bytes read, 0 at end of file, or -1
|
||||
* with errno set on error. Buffers are filled completely before the next
|
||||
* one is touched.
|
||||
*/
|
||||
ssize_t
|
||||
readv(int fd, const struct iovec *iov, int iovcnt);
|
||||
|
||||
/*
|
||||
* Write up to the sum of iov_len bytes from the iovcnt buffers of iov to
|
||||
* fd, in order; return the number of bytes written, or -1 with errno set
|
||||
* on error. The kernel applies its own validation (iovcnt 0..IOV_MAX, iov
|
||||
* NULL with iovcnt > 0 faults as EFAULT).
|
||||
*/
|
||||
ssize_t
|
||||
writev(int fd, const struct iovec *iov, int iovcnt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_UIO_H */
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef VLIBC_SYS_UTSNAME_H
|
||||
#define VLIBC_SYS_UTSNAME_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/utsname.h>.
|
||||
*
|
||||
* System identification: uname() and struct utsname. uname is XSI [CX] in
|
||||
* POSIX.1-2008 (glibc gates it behind _XOPEN_SOURCE, not _POSIX_C_SOURCE),
|
||||
* so the whole surface sits at level 2 — nothing here is POSIX.1-2008 base.
|
||||
*
|
||||
* Level 2 (muslmimic): uname, struct utsname.
|
||||
*
|
||||
* struct utsname mirrors the x86_64 kernel's struct new_utsname exactly: six
|
||||
* NUL-terminated 65-byte character arrays (the kernel's _UTSNAME_LENGTH, 64
|
||||
* usable characters plus the terminator). The kernel's uname syscall copies
|
||||
* all 390 bytes, so the layout is an ABI fact, not a style choice.
|
||||
* domainname is a Linux extension (the kernel's uts namespace domain name)
|
||||
* and is not part of the POSIX struct; it is included for glibc
|
||||
* compatibility — POSIX only requires the first five fields.
|
||||
*
|
||||
* The declaration carries no intent attribute: uname performs I/O with side
|
||||
* effects and reports failures through errno, so const/pure would be
|
||||
* unsound (see the rationale in <unistd.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* The system identification strings. Each field is NUL-terminated; the
|
||||
* 65-byte width matches the kernel buffer (64 characters plus the NUL).
|
||||
* sysname is the operating system name ("Linux"), nodename the host name,
|
||||
* release the kernel release, version the kernel build, machine the
|
||||
* hardware identifier ("x86_64"), and domainname the NIS/UTS domain name.
|
||||
*/
|
||||
struct utsname
|
||||
{
|
||||
char sysname[65]; /* operating system name */
|
||||
char nodename[65]; /* name within the communications network */
|
||||
char release[65]; /* operating system release level */
|
||||
char version[65]; /* operating system version */
|
||||
char machine[65]; /* hardware type */
|
||||
char domainname[65]; /* NIS/UTS domain name (Linux extension) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Fill the buffer with the current system identification strings; return
|
||||
* 0, or -1 with errno set when the buffer points outside the address
|
||||
* space. The strings are copies from the kernel's UTS namespace.
|
||||
*/
|
||||
int
|
||||
uname(struct utsname *buf);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_UTSNAME_H */
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef VLIBC_SYSLOG_H
|
||||
#define VLIBC_SYSLOG_H
|
||||
|
||||
/*
|
||||
* vlibc — <syslog.h>.
|
||||
*
|
||||
* The syslog(3) logging interface, writing BSD-syslog datagrams to the
|
||||
* /dev/log socket (see src/misc/syslog.c for the wire details). syslog is
|
||||
* NOT POSIX.1-2008 base — it is XSI [CX] (and historical BSD), so this
|
||||
* ENTIRE header is gated at `VLIBC_LEVEL >= 2`: at level 1 (onlyposix) it is
|
||||
* empty, exactly like <strings.h>.
|
||||
*
|
||||
* The interface has three parts:
|
||||
*
|
||||
* - facilities and severities, plus the option flags of openlog. The
|
||||
* facility constants carry the classic BSD values, which are the Linux
|
||||
* wire ABI for /dev/log: facility = code << 3, severity = 0..7, and the
|
||||
* datagram's numeric priority is facility | severity. The facility code
|
||||
* is passed on the wire, so these numbers must match what syslogd
|
||||
* expects and are therefore kernel-ABI facts, not invented values;
|
||||
*
|
||||
* - LOG_MASK / LOG_UPTO, the two macros that build the mask argument of
|
||||
* setlogmask (LOG_MASK is POSIX; LOG_UPTO is the classic convenience
|
||||
* form both musl and glibc provide);
|
||||
*
|
||||
* - openlog / syslog / closelog / setlogmask and the vsyslog entry point
|
||||
* the standard requires syslog to be implemented in terms of. None of
|
||||
* them carries an intent attribute: every one reads and writes global
|
||||
* logging state and performs I/O, so const/pure would be unsound.
|
||||
*
|
||||
* %m handling: inside the syslog format string, "%m" is replaced by the
|
||||
* strerror(errno) text (a syslog-specific feature, implemented in syslog.c —
|
||||
* NOT a printf conversion). All other conversions are the standard ones,
|
||||
* handled by the library's own vsnprintf.
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gate below always
|
||||
* sees the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||
* header first, and <stdarg.h> for va_list.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- openlog option flags (bit mask) ---- */
|
||||
|
||||
#define LOG_PID 0x01 /* include the calling process' PID in each message */
|
||||
#define LOG_CONS 0x02 /* on send failure, write the message to the console */
|
||||
#define LOG_ODELAY 0x04 /* delay opening the /dev/log socket until first use */
|
||||
#define LOG_NDELAY 0x08 /* open the /dev/log socket immediately in openlog */
|
||||
#define LOG_NOWAIT 0x10 /* do not wait for child processes (accepted, unused) */
|
||||
#define LOG_PERROR 0x20 /* also write each message to standard error */
|
||||
|
||||
/* ---- facility codes (code << 3; the wire value of the datagram) ---- */
|
||||
|
||||
#define LOG_KERN 0 /* kernel messages */
|
||||
#define LOG_USER (1 << 3) /* random user-level messages */
|
||||
#define LOG_MAIL (2 << 3) /* mail system */
|
||||
#define LOG_DAEMON (3 << 3) /* system daemons */
|
||||
#define LOG_AUTH (4 << 3) /* security/authorization messages */
|
||||
#define LOG_LPR (6 << 3) /* line printer subsystem */
|
||||
#define LOG_NEWS (7 << 3) /* network news subsystem */
|
||||
#define LOG_UUCP (8 << 3) /* UUCP subsystem */
|
||||
#define LOG_CRON (9 << 3) /* clock daemon */
|
||||
#define LOG_LOCAL0 (16 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL1 (17 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL2 (18 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL3 (19 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL4 (20 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL5 (21 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL6 (22 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL7 (23 << 3) /* reserved for local use */
|
||||
|
||||
/* ---- severities (the low three bits of the priority value) ---- */
|
||||
|
||||
#define LOG_EMERG 0 /* system is unusable */
|
||||
#define LOG_ALERT 1 /* action must be taken immediately */
|
||||
#define LOG_CRIT 2 /* critical conditions */
|
||||
#define LOG_ERR 3 /* error conditions */
|
||||
#define LOG_WARNING 4 /* warning conditions */
|
||||
#define LOG_NOTICE 5 /* normal but significant condition */
|
||||
#define LOG_INFO 6 /* informational */
|
||||
#define LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
/* Bit field holding the facility part (bits 3..9) of a priority value. */
|
||||
#define LOG_FACMASK 0x03f8
|
||||
|
||||
/* Bit field holding the severity part (bits 0..2) of a priority value. */
|
||||
#define LOG_PRIMASK 0x07
|
||||
|
||||
/* A mask with exactly pri's bit set (pri is a LOG_* severity, 0..7). */
|
||||
#define LOG_MASK(pri) (1 << (pri))
|
||||
|
||||
/* A mask with every severity from LOG_EMERG through pri included. */
|
||||
#define LOG_UPTO(pri) ((1 << ((pri) + 1)) - 1)
|
||||
|
||||
/*
|
||||
* Open a connection to the system logger. ident, when non-NULL, is copied
|
||||
* (a caller-owned pointer is not retained) and prefixes every message; a
|
||||
* NULL ident logs messages with no tag. option ORs in the LOG_* option
|
||||
* flags; facility is one of the LOG_* facility constants and is used for
|
||||
* messages whose priority carries no facility of its own. With LOG_NDELAY
|
||||
* the /dev/log socket is opened here; otherwise (and by default) it is
|
||||
* opened lazily on the first syslog call, and a failure to open it is
|
||||
* silent. Prior state — the mask from setlogmask and the default facility
|
||||
* LOG_USER — persists.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
openlog(const char *ident, int option, int facility);
|
||||
|
||||
/*
|
||||
* Log a message: build "<facility|severity>ident[pid]: message" and send it
|
||||
* as one datagram to /dev/log, subject to the setlogmask mask. severity is
|
||||
* the low three bits of priority; a facility encoded in priority overrides
|
||||
* the openlog facility, and an out-of-range (high-bit) facility is ignored,
|
||||
* falling back to the openlog facility. The format string is the printf
|
||||
* family's, with "%m" additionally meaning the strerror(errno) text. When
|
||||
* LOG_PERROR was passed to openlog the composed line also goes to standard
|
||||
* error; when the send fails and LOG_CONS was passed, it goes to the console
|
||||
* instead. A failed open or send never terminates the process.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
syslog(int priority, const char *format, ...);
|
||||
|
||||
/*
|
||||
* Close the connection to the system logger: close the /dev/log socket if
|
||||
* one is open, and clear the ident and option state installed by openlog
|
||||
* (the facility and mask are retained). A later syslog reopens the socket
|
||||
* lazily.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
closelog(void);
|
||||
|
||||
/*
|
||||
* The engine behind syslog: identical semantics, taking a va_list instead
|
||||
* of the variadic tail. Each message is formatted exactly once (through
|
||||
* vsnprintf into an internal buffer) before any send, so LOG_PERROR and the
|
||||
* datagram always carry the same text.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
vsyslog(int priority, const char *format, va_list ap);
|
||||
|
||||
/*
|
||||
* Set the process' log priority mask to maskpri — a bit per severity where
|
||||
* bit LOG_MASK(severity) set means "log this severity" — and return the
|
||||
* previous mask. A mask of 0 suppresses every message. The mask applies to
|
||||
* the severity only: facility is never filtered.
|
||||
*/
|
||||
int
|
||||
setlogmask(int maskpri);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#endif /* VLIBC_SYSLOG_H */
|
||||
@@ -0,0 +1,366 @@
|
||||
#ifndef VLIBC_TERMIOS_H
|
||||
#define VLIBC_TERMIOS_H
|
||||
|
||||
/*
|
||||
* vlibc — <termios.h>.
|
||||
*
|
||||
* General terminal interface (POSIX.1-2008 base): the termios structure and
|
||||
* the tcgetattr/tcsetattr/tcsendbreak/tcdrain/tcflush/tcflow query-and-set
|
||||
* functions plus the cfgetispeed/cfgetospeed/cfsetispeed/cfsetospeed baud
|
||||
* helpers, over the kernel's TCGETS/TCSETS ioctls (SYS_ioctl).
|
||||
*
|
||||
* Level 1 (onlyposix): tcgetattr, tcsetattr, tcsendbreak, tcdrain,
|
||||
* tcflush, tcflow, cfgetispeed, cfgetospeed,
|
||||
* cfsetispeed, cfsetospeed (POSIX base).
|
||||
* Level 2 (muslmimic): cfmakeraw, cfsetspeed (BSD/glibc extensions),
|
||||
* tcgetsid (XSI [CX] — glibc declares it only under
|
||||
* __USE_XOPEN2K8, not base __USE_POSIX).
|
||||
*
|
||||
* struct termios is a byte-for-byte copy of the x86_64 kernel termios
|
||||
* (asm-generic/termbits.h), pinned by the static assertions below. The
|
||||
* kernel TCGETS/TCSETS ioctls copy exactly sizeof(struct termios) bytes of
|
||||
* this layout (verified empirically on this host: TCGETS writes 36 bytes);
|
||||
* any divergence would silently corrupt c_cc. Unlike glibc's 60-byte
|
||||
* userspace struct (NCCS 32 + trailing ispeed/ospeed members), the speed
|
||||
* is carried only in the c_cflag CBAUD/CIBAUD bit fields, exactly as the
|
||||
* kernel encodes it — so the ioctl copies the whole structure both ways
|
||||
* with nothing left stale.
|
||||
*
|
||||
* The baud-rate constants B0..B4000000 are the KERNEL CBAUD encodings
|
||||
* (B9600 is 0x0d, B38400 is 0x0f, B57600 is 0x1001, ...), NOT glibc's
|
||||
* real-numbers-in-speed_t scheme. cfgetospeed()/cfgetispeed() return these
|
||||
* constants and cfsetospeed()/cfsetispeed()/cfsetspeed() accept them, so
|
||||
* a speed round-trips through the c_cflag bits untouched. This is the same
|
||||
* choice musl makes.
|
||||
*
|
||||
* All flag, control-character, baud, and action constants below are Linux
|
||||
* x86_64 kernel-UAPI facts transcribed from asm-generic/termbits.h and
|
||||
* asm-generic/termbits-common.h. The ioctl request numbers (TCGETS,
|
||||
* TCSETS, TCSBRK, TCXONC, TCFLSH, TIOCGSID) are Linux-only and stay
|
||||
* private to src/termios/termios.c, matching the sys/ioctl.h-less layout
|
||||
* of the tree today (they belong to a future <sys/ioctl.h>).
|
||||
*
|
||||
* No declaration carries an intent attribute: tcgetattr/tcsetattr &c
|
||||
* perform I/O, and the cf* speed helpers read or write the caller's
|
||||
* termios through a pointer that may alias arbitrary memory — const/pure
|
||||
* would be unsound (same rationale as <fcntl.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Basic types ---- */
|
||||
|
||||
typedef unsigned char cc_t; /* control-character value */
|
||||
typedef unsigned int speed_t; /* baud rate (a B* constant) */
|
||||
typedef unsigned int tcflag_t; /* mode-flag bit mask */
|
||||
|
||||
/* ---- struct termios (x86_64 kernel layout) ---- */
|
||||
|
||||
#define NCCS 19 /* control-character slots (kernel UAPI) */
|
||||
|
||||
struct termios
|
||||
{
|
||||
tcflag_t c_iflag; /* 0: input mode flags */
|
||||
tcflag_t c_oflag; /* 4: output mode flags */
|
||||
tcflag_t c_cflag; /* 8: control mode flags */
|
||||
tcflag_t c_lflag; /* 12: local mode flags */
|
||||
cc_t c_line; /* 16: line discipline */
|
||||
cc_t c_cc[NCCS]; /* 17: control characters */
|
||||
};
|
||||
|
||||
/* Pin the kernel layout (TCGETS/TCSETS copy this exact size and shape). */
|
||||
_Static_assert(sizeof(struct termios) == 36, "struct termios must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct termios, c_lflag) == 12, "c_lflag must sit at offset 12");
|
||||
_Static_assert(offsetof(struct termios, c_line) == 16, "c_line must sit at offset 16");
|
||||
_Static_assert(offsetof(struct termios, c_cc) == 17, "c_cc must sit at offset 17");
|
||||
|
||||
/* ---- Control characters (indices into c_cc) ---- */
|
||||
|
||||
#define VINTR 0 /* interrupt character */
|
||||
#define VQUIT 1 /* quit character */
|
||||
#define VERASE 2 /* erase character */
|
||||
#define VKILL 3 /* kill character */
|
||||
#define VEOF 4 /* end-of-file character */
|
||||
#define VTIME 5 /* timeout for non-canonical read */
|
||||
#define VMIN 6 /* min characters for non-canonical read */
|
||||
#define VSWTC 7 /* switch character (System V) */
|
||||
#define VSTART 8 /* start character */
|
||||
#define VSTOP 9 /* stop character */
|
||||
#define VSUSP 10 /* suspend character */
|
||||
#define VEOL 11 /* alternate end-of-line character */
|
||||
#define VREPRINT 12 /* reprint character */
|
||||
#define VDISCARD 13 /* discard character */
|
||||
#define VWERASE 14 /* word-erase character */
|
||||
#define VLNEXT 15 /* literal-next character */
|
||||
#define VEOL2 16 /* alternate end-of-line character */
|
||||
|
||||
/* ---- c_iflag: input mode flags ---- */
|
||||
|
||||
#define IGNBRK 0x0001 /* ignore break condition */
|
||||
#define BRKINT 0x0002 /* signal interrupt on break */
|
||||
#define IGNPAR 0x0004 /* ignore characters with parity errors */
|
||||
#define PARMRK 0x0008 /* mark parity and framing errors */
|
||||
#define INPCK 0x0010 /* enable input parity check */
|
||||
#define ISTRIP 0x0020 /* strip 8th bit off characters */
|
||||
#define INLCR 0x0040 /* map NL to CR on input */
|
||||
#define IGNCR 0x0080 /* ignore CR */
|
||||
#define ICRNL 0x0100 /* map CR to NL on input */
|
||||
#define IUCLC 0x0200 /* map uppercase to lowercase on input */
|
||||
#define IXON 0x0400 /* enable start/stop output control */
|
||||
#define IXANY 0x0800 /* any character will restart after stop */
|
||||
#define IXOFF 0x1000 /* enable start/stop input control */
|
||||
#define IMAXBEL 0x2000 /* ring bell on input queue full */
|
||||
#define IUTF8 0x4000 /* input is UTF-8 */
|
||||
|
||||
/* ---- c_oflag: output mode flags ---- */
|
||||
|
||||
#define OPOST 0x0001 /* perform output processing */
|
||||
#define OLCUC 0x0002 /* map lowercase to uppercase on output */
|
||||
#define ONLCR 0x0004 /* map NL to CR-NL on output */
|
||||
#define OCRNL 0x0008 /* map CR to NL on output */
|
||||
#define ONOCR 0x0010 /* no CR output at column 0 */
|
||||
#define ONLRET 0x0020 /* NL performs CR function on output */
|
||||
#define OFILL 0x0040 /* use fill characters for delay */
|
||||
#define OFDEL 0x0080 /* fill is DEL, else NUL */
|
||||
#define NLDLY 0x0100 /* newline delay mask */
|
||||
#define NL0 0x0000
|
||||
#define NL1 0x0100
|
||||
#define CRDLY 0x0600 /* carriage-return delay mask */
|
||||
#define CR0 0x0000
|
||||
#define CR1 0x0200
|
||||
#define CR2 0x0400
|
||||
#define CR3 0x0600
|
||||
#define TABDLY 0x1800 /* horizontal-tab delay mask */
|
||||
#define TAB0 0x0000
|
||||
#define TAB1 0x0800
|
||||
#define TAB2 0x1000
|
||||
#define TAB3 0x1800
|
||||
#define XTABS 0x1800 /* TAB3: expand tabs to spaces */
|
||||
#define BSDLY 0x2000 /* backspace delay mask */
|
||||
#define BS0 0x0000
|
||||
#define BS1 0x2000
|
||||
#define VTDLY 0x4000 /* vertical-tab delay mask */
|
||||
#define VT0 0x0000
|
||||
#define VT1 0x4000
|
||||
#define FFDLY 0x8000 /* form-feed delay mask */
|
||||
#define FF0 0x0000
|
||||
#define FF1 0x8000
|
||||
|
||||
/* ---- c_cflag: control mode flags ---- */
|
||||
|
||||
#define CBAUD 0x100f /* baud-rate mask (includes CBAUDEX) */
|
||||
#define CSIZE 0x0030 /* character-size mask */
|
||||
#define CS5 0x0000 /* 5 bits */
|
||||
#define CS6 0x0010 /* 6 bits */
|
||||
#define CS7 0x0020 /* 7 bits */
|
||||
#define CS8 0x0030 /* 8 bits */
|
||||
#define CSTOPB 0x0040 /* two stop bits, else one */
|
||||
#define CREAD 0x0080 /* enable receiver */
|
||||
#define PARENB 0x0100 /* enable parity */
|
||||
#define PARODD 0x0200 /* odd parity, else even */
|
||||
#define HUPCL 0x0400 /* hang up on last close */
|
||||
#define CLOCAL 0x0800 /* ignore modem status lines */
|
||||
#define CBAUDEX 0x1000 /* extended baud-rate mask (above B38400) */
|
||||
#define CRTSCTS 0x80000000 /* enable RTS/CTS flow control */
|
||||
|
||||
/* ---- c_lflag: local mode flags ---- */
|
||||
|
||||
#define ISIG 0x0001 /* enable signals */
|
||||
#define ICANON 0x0002 /* canonical mode (line-buffered) */
|
||||
#define XCASE 0x0004 /* canonical uppercase/lowercase presentation */
|
||||
#define ECHO 0x0008 /* enable echo */
|
||||
#define ECHOE 0x0010 /* echo erase as backspace */
|
||||
#define ECHOK 0x0020 /* echo KILL */
|
||||
#define ECHONL 0x0040 /* echo NL */
|
||||
#define NOFLSH 0x0080 /* disable flush after interrupt/quit/suspend */
|
||||
#define TOSTOP 0x0100 /* send SIGTTOU on background output */
|
||||
#define ECHOCTL 0x0200 /* echo control characters as ^X */
|
||||
#define ECHOPRT 0x0400 /* echo erase as backspace-print-backspace */
|
||||
#define ECHOKE 0x0800 /* BSD: erase entire line on KILL */
|
||||
#define FLUSHO 0x1000 /* output is being flushed */
|
||||
#define PENDIN 0x4000 /* reprint pending input at next read */
|
||||
#define IEXTEN 0x8000 /* enable implementation-defined input processing */
|
||||
#define EXTPROC 0x10000 /* external processing */
|
||||
|
||||
/* ---- Baud rates (kernel CBAUD encodings — see the header comment) ---- */
|
||||
|
||||
#define B0 0x00000000 /* hang up */
|
||||
#define B50 0x00000001
|
||||
#define B75 0x00000002
|
||||
#define B110 0x00000003
|
||||
#define B134 0x00000004
|
||||
#define B150 0x00000005
|
||||
#define B200 0x00000006
|
||||
#define B300 0x00000007
|
||||
#define B600 0x00000008
|
||||
#define B1200 0x00000009
|
||||
#define B1800 0x0000000a
|
||||
#define B2400 0x0000000b
|
||||
#define B4800 0x0000000c
|
||||
#define B9600 0x0000000d
|
||||
#define B19200 0x0000000e
|
||||
#define B38400 0x0000000f
|
||||
#define B57600 0x00001001 /* CBAUDEX | 1 */
|
||||
#define B115200 0x00001002
|
||||
#define B230400 0x00001003
|
||||
#define B460800 0x00001004
|
||||
#define B500000 0x00001005
|
||||
#define B576000 0x00001006
|
||||
#define B921600 0x00001007
|
||||
#define B1000000 0x00001008
|
||||
#define B1152000 0x00001009
|
||||
#define B1500000 0x0000100a
|
||||
#define B2000000 0x0000100b
|
||||
#define B2500000 0x0000100c
|
||||
#define B3000000 0x0000100d
|
||||
#define B3500000 0x0000100e
|
||||
#define B4000000 0x0000100f
|
||||
|
||||
/* ---- tcsetattr optional_actions ---- */
|
||||
|
||||
#define TCSANOW 0 /* change immediately */
|
||||
#define TCSADRAIN 1 /* change after output drains */
|
||||
#define TCSAFLUSH 2 /* drain output and discard input */
|
||||
|
||||
/* ---- tcflush queue_selector ---- */
|
||||
|
||||
#define TCIFLUSH 0 /* discard data received but not yet read */
|
||||
#define TCOFLUSH 1 /* discard data written but not yet transmitted */
|
||||
#define TCIOFLUSH 2 /* discard both */
|
||||
|
||||
/* ---- tcflow action ---- */
|
||||
|
||||
#define TCOOFF 0 /* suspend output */
|
||||
#define TCOON 1 /* restart suspended output */
|
||||
#define TCIOFF 2 /* transmit a STOP character */
|
||||
#define TCION 3 /* transmit a START character */
|
||||
|
||||
/* ---- Level 1 (POSIX base) ---- */
|
||||
|
||||
/*
|
||||
* Store the terminal attributes of the terminal open on fildes into
|
||||
* *termios_p. Return 0 on success, or -1 with errno set (ENOTTY when
|
||||
* fildes is not a terminal, EBADF on a bad descriptor).
|
||||
*/
|
||||
int
|
||||
tcgetattr(int fildes, struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Set the terminal attributes of the terminal open on fildes from
|
||||
* *termios_p, applying optional_actions (TCSANOW apply immediately,
|
||||
* TCSADRAIN wait for output to drain, TCSAFLUSH drain and then discard
|
||||
* unread input). Return 0 on success, or -1 with errno set (EINVAL for an
|
||||
* invalid optional_actions, ENOTTY/EBADF as for tcgetattr).
|
||||
*/
|
||||
int
|
||||
tcsetattr(int fildes, int optional_actions, const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Transmit a break condition on the terminal open on fildes. The duration
|
||||
* argument is ignored (POSIX leaves a nonzero duration
|
||||
* implementation-defined). Return 0 on success or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
tcsendbreak(int fildes, int duration);
|
||||
|
||||
/*
|
||||
* Wait until all output written to the terminal open on fildes has been
|
||||
* transmitted. Return 0 on success or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
tcdrain(int fildes);
|
||||
|
||||
/*
|
||||
* Discard data written but not yet transmitted (TCOFLUSH), data received
|
||||
* but not yet read (TCIFLUSH), or both (TCIOFLUSH) on the terminal open on
|
||||
* fildes. Return 0 on success or -1 with errno set (EINVAL for an invalid
|
||||
* queue_selector).
|
||||
*/
|
||||
int
|
||||
tcflush(int fildes, int queue_selector);
|
||||
|
||||
/*
|
||||
* Suspend or restart transmission (TCOOFF/TCOON) or transmit a STOP/START
|
||||
* character (TCIOFF/TCION) on the terminal open on fildes. Return 0 on
|
||||
* success or -1 with errno set (EINVAL for an invalid action).
|
||||
*/
|
||||
int
|
||||
tcflow(int fildes, int action);
|
||||
|
||||
/*
|
||||
* Return the input baud rate stored in *termios_p (a B* constant), as
|
||||
* encoded in the c_cflag CIBAUD field, or B0 when the input rate is not
|
||||
* separately stored (the kernel then treats it as equal to the output
|
||||
* rate, and cfsetispeed with B0 selects exactly that).
|
||||
*/
|
||||
speed_t
|
||||
cfgetispeed(const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Return the output baud rate stored in *termios_p (a B* constant), as
|
||||
* encoded in the c_cflag CBAUD field.
|
||||
*/
|
||||
speed_t
|
||||
cfgetospeed(const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Store speed as the input baud rate of *termios_p. Accept any value that
|
||||
* fits the CBAUD encoding (B0 means "input rate follows the output rate");
|
||||
* an out-of-range speed yields -1 with errno EINVAL.
|
||||
*/
|
||||
int
|
||||
cfsetispeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
/*
|
||||
* Store speed as the output baud rate of *termios_p. Accept any value that
|
||||
* fits the CBAUD encoding (see cfsetispeed); an out-of-range speed yields
|
||||
* -1 with errno EINVAL.
|
||||
*/
|
||||
int
|
||||
cfsetospeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* Level 2 (muslmimic): BSD/glibc extensions and XSI [CX] additions. */
|
||||
|
||||
/*
|
||||
* Set *termios_p to the "raw" mode of the old Version-7 terminal driver:
|
||||
* input available character by character, echoing and all special
|
||||
* processing of input and output characters disabled (clear IGNBRK BRKINT
|
||||
* PARMRK ISTRIP INLCR IGNCR ICRNL IXON, clear OPOST, clear ECHO ECHONL
|
||||
* ICANON ISIG IEXTEN, select CS8 and no parity). Does not report errors.
|
||||
*/
|
||||
void
|
||||
cfmakeraw(struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Store speed as both the input and the output baud rate of *termios_p
|
||||
* (cfsetospeed then cfsetispeed with the same value). Return 0 on success
|
||||
* or -1 with errno EINVAL for an out-of-range speed.
|
||||
*/
|
||||
int
|
||||
cfsetspeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
/*
|
||||
* Return the session id of the session that has the terminal open on
|
||||
* fildes as its controlling terminal, or -1 with errno set when fildes is
|
||||
* not a controlling terminal (ENOTTY).
|
||||
*/
|
||||
pid_t
|
||||
tcgetsid(int fildes);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_TERMIOS_H */
|
||||
+64
-7
@@ -4,15 +4,72 @@
|
||||
/*
|
||||
* vlibc — <tgmath.h>.
|
||||
*
|
||||
* Type-generic math. STUB: the type-generic dispatch over <math.h> is
|
||||
* IMPLEMENTED by the first math todo (#39), not here. This stub exists so
|
||||
* that including <tgmath.h> alongside the other headers compiles cleanly
|
||||
* today; it deliberately defines no type-generic macros until #39 lands
|
||||
* the <math.h> function inventory it dispatches to.
|
||||
* Type-generic math (C23 7.25). Each macro inspects the type of its
|
||||
* controlling expression and dispatches to the matching precision of the
|
||||
* real <math.h> function: a float argument selects the `...f` variant, a
|
||||
* long double argument the `...l` variant, and everything else (double,
|
||||
* any integer type, and the _Complex types, whose imaginary part is
|
||||
* dropped by the usual argument conversion) the unsuffixed double
|
||||
* variant. The controlling expression is the first floating argument, so
|
||||
* the two-argument forms dispatch on x and require y to be convertible to
|
||||
* x's type.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
* This header is ISO C core and is present in every profile. The
|
||||
* classification macros (fpclassify/isnan/isinf/isfinite/isnormal/signbit)
|
||||
* need no dispatch of their own — <math.h> already defines them over
|
||||
* GCC's type-generic builtins, so including <math.h> below makes them
|
||||
* work unchanged for every real and complex type.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
#include <math.h>
|
||||
|
||||
/* fabs */
|
||||
#define fabs(x) _Generic((x), float: fabsf, long double: fabsl, default: fabs)(x)
|
||||
|
||||
/* copysign */
|
||||
#define copysign(x, y) \
|
||||
_Generic((x), float: copysignf, long double: copysignl, default: copysign)(x, y)
|
||||
|
||||
/* rounding toward zero / ±Inf / current mode (floor, ceil, trunc, round,
|
||||
* rint, nearbyint) */
|
||||
#define floor(x) _Generic((x), float: floorf, long double: floorl, default: floor)(x)
|
||||
#define ceil(x) _Generic((x), float: ceilf, long double: ceill, default: ceil)(x)
|
||||
#define trunc(x) _Generic((x), float: truncf, long double: truncl, default: trunc)(x)
|
||||
#define round(x) _Generic((x), float: roundf, long double: roundl, default: round)(x)
|
||||
#define rint(x) _Generic((x), float: rintf, long double: rintl, default: rint)(x)
|
||||
#define nearbyint(x) \
|
||||
_Generic((x), float: nearbyintf, long double: nearbyintl, default: nearbyint)(x)
|
||||
|
||||
/* integer-returning rounding (lrint, llrint, lround, llround) */
|
||||
#define lrint(x) _Generic((x), float: lrintf, long double: lrintl, default: lrint)(x)
|
||||
#define llrint(x) _Generic((x), float: llrintf, long double: llrintl, default: llrint)(x)
|
||||
#define lround(x) _Generic((x), float: lroundf, long double: lroundl, default: lround)(x)
|
||||
#define llround(x) _Generic((x), float: llroundf, long double: llroundl, default: llround)(x)
|
||||
|
||||
/* fraction/exponent splitting (frexp, modf) and exponent scaling (ldexp,
|
||||
* scalbn, scalbln): the second argument is an int/long or a pointer to
|
||||
* the first argument's type and follows x's precision automatically */
|
||||
#define frexp(x, e) _Generic((x), float: frexpf, long double: frexpl, default: frexp)(x, e)
|
||||
#define modf(x, i) _Generic((x), float: modff, long double: modfl, default: modf)(x, i)
|
||||
#define ldexp(x, n) _Generic((x), float: ldexpf, long double: ldexpl, default: ldexp)(x, n)
|
||||
#define scalbn(x, n) _Generic((x), float: scalbnf, long double: scalbnl, default: scalbn)(x, n)
|
||||
#define scalbln(x, n) _Generic((x), float: scalblnf, long double: scalblnl, default: scalbln)(x, n)
|
||||
|
||||
/* minimum/maximum/difference */
|
||||
#define fmin(x, y) _Generic((x), float: fminf, long double: fminl, default: fmin)(x, y)
|
||||
#define fmax(x, y) _Generic((x), float: fmaxf, long double: fmaxl, default: fmax)(x, y)
|
||||
#define fdim(x, y) _Generic((x), float: fdimf, long double: fdiml, default: fdim)(x, y)
|
||||
|
||||
/* remainder (fmod, remainder, remquo: the trailing int* follows x's
|
||||
* precision) */
|
||||
#define fmod(x, y) _Generic((x), float: fmodf, long double: fmodl, default: fmod)(x, y)
|
||||
#define remainder(x, y) \
|
||||
_Generic((x), float: remainderf, long double: remainderl, default: remainder)(x, y)
|
||||
#define remquo(x, y, q) \
|
||||
_Generic((x), float: remquof, long double: remquol, default: remquo)(x, y, q)
|
||||
|
||||
/* exponent reading (ilogb returns int, logb returns x's precision) */
|
||||
#define ilogb(x) _Generic((x), float: ilogbf, long double: ilogbl, default: ilogb)(x)
|
||||
#define logb(x) _Generic((x), float: logbf, long double: logbl, default: logb)(x)
|
||||
|
||||
#endif /* VLIBC_TGMATH_H */
|
||||
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
#ifndef VLIBC_TIME_H
|
||||
#define VLIBC_TIME_H
|
||||
|
||||
/*
|
||||
* vlibc — <time.h>.
|
||||
*
|
||||
* Calendar time, clock ids, and interval timers. The ISO C functions
|
||||
* (time, clock, timespec_get, difftime, asctime/ctime/mktime/gmtime/
|
||||
* localtime, strftime) and the POSIX clock_* / timer_* / sleep family are
|
||||
* thin, level-gated wrappers over the Linux time syscalls.
|
||||
*
|
||||
* Level 1 (onlyposix): time, clock, timespec_get, difftime,
|
||||
* clock_gettime/settime/getres/getcpuclockid,
|
||||
* clock_nanosleep, nanosleep, sleep, and the
|
||||
* broken-down-time/formatting declarations
|
||||
* (mktime, gmtime/localtime and _r forms, asctime,
|
||||
* ctime, strftime, tzset).
|
||||
* Level 2 (muslmimic): POSIX interval timers (timer_create/delete/
|
||||
* settime/gettime/getoverrun) and the XSI
|
||||
* conveniences (timegm, strftime_l, usleep,
|
||||
* asctime_r, ctime_r, strptime, tzname/daylight/
|
||||
* timezone).
|
||||
*
|
||||
* The scalar time types (time_t, clock_t, clockid_t, pid_t, timer_t and
|
||||
* useconds_t) come from <sys/types.h>, which this header includes; struct
|
||||
* timespec and struct tm are defined here. timer_t is the <sys/types.h>
|
||||
* opaque pointer type (kernel timer ids are returned through it); struct
|
||||
* sigevent and struct itimerspec stay forward-declared here — their kernel
|
||||
* ABI mirrors are private to the timer implementation (see src/time/
|
||||
* timer.c) and to consumers that define matching structs.
|
||||
*
|
||||
* The clock ids and TIMER_ABSTIME mirror the kernel ABI (x86_64): the
|
||||
* CLOCK_* values are passed to SYS_clock_* and SYS_timer_* unchanged, and
|
||||
* TIMER_ABSTIME is the timer_settime/clock_nanosleep flag bit.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Base argument of timespec_get(); the only base defined by ISO C. */
|
||||
#define TIME_UTC 1
|
||||
|
||||
/* Ticks per second reported by clock() (vlibc always reports 1 MHz). */
|
||||
#define CLOCKS_PER_SEC 1000000
|
||||
|
||||
/* Clock ids (kernel ABI; SYS_clock_* argument). */
|
||||
#define CLOCK_REALTIME 0 /* wall clock since the Epoch */
|
||||
#define CLOCK_MONOTONIC 1 /* monotonic since boot */
|
||||
#define CLOCK_PROCESS_CPUTIME_ID 2 /* CPU time of this process */
|
||||
#define CLOCK_THREAD_CPUTIME_ID 3 /* CPU time of this thread */
|
||||
|
||||
/* Flag for absolute (vs relative) timer expiry. */
|
||||
#define TIMER_ABSTIME 1
|
||||
|
||||
/*
|
||||
* Elapsed time, seconds + nanoseconds. Kernel ABI layout (x86_64): the
|
||||
* wrappers pass this straight to the clock/nanosleep/timer syscalls.
|
||||
*/
|
||||
struct timespec
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
long tv_nsec; /* nanoseconds (0..999999999) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Broken-down civil time. tm_gmtoff and tm_zone are the XSI extensions
|
||||
* (seconds east of UTC / timezone name) carried for glibc compatibility.
|
||||
*/
|
||||
struct tm
|
||||
{
|
||||
int tm_sec; /* seconds after the minute (0..60) */
|
||||
int tm_min; /* minutes after the hour (0..59) */
|
||||
int tm_hour; /* hours since midnight (0..23) */
|
||||
int tm_mday; /* day of the month (1..31) */
|
||||
int tm_mon; /* months since January (0..11) */
|
||||
int tm_year; /* years since 1900 */
|
||||
int tm_wday; /* days since Sunday (0..6) */
|
||||
int tm_yday; /* days since January 1 (0..365) */
|
||||
int tm_isdst; /* daylight-saving flag */
|
||||
long tm_gmtoff; /* seconds east of UTC */
|
||||
const char *tm_zone; /* timezone abbreviation */
|
||||
};
|
||||
|
||||
/* Return the current wall-clock time in seconds; if t is not NULL store it
|
||||
* there too, and return (time_t)-1 with errno set on failure. */
|
||||
time_t
|
||||
time(time_t *t);
|
||||
|
||||
/* Approximate processor time in CLOCKS_PER_SEC units since an arbitrary
|
||||
* epoch, or (clock_t)-1 with errno set on failure. */
|
||||
clock_t
|
||||
clock(void);
|
||||
|
||||
/* Store the current time for base into ts and return base; return 0 if
|
||||
* base is not TIME_UTC or the clock cannot be read. */
|
||||
int
|
||||
timespec_get(struct timespec *ts, int base);
|
||||
|
||||
/* Read the given clock into tp. Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_gettime(clockid_t clockid, struct timespec *tp);
|
||||
|
||||
/* Set the given clock from tp. Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_settime(clockid_t clockid, const struct timespec *tp);
|
||||
|
||||
/* Resolution of the given clock; store it in res when res is not NULL.
|
||||
* Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_getres(clockid_t clockid, struct timespec *res);
|
||||
|
||||
/* Clock id measuring the CPU time of the given process (0 = calling
|
||||
* process). Return 0, or -1 with errno set when the process is unknown. */
|
||||
int
|
||||
clock_getcpuclockid(pid_t pid, clockid_t *clock_id);
|
||||
|
||||
/* Sleep on the given clock. Without TIMER_ABSTIME request is relative and
|
||||
* the sleep restarts from remain on signal interruption. */
|
||||
int
|
||||
clock_nanosleep(clockid_t clockid, int flags, const struct timespec *request,
|
||||
struct timespec *remain);
|
||||
|
||||
/* Sleep request seconds; when interrupted and remain is not NULL the sleep
|
||||
* restarts from the remaining time. Return 0 or -1 with errno set. */
|
||||
int
|
||||
nanosleep(const struct timespec *request, struct timespec *remain);
|
||||
|
||||
/* Sleep seconds (whole seconds; signals can shorten the sleep, which is
|
||||
* then resumed). Return the unslept seconds, normally 0. */
|
||||
unsigned int
|
||||
sleep(unsigned int seconds);
|
||||
|
||||
/* Convert broken-down civil time back to calendar seconds. */
|
||||
time_t
|
||||
mktime(struct tm *tm);
|
||||
|
||||
/* Broken-down local time; localtime uses an internal static buffer.
|
||||
* Return NULL on error. */
|
||||
struct tm *
|
||||
localtime(const time_t *timer);
|
||||
|
||||
/* Broken-down local time into buf. Return buf or NULL on error. */
|
||||
struct tm *
|
||||
localtime_r(const time_t *timer, struct tm *buf);
|
||||
|
||||
/* Broken-down UTC; gmtime uses an internal static buffer. */
|
||||
struct tm *
|
||||
gmtime(const time_t *timer);
|
||||
|
||||
/* Broken-down UTC into buf. Return buf or NULL on error. */
|
||||
struct tm *
|
||||
gmtime_r(const time_t *timer, struct tm *buf);
|
||||
|
||||
/* Fixed-format rendering of broken-down time ("Sun Sep 16 01:03:52 1973"). */
|
||||
char *
|
||||
asctime(const struct tm *tm);
|
||||
|
||||
/* asctime(localtime(timer)); fixed-format wall-clock rendering. */
|
||||
char *
|
||||
ctime(const time_t *timer);
|
||||
|
||||
/* Format broken-down time per format into s (at most maxsize bytes);
|
||||
* return the bytes written (0 when the buffer was too small). */
|
||||
size_t
|
||||
strftime(char *restrict s, size_t maxsize, const char *restrict format,
|
||||
const struct tm *restrict timeptr);
|
||||
|
||||
/* Seconds between two calendar times (b subtracted from a). */
|
||||
double
|
||||
difftime(time_t a, time_t b);
|
||||
|
||||
/* Establish the local timezone from TZ / the system default. */
|
||||
void
|
||||
tzset(void);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): POSIX interval timers and XSI conveniences. */
|
||||
|
||||
/* Opaque kernel-ABI structures; consumers define layout-compatible structs
|
||||
* (see src/time/timer.c). */
|
||||
struct sigevent;
|
||||
struct itimerspec;
|
||||
|
||||
/* Create a per-process interval timer on clockid; the timer id is stored
|
||||
* through timerid. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_create(clockid_t clockid, const struct sigevent *restrict evp, timer_t *restrict timerid);
|
||||
|
||||
/* Destroy the given timer. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_delete(timer_t timerid);
|
||||
|
||||
/* Arm/disarm the timer; when old_value is not NULL the previous setting is
|
||||
* stored there. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_settime(timer_t timerid, int flags, const struct itimerspec *restrict new_value,
|
||||
struct itimerspec *restrict old_value);
|
||||
|
||||
/* Remaining time until the timer expires (and its interval). Return 0 or
|
||||
* -1 with errno set. */
|
||||
int
|
||||
timer_gettime(timer_t timerid, struct itimerspec *curr_value);
|
||||
|
||||
/* Number of timer expirations between the most recent signal delivery and
|
||||
* the one before it; -1 with errno set when there was no pending delivery. */
|
||||
int
|
||||
timer_getoverrun(timer_t timerid);
|
||||
|
||||
/* mktime in UTC rather than local time. */
|
||||
time_t
|
||||
timegm(struct tm *tm);
|
||||
|
||||
/* strftime with an explicit locale object (locale_t is not yet defined;
|
||||
* the locale argument is an opaque pointer). */
|
||||
size_t
|
||||
strftime_l(char *restrict s, size_t maxsize, const char *restrict format,
|
||||
const struct tm *restrict timeptr, void *loc);
|
||||
|
||||
/* Microsecond sleep (whole microseconds; signals resume the sleep).
|
||||
* Return 0 or the unslept microseconds with errno set. */
|
||||
useconds_t
|
||||
usleep(useconds_t usec);
|
||||
|
||||
/* asctime into a caller-provided buffer of at least 26 bytes. */
|
||||
char *
|
||||
asctime_r(const struct tm *tm, char *buf);
|
||||
|
||||
/* ctime into a caller-provided buffer of at least 26 bytes. */
|
||||
char *
|
||||
ctime_r(const time_t *timer, char *buf);
|
||||
|
||||
/* Parse format from s into broken-down time; return the first character
|
||||
* not consumed, or NULL when the format did not match. */
|
||||
char *
|
||||
strptime(const char *restrict s, const char *restrict format, struct tm *restrict tm);
|
||||
|
||||
/* Timezone state maintained by tzset(). */
|
||||
extern char *tzname[2];
|
||||
extern int daylight;
|
||||
extern long timezone;
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_TIME_H */
|
||||
@@ -24,6 +24,31 @@
|
||||
* Level 2 (muslmimic): vfork, setpgrp (obsolescent), setgroups, getpgid,
|
||||
* getsid, tcgetpgrp, tcsetpgrp (XSI).
|
||||
*
|
||||
* System limits and options (todo 32) are declared after the process
|
||||
* block:
|
||||
*
|
||||
* Level 1 (onlyposix): sysconf, pathconf, fpathconf, the _SC_* and _PC_*
|
||||
* configuration keys.
|
||||
* Level 2 (muslmimic): gethostname, sethostname, gethostid (XSI/BSD
|
||||
* system identification — NOT POSIX.1-2008 base),
|
||||
* and the _CS_* keys for confstr().
|
||||
*
|
||||
* The current-working-directory, link/symlink/readlink, path-removal and
|
||||
* *at operations, the terminal-identity and login functions, getopt and
|
||||
* confstr (todo 38) come after the system-limits block:
|
||||
*
|
||||
* Level 1 (onlyposix): getcwd, chdir, fchdir, readlink, symlink, link,
|
||||
* unlink, rmdir, linkat, readlinkat, renameat,
|
||||
* symlinkat, unlinkat, isatty, ttyname, ttyname_r,
|
||||
* getlogin, getlogin_r, getopt plus the optarg/
|
||||
* optind/opterr/optopt globals, and confstr (its
|
||||
* _CS_* key constants are level 2, above).
|
||||
* Level 2 (muslmimic): getwd (obsolescent), chroot, swab (XSI).
|
||||
* basename/dirname live in <libgen.h> and realpath
|
||||
* in <stdlib.h> (their POSIX homes), also XSI.
|
||||
*
|
||||
* uname() and struct utsname live in <sys/utsname.h> (todo 32).
|
||||
*
|
||||
* The open-flag constants (O_RDONLY, O_CREAT, O_CLOEXEC, ...) belong to
|
||||
* <fcntl.h> and are deliberately not defined here; the oflag arguments below
|
||||
* are plain int and take their values from that header. The optional mode
|
||||
@@ -297,9 +322,369 @@ getpgrp(void);
|
||||
pid_t
|
||||
setsid(void);
|
||||
|
||||
/* Level 1 (POSIX base): system limits and options. */
|
||||
|
||||
/*
|
||||
* The configuration keys for sysconf(), pathconf() and fpathconf(), and
|
||||
* (level 2) the _CS_* keys for confstr() (todo 38).
|
||||
*
|
||||
* POSIX defines the _SC_* and _PC_* names and the SEMANTICS of the values
|
||||
* the configuration functions return for them, but NOT their numeric
|
||||
* values: those are private to each C library (glibc numbers them
|
||||
* arbitrarily in <bits/confname.h>, musl in a private table). The values
|
||||
* below are vlibc's OWN stable ABI, assigned in declaration order and never
|
||||
* reused — program source must pass the symbolic constants, never literal
|
||||
* integers. They are deliberately NOT glibc's numbers. Any value outside
|
||||
* these enums is unknown to the configuration functions and yields -1
|
||||
* without errno (the unsupported-key convention, see below).
|
||||
*/
|
||||
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _SC_/_PC_ names */
|
||||
enum
|
||||
{
|
||||
/* Process and file limits (sysconf). */
|
||||
_SC_ARG_MAX = 0, /* max bytes of arguments + environment for exec */
|
||||
_SC_CHILD_MAX, /* max simultaneous processes per real user id */
|
||||
_SC_CLK_TCK, /* clock ticks per second, the times() unit (100) */
|
||||
_SC_NGROUPS_MAX, /* max supplementary group ids */
|
||||
_SC_OPEN_MAX, /* max open file descriptors per process */
|
||||
_SC_PAGESIZE, /* memory page size in bytes (from AT_PAGESZ) */
|
||||
_SC_PAGE_SIZE, /* POSIX synonym of _SC_PAGESIZE */
|
||||
_SC_NPROCESSORS_CONF, /* number of processors configured */
|
||||
_SC_NPROCESSORS_ONLN, /* number of processors online */
|
||||
_SC_PHYS_PAGES, /* number of physical memory pages */
|
||||
_SC_AVPHYS_PAGES, /* number of currently available memory pages */
|
||||
_SC_SYMLOOP_MAX, /* max symlink traversals while resolving a path */
|
||||
_SC_STREAM_MAX, /* max open standard I/O streams (FOPEN_MAX) */
|
||||
_SC_TZNAME_MAX, /* max bytes in a timezone name */
|
||||
_SC_VERSION, /* POSIX.1 version, 200809L */
|
||||
_SC_2_VERSION, /* POSIX.2 version, 200809L */
|
||||
_SC_JOB_CONTROL, /* 1 if job control is supported */
|
||||
_SC_SAVED_IDS, /* 1 if saved set-user/group-id is supported */
|
||||
_SC_HOST_NAME_MAX, /* max bytes in a host name (excluding NUL) */
|
||||
_SC_LOGIN_NAME_MAX, /* max bytes in a login name */
|
||||
_SC_GETPW_R_SIZE_MAX, /* recommended buffer size for getpw*_r */
|
||||
_SC_IOV_MAX, /* max iovec entries in readv/writev */
|
||||
_SC_LINE_MAX, /* max bytes in an input line (POSIX.2) */
|
||||
_SC_RE_DUP_MAX, /* max duplicate regexp interval counts */
|
||||
_SC_COLL_WEIGHTS_MAX, /* max weights for collating elements */
|
||||
_SC_EXPR_NEST_MAX, /* max expression nesting in expr(1) */
|
||||
|
||||
/* POSIX option indicators (sysconf). Values follow the _POSIX_*
|
||||
* convention: a version value means supported, -1 unsupported. */
|
||||
_SC_THREADS, /* _POSIX_THREADS */
|
||||
_SC_SEMAPHORES, /* _POSIX_SEMAPHORES */
|
||||
_SC_SHARED_MEMORY_OBJECTS, /* _POSIX_SHARED_MEMORY_OBJECTS */
|
||||
_SC_MESSAGE_PASSING, /* _POSIX_MESSAGE_PASSING */
|
||||
_SC_MONOTONIC_CLOCK, /* _POSIX_MONOTONIC_CLOCK */
|
||||
_SC_TIMERS, /* _POSIX_TIMERS */
|
||||
_SC_SYNCHRONIZED_IO, /* _POSIX_SYNCHRONIZED_IO */
|
||||
_SC_THREAD_SAFE_FUNCTIONS, /* _POSIX_THREAD_SAFE_FUNCTIONS */
|
||||
_SC_THREAD_ATTR_STACKADDR, /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
_SC_THREAD_ATTR_STACKSIZE, /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
_SC_THREAD_PRIORITY_SCHEDULING, /* _POSIX_THREAD_PRIORITY_SCHEDULING */
|
||||
_SC_THREAD_PRIO_INHERIT, /* _POSIX_THREAD_PRIO_INHERIT */
|
||||
_SC_THREAD_PRIO_PROTECT, /* _POSIX_THREAD_PRIO_PROTECT */
|
||||
_SC_THREAD_PROCESS_SHARED, /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
_SC_REALTIME_SIGNALS, /* _POSIX_REALTIME_SIGNALS */
|
||||
|
||||
/* POSIX.1-2008 option indicators that use plain 1 for supported. */
|
||||
_SC_REGEXP, /* 1 if the RE functions are supported */
|
||||
_SC_SHELL, /* 1 if the shell is supported */
|
||||
_SC_XOPEN_VERSION, /* XSI version, 700 */
|
||||
_SC_XOPEN_UNIX, /* 1 if XSI interfaces are supported */
|
||||
_SC_XOPEN_CRYPT, /* 1 if the crypt function is available */
|
||||
_SC_XOPEN_ENH_I18N, /* 1 if enhanced internationalization is present */
|
||||
_SC_XOPEN_SHM, /* 1 if XSI shared memory is present */
|
||||
_SC_2_C_BIND, /* POSIX.2 C-language binding */
|
||||
_SC_2_C_DEV, /* POSIX.2 C-language development utilities */
|
||||
_SC_2_FORT_DEV, /* POSIX.2 FORTRAN development utilities */
|
||||
_SC_2_FORT_RUN, /* POSIX.2 FORTRAN runtime utilities */
|
||||
_SC_2_LOCALEDEF, /* POSIX.2 locale creation utilities */
|
||||
_SC_2_SW_DEV, /* POSIX.2 software development utilities */
|
||||
_SC_2_UPE /* POSIX.2 user-portability utilities */
|
||||
};
|
||||
|
||||
/*
|
||||
* The _PC_* keys for pathconf() and fpathconf(). Each names a limit or an
|
||||
* option of the FILESYSTEM holding the queried path or descriptor; see the
|
||||
* pathconf() documentation below for the supported keys and their values.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
_PC_LINK_MAX = 0, /* max link count of a file */
|
||||
_PC_MAX_CANON, /* max bytes in a terminal canonical input line */
|
||||
_PC_MAX_INPUT, /* max bytes available on a terminal input queue */
|
||||
_PC_NAME_MAX, /* max bytes in a file name (excluding NUL) */
|
||||
_PC_PATH_MAX, /* max bytes in a pathname (excluding NUL) */
|
||||
_PC_PIPE_BUF, /* max bytes atomically writable to a pipe */
|
||||
_PC_CHOWN_RESTRICTED, /* 1 if chown is restricted to the owner */
|
||||
_PC_NO_TRUNC, /* 1 if overlong file names are an error */
|
||||
_PC_VDISABLE, /* the terminal disable character (0 = none) */
|
||||
_PC_ASYNC_IO, /* 200809L if asynchronous I/O is supported */
|
||||
_PC_SYNC_IO, /* 200809L if synchronized I/O is supported */
|
||||
_PC_PRIO_IO, /* 200809L if prioritized I/O is supported */
|
||||
_PC_SOCK_MAXBUF, /* max bytes storable in a socket buffer */
|
||||
_PC_FILESIZEBITS, /* bits in a file size field */
|
||||
_PC_REC_INCR_XFER_SIZE, /* recommended increment for transfer sizes */
|
||||
_PC_REC_MAX_XFER_SIZE, /* recommended maximum transfer size */
|
||||
_PC_REC_MIN_XFER_SIZE, /* recommended minimum transfer size */
|
||||
_PC_REC_XFER_ALIGN, /* recommended transfer alignment */
|
||||
_PC_ALLOC_SIZE_MIN, /* minimum bytes of a file allocation */
|
||||
_PC_SYMLINK_MAX, /* max bytes in a symbolic link target */
|
||||
_PC_2_SYMLINKS /* 1 if symlinks support the ".." resolution rule */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Query a system configuration value for key name (one of the _SC_* above)
|
||||
* and return it as a long. A negative value means the option is not
|
||||
* supported or the key is unknown; for unsupported options and for any key
|
||||
* outside the _SC_* enum, -1 is returned WITHOUT setting errno (callers
|
||||
* must not rely on errno after -1).
|
||||
*
|
||||
* The supported keys and their vlibc values: _SC_ARG_MAX (2097152),
|
||||
* _SC_CHILD_MAX (65535), _SC_CLK_TCK (100, the fixed x86_64 clock-tick
|
||||
* rate — see the note in src/misc/sysconf.c, todo 35's times() consumes
|
||||
* that same constant), _SC_NGROUPS_MAX (65536), _SC_OPEN_MAX (the current
|
||||
* RLIMIT_NOFILE soft limit, 1024 if it cannot be read), _SC_PAGESIZE and
|
||||
* _SC_PAGE_SIZE (the memory page size from the AT_PAGESZ auxiliary vector
|
||||
* entry, 4096 on x86_64), _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN
|
||||
* (the processor count), _SC_PHYS_PAGES and _SC_AVPHYS_PAGES (physical and
|
||||
* available memory page counts), _SC_SYMLOOP_MAX (40), _SC_STREAM_MAX
|
||||
* (16), _SC_TZNAME_MAX (6), _SC_VERSION and _SC_2_VERSION (200809L),
|
||||
* _SC_JOB_CONTROL, _SC_SAVED_IDS, _SC_REGEXP, _SC_SHELL (1 each),
|
||||
* _SC_HOST_NAME_MAX (64), _SC_LOGIN_NAME_MAX (256), _SC_GETPW_R_SIZE_MAX
|
||||
* (1024), _SC_IOV_MAX (1024), _SC_LINE_MAX (2048), _SC_RE_DUP_MAX (32767),
|
||||
* _SC_COLL_WEIGHTS_MAX (255), _SC_EXPR_NEST_MAX (32), _SC_XOPEN_VERSION
|
||||
* (700), _SC_XOPEN_UNIX, _SC_XOPEN_CRYPT, _SC_XOPEN_ENH_I18N, _SC_XOPEN_SHM
|
||||
* (1 each), and the thread/option version keys (_SC_THREADS ... _SC_2_UPE,
|
||||
* 200809L each).
|
||||
*/
|
||||
long
|
||||
sysconf(int name);
|
||||
|
||||
/*
|
||||
* Query a configurable limit or option of the filesystem holding path (the
|
||||
* _PC_* keys above) and return it as a long; -1 with errno set when path
|
||||
* cannot be examined. Fixed values: _PC_LINK_MAX (32000), _PC_MAX_CANON
|
||||
* and _PC_MAX_INPUT (255), _PC_PATH_MAX (4096), _PC_PIPE_BUF (4096),
|
||||
* _PC_CHOWN_RESTRICTED, _PC_NO_TRUNC and _PC_2_SYMLINKS (1), _PC_VDISABLE
|
||||
* (0), _PC_FILESIZEBITS (64), and _PC_REC_MIN_XFER_SIZE,
|
||||
* _PC_REC_XFER_ALIGN and _PC_ALLOC_SIZE_MIN (the filesystem block size).
|
||||
* _PC_NAME_MAX is the filesystem's f_namelen (255 on ext4/xfs). For keys
|
||||
* whose option the filesystem does not provide (_PC_ASYNC_IO, _PC_SYNC_IO,
|
||||
* _PC_PRIO_IO, _PC_SOCK_MAXBUF, _PC_REC_INCR_XFER_SIZE,
|
||||
* _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX) and for any key outside the
|
||||
* _PC_* enum, -1 is returned WITHOUT setting errno.
|
||||
*/
|
||||
long
|
||||
pathconf(const char *path, int name);
|
||||
|
||||
/*
|
||||
* Like pathconf(), but the filesystem is the one holding the open
|
||||
* descriptor fildes.
|
||||
*/
|
||||
long
|
||||
fpathconf(int fildes, int name);
|
||||
|
||||
/* Level 1 (POSIX base): current directory, links, and path operations. */
|
||||
|
||||
/*
|
||||
* Store the absolute pathname of the current working directory into buf.
|
||||
* When buf is NULL, the path is returned in a malloc'd buffer instead
|
||||
* (size is then only a hint: 0 grows from a default of 128 bytes, and
|
||||
* the buffer is enlarged as the kernel reports ERANGE); the caller must
|
||||
* free it. Return buf (or the malloc'd buffer), or NULL with errno set
|
||||
* (EINVAL for a non-NULL buf with size 0).
|
||||
*/
|
||||
char *
|
||||
getcwd(char *buf, size_t size);
|
||||
|
||||
/*
|
||||
* Change the current working directory to path (chdir) or to the
|
||||
* directory referred to by the open descriptor fildes (fchdir). Return
|
||||
* 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
chdir(const char *path);
|
||||
|
||||
int
|
||||
fchdir(int fildes);
|
||||
|
||||
/*
|
||||
* Create a symbolic link: path2 is created as a link whose contents are
|
||||
* the string path1 (which need not exist). Return 0, or -1 with errno
|
||||
* set.
|
||||
*/
|
||||
int
|
||||
symlink(const char *path1, const char *path2);
|
||||
|
||||
/*
|
||||
* Like symlink(), but the link is created in the directory named by fd
|
||||
* when path2 is relative (use AT_FDCWD for the current working
|
||||
* directory). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
symlinkat(const char *path1, int fd, const char *path2);
|
||||
|
||||
/*
|
||||
* Read the contents of the symbolic link path into buf (bufsize bytes,
|
||||
* no NUL is appended) and return the number of bytes placed in buf, 0
|
||||
* for an empty target, or -1 with errno set. The buffer is not
|
||||
* terminated: callers sized for bufsize + 1 append the NUL themselves.
|
||||
*/
|
||||
ssize_t
|
||||
readlink(const char *restrict path, char *restrict buf, size_t bufsize);
|
||||
|
||||
/*
|
||||
* Like readlink(), but path is resolved in the directory named by fd
|
||||
* when relative. Return the number of bytes placed in buf, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
ssize_t
|
||||
readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize);
|
||||
|
||||
/*
|
||||
* Create a hard link: new is created as a second name for the file old
|
||||
* (which must not be a directory and must be on the same filesystem).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
link(const char *old, const char *new);
|
||||
|
||||
/*
|
||||
* Like link(), but old and new are resolved against the directories
|
||||
* named by fd1 and fd2 when relative (AT_FDCWD selects the current
|
||||
* working directory) and flag may hold AT_SYMLINK_FOLLOW from <fcntl.h>.
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
linkat(int fd1, const char *old, int fd2, const char *new, int flag);
|
||||
|
||||
/*
|
||||
* Remove the name path (a hard link; symbolic links are removed, not
|
||||
* followed). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
unlink(const char *path);
|
||||
|
||||
/*
|
||||
* Like unlink(), but path is resolved in the directory named by fd when
|
||||
* relative and flag may hold AT_REMOVEDIR from <fcntl.h> (making it
|
||||
* remove an empty directory instead). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
unlinkat(int fd, const char *path, int flag);
|
||||
|
||||
/*
|
||||
* Remove the empty directory path. Return 0, or -1 with errno set
|
||||
* (ENOTEMPTY/EEXIST when the directory is not empty).
|
||||
*/
|
||||
int
|
||||
rmdir(const char *path);
|
||||
|
||||
/*
|
||||
* Rename old to new within the filesystem: old is resolved against fd1
|
||||
* and new against fd2 (AT_FDCWD for the current working directory). Both
|
||||
* names may be directories. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
renameat(int fd1, const char *old, int fd2, const char *new);
|
||||
|
||||
/* Level 1 (POSIX base): terminal identity, login name, getopt, confstr. */
|
||||
|
||||
/*
|
||||
* Test whether fildes refers to a terminal. Return 1 when it does, and
|
||||
* 0 otherwise (errno may be set — e.g. EBADF — but the caller must not
|
||||
* rely on it after a 0 return).
|
||||
*/
|
||||
int
|
||||
isatty(int fildes);
|
||||
|
||||
/*
|
||||
* Return the pathname of the terminal open on fildes in a shared static
|
||||
* buffer (overwritten by the next call), or NULL with errno set when
|
||||
* fildes is not a terminal. ttyname_r stores the path in the caller's
|
||||
* name buffer instead and returns 0, or an error NUMBER (ENOTTY when
|
||||
* fildes is not a terminal, ERANGE when the name does not fit in
|
||||
* namesize).
|
||||
*/
|
||||
char *
|
||||
ttyname(int fildes);
|
||||
|
||||
int
|
||||
ttyname_r(int fildes, char *name, size_t namesize);
|
||||
|
||||
/*
|
||||
* Return the login name of the user associated with the calling process
|
||||
* (the owner of its controlling terminal) in a shared static buffer, or
|
||||
* NULL with errno set when the name cannot be determined. getlogin_r
|
||||
* stores the name in the caller's buffer and returns 0, or an error
|
||||
* number (ERANGE when the name does not fit in namesize).
|
||||
*/
|
||||
char *
|
||||
getlogin(void);
|
||||
|
||||
int
|
||||
getlogin_r(char *name, size_t namesize);
|
||||
|
||||
/*
|
||||
* Command-line option parsing (POSIX.1-2008). getopt returns the next
|
||||
* option character from argv per the option characters in optstring; a
|
||||
* character followed by ':' in optstring takes an argument (delivered in
|
||||
* optarg). A leading ':' in optstring makes missing-argument errors
|
||||
* return ':' silently instead of '?' with a diagnostic to stderr.
|
||||
* Return -1 when all options have been consumed: optind then points at
|
||||
* the first non-option argument, and "--" terminates the scan.
|
||||
*/
|
||||
int
|
||||
getopt(int argc, char *const argv[], const char *optstring);
|
||||
|
||||
/* The getopt option state (POSIX.1-2008 base). */
|
||||
extern char *optarg; /* the argument of the option just returned */
|
||||
extern int optind; /* index of the next argument to scan (init 1) */
|
||||
extern int opterr; /* nonzero: write diagnostics to stderr (init 1) */
|
||||
extern int optopt; /* the option character that caused an error */
|
||||
|
||||
/*
|
||||
* Query the implementation-defined string value of configuration name
|
||||
* (one of the _CS_* keys above). When buf is not NULL and len is large
|
||||
* enough, the value (NUL-terminated) is copied into buf; the return
|
||||
* value is the length of the value including the terminating NUL, so a
|
||||
* return greater than len means the buffer was too small and nothing was
|
||||
* copied. For an unknown name, 0 is returned and errno is set to EINVAL.
|
||||
*/
|
||||
size_t
|
||||
confstr(int name, char *buf, size_t len);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): Linux extensions + XSI + obsolescent. */
|
||||
|
||||
/*
|
||||
* Store the current working directory into pathname, which must hold at
|
||||
* least PATH_MAX bytes. Return pathname, or NULL with errno set.
|
||||
* Obsolescent: prefer getcwd().
|
||||
*/
|
||||
char *
|
||||
getwd(char *pathname);
|
||||
|
||||
/*
|
||||
* Change the process root directory to path (used with chdir by
|
||||
* chroot-style confinement). Requires privilege. Return 0, or -1 with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
int
|
||||
chroot(const char *path);
|
||||
|
||||
/*
|
||||
* Copy nbytes bytes from from to to, swapping each adjacent pair of
|
||||
* bytes (the odd byte of an odd count is not copied). Used for historic
|
||||
* byte-order conversion; returns nothing. XSI.
|
||||
*/
|
||||
void
|
||||
swab(const void *restrict from, void *restrict to, ssize_t nbytes);
|
||||
|
||||
/*
|
||||
* Like dup2(), but with descriptor flags (O_CLOEXEC from <fcntl.h>) applied
|
||||
* atomically; return fildes2, or -1 with errno set. Linux-specific.
|
||||
@@ -370,6 +755,58 @@ tcgetpgrp(int fildes);
|
||||
|
||||
int
|
||||
tcsetpgrp(int fildes, pid_t pgid);
|
||||
|
||||
/* Level 2 (muslmimic): system identification (XSI / BSD, not POSIX base). */
|
||||
|
||||
/*
|
||||
* The _CS_* keys for confstr() (todo 38, which owns the function and its
|
||||
* string results). Values are vlibc-local ABI like the _SC_ and _PC_ keys
|
||||
* above; they are defined here now so todo 38 only implements confstr()
|
||||
* itself.
|
||||
*/
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _CS_ names */
|
||||
enum
|
||||
{
|
||||
_CS_PATH = 0, /* value of PATH (always a usable default) */
|
||||
_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, /* 1 if the V6 width-restricted envs exist */
|
||||
_CS_POSIX_V7_WIDTH_RESTRICTED_ENVS, /* 1 if the V7 width-restricted envs exist */
|
||||
_CS_LFS_CFLAGS, /* cpp/cflags for large-file support */
|
||||
_CS_LFS_LDFLAGS, /* ld flags for large-file support */
|
||||
_CS_LFS_LIBS, /* libraries for large-file support */
|
||||
_CS_LFS_LINTFLAGS, /* lint flags for large-file support */
|
||||
_CS_LFS64_CFLAGS, /* cpp/cflags for large-file64 support */
|
||||
_CS_LFS64_LDFLAGS, /* ld flags for large-file64 support */
|
||||
_CS_LFS64_LIBS, /* libraries for large-file64 support */
|
||||
_CS_LFS64_LINTFLAGS, /* lint flags for large-file64 support */
|
||||
_CS_GNU_LIBC_VERSION, /* "glibc X.Y" (vlibc extension) */
|
||||
_CS_GNU_LIBPTHREAD_VERSION /* "NPTL X.Y" (vlibc extension) */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Store the NUL-terminated host name of the current machine into name,
|
||||
* sized len bytes. Return 0 on success; if the name (including its NUL
|
||||
* terminator) does not fit in len bytes, nothing is written beyond the
|
||||
* fit and -1 is returned with errno set to ENAMETOOLONG. XSI.
|
||||
*/
|
||||
int
|
||||
gethostname(char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Set the kernel's host name from name (len bytes, no NUL required).
|
||||
* Requires privilege. Return 0, or -1 with errno set. BSD, not POSIX.
|
||||
*/
|
||||
int
|
||||
sethostname(const char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Return the 32-bit host identifier of the current machine. vlibc returns
|
||||
* 0 (musl's behavior): the historic value was derived from the host's
|
||||
* primary IP address via gethostbyname, which the modern library does not
|
||||
* perform. [OB] — obsolescent in POSIX.
|
||||
*/
|
||||
long
|
||||
gethostid(void);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -0,0 +1,565 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <grp.h>
|
||||
|
||||
/*
|
||||
* vlibc — group database (todo 37).
|
||||
*
|
||||
* getgrnam/getgrgid scan /etc/group with a fresh fopen per call (no
|
||||
* persistent cursor); getgrent walks one FILE kept open between calls,
|
||||
* which setgrent rewinds and endgrent closes. Every line is read with
|
||||
* getline, one at a time — the whole file is never cached. fgetgrent is
|
||||
* the XSI variant that parses the next record from an arbitrary caller-
|
||||
* supplied stream; it is gated at level 2 (whole-level-gate rule: XSI is
|
||||
* not POSIX.1-2008 base), everything else here is level 1.
|
||||
*
|
||||
* Storage model: two static buffers back every non-reentrant result. A
|
||||
* successful call returns a struct whose string members and gr_mem
|
||||
* entries point into a private result copy; the getline scan that found
|
||||
* the match ran in a separate buffer, so a caller may feed the returned
|
||||
* record's own name back into a later lookup (the
|
||||
* getgrnam(getgrgid(x)->gr_name) round-trip) without the scan clobbering
|
||||
* its key. The result copy is replaced by the next call to any of
|
||||
* getgrnam/getgrgid/getgrent/fgetgrent (or a realloc of the copy),
|
||||
* matching POSIX result-lifetime rules. The _r forms never use this
|
||||
* static storage for their result: the caller's struct and buffer
|
||||
* receive the record, and the caller's *result is how the outcome is
|
||||
* reported.
|
||||
*
|
||||
* Record syntax: name:passwd:gid:member,member,... — the first three
|
||||
* fields are ':'-separated and the member list is the comma-separated
|
||||
* tail (empty when the group has no members). Blank or comment-style
|
||||
* lines and records whose gid is not a decimal number are skipped like
|
||||
* malformed input.
|
||||
*/
|
||||
|
||||
/* The database path; not overridable (no NSS, no shadow, POSIX only). */
|
||||
#define GRP_PATH "/etc/group"
|
||||
|
||||
/* Non-reentrant result: struct, the private copy it points into, and the
|
||||
* member pointer array whose entries point into that same copy. */
|
||||
static struct group gr_result;
|
||||
static char *gr_out;
|
||||
static size_t gr_out_cap;
|
||||
static char **gr_mem;
|
||||
static size_t gr_mem_cap;
|
||||
|
||||
/* The getline scan buffer shared by every lookup and stream walk. */
|
||||
static char *gr_line;
|
||||
static size_t gr_cap;
|
||||
|
||||
/* The persistent getgrent stream; NULL when closed. */
|
||||
static FILE *gr_stream;
|
||||
|
||||
/*
|
||||
* Parse the leading digit string of s as an unsigned id. Accepts only
|
||||
* plain decimal digits and rejects empty fields and overflow; returns 0
|
||||
* on success.
|
||||
*/
|
||||
static int
|
||||
gr_parse_id(const char *s, unsigned *out)
|
||||
{
|
||||
unsigned long v = 0;
|
||||
const char *p;
|
||||
|
||||
if (*s == '\0')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (p = s; *p != '\0'; p++)
|
||||
{
|
||||
if (*p < '0' || *p > '9')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
v = v * 10 + (unsigned long)(*p - '0');
|
||||
if (v > 0xffffffffUL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*out = (unsigned)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grow the static member pointer array to hold cnt entries plus the
|
||||
* terminating NULL; return 0 on success, -1 when realloc fails. */
|
||||
static int
|
||||
gr_reserve_mem(size_t cnt)
|
||||
{
|
||||
char **newmem;
|
||||
size_t ncap;
|
||||
|
||||
if (gr_mem_cap >= cnt + 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ncap = gr_mem_cap == 0 ? 8 : gr_mem_cap * 2;
|
||||
while (ncap < cnt + 1)
|
||||
{
|
||||
ncap *= 2;
|
||||
}
|
||||
newmem = (char **)realloc((void *)gr_mem, ncap * sizeof(*newmem));
|
||||
if (newmem == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gr_mem = newmem;
|
||||
gr_mem_cap = ncap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the comma-separated member tail in place: every name becomes a
|
||||
* NUL-terminated substring of line, member[i] points at the i-th one and
|
||||
* member[cnt] is NULL. An empty tail yields a single NULL entry. The
|
||||
* parsed group's gr_mem is set to the shared member array.
|
||||
*/
|
||||
static int
|
||||
gr_split_members(char *tail, struct group *gr)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
char *p;
|
||||
|
||||
if (*tail != '\0')
|
||||
{
|
||||
cnt = 1;
|
||||
for (p = tail; *p != '\0'; p++)
|
||||
{
|
||||
if (*p == ',')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gr_reserve_mem(cnt) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
p = tail;
|
||||
for (size_t i = 0; i < cnt; i++)
|
||||
{
|
||||
char *comma = strchr(p, ',');
|
||||
|
||||
if (comma != NULL)
|
||||
{
|
||||
*comma = '\0';
|
||||
}
|
||||
gr_mem[i] = p;
|
||||
p = comma == NULL ? p + strlen(p) : comma + 1;
|
||||
}
|
||||
gr_mem[cnt] = NULL;
|
||||
gr->gr_mem = gr_mem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the line (newline already stripped) into its fields in place,
|
||||
* replacing the first three ':' separators with NULs. On success the
|
||||
* struct fields point into line and the member list has been split in
|
||||
* place, and 0 is returned; a record with fewer than three separators or
|
||||
* a non-numeric gid yields -1.
|
||||
*/
|
||||
static int
|
||||
gr_split(struct group *gr, char *line)
|
||||
{
|
||||
char *fields[4];
|
||||
char *p;
|
||||
unsigned gid;
|
||||
int i;
|
||||
|
||||
p = line;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
fields[i] = p;
|
||||
p = strchr(p, ':');
|
||||
if (p == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
p++;
|
||||
}
|
||||
fields[3] = p;
|
||||
if (gr_parse_id(fields[2], &gid) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (gr_split_members(fields[3], gr) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gr->gr_name = fields[0];
|
||||
gr->gr_passwd = fields[1];
|
||||
gr->gr_gid = (gid_t)gid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Strip a trailing newline (and CR) from the line just read by getline. */
|
||||
static void
|
||||
gr_chomp(char *line, ssize_t len)
|
||||
{
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
||||
{
|
||||
line[--len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make line — a chomped, already-split record in the scan buffer — the
|
||||
* non-reentrant result: copy it into the private result buffer and
|
||||
* re-split it there, so the returned struct's strings and member
|
||||
* pointers reference storage the scanner never touches again. Returns
|
||||
* the result struct, or NULL if the result copy could not be grown.
|
||||
*/
|
||||
static struct group *
|
||||
gr_publish(char *line)
|
||||
{
|
||||
size_t len = strlen(line) + 1;
|
||||
|
||||
if (len > gr_out_cap)
|
||||
{
|
||||
char *nb = (char *)realloc(gr_out, len);
|
||||
|
||||
if (nb == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_out = nb;
|
||||
gr_out_cap = len;
|
||||
}
|
||||
strcpy(gr_out, line);
|
||||
if (gr_split(&gr_result, gr_out) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &gr_result;
|
||||
}
|
||||
|
||||
/* Match predicate over a parsed record, keyed by name or gid. */
|
||||
typedef int (*gr_matcher)(const struct group *gr, const void *key);
|
||||
|
||||
static int
|
||||
gr_match_name(const struct group *gr, const void *key)
|
||||
{
|
||||
return strcmp(gr->gr_name, (const char *)key) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gr_match_gid(const struct group *gr, const void *key)
|
||||
{
|
||||
return gr->gr_gid == *(const gid_t *)key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-reentrant lookup core: open the database, scan with getline until
|
||||
* the predicate matches, and hand back a pointer into the shared static
|
||||
* line buffer. NULL means "no such entry" (or an unopenable database).
|
||||
*/
|
||||
static struct group *
|
||||
gr_find(gr_matcher match, const void *key)
|
||||
{
|
||||
FILE *f = fopen(GRP_PATH, "r");
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0 && match(&cur, key))
|
||||
{
|
||||
(void)fclose(f);
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
(void)fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant serializer: copy the parsed record into the caller's struct
|
||||
* and buffer. The string bytes go first; the NULL-terminated member
|
||||
* pointer array follows on a pointer-aligned boundary. Returns 0 on
|
||||
* success, or ERANGE (as the POSIX _r functions return, never touching
|
||||
* errno) when the record needs more than bufsize bytes.
|
||||
*/
|
||||
static int
|
||||
gr_store(const struct group *in, struct group *gr, char *buf, size_t bufsize)
|
||||
{
|
||||
size_t nmem = 0;
|
||||
size_t strneed;
|
||||
size_t ptoff;
|
||||
char *s;
|
||||
char **mem;
|
||||
size_t i;
|
||||
|
||||
while (in->gr_mem != NULL && in->gr_mem[nmem] != NULL)
|
||||
{
|
||||
nmem++;
|
||||
}
|
||||
strneed = strlen(in->gr_name) + 1;
|
||||
strneed += strlen(in->gr_passwd) + 1;
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
strneed += strlen(in->gr_mem[i]) + 1;
|
||||
}
|
||||
ptoff = (strneed + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
|
||||
if (bufsize < ptoff + (nmem + 1) * sizeof(char *))
|
||||
{
|
||||
return ERANGE;
|
||||
}
|
||||
gr->gr_gid = in->gr_gid;
|
||||
mem = (char **)(void *)(buf + ptoff);
|
||||
gr->gr_mem = mem;
|
||||
s = buf;
|
||||
gr->gr_name = s;
|
||||
s += strlen(in->gr_name) + 1;
|
||||
gr->gr_passwd = s;
|
||||
s += strlen(in->gr_passwd) + 1;
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
size_t len = strlen(in->gr_mem[i]) + 1;
|
||||
|
||||
gr->gr_mem[i] = s;
|
||||
s += len;
|
||||
}
|
||||
gr->gr_mem[nmem] = NULL;
|
||||
strcpy(gr->gr_name, in->gr_name);
|
||||
strcpy(gr->gr_passwd, in->gr_passwd);
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
size_t len = strlen(in->gr_mem[i]) + 1;
|
||||
|
||||
__builtin_memcpy(gr->gr_mem[i], in->gr_mem[i], len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrnam(const char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return gr_find(gr_match_name, name);
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrgid(gid_t gid)
|
||||
{
|
||||
return gr_find(gr_match_gid, &gid);
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrent(void)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
gr_stream = fopen(GRP_PATH, "r");
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, gr_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0)
|
||||
{
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setgrent(void)
|
||||
{
|
||||
if (gr_stream != NULL)
|
||||
{
|
||||
rewind(gr_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
endgrent(void)
|
||||
{
|
||||
if (gr_stream != NULL)
|
||||
{
|
||||
(void)fclose(gr_stream);
|
||||
gr_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant lookup core: scan for the first matching record and serialize
|
||||
* it into the caller's storage. Returns 0 with *result pointing at the
|
||||
* filled struct on success, 0 with *result NULL when no record matches,
|
||||
* ERANGE when the record does not fit bufsize, and EINVAL for a NULL
|
||||
* result pointer.
|
||||
*/
|
||||
static int
|
||||
gr_find_r(gr_matcher match, const void *key, struct group *gr, char *buf, size_t bufsize,
|
||||
struct group **result)
|
||||
{
|
||||
FILE *f;
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || gr == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
f = fopen(GRP_PATH, "r");
|
||||
if (f == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) != 0 || !match(&cur, key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = gr_store(&cur, gr, buf, bufsize);
|
||||
(void)fclose(f);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = gr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
(void)fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
getgrnam_r(const char *name, struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
if (result != NULL)
|
||||
{
|
||||
*result = NULL;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
return gr_find_r(gr_match_name, name, gr, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
return gr_find_r(gr_match_gid, &gid, gr, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getgrent_r(struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || gr == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
gr_stream = fopen(GRP_PATH, "r");
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, gr_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = gr_store(&cur, gr, buf, bufsize);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = gr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
struct group *
|
||||
fgetgrent(FILE *stream)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0)
|
||||
{
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -12,6 +12,14 @@
|
||||
* All wrappers are sequentially consistent by default: a libc-internal
|
||||
* operation is almost never hot enough to justify weakening, and a single
|
||||
* memory order everywhere is the least error-prone choice.
|
||||
*
|
||||
* The fetch_or / fetch_and / fetch_sub family and the add_fetch / sub_fetch
|
||||
* result-returning forms were added by todo 44 for the futex lock words of
|
||||
* the pthread layer (#46-#48): futex wait words are built with or/and
|
||||
* (musl's a_or/a_and) and waiter counts with add/sub that return the NEW
|
||||
* value (musl's a_inc/a_dec pair). The fetch_* forms return the OLD value,
|
||||
* matching the C11/GCC __atomic_fetch_* convention; the *_fetch forms
|
||||
* return the NEW one.
|
||||
*/
|
||||
|
||||
static inline int
|
||||
@@ -32,6 +40,36 @@ atomic_fetch_add(volatile int *p, int v) // NOLINT(readability-non-const-paramet
|
||||
return __atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_add_fetch(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_sub(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_sub_fetch(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_sub_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_or(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_and(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_exchange(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
@@ -71,6 +109,36 @@ atomic_fetch_add_l(volatile long *p, long v) // NOLINT(readability-non-const-par
|
||||
return __atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_add_fetch_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_sub_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_sub_fetch_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_sub_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_or_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_and_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_exchange_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef VLIBC_INTERNAL_FUTEX_H
|
||||
#define VLIBC_INTERNAL_FUTEX_H
|
||||
|
||||
/*
|
||||
* vlibc — internal futex substrate (todo 44).
|
||||
*
|
||||
* Linux futex operations over SYS_futex, the kernel's process-shared sleep
|
||||
* primitive that every lock, condvar, barrier, and rwlock in the library
|
||||
* rests on. Two calling conventions are deliberately provided:
|
||||
*
|
||||
* - __futex() is the RAW seam: it returns exactly what the kernel put in
|
||||
* rax (0 or a positive value on success, a NEGATIVE errno on failure)
|
||||
* and never touches errno. Consumers that need custom error handling
|
||||
* (retry-on-EINTR, -ENOSYS fallbacks, or errno-code returns) use this.
|
||||
*
|
||||
* - __futex_wait() / __futex_timedwait() / __futex_wake() are the mapped
|
||||
* wrappers in the house syscall_ret() convention: 0 (or the number of
|
||||
* waiters woken) on success, -1 with errno set on failure. These are
|
||||
* what the pthread layer (#45-#48) calls.
|
||||
*
|
||||
* Op codes and flags are kernel UAPI constants (linux/futex.h); the values
|
||||
* are pinned below so this header stays self-contained. FUTEX_PRIVATE adds
|
||||
* no extra atomics: the wait word is shared only between threads of one
|
||||
* process. The ancient-kernel ENOSYS fallback path musl carries (retry
|
||||
* without FUTEX_PRIVATE) is deliberately NOT implemented — every kernel
|
||||
* since 2.6.22 handles the private flag, and the plan targets modern
|
||||
* x86_64 only.
|
||||
*
|
||||
* Timeout semantics: __futex_timedwait() takes an ABSOLUTE deadline and
|
||||
* uses FUTEX_WAIT_BITSET (kernel-compared, no userspace clock read):
|
||||
* CLOCK_MONOTONIC by default, CLOCK_REALTIME when the FUTEX_CLOCK_REALTIME
|
||||
* flag is added. A deadline already in the past returns -1/ETIMEDOUT from
|
||||
* the kernel immediately. musl's relative-timeout __timedwait shape is not
|
||||
* copied: it exists there only to keep pre-WAIT_BITSET kernels working.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "../internal/libc.h"
|
||||
|
||||
/* futex op codes (linux/futex.h). */
|
||||
#define FUTEX_WAIT 0
|
||||
#define FUTEX_WAKE 1
|
||||
#define FUTEX_FD 2
|
||||
#define FUTEX_REQUEUE 3
|
||||
#define FUTEX_CMP_REQUEUE 4
|
||||
#define FUTEX_WAKE_OP 5
|
||||
#define FUTEX_LOCK_PI 6
|
||||
#define FUTEX_UNLOCK_PI 7
|
||||
#define FUTEX_TRYLOCK_PI 8
|
||||
#define FUTEX_WAIT_BITSET 9
|
||||
|
||||
/* op flags (linux/futex.h). */
|
||||
#define FUTEX_PRIVATE 128
|
||||
#define FUTEX_CLOCK_REALTIME 256
|
||||
|
||||
/* bitset for "any bits match" (linux/futex.h). */
|
||||
#define FUTEX_BITSET_MATCH_ANY 0xffffffffu
|
||||
|
||||
/*
|
||||
* The __futex family lives in the implementation-reserved namespace, same
|
||||
* as the __syscall<n> seam — intentional, waived here for the whole block.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* Raw futex syscall: return the kernel's raw result — 0 or a positive value
|
||||
* on success, a negative errno on failure. errno is never touched. op may
|
||||
* be any FUTEX_* op code; ts is the timeout pointer for FUTEX_WAIT /
|
||||
* FUTEX_WAIT_BITSET, NULL for the others.
|
||||
*/
|
||||
hidden long
|
||||
__futex(volatile int *addr, int op, int val, void *ts);
|
||||
|
||||
/*
|
||||
* Mapped wait: block until *addr != val or a wakeup arrives. Returns 0 on
|
||||
* wakeup, -1 with errno set on failure. priv != 0 selects FUTEX_PRIVATE.
|
||||
*/
|
||||
hidden int
|
||||
__futex_wait(volatile int *addr, int val, int priv);
|
||||
|
||||
/*
|
||||
* Mapped timed wait: like __futex_wait, but with an ABSOLUTE deadline on
|
||||
* clock clk (CLOCK_MONOTONIC or CLOCK_REALTIME). Returns 0 on wakeup, -1
|
||||
* with errno set (ETIMEDOUT when the deadline passes, EINTR when a signal
|
||||
* interrupts the wait) on failure.
|
||||
*/
|
||||
hidden int
|
||||
__futex_timedwait(volatile int *addr, int val, int priv, int clk, const struct timespec *at);
|
||||
|
||||
/*
|
||||
* Mapped wake: wake up to cnt waiters on *addr (cnt < 0 = wake all, the
|
||||
* INT_MAX kernel convention musl uses). Returns the number of waiters
|
||||
* actually woken, or -1 with errno set on failure.
|
||||
*/
|
||||
hidden int
|
||||
__futex_wake(volatile int *addr, int cnt, int priv);
|
||||
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_INTERNAL_FUTEX_H */
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* cbrt(x) = cube root of x, after musl src/math/cbrt.c (FreeBSD msun
|
||||
* s_cbrt.c, Bruce D. Evans): a 5-bit bit-hack estimate, a degree-4
|
||||
* polynomial refinement to 23 bits, and one Newton step to 53 bits. */
|
||||
static const unsigned int B1 = 715094163; /* B1 = (1023-1023/3-0.03306235651)*2**20 */
|
||||
static const unsigned int B2 = 696219795; /* B2 = (1023-1023/3-54/3-0.03306235651)*2**20 */
|
||||
|
||||
/* |1/cbrt(x) - p(x)| < 2**-23.5 (~[-7.93e-8, 7.929e-8]). */
|
||||
static const double P0 = 1.87595182427177009643; /* 0x3ffe03e60f61e692 */
|
||||
static const double P1 = -1.88497979543377169875; /* 0xbffe28e092f02420 */
|
||||
static const double P2 = 1.621429720105354466140; /* 0x3ff9f1604a49d6c2 */
|
||||
static const double P3 = -0.758397934778766047437; /* 0xbfe844cbbee751d9 */
|
||||
static const double P4 = 0.145996192886612446982; /* 0x3fc2b000d4e4edd7 */
|
||||
|
||||
double
|
||||
cbrt(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
double r;
|
||||
double s;
|
||||
double t;
|
||||
double w;
|
||||
unsigned int hx;
|
||||
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32) & 0x7fffffffU;
|
||||
if (hx >= 0x7ff00000U)
|
||||
return x + x; /* cbrt(NaN,Inf) is itself */
|
||||
|
||||
/* rough cbrt to 5 bits */
|
||||
if (hx < 0x00100000U)
|
||||
{ /* zero or subnormal? */
|
||||
u.f = x * 0x1p54;
|
||||
hx = (unsigned int)(u.i >> 32) & 0x7fffffffU;
|
||||
if (hx == 0)
|
||||
return x; /* cbrt(0) is itself */
|
||||
hx = hx / 3 + B2;
|
||||
}
|
||||
else
|
||||
hx = hx / 3 + B1;
|
||||
u.i &= 1ULL << 63;
|
||||
u.i |= (unsigned long long)hx << 32;
|
||||
t = u.f;
|
||||
|
||||
/* new cbrt to 23 bits */
|
||||
r = (t * t) * (t / x);
|
||||
t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4));
|
||||
|
||||
/* round t away from zero to 23 bits, larger in magnitude than cbrt(x) */
|
||||
u.f = t;
|
||||
u.i = (u.i + 0x80000000ULL) & 0xffffffffc0000000ULL;
|
||||
t = u.f;
|
||||
|
||||
/* one Newton iteration to 53 bits with error < 0.667 ulps */
|
||||
s = t * t; /* t*t is exact */
|
||||
r = x / s; /* error <= 0.5 ulps; |r| < |t| */
|
||||
w = t + t; /* t+t is exact */
|
||||
r = (r - t) / (w + r);
|
||||
t = t + t * r;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* cbrtf(x) = cube root of x, after musl src/math/cbrtf.c: the 5-bit
|
||||
* bit-hack plus two Newton steps evaluated in double precision. */
|
||||
static const unsigned int B1f = 709958130; /* B1f = (127-127.0/3-0.03306235651)*2**23 */
|
||||
static const unsigned int B2f = 642849266; /* B2f = (127-127.0/3-24/3-0.03306235651)*2**23 */
|
||||
|
||||
float
|
||||
cbrtf(float x)
|
||||
{
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} u;
|
||||
double r;
|
||||
double T;
|
||||
unsigned int hx;
|
||||
|
||||
u.f = x;
|
||||
hx = u.i & 0x7fffffffU;
|
||||
if (hx >= 0x7f800000U)
|
||||
return x + x; /* cbrt(NaN,Inf) is itself */
|
||||
|
||||
/* rough cbrt to 5 bits */
|
||||
if (hx < 0x00800000U)
|
||||
{ /* zero or subnormal? */
|
||||
if (hx == 0)
|
||||
return x; /* cbrt(+-0) is itself */
|
||||
u.f = x * 0x1p24f;
|
||||
hx = u.i & 0x7fffffffU;
|
||||
hx = hx / 3 + B2f;
|
||||
}
|
||||
else
|
||||
hx = hx / 3 + B1f;
|
||||
u.i &= 0x80000000U;
|
||||
u.i |= hx;
|
||||
|
||||
/* first Newton step (solving t*t - x/t == 0) to 16 bits, in double */
|
||||
T = u.f;
|
||||
r = T * T * T;
|
||||
T = T * ((double)x + x + r) / (x + r + r);
|
||||
|
||||
/* second Newton step to 47 bits; rounding to 24 bits is exact in
|
||||
* round-to-nearest mode */
|
||||
r = T * T * T;
|
||||
T = T * ((double)x + x + r) / (x + r + r);
|
||||
return (float)T;
|
||||
}
|
||||
|
||||
/* cbrtl(x) = cube root of x, after musl src/math/cbrtl.c (ld80 branch,
|
||||
* S. Kargl / B. Evans / D. Schultz): reduce x to [1,8) via e%3 and run the
|
||||
* cbrtf-style estimates in double, then one long-double Newton step. */
|
||||
static const unsigned int B1l = 709958130; /* B1l = (127-127.0/3-0.03306235651)*2**23 */
|
||||
|
||||
long double
|
||||
cbrtl(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} v;
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} uft;
|
||||
long double r;
|
||||
long double s;
|
||||
long double t;
|
||||
long double w;
|
||||
double dr;
|
||||
double dt;
|
||||
double dx;
|
||||
float ft;
|
||||
int e;
|
||||
int sign;
|
||||
|
||||
u.f = x;
|
||||
e = u.p.se & 0x7fff;
|
||||
sign = u.p.se & 0x8000;
|
||||
if (e == 0x7fff)
|
||||
return x + x; /* cbrt(+-Inf) = +-Inf, cbrt(NaN) = NaN */
|
||||
if (e == 0)
|
||||
{ /* adjust subnormal numbers */
|
||||
u.f *= 0x1p120L;
|
||||
e = u.p.se & 0x7fff;
|
||||
if (e == 0)
|
||||
return x; /* cbrt(+-0) = +-0 */
|
||||
e -= 120;
|
||||
}
|
||||
e -= 0x3fff;
|
||||
u.p.se = 0x3fff;
|
||||
x = u.f;
|
||||
switch (e % 3)
|
||||
{
|
||||
case 1:
|
||||
case -2:
|
||||
x *= 2;
|
||||
e--;
|
||||
break;
|
||||
case 2:
|
||||
case -1:
|
||||
x *= 4;
|
||||
e -= 2;
|
||||
break;
|
||||
}
|
||||
v.f = 1.0L;
|
||||
v.p.se = (unsigned short)(sign | (0x3fff + e / 3));
|
||||
|
||||
/* ~5-bit estimate */
|
||||
uft.f = x;
|
||||
uft.i = (uft.i & 0x7fffffffU) / 3 + B1l;
|
||||
ft = uft.f;
|
||||
|
||||
/* ~16-bit estimate */
|
||||
dx = x;
|
||||
dt = ft;
|
||||
dr = dt * dt * dt;
|
||||
dt = dt * (dx + dx + dr) / (dx + dr + dr);
|
||||
|
||||
/* ~47-bit estimate */
|
||||
dr = dt * dt * dt;
|
||||
dt = dt * (dx + dx + dr) / (dx + dr + dr);
|
||||
|
||||
/* round dt away from zero to 32 bits so t*t is exact */
|
||||
t = dt + (0x1.0p32L + 0x1.0p-31L) - 0x1.0p32;
|
||||
|
||||
/* final Newton iteration to 64 bits with error < 0.667 ulps */
|
||||
s = t * t; /* t*t is exact */
|
||||
r = x / s; /* error <= 0.5 ulps; |r| < |t| */
|
||||
w = t + t; /* t+t is exact */
|
||||
r = (r - t) / (w + r);
|
||||
t = t + t * r;
|
||||
t *= v.f;
|
||||
return t;
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Smallest integral value not less than x (C23 7.12.9.1), all three
|
||||
* precisions.
|
||||
*
|
||||
* As with floor (see floor.c), GCC does not fold the __builtin_ceil forms
|
||||
* on this target, so each function works on the IEEE 754 bit pattern.
|
||||
* Clearing the fraction bits rounds toward zero, which is already the
|
||||
* correct direction for a negative argument; a positive argument with a
|
||||
* nonzero fraction must then step up by one (an exact integer + 1.0).
|
||||
* ceil(-0.3) is a real -0.0, and ±0/±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
static double
|
||||
ceil_d(double x)
|
||||
{
|
||||
const unsigned long long sign_mask = 1ULL << 63;
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -0.0 : 1.0;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
frac = (1ULL << shift) - 1;
|
||||
if ((bits & sign_mask) != 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x + 1.0;
|
||||
}
|
||||
|
||||
static float
|
||||
ceil_f(float x)
|
||||
{
|
||||
const unsigned int sign_mask = 1U << 31;
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -0.0f : 1.0f;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
frac = (1U << shift) - 1U;
|
||||
if ((bits & sign_mask) != 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x + 1.0f;
|
||||
}
|
||||
|
||||
static long double
|
||||
ceil_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0 && (p.se & 0x7fff) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 1.0L;
|
||||
}
|
||||
|
||||
shift = 63 - (e - 16383);
|
||||
frac = (1ULL << shift) - 1ULL;
|
||||
if ((p.se & 0x8000) != 0)
|
||||
{
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if ((p.m & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x + 1.0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a float argument.
|
||||
*/
|
||||
float
|
||||
ceilf(float x)
|
||||
{
|
||||
return ceil_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a double argument.
|
||||
*/
|
||||
double
|
||||
ceil(double x)
|
||||
{
|
||||
return ceil_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
ceill(long double x)
|
||||
{
|
||||
return ceil_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Copy the sign of y onto the magnitude of x, all three precisions (C23
|
||||
* 7.12.7.3). Each function below splices the sign bit of y into the IEEE
|
||||
* 754 bit pattern of x: the magnitude of x is untouched (so a NaN keeps
|
||||
* its payload) and copysign(±0, y) carries y's sign. GCC's
|
||||
* __builtin_copysign forms do fold to andp/orp pairs on this target, but
|
||||
* the compiler diagnoses the __builtin_ call inside the identically named
|
||||
* function as infinite recursion, so the splice is written out directly;
|
||||
* it compiles to the same two instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A float with the magnitude of x and the sign of y.
|
||||
*/
|
||||
float
|
||||
copysignf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
xb = (xb & 0x7fffffffU) | (yb & 0x80000000U);
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* A double with the magnitude of x and the sign of y.
|
||||
*/
|
||||
double
|
||||
copysign(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
xb = (xb & 0x7fffffffffffffffULL) | (yb & 0x8000000000000000ULL);
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* A long double with the magnitude of x and the sign of y. The x86 80-bit
|
||||
* extended format keeps the sign in bit 15 of the sign/exponent word at
|
||||
* bytes 8..9, so only that bit is spliced.
|
||||
*/
|
||||
long double
|
||||
copysignl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} xp;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} yp;
|
||||
|
||||
__builtin_memcpy(&xp, &x, sizeof xp);
|
||||
__builtin_memcpy(&yp, &y, sizeof yp);
|
||||
xp.se = (unsigned short)((xp.se & 0x7fff) | (yp.se & 0x8000));
|
||||
__builtin_memcpy(&x, &xp, sizeof xp);
|
||||
return x;
|
||||
}
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static const double ln2hi = 6.93147180369123816490e-01; /* 0x3fe62e42fee00000 */
|
||||
static const double ln2lo = 1.90821492927058770002e-10; /* 0x3dea39ef35793c76 */
|
||||
static const double invln2 = 1.44269504088896338700e+00; /* 0x3ff71547652b82fe */
|
||||
static const double P1 = 1.66666666666666019037e-01; /* 0x3FC555555555553E */
|
||||
static const double P2 = -2.77777777770155933842e-03; /* 0xBF66C16C16BEBD93 */
|
||||
static const double P3 = 6.61375632143793436117e-05; /* 0x3F11566AAF25DE2C */
|
||||
static const double P4 = -1.65339022054652515390e-06; /* 0xBEBBBD41C5D26BF1 */
|
||||
static const double P5 = 4.13813679705723846039e-08; /* 0x3E66376972BEA4D0 */
|
||||
static const double o_threshold = 709.782712893384;
|
||||
static const double u_threshold = -745.133219101941;
|
||||
static const double twom1000 = 9.33263618503218878990e-302;
|
||||
static const double tiny = 1.0e-300;
|
||||
|
||||
static double
|
||||
vlibc_exp_core(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
unsigned long long sign = u.i >> 63;
|
||||
unsigned int hx = (unsigned int)((u.i >> 32) & 0x7fffffffULL);
|
||||
|
||||
/* NaN or +/-Inf: exp(-inf)=+0, exp(+inf)=+inf, exp(NaN)=NaN */
|
||||
if ((u.i & 0x7fffffffffffffffULL) >= 0x7ff0000000000000ULL)
|
||||
{
|
||||
if ((u.i & 0x000fffffffffffffULL) != 0ULL)
|
||||
return x + x; /* NaN */
|
||||
return sign != 0ULL ? 0.0 : __builtin_huge_val();
|
||||
}
|
||||
|
||||
if (x > o_threshold)
|
||||
return __builtin_huge_val(); /* caller marks ERANGE */
|
||||
if (x < u_threshold)
|
||||
return 0.0; /* caller marks ERANGE */
|
||||
|
||||
double hi = 0.0;
|
||||
double lo = 0.0;
|
||||
int k = 0;
|
||||
|
||||
if (hx > 0x3fd62e42U)
|
||||
{ /* |x| > 0.5 ln2 */
|
||||
if (hx < 0x3ff0a2b2U)
|
||||
{ /* |x| < 1.5 ln2 */
|
||||
if (sign == 0ULL)
|
||||
{
|
||||
hi = x - ln2hi;
|
||||
lo = ln2lo;
|
||||
k = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = x + ln2hi;
|
||||
lo = -ln2lo;
|
||||
k = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
k = (int)(invln2 * x + (sign != 0ULL ? -0.5 : 0.5));
|
||||
double t = (double)k;
|
||||
hi = x - t * ln2hi;
|
||||
lo = t * ln2lo;
|
||||
}
|
||||
x = hi - lo;
|
||||
}
|
||||
else if (hx < 0x3e300000U)
|
||||
{ /* |x| < 2^-28: exp(x) ~ 1+x */
|
||||
(void)tiny;
|
||||
return 1.0 + x;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
|
||||
double t = x * x;
|
||||
double c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5))));
|
||||
|
||||
if (k == 0)
|
||||
return 1.0 - ((x * c) / (c - 2.0) - x);
|
||||
|
||||
double y = 1.0 - ((lo - (x * c) / (2.0 - c)) - hi);
|
||||
|
||||
/* scale y by 2^k by adding k to the exponent field */
|
||||
if (k >= -1021 && k <= 1023)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)k << 52;
|
||||
return b.f;
|
||||
}
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023;
|
||||
/* k <= -1022: scale up into the normal range, then multiply down */
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000;
|
||||
}
|
||||
|
||||
double
|
||||
exp(double x)
|
||||
{
|
||||
if (x > o_threshold)
|
||||
SET_ERANGE();
|
||||
else if (x < u_threshold)
|
||||
SET_ERANGE();
|
||||
return vlibc_exp_core(x);
|
||||
}
|
||||
|
||||
#ifndef exp
|
||||
double
|
||||
exp(double);
|
||||
#endif
|
||||
|
||||
double
|
||||
exp2(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
|
||||
/* NaN or +/-Inf: 2^(-inf)=+0, 2^(+inf)=+inf, 2^(NaN)=NaN */
|
||||
if ((u.i & 0x7fffffffffffffffULL) >= 0x7ff0000000000000ULL)
|
||||
{
|
||||
if ((u.i & 0x000fffffffffffffULL) != 0ULL)
|
||||
return x; /* NaN */
|
||||
return (u.i >> 63) != 0ULL ? 0.0 : __builtin_huge_val();
|
||||
}
|
||||
|
||||
if (x >= 1024.0)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_val();
|
||||
}
|
||||
if (x <= -1075.0)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* round to nearest integer; |x| < 1024 so the conversion is safe */
|
||||
long long k = (long long)(x + (x > 0.0 ? 0.5 : -0.5));
|
||||
/* exact by Sterbenz: k is within 2^52 of x */
|
||||
double r = x - (double)k;
|
||||
/* e^(r ln2) = 2^r */
|
||||
double y = vlibc_exp_core(r * 0.6931471805599453);
|
||||
|
||||
/* scale y by 2^k by adding k to the exponent field */
|
||||
if (k >= -1021 && k <= 1023)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)k << 52;
|
||||
return b.f;
|
||||
}
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023;
|
||||
/* k <= -1022: scale up into the normal range, then multiply down */
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000;
|
||||
}
|
||||
|
||||
float
|
||||
exp2f(float x)
|
||||
{
|
||||
if (x >= 128.0f)
|
||||
return __builtin_huge_valf();
|
||||
if (x <= -150.0f)
|
||||
return 0.0f;
|
||||
return (float)exp2((double)x);
|
||||
}
|
||||
|
||||
long double
|
||||
exp2l(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0 ? 0.0L : x; /* +/-Inf */
|
||||
}
|
||||
|
||||
if (x >= 16384.0L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x <= -16445.0L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0L;
|
||||
}
|
||||
|
||||
/* round to nearest integer; |x| < 16384 so the conversion is safe */
|
||||
long long k = (long long)(x + (x > 0.0L ? 0.5L : -0.5L));
|
||||
/* exact by Sterbenz: k is within 2^63 of x */
|
||||
long double r = x - (long double)k;
|
||||
long double y = r * 0.693147180559945309417232121458176568L;
|
||||
|
||||
/* e^y, |y| <= ln2/2: Taylor series with compensated summation */
|
||||
long double s;
|
||||
if (y > -1e-8L && y < 1e-8L)
|
||||
s = 1.0L + y;
|
||||
else
|
||||
{
|
||||
long double term = y;
|
||||
long double comp = 0.0L;
|
||||
s = 1.0L + y;
|
||||
for (int i = 2; i <= 60; i++)
|
||||
{
|
||||
term *= y / (long double)i;
|
||||
long double yk = term - comp;
|
||||
long double t = s + yk;
|
||||
comp = (t - s) - yk;
|
||||
s = t;
|
||||
if (term > -1e-30L && term < 1e-30L)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (k < -16445)
|
||||
return x > 0.0L ? 0.0L : -0.0L; /* unreachable; defensive */
|
||||
|
||||
/* scale s by 2^k: construct 2^k in long double and multiply */
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} two;
|
||||
|
||||
if (k >= -16382 && k <= 16383)
|
||||
{
|
||||
two.p.m = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + (int)k);
|
||||
return s * two.f;
|
||||
}
|
||||
if (k == 16384)
|
||||
{
|
||||
/* x in [16383.5, 16384): s = 2^r with r in [-0.5, 0) < 1, so
|
||||
* s * 2^16383 * 2 stays finite */
|
||||
two.p.m = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + 16383);
|
||||
return s * two.f * 2.0L;
|
||||
}
|
||||
/* k in [-16445, -16383]: 2^k is a pseudo-denormal, int bit clear */
|
||||
two.p.m = 1ULL << (unsigned int)(k + 16445);
|
||||
two.p.se = 0;
|
||||
return s * two.f;
|
||||
}
|
||||
|
||||
static const double Q1 = -3.33333333333331316428e-02; /* 0xbfa1111111110f54 */
|
||||
static const double Q2 = 1.58730158725481460165e-03; /* 0x3f5a01a019fe5585 */
|
||||
static const double Q3 = -7.93650757867487942473e-05; /* 0xbf14ce199eaadbb7 */
|
||||
static const double Q4 = 4.00821782732936260352e-06; /* 0x3ed0cfca86e65239 */
|
||||
static const double Q5 = -2.01099218183624371326e-07; /* 0xbe8afdb76e09c32d */
|
||||
|
||||
/* expm1(x) = e^x - 1, accurate for small x (fdlibm s_expm1). */
|
||||
double
|
||||
expm1(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
double hi = 0.0;
|
||||
double lo = 0.0;
|
||||
double c = 0.0;
|
||||
unsigned int sign;
|
||||
unsigned int hx;
|
||||
int k = 0;
|
||||
|
||||
u.f = x;
|
||||
sign = (unsigned int)(u.i >> 63);
|
||||
hx = (unsigned int)((u.i >> 32) & 0x7fffffffULL);
|
||||
|
||||
/* filter out huge and non-finite argument */
|
||||
if (hx >= 0x7ff00000U)
|
||||
{
|
||||
if ((u.i & 0xfffffffffffffULL) != 0ULL)
|
||||
return x; /* NaN */
|
||||
return sign != 0U ? -1.0 : x; /* expm1(+-inf) = {-1, +inf} */
|
||||
}
|
||||
if (x == 0.0)
|
||||
return x; /* preserve -0.0 */
|
||||
|
||||
if (x > o_threshold)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_val(); /* overflow */
|
||||
}
|
||||
if (sign != 0U && hx >= 0x4043687AU)
|
||||
return -1.0; /* x < -56*ln2, exp(x) < 2^-56 -> -1.0 */
|
||||
|
||||
/* argument reduction */
|
||||
if (hx > 0x3fd62e42U)
|
||||
{
|
||||
if (hx < 0x3ff0a2b2U)
|
||||
{
|
||||
if (sign == 0U)
|
||||
{
|
||||
hi = x - ln2hi;
|
||||
lo = ln2lo;
|
||||
k = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = x + ln2hi;
|
||||
lo = -ln2lo;
|
||||
k = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
k = (int)(invln2 * x + (sign != 0U ? -0.5 : 0.5));
|
||||
{
|
||||
double t = (double)k;
|
||||
hi = x - t * ln2hi; /* t*ln2hi is exact */
|
||||
lo = t * ln2lo;
|
||||
}
|
||||
}
|
||||
x = hi - lo;
|
||||
c = (hi - x) - lo;
|
||||
}
|
||||
else if (hx < 0x3c900000U)
|
||||
{
|
||||
return x; /* |x| < 2^-54: expm1(x) == x */
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
|
||||
/* x is now in primary range */
|
||||
{
|
||||
double hfx = 0.5 * x;
|
||||
double hxs = x * hfx;
|
||||
double r1 = 1.0 + hxs * (Q1 + hxs * (Q2 + hxs * (Q3 + hxs * (Q4 + hxs * Q5))));
|
||||
double t = 3.0 - r1 * hfx;
|
||||
double e = hxs * ((r1 - t) / (6.0 - x * t));
|
||||
|
||||
if (k == 0)
|
||||
return x - (x * e - hxs); /* c is 0 */
|
||||
|
||||
e = (x * (e - c) - c) - hxs;
|
||||
if (k == -1)
|
||||
return 0.5 * (x - e) - 0.5;
|
||||
if (k == 1)
|
||||
{
|
||||
if (x < -0.25)
|
||||
return -2.0 * (e - (x + 0.5));
|
||||
return 1.0 + 2.0 * (x - e);
|
||||
}
|
||||
/* general: expm1(x) = 2^k * (1 - (e - x)) - 1 */
|
||||
{
|
||||
double y = 1.0 - (e - x);
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023 - 1.0;
|
||||
b.f = y;
|
||||
if (k >= -1021)
|
||||
b.i += (unsigned long long)k << 52;
|
||||
else
|
||||
{
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000 - 1.0;
|
||||
}
|
||||
return b.f - 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
expm1f(float x)
|
||||
{
|
||||
if (x >= 88.72283905206835f && !isinf(x))
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_valf(); /* overflow */
|
||||
}
|
||||
return (float)expm1((double)x);
|
||||
}
|
||||
|
||||
/* 80-bit x87 expm1l */
|
||||
long double
|
||||
expm1l(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
long double r;
|
||||
long double sum;
|
||||
long long k;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0U ? -1.0L : x; /* expm1(+-inf) = {-1, +inf} */
|
||||
}
|
||||
if (x == 0.0L)
|
||||
return x;
|
||||
|
||||
if (x > 11356.523406294143L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x < -16447.0L)
|
||||
return -1.0L;
|
||||
|
||||
/* 2^x = 2^k * e^r with r in [-ln2/2, ln2/2]; ln2 split hi/lo so that
|
||||
* k*ln2hi is exact and r keeps full 64-bit precision */
|
||||
{
|
||||
const long double ln2hi_l = 6.93147180369123816490e-01L;
|
||||
const long double ln2lo_l = 1.90821492927058770002e-10L;
|
||||
const long double invln2l = 1.442695040888963407359924681001892137L;
|
||||
k = (long long)(x * invln2l + (x > 0.0L ? 0.5L : -0.5L));
|
||||
r = x - (long double)k * ln2hi_l - (long double)k * ln2lo_l;
|
||||
}
|
||||
|
||||
/* expm1(r) = e^r - 1 via Kahan-compensated Taylor sum */
|
||||
{
|
||||
long double term = r;
|
||||
long double comp = 0.0L;
|
||||
int i;
|
||||
sum = r;
|
||||
for (i = 2; i <= 60; i++)
|
||||
{
|
||||
long double yt;
|
||||
long double t;
|
||||
term *= r / (long double)i;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
yt = term - comp;
|
||||
t = sum + yt;
|
||||
comp = (t - sum) - yt;
|
||||
sum = t;
|
||||
}
|
||||
}
|
||||
|
||||
if (k == 0)
|
||||
return sum;
|
||||
|
||||
/* scale (sum + 1) by 2^k and subtract 1 */
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} b;
|
||||
long double z = sum + 1.0L;
|
||||
|
||||
if (k >= 16384)
|
||||
return z * 2.0L * 0x1p16383L - 1.0L;
|
||||
if (k >= -16382)
|
||||
{
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k);
|
||||
return z * b.f - 1.0L;
|
||||
}
|
||||
/* k in [-16445, -16383]: e^x < 2^-16382, so e^x - 1 rounds to -1 */
|
||||
return -1.0L;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
expf(float x)
|
||||
{
|
||||
if (isinf(x))
|
||||
return x > 0.0f ? x : 0.0f; /* exp(+inf) = +inf, exp(-inf) = +0 */
|
||||
if (x >= 88.72283905206835f)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_valf(); /* overflow */
|
||||
}
|
||||
if (x < -103.97207708219999f)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0f; /* underflow to zero */
|
||||
}
|
||||
{
|
||||
double y = exp((double)x);
|
||||
if (y < 0x1p-149) /* glibc flags ERANGE below FLT_TRUE_MIN */
|
||||
SET_ERANGE();
|
||||
return (float)y;
|
||||
}
|
||||
}
|
||||
|
||||
/* 80-bit x87 expl: exp(x) = 2^k * e^r, r in [-ln2/2, ln2/2] */
|
||||
long double
|
||||
expl(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
long double r;
|
||||
long double sum;
|
||||
long long k;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0U ? 0.0L : x; /* exp(+-inf) = {+0, inf} */
|
||||
}
|
||||
if (x == 0.0L)
|
||||
return 1.0L;
|
||||
|
||||
if (x >= 0xb.17217f7d1cf79acp+10L) /* first 80-bit x where e^x rounds to +inf */
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x <= -11399.8484L) /* exp(x) < 2^-16446: rounds to +0 */
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0L;
|
||||
}
|
||||
|
||||
/* ln2 split so k*ln2hi is exact (15-bit hi) and r keeps full
|
||||
* 64-bit precision; k fits long long since |x| < 11356.6 */
|
||||
{
|
||||
const long double ln2hi_l = 6.9314575195312500000000e-1L;
|
||||
const long double ln2lo_l = 1.4286068203094172321214581765680755e-6L;
|
||||
const long double invln2l = 1.442695040888963407359924681001892137L;
|
||||
k = (long long)(x * invln2l + (x > 0.0L ? 0.5L : -0.5L));
|
||||
r = x - (long double)k * ln2hi_l;
|
||||
r -= (long double)k * ln2lo_l;
|
||||
}
|
||||
|
||||
/* e^r via Kahan-compensated Taylor */
|
||||
{
|
||||
long double term = r;
|
||||
long double comp = 0.0L;
|
||||
int i;
|
||||
sum = 1.0L + r;
|
||||
for (i = 2; i <= 60; i++)
|
||||
{
|
||||
long double yt;
|
||||
long double t;
|
||||
term *= r / (long double)i;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
yt = term - comp;
|
||||
t = sum + yt;
|
||||
comp = (t - sum) - yt;
|
||||
sum = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* scale sum by 2^k */
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} b;
|
||||
|
||||
if (k == 0)
|
||||
return sum;
|
||||
if (k >= 16384)
|
||||
return sum * 2.0L * 0x1p16383L; /* overflow path */
|
||||
if (k <= -16382)
|
||||
{
|
||||
/* result is subnormal: scale up by 2^(k+16382), then by 2^-16382 */
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k + 16382);
|
||||
sum *= b.f;
|
||||
return sum * 0x1p-16382L;
|
||||
}
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k);
|
||||
return sum * b.f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Absolute value, all three precisions (C23 7.12.7.2). GCC folds
|
||||
* __builtin_fabs/__builtin_fabsf/__builtin_fabsl into a single
|
||||
* sign-clearing SSE/x87 instruction at every optimization level (verified
|
||||
* at -O0 and -O2), so the call never recurses and no errno path exists.
|
||||
* The sign-bit clear is exact: fabs(-0.0) is +0.0, fabs(±Inf) is +Inf,
|
||||
* and a NaN keeps its payload.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Absolute value of x as a float.
|
||||
*/
|
||||
float
|
||||
fabsf(float x)
|
||||
{
|
||||
return __builtin_fabsf(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute value of x as a double.
|
||||
*/
|
||||
double
|
||||
fabs(double x)
|
||||
{
|
||||
return __builtin_fabs(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute value of x as a long double.
|
||||
*/
|
||||
long double
|
||||
fabsl(long double x)
|
||||
{
|
||||
return __builtin_fabsl(x);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The positive difference x - y when x > y and +0.0 otherwise (C23
|
||||
* 7.12.12.2), all three precisions.
|
||||
*
|
||||
* C23 keeps fdim(x, NaN) and fdim(NaN, x) NaN, so the two arguments are
|
||||
* screened before the ordering test; a NaN result is the quiet NaN that
|
||||
* the x + y addition below produces (matching the host glibc). With both
|
||||
* operands numeric, x > y is an ordinary comparison (no exceptions) and
|
||||
* the single subtraction x - y is the whole computation: an exact
|
||||
* representable difference stays exact, and a difference too large for the
|
||||
* format overflows through the hardware into +Inf exactly as glibc's does,
|
||||
* with the overflow flag raised and no extra help needed. x <= y -- the
|
||||
* signed-zero and equal cases included -- returns a plain +0.0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* fdim of two floats.
|
||||
*/
|
||||
float
|
||||
fdimf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
if (isnan(x) || isnan(y))
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
if (x > y)
|
||||
{
|
||||
return x - y;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* fdim of two doubles.
|
||||
*/
|
||||
double
|
||||
fdim(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
if (isnan(x) || isnan(y))
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
if (x > y)
|
||||
{
|
||||
return x - y;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* fdim of two long doubles.
|
||||
*/
|
||||
long double
|
||||
fdiml(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
if (isnan(x) || isnan(y))
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
if (x > y)
|
||||
{
|
||||
return x - y;
|
||||
}
|
||||
return 0.0L;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Largest integral value not greater than x (C23 7.12.9.2), all three
|
||||
* precisions.
|
||||
*
|
||||
* GCC emits an external floor/floorf/floorl call for the __builtin_ forms
|
||||
* at every optimization level on this target (no SSE4.1 in the default
|
||||
* -march, so the roundsd expansion is unavailable and the builtin is not
|
||||
* folded), so each function below is implemented directly on the IEEE 754
|
||||
* bit pattern instead: clearing the fraction bits rounds toward zero, and
|
||||
* a negative argument with a nonzero fraction must then step down by one.
|
||||
* The clearing and the exact integer - 1.0 are both exact, so no rounding
|
||||
* mode and no floating-point exception is involved; ±0/±Inf/NaN pass
|
||||
* through and signed zero is preserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. A finite value with exponent e has
|
||||
* |x| in [2^(e-1023), 2^(e-1022)) and a fraction only when e - 1023 < 52,
|
||||
* i.e. e < 1075; for e in [1023, 1074] exactly the low (1075 - e) bits of
|
||||
* the fraction word are the fractional part.
|
||||
*/
|
||||
static double
|
||||
floor_d(double x)
|
||||
{
|
||||
const unsigned long long sign_mask = 1ULL << 63;
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
/* |x| >= 2^52 is already integral; Inf (e == 0x7ff) and NaN must pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* |x| < 1: floor is +0 for a nonnegative x and -1 for a negative one,
|
||||
* except that ±0 is its own floor. */
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -1.0 : 0.0;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
frac = (1ULL << shift) - 1;
|
||||
if ((bits & sign_mask) == 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Negative: round toward zero, then take one more step down when a
|
||||
* fraction was dropped. The truncation leaves an integer-valued
|
||||
* double, so subtracting 1.0 is exact. */
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x - 1.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction. Fraction bits exist exactly when the
|
||||
* exponent e is in [127, 149]; the low (150 - e) bits are fractional.
|
||||
*/
|
||||
static float
|
||||
floor_f(float x)
|
||||
{
|
||||
const unsigned int sign_mask = 1U << 31;
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -1.0f : 0.0f;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
frac = (1U << shift) - 1U;
|
||||
if ((bits & sign_mask) == 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x - 1.0f;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit) in bytes 0..7 and a sign/exponent word se in bytes 8..9,
|
||||
* with the sign in bit 15 and the exponent (biased by 16383) in bits
|
||||
* 14..0. A value m * 2^(e - 16446) has fractional bits only when
|
||||
* e - 16383 < 63, i.e. e < 16446; for e in [16383, 16445] exactly the low
|
||||
* (63 - (e - 16383)) bits of m are fractional.
|
||||
*/
|
||||
static long double
|
||||
floor_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
/* |x| >= 2^63 is already integral; Inf (e == 0x7fff) and NaN pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0 && (p.se & 0x7fff) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (p.se & 0x8000) != 0 ? -1.0L : 0.0L;
|
||||
}
|
||||
|
||||
shift = 63 - (e - 16383);
|
||||
frac = (1ULL << shift) - 1ULL;
|
||||
if ((p.se & 0x8000) == 0)
|
||||
{
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if ((p.m & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x - 1.0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* As floor, for a float argument.
|
||||
*/
|
||||
float
|
||||
floorf(float x)
|
||||
{
|
||||
return floor_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As floor, for a double argument.
|
||||
*/
|
||||
double
|
||||
floor(double x)
|
||||
{
|
||||
return floor_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As floor, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
floorl(long double x)
|
||||
{
|
||||
return floor_ld(x);
|
||||
}
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The larger of x and y (C23 7.12.12.3), all three precisions.
|
||||
*
|
||||
* The mirror image of fmin (see fmin.c for the full reasoning): a quiet
|
||||
* NaN argument is ignored in favor of the numeric one, a signaling NaN
|
||||
* argument makes the result that signaling NaN quieted, and a +0.0/-0.0
|
||||
* pair returns +0.0. Only the equal-argument tie rule (and, with it, the
|
||||
* direction of the zero preference) differs from fmin; the numeric
|
||||
* comparisons, the NaN screens and the word-level quiet splice are
|
||||
* otherwise identical, so the implementation shares the shape of fmin.c
|
||||
* and is likewise pure.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction; exponent 0x7ff with a nonzero fraction
|
||||
* is a NaN, quiet bit 0x0008000000000000.
|
||||
*/
|
||||
static int
|
||||
fmax_nan_d(unsigned long long b)
|
||||
{
|
||||
return ((b >> 52) & 0x7ff) == 0x7ff && (b & 0xFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmax_snan_d(unsigned long long b)
|
||||
{
|
||||
return fmax_nan_d(b) && (b & 0x0008000000000000ULL) == 0;
|
||||
}
|
||||
|
||||
static double
|
||||
fmax_mm_d(double x, double y)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
if (fmax_snan_d(xb))
|
||||
{
|
||||
xb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmax_snan_d(yb))
|
||||
{
|
||||
yb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
if (fmax_nan_d(xb))
|
||||
{
|
||||
return fmax_nan_d(yb) ? x : y;
|
||||
}
|
||||
if (fmax_nan_d(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Equal, so possibly a +0/-0 pair: fmax prefers +0. */
|
||||
if (x == 0.0)
|
||||
{
|
||||
xneg = (int)(xb >> 63);
|
||||
yneg = (int)(yb >> 63);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return xneg ? y : x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction; quiet bit 0x00400000.
|
||||
*/
|
||||
static int
|
||||
fmax_nan_f(unsigned int b)
|
||||
{
|
||||
return ((b >> 23) & 0xff) == 0xff && (b & 0x7FFFFFU) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmax_snan_f(unsigned int b)
|
||||
{
|
||||
return fmax_nan_f(b) && (b & 0x00400000U) == 0;
|
||||
}
|
||||
|
||||
static float
|
||||
fmax_mm_f(float x, float y)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
if (fmax_snan_f(xb))
|
||||
{
|
||||
xb |= 0x00400000U;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmax_snan_f(yb))
|
||||
{
|
||||
yb |= 0x00400000U;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
if (fmax_nan_f(xb))
|
||||
{
|
||||
return fmax_nan_f(yb) ? x : y;
|
||||
}
|
||||
if (fmax_nan_f(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (x == 0.0f)
|
||||
{
|
||||
xneg = (int)(xb >> 31);
|
||||
yneg = (int)(yb >> 31);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return xneg ? y : x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m (explicit integer
|
||||
* bit) in bytes 0..7, sign/exponent word se in bytes 8..9 with the sign in
|
||||
* bit 15 and the exponent biased by 16383 in bits 14..0. NaN has
|
||||
* (se & 0x7fff) == 0x7fff with a nonzero fraction; the quiet bit is bit 62
|
||||
* of m.
|
||||
*/
|
||||
struct fmax_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static int
|
||||
fmax_nan_l(struct fmax_ld_word p)
|
||||
{
|
||||
return (p.se & 0x7fff) == 0x7fff && (p.m & 0x7FFFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmax_snan_l(struct fmax_ld_word p)
|
||||
{
|
||||
return fmax_nan_l(p) && (p.m & (1ULL << 62)) == 0;
|
||||
}
|
||||
|
||||
static long double
|
||||
fmax_mm_l(long double x, long double y)
|
||||
{
|
||||
struct fmax_ld_word p;
|
||||
struct fmax_ld_word q;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
|
||||
if (fmax_snan_l(p))
|
||||
{
|
||||
p.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if (fmax_snan_l(q))
|
||||
{
|
||||
q.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&y, &q, sizeof q);
|
||||
return y;
|
||||
}
|
||||
if (fmax_nan_l(p))
|
||||
{
|
||||
return fmax_nan_l(q) ? x : y;
|
||||
}
|
||||
if (fmax_nan_l(q))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (x == 0.0L)
|
||||
{
|
||||
xneg = (int)(p.se >> 15);
|
||||
yneg = (int)(q.se >> 15);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return xneg ? y : x;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The larger of two floats.
|
||||
*/
|
||||
float
|
||||
fmaxf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmax_mm_f(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The larger of two doubles.
|
||||
*/
|
||||
double
|
||||
fmax(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmax_mm_d(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The larger of two long doubles.
|
||||
*/
|
||||
long double
|
||||
fmaxl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmax_mm_l(x, y);
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The smaller of x and y (C23 7.12.12.4), all three precisions.
|
||||
*
|
||||
* A quiet NaN argument is ignored in favor of the numeric one; a signaling
|
||||
* NaN argument makes the result a quiet NaN (the host glibc returns the
|
||||
* signaling argument quieted, payload preserved). When the arguments are
|
||||
* +0.0 and -0.0 the result is -0.0, and a pair of equal nonzero values
|
||||
* returns either. Numeric ordering is an ordinary comparison on the
|
||||
* already-NaN-screened operands, so no exception is raised and the chosen
|
||||
* operand is returned with its bits untouched. The NaN detection and the
|
||||
* quiet splice are done on the raw IEEE 754 word (the __builtin_isnan
|
||||
* classification would be equally exact but the word form also exposes the
|
||||
* signaling bit); no rounding mode and no arithmetic instruction is
|
||||
* involved, so the function is pure and fold-free at every level.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction; exponent 0x7ff with a nonzero fraction
|
||||
* is a NaN, and the quiet bit is 0x0008000000000000 (bit 51).
|
||||
*/
|
||||
static int
|
||||
fmin_nan_d(unsigned long long b)
|
||||
{
|
||||
return ((b >> 52) & 0x7ff) == 0x7ff && (b & 0xFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmin_snan_d(unsigned long long b)
|
||||
{
|
||||
return fmin_nan_d(b) && (b & 0x0008000000000000ULL) == 0;
|
||||
}
|
||||
|
||||
static double
|
||||
fmin_mm_d(double x, double y, int want_max)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
/* A signaling NaN in either argument wins, quieted with its payload. */
|
||||
if (fmin_snan_d(xb))
|
||||
{
|
||||
xb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmin_snan_d(yb))
|
||||
{
|
||||
yb |= 0x0008000000000000ULL;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
|
||||
/* Quiet NaNs are ignored: the numeric argument (or x, for two) wins. */
|
||||
if (fmin_nan_d(xb))
|
||||
{
|
||||
return fmin_nan_d(yb) ? x : y;
|
||||
}
|
||||
if (fmin_nan_d(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
/* Equal, so possibly a +0/-0 pair: fmin prefers -0, fmax prefers +0. */
|
||||
if (x == 0.0)
|
||||
{
|
||||
xneg = (int)(xb >> 63);
|
||||
yneg = (int)(yb >> 63);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return want_max ? (xneg ? y : x) : (xneg ? x : y);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction; quiet bit 0x00400000 (bit 22).
|
||||
*/
|
||||
static int
|
||||
fmin_nan_f(unsigned int b)
|
||||
{
|
||||
return ((b >> 23) & 0xff) == 0xff && (b & 0x7FFFFFU) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmin_snan_f(unsigned int b)
|
||||
{
|
||||
return fmin_nan_f(b) && (b & 0x00400000U) == 0;
|
||||
}
|
||||
|
||||
static float
|
||||
fmin_mm_f(float x, float y, int want_max)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
|
||||
if (fmin_snan_f(xb))
|
||||
{
|
||||
xb |= 0x00400000U;
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
if (fmin_snan_f(yb))
|
||||
{
|
||||
yb |= 0x00400000U;
|
||||
__builtin_memcpy(&y, &yb, sizeof yb);
|
||||
return y;
|
||||
}
|
||||
if (fmin_nan_f(xb))
|
||||
{
|
||||
return fmin_nan_f(yb) ? x : y;
|
||||
}
|
||||
if (fmin_nan_f(yb))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (x == 0.0f)
|
||||
{
|
||||
xneg = (int)(xb >> 31);
|
||||
yneg = (int)(yb >> 31);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return want_max ? (xneg ? y : x) : (xneg ? x : y);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the
|
||||
* integer bit is explicit) and a sign/exponent word se in bytes 8..9, sign
|
||||
* in bit 15 and exponent biased by 16383 in bits 14..0. A NaN has
|
||||
* (se & 0x7fff) == 0x7fff with a nonzero fraction; the quiet bit is bit 62
|
||||
* of m.
|
||||
*/
|
||||
struct fmin_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static int
|
||||
fmin_nan_l(struct fmin_ld_word p)
|
||||
{
|
||||
return (p.se & 0x7fff) == 0x7fff && (p.m & 0x7FFFFFFFFFFFFFFFULL) != 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fmin_snan_l(struct fmin_ld_word p)
|
||||
{
|
||||
return fmin_nan_l(p) && (p.m & (1ULL << 62)) == 0;
|
||||
}
|
||||
|
||||
static long double
|
||||
fmin_mm_l(long double x, long double y, int want_max)
|
||||
{
|
||||
struct fmin_ld_word p;
|
||||
struct fmin_ld_word q;
|
||||
int xneg;
|
||||
int yneg;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
|
||||
if (fmin_snan_l(p))
|
||||
{
|
||||
p.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if (fmin_snan_l(q))
|
||||
{
|
||||
q.m |= 1ULL << 62;
|
||||
__builtin_memcpy(&y, &q, sizeof q);
|
||||
return y;
|
||||
}
|
||||
if (fmin_nan_l(p))
|
||||
{
|
||||
return fmin_nan_l(q) ? x : y;
|
||||
}
|
||||
if (fmin_nan_l(q))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (y < x)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
if (x == 0.0L)
|
||||
{
|
||||
xneg = (int)(p.se >> 15);
|
||||
yneg = (int)(q.se >> 15);
|
||||
if (xneg != yneg)
|
||||
{
|
||||
return want_max ? (xneg ? y : x) : (xneg ? x : y);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The smaller of two floats.
|
||||
*/
|
||||
float
|
||||
fminf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmin_mm_f(x, y, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The smaller of two doubles.
|
||||
*/
|
||||
double
|
||||
fmin(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmin_mm_d(x, y, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The smaller of two long doubles.
|
||||
*/
|
||||
long double
|
||||
fminl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmin_mm_l(x, y, 0);
|
||||
}
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The floating-point remainder x - n*y with n = x/y truncated toward zero
|
||||
* (C23 7.12.10.1), all three precisions. The remainder is always exact
|
||||
* and carries x's sign (a zero result is a signed zero with x's sign), so
|
||||
* every implementation must agree bit for bit; the result is computed with
|
||||
* a restoring long division on the significands, in the style of the musl
|
||||
* fmod family but derived here from first principles.
|
||||
*
|
||||
* Each operand is normalized into
|
||||
*
|
||||
* value = m * 2^(ex - W)
|
||||
*
|
||||
* where m is the significand with its msb pinned to bit P (P = 52/23/63
|
||||
* and W = 1075/150/16446 for double/float/80-bit) and ex is the exponent
|
||||
* in the same units as the biased field (so ex is the stored exponent for
|
||||
* normals and goes negative for subnormals). Aligned this way, one binary
|
||||
* long-division step per exponent difference decides whether a multiple of
|
||||
* the divisor fits: the current remainder significand is compared against
|
||||
* the divisor significand, the divisor is subtracted once when it fits,
|
||||
* and the remainder is doubled for the next, half-weight, digit. The
|
||||
* comparison invariant keeps the remainder below twice the divisor at
|
||||
* every step, so one subtraction per digit is always enough and every
|
||||
* subtraction (and the exact zero test) is a plain integer operation.
|
||||
*
|
||||
* Domain errors mirror the host glibc: fmod(+-0, +-0), fmod(+-Inf, y) and
|
||||
* any NaN argument return a NaN (the classic indefinite pattern, which is
|
||||
* what glibc's x87 path produces) and, in the library build only, set
|
||||
* errno to EDOM. fmod(x, +-Inf) is x for a finite x, and |x| <= |y| hands
|
||||
* x back unchanged, which keeps the signed-zero and exact cases exact.
|
||||
*/
|
||||
|
||||
/* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. Value = m * 2^(ex - 1075) with the msb
|
||||
* of m at bit 52. */
|
||||
static double
|
||||
fmod_d(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
unsigned long long sx;
|
||||
unsigned long long mx;
|
||||
unsigned long long my;
|
||||
unsigned long long s;
|
||||
unsigned long long i;
|
||||
int ex;
|
||||
int ey;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
sx = xb & (1ULL << 63);
|
||||
ex = (int)((xb >> 52) & 0x7ff);
|
||||
ey = (int)((yb >> 52) & 0x7ff);
|
||||
|
||||
/* x not finite, y zero, or y a NaN: domain error. */
|
||||
if (ex == 0x7ff || (yb << 1) == 0 || (ey == 0x7ff && (yb & 0xFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
return __builtin_nan(""); /* indefinite NaN, sign negative */
|
||||
}
|
||||
/* x zero or a finite x below an infinite y: x unchanged. (x a NaN or
|
||||
* +-Inf fell into the domain branch above, which returns a NaN for
|
||||
* both.) */
|
||||
if ((xb << 1) == 0 || ey == 0x7ff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Compare magnitudes directly on the (biased exponent, fraction)
|
||||
* words: finite IEEE magnitudes order lexicographically, so a plain
|
||||
* integer compare of the sign-stripped words decides |x| vs |y|. */
|
||||
if ((xb & 0x7FFFFFFFFFFFFFFFULL) <= (yb & 0x7FFFFFFFFFFFFFFFULL))
|
||||
{
|
||||
if ((xb & 0x7FFFFFFFFFFFFFFFULL) == (yb & 0x7FFFFFFFFFFFFFFFULL))
|
||||
{
|
||||
return x * 0.0; /* exact: result is +-0 with x's sign */
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Normalize x: subnormals (ex == 0) get their msb shifted up to bit
|
||||
* 52 and ex counts the shift below the smallest normal. */
|
||||
mx = xb & 0xFFFFFFFFFFFFFULL;
|
||||
if (ex == 0)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(mx);
|
||||
mx <<= (52 - s);
|
||||
ex = (int)s - 51;
|
||||
}
|
||||
else
|
||||
{
|
||||
mx |= 1ULL << 52;
|
||||
}
|
||||
my = yb & 0xFFFFFFFFFFFFFULL;
|
||||
if (ey == 0)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(my);
|
||||
my <<= (52 - s);
|
||||
ey = (int)s - 51;
|
||||
}
|
||||
else
|
||||
{
|
||||
my |= 1ULL << 52;
|
||||
}
|
||||
|
||||
/* Long division: subtract the aligned divisor significand once per
|
||||
* bit of quotient, doubling the remainder between bits. */
|
||||
for (; ex > ey; ex--)
|
||||
{
|
||||
if (mx >= my)
|
||||
{
|
||||
i = mx - my;
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
mx <<= 1;
|
||||
}
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
|
||||
/* Bring the msb of the remainder back to bit 52. */
|
||||
for (; (mx >> 52) == 0; mx <<= 1, ex--)
|
||||
{
|
||||
}
|
||||
|
||||
if (ex > 0)
|
||||
{
|
||||
xb = sx | ((unsigned long long)ex << 52) | (mx - (1ULL << 52));
|
||||
}
|
||||
else
|
||||
{
|
||||
xb = sx | (mx >> (1 - ex));
|
||||
}
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. Value = m * 2^(ex - 150) with the msb of
|
||||
* m at bit 23. */
|
||||
static float
|
||||
fmod_f(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
unsigned int sx;
|
||||
unsigned int mx;
|
||||
unsigned int my;
|
||||
unsigned int s;
|
||||
unsigned int i;
|
||||
int ex;
|
||||
int ey;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
sx = xb & (1U << 31);
|
||||
ex = (int)((xb >> 23) & 0xff);
|
||||
ey = (int)((yb >> 23) & 0xff);
|
||||
|
||||
if (ex == 0xff || (yb << 1) == 0 || (ey == 0xff && (yb & 0x7FFFFFU) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
return __builtin_nanf("");
|
||||
}
|
||||
if ((xb << 1) == 0 || ey == 0xff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
if ((xb & 0x7FFFFFFFU) <= (yb & 0x7FFFFFFFU))
|
||||
{
|
||||
if ((xb & 0x7FFFFFFFU) == (yb & 0x7FFFFFFFU))
|
||||
{
|
||||
return x * 0.0f;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
mx = xb & 0x7FFFFFU;
|
||||
if (ex == 0)
|
||||
{
|
||||
s = 31 - (unsigned int)__builtin_clz(mx);
|
||||
mx <<= (23 - s);
|
||||
ex = (int)s - 22;
|
||||
}
|
||||
else
|
||||
{
|
||||
mx |= 1U << 23;
|
||||
}
|
||||
my = yb & 0x7FFFFFU;
|
||||
if (ey == 0)
|
||||
{
|
||||
s = 31 - (unsigned int)__builtin_clz(my);
|
||||
my <<= (23 - s);
|
||||
ey = (int)s - 22;
|
||||
}
|
||||
else
|
||||
{
|
||||
my |= 1U << 23;
|
||||
}
|
||||
|
||||
for (; ex > ey; ex--)
|
||||
{
|
||||
if (mx >= my)
|
||||
{
|
||||
i = mx - my;
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0f;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
mx <<= 1;
|
||||
}
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0f;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
|
||||
for (; (mx >> 23) == 0; mx <<= 1, ex--)
|
||||
{
|
||||
}
|
||||
|
||||
if (ex > 0)
|
||||
{
|
||||
xb = sx | ((unsigned int)ex << 23) | (mx - (1U << 23));
|
||||
}
|
||||
else
|
||||
{
|
||||
xb = sx | (mx >> (1 - ex));
|
||||
}
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m with an explicit
|
||||
* integer bit in bytes 0..7, sign/exponent word se in bytes 8..9. Value =
|
||||
* m * 2^(ex - 16446) with the msb of m at bit 63; a canonical normal has
|
||||
* ex = se & 0x7fff, and a subnormal (se field 0) or a defensive unnormal
|
||||
* (nonzero field with m < 2^63) is normalized by shifting m's msb up to
|
||||
* bit 63. The division reuses the same digit loop as the narrower
|
||||
* formats; only the doubling step differs, because a full-width
|
||||
* significand has no free bit above its msb (see the three-way branch in
|
||||
* the loop below).
|
||||
*/
|
||||
struct fmod_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static long double
|
||||
fmod_l(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct fmod_ld_word p;
|
||||
struct fmod_ld_word q;
|
||||
unsigned long long s;
|
||||
unsigned long long mx;
|
||||
unsigned long long my;
|
||||
unsigned long long i;
|
||||
int ex;
|
||||
int ey;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
ex = p.se & 0x7fff;
|
||||
ey = q.se & 0x7fff;
|
||||
|
||||
/* x not finite, y zero, or y a NaN: domain error. */
|
||||
if (ex == 0x7fff || (q.m == 0 && ey == 0) ||
|
||||
(ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
p.m = 0xC000000000000000ULL;
|
||||
p.se = 0xFFFF;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
/* x zero or a finite x below an infinite y: x unchanged. (x a NaN or
|
||||
* +-Inf fell into the domain branch above.) */
|
||||
if ((p.m == 0 && ex == 0) || ey == 0x7fff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Normalize both significands to the [2^63, 2^64) frame; a subnormal
|
||||
* (field 0) or defensive unnormal (field nonzero, msb below 63) has
|
||||
* its msb shifted up to bit 63 with the exponent adjusted, so every
|
||||
* value below obeys value = m * 2^(ex - 16446). */
|
||||
if (p.m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(p.m);
|
||||
p.m <<= (63 - s);
|
||||
ex = (ex == 0 ? 1 : ex) + (int)s - 63;
|
||||
}
|
||||
if (q.m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - (unsigned long long)__builtin_clzll(q.m);
|
||||
q.m <<= (63 - s);
|
||||
ey = (ey == 0 ? 1 : ey) + (int)s - 63;
|
||||
}
|
||||
mx = p.m;
|
||||
my = q.m;
|
||||
|
||||
if (ex < ey || (ex == ey && mx <= my))
|
||||
{
|
||||
if (ex == ey && mx == my)
|
||||
{
|
||||
return x * 0.0L;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Long division with a full-width significand: a 64-bit divisor
|
||||
* leaves no headroom above its own msb for the per-bit doubling, so
|
||||
* the doubled remainder that overflows is exactly one divisor at the
|
||||
* next, half, weight and is absorbed by a subtraction there. */
|
||||
for (; ex > ey; ex--)
|
||||
{
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0L;
|
||||
}
|
||||
mx = 2 * i;
|
||||
}
|
||||
else if (2 * mx < mx)
|
||||
{
|
||||
mx = 2 * mx - my;
|
||||
}
|
||||
else
|
||||
{
|
||||
mx = 2 * mx;
|
||||
}
|
||||
}
|
||||
i = mx - my;
|
||||
if (mx >= my)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
return x * 0.0L;
|
||||
}
|
||||
mx = i;
|
||||
}
|
||||
|
||||
/* Bring the msb of the remainder back to bit 63. */
|
||||
for (; mx < 0x8000000000000000ULL; mx <<= 1, ex--)
|
||||
{
|
||||
}
|
||||
|
||||
if (ex > 0)
|
||||
{
|
||||
p.se = (unsigned short)((p.se & 0x8000) | (unsigned short)ex);
|
||||
p.m = mx;
|
||||
}
|
||||
else
|
||||
{
|
||||
p.se = (unsigned short)(p.se & 0x8000);
|
||||
p.m = mx >> (1 - ex);
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float remainder, as fmod_d.
|
||||
*/
|
||||
float
|
||||
fmodf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmod_f(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The double remainder, as fmod_d.
|
||||
*/
|
||||
double
|
||||
fmod(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmod_d(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* The long double remainder, as fmod_d.
|
||||
*/
|
||||
long double
|
||||
fmodl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return fmod_l(x, y);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Split x into a fraction f in [1/2, 1) (or 0) and an integer exponent
|
||||
* *exp with x == f * 2^(*exp), all three precisions (C23 7.12.6.4).
|
||||
* frexp(±0) returns x with *exp 0; frexp(±Inf) and frexp(NaN) return x
|
||||
* with *exp 0 (the glibc behavior the tests pin down; the C standard
|
||||
* leaves the Inf/NaN exponent unspecified). The fraction keeps x's sign,
|
||||
* so frexp(-6.0) is -0.75 with *exp 3 and frexp(0x1p-1074) is 0.5 with
|
||||
* *exp -1073.
|
||||
*
|
||||
* Each precision works from the same unified view used across the
|
||||
* src/math/ slices (see math_impl.h): a finite value is m * 2^p with m the
|
||||
* integer significand carrying the explicit integer bit. The fraction is
|
||||
* m * 2^-w (w = 53/24/64 significand bits), which sits in [1/2, 1), so
|
||||
* *exp is p + w and the result keeps the significand's low bits — for a
|
||||
* normal input the fraction field is simply unchanged and only the
|
||||
* exponent field is rewritten.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction, 53-bit significand. A normal input has
|
||||
* p = ef - 1075, so *exp = ef - 1022 and the fraction field is the input's
|
||||
* own 52-bit fraction (the implicit bit stays put as the leading 1 of the
|
||||
* fraction's [1/2, 1) significand). A subnormal input with fraction msb
|
||||
* at position s (0..51) is value frac * 2^-1074; shifting frac up to the
|
||||
* implicit-bit position makes m = frac << (52 - s), hence *exp = s - 1073
|
||||
* and fraction field m - 2^52.
|
||||
*/
|
||||
double
|
||||
frexp(double x, int *exp)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long sign;
|
||||
unsigned long long ef;
|
||||
unsigned long long frac;
|
||||
unsigned long long m;
|
||||
int s;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1ULL << 63);
|
||||
ef = (bits >> 52) & 0x7ff;
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
if (ef == 0x7ff)
|
||||
{
|
||||
*exp = 0;
|
||||
return x; /* ±Inf and NaN */
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x; /* ±0 */
|
||||
}
|
||||
s = 63 - __builtin_clzll(frac);
|
||||
m = frac << (52 - s);
|
||||
*exp = s - 1073;
|
||||
bits = sign | (0x3feULL << 52) | (m - (1ULL << 52));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
*exp = (int)ef - 1022;
|
||||
bits = sign | (0x3feULL << 52) | frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction, 24-bit significand. Normal *exp =
|
||||
* ef - 126; a subnormal input with fraction msb at s (0..22) normalizes to
|
||||
* m = frac << (23 - s) and has *exp = s - 148.
|
||||
*/
|
||||
float
|
||||
frexpf(float x, int *exp)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int sign;
|
||||
unsigned int ef;
|
||||
unsigned int frac;
|
||||
unsigned int m;
|
||||
int s;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1U << 31);
|
||||
ef = (bits >> 23) & 0xff;
|
||||
frac = bits & 0x7FFFFFU;
|
||||
|
||||
if (ef == 0xff)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
s = 31 - __builtin_clz(frac);
|
||||
m = frac << (23 - s);
|
||||
*exp = s - 148;
|
||||
bits = sign | (0x7eU << 23) | (m - (1U << 23));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
*exp = (int)ef - 126;
|
||||
bits = sign | (0x7eU << 23) | frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m with an explicit
|
||||
* integer bit, plus a sign/exponent word se biased by 16383. A canonical
|
||||
* normal has m in [2^63, 2^64) and p = ef - 16446, so *exp = ef - 16382
|
||||
* and the fraction is m * 2^-64 with se rewritten to 16382. A subnormal
|
||||
* (ef == 0) is value m * 2^-16445; shifting m's msb (position s, 0..62)
|
||||
* up to bit 63 gives *exp = s - 16444. Unnormal inputs (ef > 0 with
|
||||
* m < 2^63) normalize the same way with *exp = ef + s - 16445.
|
||||
*/
|
||||
long double
|
||||
frexpl(long double x, int *exp)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long m;
|
||||
int ef;
|
||||
int s;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
m = p.m;
|
||||
ef = p.se & 0x7fff;
|
||||
|
||||
if (ef == 0x7fff)
|
||||
{
|
||||
*exp = 0;
|
||||
return x;
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
*exp = 0;
|
||||
return x; /* ±0 and empty degenerate encodings */
|
||||
}
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - __builtin_clzll(m);
|
||||
m <<= (63 - s);
|
||||
*exp = (ef == 0 ? 1 : ef) + s - 16445;
|
||||
p.se = (unsigned short)((p.se & 0x8000) | 0x3ffe);
|
||||
p.m = m;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
*exp = ef - 16382;
|
||||
p.se = (unsigned short)((p.se & 0x8000) | 0x3ffe);
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* hypot(x,y) = sqrt(x*x + y*y) without spurious overflow/underflow, after
|
||||
* musl src/math/hypot.c: |x| >= |y| via union sort, inf/NaN specials, a
|
||||
* power-of-two scaling window, and an exact double-double square so the
|
||||
* final sqrt argument is precise. No errno: the scaling avoids overflow. */
|
||||
static const double split = 0x1p27 + 1.0; /* FLT_EVAL_METHOD <= 1 on x86-64 */
|
||||
|
||||
static double
|
||||
vlibc_hypot_core(double x, double y)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} ux, uy, ut;
|
||||
int ex;
|
||||
int ey;
|
||||
double hx;
|
||||
double lx;
|
||||
double hy;
|
||||
double ly;
|
||||
double z;
|
||||
double xc;
|
||||
double xh;
|
||||
double xl;
|
||||
|
||||
/* arrange |x| >= |y| */
|
||||
ux.f = x;
|
||||
uy.f = y;
|
||||
ux.i &= 0x7fffffffffffffffULL;
|
||||
uy.i &= 0x7fffffffffffffffULL;
|
||||
if (ux.i < uy.i)
|
||||
{
|
||||
ut = ux;
|
||||
ux = uy;
|
||||
uy = ut;
|
||||
}
|
||||
ex = (int)(ux.i >> 52);
|
||||
ey = (int)(uy.i >> 52);
|
||||
x = ux.f;
|
||||
y = uy.f;
|
||||
/* note: hypot(inf,nan) == inf */
|
||||
if (ey == 0x7ff)
|
||||
return y;
|
||||
if (ex == 0x7ff || uy.i == 0)
|
||||
return x;
|
||||
/* note: hypot(x,y) ~= x + y*y/x/2 with inexact for small y/x */
|
||||
if (ex - ey > 64)
|
||||
return x + y;
|
||||
|
||||
/* precise sqrt argument in nearest rounding mode without overflow:
|
||||
* xh*xh must not overflow and xl*xl must not underflow */
|
||||
z = 1.0;
|
||||
if (ex > 0x3ff + 510)
|
||||
{
|
||||
z = 0x1p700;
|
||||
x *= 0x1p-700;
|
||||
y *= 0x1p-700;
|
||||
}
|
||||
else if (ey < 0x3ff - 450)
|
||||
{
|
||||
z = 0x1p-700;
|
||||
x *= 0x1p700;
|
||||
y *= 0x1p700;
|
||||
}
|
||||
/* hx+lx == x*x exactly (double-double square) */
|
||||
xc = x * split;
|
||||
xh = x - xc + xc;
|
||||
xl = x - xh;
|
||||
hx = x * x;
|
||||
lx = xh * xh - hx + 2.0 * xh * xl + xl * xl;
|
||||
xc = y * split;
|
||||
xh = y - xc + xc;
|
||||
xl = y - xh;
|
||||
hy = y * y;
|
||||
ly = xh * xh - hy + 2.0 * xh * xl + xl * xl;
|
||||
x = ly + lx + hy + hx;
|
||||
__asm__("sqrtsd %1, %0" : "=x"(y) : "x"(x));
|
||||
return z * y;
|
||||
}
|
||||
|
||||
/* hypotf after musl src/math/hypotf.c: same shape, scaled into a safe
|
||||
* window and evaluated in double for the final square and sqrt. */
|
||||
static float
|
||||
vlibc_hypotf_core(float x, float y)
|
||||
{
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} ux, uy, ut;
|
||||
float z;
|
||||
|
||||
ux.f = x;
|
||||
uy.f = y;
|
||||
ux.i &= 0x7fffffffU;
|
||||
uy.i &= 0x7fffffffU;
|
||||
if (ux.i < uy.i)
|
||||
{
|
||||
ut = ux;
|
||||
ux = uy;
|
||||
uy = ut;
|
||||
}
|
||||
x = ux.f;
|
||||
y = uy.f;
|
||||
if (uy.i == 0xffU << 23)
|
||||
return y; /* hypot(inf,nan) == inf */
|
||||
if (ux.i >= 0xffU << 23 || uy.i == 0 || ux.i - uy.i >= 25U << 23)
|
||||
return x + y;
|
||||
z = 1.0f;
|
||||
if (ux.i >= (0x7fU + 60) << 23)
|
||||
{
|
||||
z = 0x1p90f;
|
||||
x *= 0x1p-90f;
|
||||
y *= 0x1p-90f;
|
||||
}
|
||||
else if (uy.i < (0x7fU - 60) << 23)
|
||||
{
|
||||
z = 0x1p-90f;
|
||||
x *= 0x1p90f;
|
||||
y *= 0x1p90f;
|
||||
}
|
||||
x = (float)((double)x * x + (double)y * y);
|
||||
__asm__("sqrtss %1, %0" : "=x"(y) : "x"(x));
|
||||
return z * y;
|
||||
}
|
||||
|
||||
/* hypotl after musl src/math/hypotl.c (ld80 branch): the full 64-bit
|
||||
* mantissa path with a 2^10000 scaling window and an exact double-double
|
||||
* square. */
|
||||
static long double
|
||||
vlibc_hypotl_core(long double x, long double y)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} ux;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} uy;
|
||||
int ex;
|
||||
int ey;
|
||||
long double hx;
|
||||
long double lx;
|
||||
long double hy;
|
||||
long double ly;
|
||||
long double z;
|
||||
long double xc;
|
||||
long double xh;
|
||||
long double xl;
|
||||
|
||||
ux.f = x;
|
||||
uy.f = y;
|
||||
ux.p.se &= 0x7fff;
|
||||
uy.p.se &= 0x7fff;
|
||||
if (ux.p.se < uy.p.se)
|
||||
{
|
||||
ex = uy.p.se;
|
||||
ey = ux.p.se;
|
||||
x = uy.f;
|
||||
y = ux.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
ex = ux.p.se;
|
||||
ey = uy.p.se;
|
||||
x = ux.f;
|
||||
y = uy.f;
|
||||
}
|
||||
if (ex == 0x7fff && __builtin_isinf(y))
|
||||
return y; /* hypot(inf,nan) == inf */
|
||||
if (ex == 0x7fff || y == 0.0L)
|
||||
return x;
|
||||
if (ex - ey > 64)
|
||||
return x + y;
|
||||
z = 1.0L;
|
||||
if (ex > 0x3fff + 8000)
|
||||
{
|
||||
z = 0x1p10000L;
|
||||
x *= 0x1p-10000L;
|
||||
y *= 0x1p-10000L;
|
||||
}
|
||||
else if (ey < 0x3fff - 8000)
|
||||
{
|
||||
z = 0x1p-10000L;
|
||||
x *= 0x1p10000L;
|
||||
y *= 0x1p10000L;
|
||||
}
|
||||
/* hx+lx == x*x exactly (double-double square) */
|
||||
xc = x * (0x1p32L + 1.0L);
|
||||
xh = x - xc + xc;
|
||||
xl = x - xh;
|
||||
hx = x * x;
|
||||
lx = xh * xh - hx + 2.0L * xh * xl + xl * xl;
|
||||
xc = y * (0x1p32L + 1.0L);
|
||||
xh = y - xc + xc;
|
||||
xl = y - xh;
|
||||
hy = y * y;
|
||||
ly = xh * xh - hy + 2.0L * xh * xl + xl * xl;
|
||||
x = ly + lx + hy + hx;
|
||||
__asm__("fsqrt" : "+t"(x));
|
||||
return z * x;
|
||||
}
|
||||
|
||||
double
|
||||
hypot(double x, double y)
|
||||
{
|
||||
double z = vlibc_hypot_core(x, y);
|
||||
|
||||
if (z == __builtin_huge_val() && __builtin_isfinite(x) && __builtin_isfinite(y))
|
||||
SET_ERANGE(); /* genuine overflow: glibc parity */
|
||||
return z;
|
||||
}
|
||||
|
||||
float
|
||||
hypotf(float x, float y)
|
||||
{
|
||||
float z = vlibc_hypotf_core(x, y);
|
||||
|
||||
if (z == __builtin_huge_valf() && __builtin_isfinite(x) && __builtin_isfinite(y))
|
||||
SET_ERANGE();
|
||||
return z;
|
||||
}
|
||||
|
||||
long double
|
||||
hypotl(long double x, long double y)
|
||||
{
|
||||
long double z = vlibc_hypotl_core(x, y);
|
||||
|
||||
if (z == __builtin_huge_vall() && __builtin_isfinite(x) && __builtin_isfinite(y))
|
||||
SET_ERANGE();
|
||||
return z;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The signed exponent of x as an int (C23 7.12.6.5p1), all three
|
||||
* precisions: ilogb(x) is floor(log2 |x|) for a nonzero finite x. The
|
||||
* sentinel returns are FP_ILOGB0 (INT_MIN) for ±0 and FP_ILOGBNAN
|
||||
* (INT_MAX) for ±Inf and NaN, without touching errno, so the functions
|
||||
* carry the const attribute and never trap. Subnormal arguments are
|
||||
* normalized by scanning the fraction for its highest set bit.
|
||||
*
|
||||
* ilogb(1.0) == 0, ilogb(8.0) == 3, ilogb(0.5) == -1, and the smallest
|
||||
* subnormal double, 2^-1074, yields -1074.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bits 62..52 hold the exponent biased by 1023 and
|
||||
* bits 51..0 the fraction; a subnormal with fraction F (no implicit
|
||||
* bit) is F * 2^-1074, so its exponent is the fraction's highest set
|
||||
* bit position minus 1074.
|
||||
*/
|
||||
int
|
||||
ilogb(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e == 0x7ff)
|
||||
{
|
||||
/* ±Inf and NaN both map to FP_ILOGBNAN. */
|
||||
return FP_ILOGBNAN;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
msb = 63 - __builtin_clzll(frac);
|
||||
return msb - 1074;
|
||||
}
|
||||
return e - 1023;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bits 30..23 hold the exponent biased by 127 and
|
||||
* bits 22..0 the fraction; a subnormal with fraction F is F * 2^-149.
|
||||
*/
|
||||
int
|
||||
ilogbf(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0x7FFFFFU;
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e == 0xff)
|
||||
{
|
||||
return FP_ILOGBNAN;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
msb = 31 - __builtin_clz(frac);
|
||||
return msb - 149;
|
||||
}
|
||||
return e - 127;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (explicit
|
||||
* integer bit) with a sign/exponent word se biased by 16383. A normal m
|
||||
* in [2^63, 2^64) sits in [2^(e-16383), 2^(e-16382)). A subnormal with
|
||||
* e == 0 is interpreted as if e were 1, i.e. m * 2^-16445; an unnormal
|
||||
* (e > 0 with m < 2^63) is m * 2^(e - 16446).
|
||||
*/
|
||||
int
|
||||
ilogbl(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e == 0x7fff)
|
||||
{
|
||||
return FP_ILOGBNAN;
|
||||
}
|
||||
if (p.m == 0)
|
||||
{
|
||||
return FP_ILOGB0;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return msb - 16445;
|
||||
}
|
||||
if (p.m < 0x8000000000000000ULL)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return msb + e - 16446;
|
||||
}
|
||||
return e - 16383;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "math_impl.h"
|
||||
|
||||
/*
|
||||
* x times 2^n (C23 7.12.6.3), all three precisions: the inverse of frexp.
|
||||
* Scaling a finite value by a power of two is exact whenever the result is
|
||||
* a normal number; only a result that lands in the subnormal range can
|
||||
* need rounding (the vl_rne_u64 round-to-nearest-even shift in the shared
|
||||
* scale cores, see math_impl.h). Overflow returns +-Inf and, in the
|
||||
* library build, sets errno to ERANGE as POSIX requires.
|
||||
*
|
||||
* The errno write is a TCB dereference that only exists when this file is
|
||||
* compiled as part of the real library (config.h present). The host-
|
||||
* linked standalone test binaries compile these sources without
|
||||
* HAVE_CONFIG_H, so they never reference __errno_location and never touch
|
||||
* the host's errno thread slot; the tests therefore check values only.
|
||||
* ldexp(±0) is ±0 for any n, and ±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Scale a double: the shared core reports overflow through the flag and
|
||||
* this wrapper raises ERANGE when the library build demands it.
|
||||
*/
|
||||
double
|
||||
ldexp(double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
double r = vl_scale2_d(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
float
|
||||
ldexpf(float x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
float r = vl_scale2_f(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
long double
|
||||
ldexpl(long double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
long double r = vl_scale2_ld(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x in the current rounding direction,
|
||||
* returned as long long (C23 7.12.9.8), all three precisions. A result
|
||||
* outside the range of long long is a range error whose return value is
|
||||
* unspecified, so the header declares no const attribute; the test
|
||||
* corpus keeps |x| < 2^62 where every result is exact.
|
||||
*
|
||||
* As for lrint (see lrint.c): the __builtin_llrint* forms are never
|
||||
* folded on this target, so llrint is built as rint-then-convert, with
|
||||
* __builtin_rint* folding to the in-line round-to-nearest-even sequence
|
||||
* and the cast to long long being exact on the integral result.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As llrint, for a float argument.
|
||||
*/
|
||||
long long
|
||||
llrintf(float x)
|
||||
{
|
||||
return (long long)__builtin_rintf(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As llrint, for a double argument.
|
||||
*/
|
||||
long long
|
||||
llrint(double x)
|
||||
{
|
||||
return (long long)__builtin_rint(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As llrint, for a long double argument.
|
||||
*/
|
||||
long long
|
||||
llrintl(long double x)
|
||||
{
|
||||
return (long long)__builtin_rintl(x);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x, halfway cases rounded away from zero,
|
||||
* returned as long long (C23 7.12.9.10), all three precisions.
|
||||
*
|
||||
* Rounding is delegated to the round/roundf/roundl implementations (see
|
||||
* round.c): on this target every __builtin_round* is an external call even
|
||||
* at -O2 -- no SSE4.1 in the default -march -- so round() here reaches the
|
||||
* library's own half-away-from-zero rounding, returning an exactly
|
||||
* integral value of the same floating type. Narrowing that value to long
|
||||
* long is then a plain, exact conversion whenever it is in range.
|
||||
*
|
||||
* The unrepresentable cases mirror the host glibc on x86-64, whose lround
|
||||
* family is the hardware cvttsd2si sequence: a NaN, an Inf, and any finite
|
||||
* result of magnitude >= 2^63 (the smallest such rounded value is exactly
|
||||
* 2^63) all collapse to LLONG_MIN with errno untouched -- measured against
|
||||
* glibc 2.44, which returns LLONG_MIN for 1e300, -1e300, +-Inf and NaN and
|
||||
* never sets errno. The range checks happen in the argument's own
|
||||
* precision (0x1p63 is exact in float, double and the x87 extended
|
||||
* format), and -2^63 -- exactly representable and equal to LLONG_MIN -- is
|
||||
* deliberately allowed through the strict lower-bound test.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As llround, for a float argument.
|
||||
*/
|
||||
long long
|
||||
llroundf(float x)
|
||||
{
|
||||
float r = roundf(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63f || r < -0x1p63f)
|
||||
{
|
||||
return LLONG_MIN;
|
||||
}
|
||||
return (long long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As llround, for a double argument.
|
||||
*/
|
||||
long long
|
||||
llround(double x)
|
||||
{
|
||||
double r = round(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63 || r < -0x1p63)
|
||||
{
|
||||
return LLONG_MIN;
|
||||
}
|
||||
return (long long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As llround, for a long double argument.
|
||||
*/
|
||||
long long
|
||||
llroundl(long double x)
|
||||
{
|
||||
long double r = roundl(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63L || r < -0x1p63L)
|
||||
{
|
||||
return LLONG_MIN;
|
||||
}
|
||||
return (long long)r;
|
||||
}
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static const double two54 = 1.80143985094819840000e+16; /* 0x4350000000000000 */
|
||||
static const double ln2_hi = 6.93147180369123816490e-01; /* 0x3fe62e42fee00000 */
|
||||
static const double ln2_lo = 1.90821492927058770002e-10; /* 0x3dea39ef35793c76 */
|
||||
static const double Lg1 = 6.666666666666735130e-01; /* 0x3FE5555555555593 */
|
||||
static const double Lg2 = 3.999999999940941908e-01; /* 0x3FD999999997FA04 */
|
||||
static const double Lg3 = 2.857142874366239149e-01; /* 0x3FD2492494229359 */
|
||||
static const double Lg4 = 2.222219843214978396e-01; /* 0x3FCC71C51D8E78AF */
|
||||
static const double Lg5 = 1.818357216161805012e-01; /* 0x3FC7466496CB03DE */
|
||||
static const double Lg6 = 1.531383769920937332e-01; /* 0x3FC39A09D078C69F */
|
||||
static const double Lg7 = 1.479819860511658591e-01; /* 0x3FC2F112DF3E5244 */
|
||||
|
||||
/* 80-bit ld80 ln2 split: ln2hi_l is a 15-bit hi word (0xb.172p-4L) so that
|
||||
* k * ln2hi_l is exact for |k| < 2^15, and ln2hi_l + ln2lo_l == ln2. */
|
||||
static const long double ln2hi_l = 6.9314575195312500000000e-1L; /* 0xb.172p-4L */
|
||||
static const long double ln2lo_l = 1.4286068203094172321214581765680755e-6L;
|
||||
static const long double ivln2_l = 0x1.71547652b82fe178p+0L; /* 1/ln2, 64-bit */
|
||||
static const long double ivln10_l = 0x1.bcb7b1526e50e32ap-2L; /* 1/ln10, 64-bit */
|
||||
|
||||
/* log(x) = k * ln2 + log(1+f), fdlibm e_log with the double split into
|
||||
* ln2_hi/ln2_lo so k*ln2_hi is exact for |k| <= 1024. */
|
||||
static double
|
||||
vlibc_log_core(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
unsigned int hx;
|
||||
unsigned int lx;
|
||||
int k;
|
||||
int i;
|
||||
int j;
|
||||
double f;
|
||||
double s;
|
||||
double z;
|
||||
double R;
|
||||
double w;
|
||||
double t1;
|
||||
double t2;
|
||||
double hfsq;
|
||||
double dk;
|
||||
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32);
|
||||
lx = (unsigned int)u.i;
|
||||
k = 0;
|
||||
|
||||
if (hx >= 0x80000000U)
|
||||
{ /* sign bit set: -0 or negative */
|
||||
if (((hx & 0x7fffffffU) | lx) == 0U)
|
||||
return -__builtin_huge_val(); /* log(-0) = -inf */
|
||||
return __builtin_nan(""); /* log(negative) = NaN */
|
||||
}
|
||||
if ((hx | lx) == 0U)
|
||||
return -__builtin_huge_val(); /* log(+0) = -inf */
|
||||
if (hx >= 0x7ff00000U)
|
||||
return x + x; /* +inf -> +inf, NaN -> NaN */
|
||||
|
||||
if (hx < 0x00100000U)
|
||||
{ /* subnormal: scale into the normal range */
|
||||
k -= 54;
|
||||
x *= two54;
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32);
|
||||
lx = (unsigned int)u.i;
|
||||
}
|
||||
k += (int)(hx >> 20) - 1023;
|
||||
hx &= 0x000fffffU;
|
||||
i = (int)((hx + 0x95f64U) & 0x100000U);
|
||||
u.i = ((unsigned long long)(hx | (i ^ 0x3ff00000U)) << 32) | (unsigned long long)lx;
|
||||
x = u.f;
|
||||
k += i >> 20;
|
||||
f = x - 1.0;
|
||||
|
||||
if ((0x000fffffU & (2U + hx)) < 3U)
|
||||
{ /* |f| < 2^-20 */
|
||||
if (f == 0.0)
|
||||
{
|
||||
if (k == 0)
|
||||
return 0.0;
|
||||
dk = (double)k;
|
||||
return dk * ln2_hi + dk * ln2_lo;
|
||||
}
|
||||
R = f * f * (0.5 - 0.33333333333333333333 * f);
|
||||
if (k == 0)
|
||||
return f - R;
|
||||
dk = (double)k;
|
||||
return dk * ln2_hi - ((R - dk * ln2_lo) - f);
|
||||
}
|
||||
s = f / (2.0 + f);
|
||||
dk = (double)k;
|
||||
z = s * s;
|
||||
i = (int)hx - 0x6147a;
|
||||
w = z * z;
|
||||
j = 0x6b851 - (int)hx;
|
||||
t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
|
||||
t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
|
||||
i |= j;
|
||||
R = t2 + t1;
|
||||
if (i > 0)
|
||||
{
|
||||
hfsq = 0.5 * f * f;
|
||||
if (k == 0)
|
||||
return f - (hfsq - s * (hfsq + R));
|
||||
return dk * ln2_hi - ((hfsq - (s * (hfsq + R) + dk * ln2_lo)) - f);
|
||||
}
|
||||
if (k == 0)
|
||||
return f - s * (f - R);
|
||||
return dk * ln2_hi - ((s * (f - R) - dk * ln2_lo) - f);
|
||||
}
|
||||
|
||||
double
|
||||
log(double x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0)
|
||||
SET_ERANGE();
|
||||
return vlibc_log_core(x);
|
||||
}
|
||||
|
||||
float
|
||||
logf(float x)
|
||||
{
|
||||
return (float)log((double)x);
|
||||
}
|
||||
|
||||
/* z = A/(2^64 + A) for 0 <= A <= 2^62, correctly rounded to long double,
|
||||
* via integer long division of A^2/(2^64 + A) */
|
||||
static long double
|
||||
vlibc_logl_ratio(unsigned long long A)
|
||||
{
|
||||
unsigned long long a0;
|
||||
unsigned long long a1;
|
||||
unsigned long long p0;
|
||||
unsigned long long p1;
|
||||
unsigned long long p2;
|
||||
unsigned long long lo;
|
||||
unsigned long long hi;
|
||||
unsigned long long q;
|
||||
unsigned long long rhi;
|
||||
unsigned long long rlo;
|
||||
unsigned long long zq;
|
||||
int msb;
|
||||
int e;
|
||||
int j;
|
||||
unsigned long long dj;
|
||||
unsigned long long shi;
|
||||
unsigned long long slo;
|
||||
unsigned long long M;
|
||||
unsigned long long c2;
|
||||
unsigned long long l2;
|
||||
int i;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long mm;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} two;
|
||||
|
||||
a0 = A & 0xffffffffULL;
|
||||
a1 = A >> 32;
|
||||
p0 = a0 * a0;
|
||||
p1 = (a1 * a0) << 1;
|
||||
p2 = a1 * a1;
|
||||
lo = p0 + (p1 << 32);
|
||||
hi = p2 + (p1 >> 32) + (lo < p0);
|
||||
|
||||
/* q = floor(A^2 / (2^64 + A)); the top dividend bits shift in with no
|
||||
* quotient bits (q < 2^60), so start with R = hi and shift in lo */
|
||||
q = 0;
|
||||
rhi = 0;
|
||||
rlo = hi;
|
||||
for (i = 63; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (rhi << 1) | (rlo >> 63);
|
||||
unsigned long long nlo = (rlo << 1) | ((lo >> i) & 1ULL);
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
if (i < 60)
|
||||
q |= 1ULL << i;
|
||||
}
|
||||
rhi = nhi;
|
||||
rlo = nlo;
|
||||
}
|
||||
|
||||
/* z*2^64 = zq - R/D with R = (rhi,rlo) in [0, D) */
|
||||
zq = A - q;
|
||||
if (zq == 0)
|
||||
return 0.0L;
|
||||
if (A == 1)
|
||||
{ /* z = (2^64 - 1) * 2^-128: below the general path's reach */
|
||||
two.p.mm = 0xffffffffffffffffULL;
|
||||
two.p.se = (unsigned short)(16383 - 65);
|
||||
return two.f;
|
||||
}
|
||||
/* e = floor(log2 z): z*2^64 is in (zq-1, zq]; significand is
|
||||
* M = round(z * 2^(63-e)) = round(z*2^64 * 2^j) with j = -1-e */
|
||||
msb = 63 - __builtin_clzll((rhi != 0 || rlo != 0) ? zq - 1 : zq);
|
||||
e = msb - 64;
|
||||
j = -1 - e;
|
||||
/* floor(R * 2^j / D) via j more long-division steps */
|
||||
dj = 0;
|
||||
shi = rhi;
|
||||
slo = rlo;
|
||||
for (i = j - 1; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (shi << 1) | (slo >> 63);
|
||||
unsigned long long nlo = slo << 1;
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
dj |= 1ULL << i;
|
||||
}
|
||||
shi = nhi;
|
||||
slo = nlo;
|
||||
}
|
||||
/* M = zq*2^j - dj - r_j/D; round to nearest even */
|
||||
M = (zq << j) - dj;
|
||||
c2 = (shi << 1) + (slo >> 63);
|
||||
l2 = slo << 1;
|
||||
if (c2 > 1 || (c2 == 1 && l2 > A) || (c2 == 1 && l2 == A && (M & 1ULL)))
|
||||
M--;
|
||||
two.p.mm = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + e - 63);
|
||||
return (long double)M * two.f;
|
||||
}
|
||||
|
||||
/* 80-bit x87 log: normalize m to [2^63, 2^64), then
|
||||
* log(x) = k * ln2 + 2*atanh(f/(2+f)). The k*ln2 part is exact and the
|
||||
* atanh sum is accumulated as a double-double pair so the result keeps
|
||||
* full 64-bit precision even when k*ln2 and ln(1+f) cancel. The caller
|
||||
* scales by c to get log, log2, or log10. */
|
||||
static long double
|
||||
vlibc_logl_scaled(long double x, long double c)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
int k;
|
||||
int s;
|
||||
int n;
|
||||
long double f;
|
||||
long double z;
|
||||
long double w;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
long double a1;
|
||||
long double a2;
|
||||
long double b1;
|
||||
long double b2;
|
||||
long double hi;
|
||||
long double lo;
|
||||
|
||||
u.f = x;
|
||||
se = u.p.se;
|
||||
m = u.p.m;
|
||||
if (m == 0)
|
||||
return -__builtin_huge_vall(); /* +-0 -> -inf */
|
||||
if (se != 0U && m < 0x8000000000000000ULL)
|
||||
return __builtin_nanl(""); /* unnormal: invalid operand, glibc NaN */
|
||||
if ((se & 0x8000U) != 0U)
|
||||
return __builtin_nanl(""); /* negative incl -inf -> NaN */
|
||||
if ((se & 0x7fffU) == 0x7fffU)
|
||||
return x; /* +inf, NaN */
|
||||
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{ /* subnormal or pseudo-denormal: normalize */
|
||||
s = __builtin_clzll(m);
|
||||
m <<= s;
|
||||
}
|
||||
else
|
||||
s = 0;
|
||||
k = (se == 0U ? -16382 : (int)(se & 0x7fffU) - 16383) - s;
|
||||
|
||||
f = (long double)m * 0x1p-63L - 1.0L; /* exact, f in [0,1) */
|
||||
if (f >= 0.5L)
|
||||
{ /* fold [0.5,1) into [-0.25,0) so k*ln2 + ln(1+f) does not cancel:
|
||||
* z = -v/(1-v) = -(v + v^2 + v^3 + ...) with v = (2^64 - m)*2^-65 in
|
||||
* (0,1/8], accumulated with TwoSum so the fast convergence keeps z
|
||||
* accurate (a plain division would round its 65-bit denominator) */
|
||||
unsigned long long C = 0ULL - m;
|
||||
long double v = (long double)C * 0x1p-65L;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
int n;
|
||||
|
||||
k += 1;
|
||||
sh = -v;
|
||||
sl = 0.0L;
|
||||
term = v;
|
||||
for (n = 2; n <= 43; n++)
|
||||
{
|
||||
term *= v;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = -term;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
z = sh + sl;
|
||||
}
|
||||
else
|
||||
z = vlibc_logl_ratio(m - 0x8000000000000000ULL);
|
||||
w = z * z;
|
||||
term = z;
|
||||
sh = z;
|
||||
sl = 0.0L;
|
||||
for (n = 3; n <= 43; n += 2)
|
||||
{ /* TwoSum accumulation: (sh,sl) == sum of terms to ~2^-126 */
|
||||
term *= w;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = term / (long double)n;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
a1 = (long double)k * ln2hi_l; /* exact: |k| < 2^15, ln2hi_l is 15 bits */
|
||||
a2 = (long double)k * ln2lo_l;
|
||||
b1 = 2.0L * sh;
|
||||
b2 = 2.0L * sl;
|
||||
/* double-double add (a1,a2) + (b1,b2), then renormalize */
|
||||
s2 = a1 + b1;
|
||||
zz = s2 - a1;
|
||||
e = (a1 - (s2 - zz)) + (b1 - zz);
|
||||
hi = s2;
|
||||
lo = e + a2 + b2;
|
||||
s2 = hi + lo;
|
||||
lo -= s2 - hi;
|
||||
hi = s2;
|
||||
return hi * c + lo * c;
|
||||
}
|
||||
|
||||
static long double
|
||||
vlibc_logl_core(long double x)
|
||||
{
|
||||
return vlibc_logl_scaled(x, 1.0L);
|
||||
}
|
||||
|
||||
long double
|
||||
logl(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_logl_core(x);
|
||||
}
|
||||
|
||||
double
|
||||
log2(double x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0)
|
||||
SET_ERANGE();
|
||||
return (double)vlibc_logl_scaled((long double)x, ivln2_l);
|
||||
}
|
||||
|
||||
float
|
||||
log2f(float x)
|
||||
{
|
||||
return (float)log2((double)x);
|
||||
}
|
||||
|
||||
long double
|
||||
log2l(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_logl_scaled(x, ivln2_l);
|
||||
}
|
||||
|
||||
double
|
||||
log10(double x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0)
|
||||
SET_ERANGE();
|
||||
return (double)vlibc_logl_scaled((long double)x, ivln10_l);
|
||||
}
|
||||
|
||||
float
|
||||
log10f(float x)
|
||||
{
|
||||
return (float)log10((double)x);
|
||||
}
|
||||
|
||||
long double
|
||||
log10l(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
else if (x == 0.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_logl_scaled(x, ivln10_l);
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static const double ln2_hi = 6.93147180369123816490e-01; /* 0x3fe62e42fee00000 */
|
||||
static const double ln2_lo = 1.90821492927058770002e-10; /* 0x3dea39ef35793c76 */
|
||||
static const double Lg1 = 6.666666666666735130e-01; /* 0x3FE5555555555593 */
|
||||
static const double Lg2 = 3.999999999940941908e-01; /* 0x3FD999999997FA04 */
|
||||
static const double Lg3 = 2.857142874366239149e-01; /* 0x3FD2492494229359 */
|
||||
static const double Lg4 = 2.222219843214978396e-01; /* 0x3FCC71C51D8E78AF */
|
||||
static const double Lg5 = 1.818357216161805012e-01; /* 0x3FC7466496CB03DE */
|
||||
static const double Lg6 = 1.531383769920937332e-01; /* 0x3FC39A09D078C69F */
|
||||
static const double Lg7 = 1.479819860511658591e-01; /* 0x3FC2F112DF3E5244 */
|
||||
|
||||
/* 80-bit ld80 ln2 split: ln2hi_l is a 15-bit hi word (0xb.172p-4L) so that
|
||||
* k * ln2hi_l is exact for |k| < 2^15, and ln2hi_l + ln2lo_l == ln2. */
|
||||
static const long double ln2hi_l = 6.9314575195312500000000e-1L; /* 0xb.172p-4L */
|
||||
static const long double ln2lo_l = 1.4286068203094172321214581765680755e-6L;
|
||||
|
||||
/* log1p(x) = log(1+x) for the double case: fdlibm e_log1p with the argument
|
||||
* reduced into [sqrt(2)/2-1, sqrt(2)-1] so no precision is lost for small x. */
|
||||
static double
|
||||
vlibc_log1p_core(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
unsigned int hx;
|
||||
unsigned int hu;
|
||||
int k;
|
||||
double hfsq;
|
||||
double f;
|
||||
double c;
|
||||
double s;
|
||||
double z;
|
||||
double R;
|
||||
double w;
|
||||
double t1;
|
||||
double t2;
|
||||
double dk;
|
||||
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32);
|
||||
k = 1;
|
||||
if (hx < 0x3fda827aU || (hx >> 31) != 0U)
|
||||
{ /* 1+x < sqrt(2)+, or x < 0 */
|
||||
if (hx >= 0xbff00000U)
|
||||
{ /* x <= -1.0 */
|
||||
if (x == -1.0)
|
||||
return -__builtin_huge_val(); /* log1p(-1) = -inf */
|
||||
return __builtin_nan(""); /* log1p(x<-1) = NaN */
|
||||
}
|
||||
if (hx << 1 < 0x3ca00000U << 1)
|
||||
return x; /* |x| < 2^-53: log1p(x) == x */
|
||||
if (hx <= 0xbfd2bec4U)
|
||||
{ /* x in [-sqrt(2)/2+1, sqrt(2)-1]: no reduction needed */
|
||||
k = 0;
|
||||
c = 0.0;
|
||||
f = x;
|
||||
}
|
||||
}
|
||||
else if (hx >= 0x7ff00000U)
|
||||
return x; /* +inf -> +inf, NaN -> NaN */
|
||||
|
||||
if (k != 0)
|
||||
{ /* reduce 1+x into [sqrt(2)/2, sqrt(2)] */
|
||||
u.f = 1.0 + x;
|
||||
hu = (unsigned int)(u.i >> 32);
|
||||
hu += 0x3ff00000U - 0x3fe6a09eU;
|
||||
k = (int)(hu >> 20) - 0x3ff;
|
||||
if (k < 54)
|
||||
{
|
||||
c = k >= 2 ? 1.0 - (u.f - x) : x - (u.f - 1.0);
|
||||
c /= u.f;
|
||||
}
|
||||
else
|
||||
c = 0.0;
|
||||
hu = (hu & 0x000fffffU) + 0x3fe6a09eU;
|
||||
u.i = ((unsigned long long)hu << 32) | (u.i & 0xffffffffULL);
|
||||
f = u.f - 1.0;
|
||||
}
|
||||
hfsq = 0.5 * f * f;
|
||||
s = f / (2.0 + f);
|
||||
z = s * s;
|
||||
w = z * z;
|
||||
t1 = w * (Lg2 + w * (Lg4 + w * Lg6));
|
||||
t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7)));
|
||||
R = t2 + t1;
|
||||
dk = (double)k;
|
||||
return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi;
|
||||
}
|
||||
|
||||
double
|
||||
log1p(double x)
|
||||
{
|
||||
if (x < -1.0)
|
||||
SET_EDOM();
|
||||
else if (x == -1.0)
|
||||
SET_ERANGE();
|
||||
return vlibc_log1p_core(x);
|
||||
}
|
||||
|
||||
float
|
||||
log1pf(float x)
|
||||
{
|
||||
return (float)log1p((double)x);
|
||||
}
|
||||
|
||||
/* z = A/(2^64 + A) for 0 <= A <= 2^62, correctly rounded to long double,
|
||||
* via integer long division of A^2/(2^64 + A) */
|
||||
static long double
|
||||
vlibc_logl_ratio(unsigned long long A)
|
||||
{
|
||||
unsigned long long a0;
|
||||
unsigned long long a1;
|
||||
unsigned long long p0;
|
||||
unsigned long long p1;
|
||||
unsigned long long p2;
|
||||
unsigned long long lo;
|
||||
unsigned long long hi;
|
||||
unsigned long long q;
|
||||
unsigned long long rhi;
|
||||
unsigned long long rlo;
|
||||
unsigned long long zq;
|
||||
int msb;
|
||||
int e;
|
||||
int j;
|
||||
unsigned long long dj;
|
||||
unsigned long long shi;
|
||||
unsigned long long slo;
|
||||
unsigned long long M;
|
||||
unsigned long long c2;
|
||||
unsigned long long l2;
|
||||
int i;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long mm;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} two;
|
||||
|
||||
a0 = A & 0xffffffffULL;
|
||||
a1 = A >> 32;
|
||||
p0 = a0 * a0;
|
||||
p1 = (a1 * a0) << 1;
|
||||
p2 = a1 * a1;
|
||||
lo = p0 + (p1 << 32);
|
||||
hi = p2 + (p1 >> 32) + (lo < p0);
|
||||
|
||||
/* q = floor(A^2 / (2^64 + A)); the top dividend bits shift in with no
|
||||
* quotient bits (q < 2^60), so start with R = hi and shift in lo */
|
||||
q = 0;
|
||||
rhi = 0;
|
||||
rlo = hi;
|
||||
for (i = 63; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (rhi << 1) | (rlo >> 63);
|
||||
unsigned long long nlo = (rlo << 1) | ((lo >> i) & 1ULL);
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
if (i < 60)
|
||||
q |= 1ULL << i;
|
||||
}
|
||||
rhi = nhi;
|
||||
rlo = nlo;
|
||||
}
|
||||
|
||||
/* z*2^64 = zq - R/D with R = (rhi,rlo) in [0, D) */
|
||||
zq = A - q;
|
||||
if (zq == 0)
|
||||
return 0.0L;
|
||||
if (A == 1)
|
||||
{ /* z = (2^64 - 1) * 2^-128: below the general path's reach */
|
||||
two.p.mm = 0xffffffffffffffffULL;
|
||||
two.p.se = (unsigned short)(16383 - 65);
|
||||
return two.f;
|
||||
}
|
||||
/* e = floor(log2 z): z*2^64 is in (zq-1, zq]; significand is
|
||||
* M = round(z * 2^(63-e)) = round(z*2^64 * 2^j) with j = -1-e */
|
||||
msb = 63 - __builtin_clzll((rhi != 0 || rlo != 0) ? zq - 1 : zq);
|
||||
e = msb - 64;
|
||||
j = -1 - e;
|
||||
/* floor(R * 2^j / D) via j more long-division steps */
|
||||
dj = 0;
|
||||
shi = rhi;
|
||||
slo = rlo;
|
||||
for (i = j - 1; i >= 0; i--)
|
||||
{
|
||||
unsigned long long nhi = (shi << 1) | (slo >> 63);
|
||||
unsigned long long nlo = slo << 1;
|
||||
if (nhi > 1 || (nhi == 1 && nlo >= A))
|
||||
{
|
||||
unsigned long long nnlo = nlo - A;
|
||||
unsigned long long borrow = nnlo > nlo;
|
||||
nlo = nnlo;
|
||||
nhi -= 1 + borrow;
|
||||
dj |= 1ULL << i;
|
||||
}
|
||||
shi = nhi;
|
||||
slo = nlo;
|
||||
}
|
||||
/* M = zq*2^j - dj - r_j/D; round to nearest even */
|
||||
M = (zq << j) - dj;
|
||||
c2 = (shi << 1) + (slo >> 63);
|
||||
l2 = slo << 1;
|
||||
if (c2 > 1 || (c2 == 1 && l2 > A) || (c2 == 1 && l2 == A && (M & 1ULL)))
|
||||
M--;
|
||||
two.p.mm = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + e - 63);
|
||||
return (long double)M * two.f;
|
||||
}
|
||||
|
||||
/* 80-bit x87 log: normalize m to [2^63, 2^64), then
|
||||
* log(x) = k * ln2 + 2*atanh(f/(2+f)). The k*ln2 part is exact and the
|
||||
* atanh sum is accumulated as a double-double pair so the result keeps
|
||||
* full 64-bit precision even when k*ln2 and ln(1+f) cancel. */
|
||||
static long double
|
||||
vlibc_logl_core(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
int k;
|
||||
int s;
|
||||
int n;
|
||||
long double f;
|
||||
long double z;
|
||||
long double w;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
long double a1;
|
||||
long double a2;
|
||||
long double b1;
|
||||
long double b2;
|
||||
long double hi;
|
||||
long double lo;
|
||||
|
||||
u.f = x;
|
||||
se = u.p.se;
|
||||
m = u.p.m;
|
||||
if (m == 0)
|
||||
return -__builtin_huge_vall(); /* +-0 -> -inf */
|
||||
if (se != 0U && m < 0x8000000000000000ULL)
|
||||
return __builtin_nanl(""); /* unnormal: invalid operand, glibc NaN */
|
||||
if ((se & 0x8000U) != 0U)
|
||||
return __builtin_nanl(""); /* negative incl -inf -> NaN */
|
||||
if ((se & 0x7fffU) == 0x7fffU)
|
||||
return x; /* +inf, NaN */
|
||||
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{ /* subnormal or pseudo-denormal: normalize */
|
||||
s = __builtin_clzll(m);
|
||||
m <<= s;
|
||||
}
|
||||
else
|
||||
s = 0;
|
||||
k = (se == 0U ? -16382 : (int)(se & 0x7fffU) - 16383) - s;
|
||||
|
||||
f = (long double)m * 0x1p-63L - 1.0L; /* exact, f in [0,1) */
|
||||
if (f >= 0.5L)
|
||||
{ /* fold [0.5,1) into [-0.25,0) so k*ln2 + ln(1+f) does not cancel:
|
||||
* z = -v/(1-v) = -(v + v^2 + v^3 + ...) with v = (2^64 - m)*2^-65 in
|
||||
* (0,1/8], accumulated with TwoSum so the fast convergence keeps z
|
||||
* accurate (a plain division would round its 65-bit denominator) */
|
||||
unsigned long long C = 0ULL - m;
|
||||
long double v = (long double)C * 0x1p-65L;
|
||||
long double term;
|
||||
long double sh;
|
||||
long double sl;
|
||||
long double t;
|
||||
long double e;
|
||||
long double s2;
|
||||
long double zz;
|
||||
int n;
|
||||
|
||||
k += 1;
|
||||
sh = -v;
|
||||
sl = 0.0L;
|
||||
term = v;
|
||||
for (n = 2; n <= 43; n++)
|
||||
{
|
||||
term *= v;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = -term;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
z = sh + sl;
|
||||
}
|
||||
else
|
||||
z = vlibc_logl_ratio(m - 0x8000000000000000ULL);
|
||||
w = z * z;
|
||||
term = z;
|
||||
sh = z;
|
||||
sl = 0.0L;
|
||||
for (n = 3; n <= 43; n += 2)
|
||||
{ /* TwoSum accumulation: (sh,sl) == sum of terms to ~2^-126 */
|
||||
term *= w;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
t = term / (long double)n;
|
||||
s2 = sh + t;
|
||||
zz = s2 - sh;
|
||||
e = (sh - (s2 - zz)) + (t - zz);
|
||||
sh = s2;
|
||||
sl += e;
|
||||
}
|
||||
a1 = (long double)k * ln2hi_l; /* exact: |k| < 2^15, ln2hi_l is 15 bits */
|
||||
a2 = (long double)k * ln2lo_l;
|
||||
b1 = 2.0L * sh;
|
||||
b2 = 2.0L * sl;
|
||||
/* double-double add (a1,a2) + (b1,b2), then renormalize */
|
||||
s2 = a1 + b1;
|
||||
zz = s2 - a1;
|
||||
e = (a1 - (s2 - zz)) + (b1 - zz);
|
||||
hi = s2;
|
||||
lo = e + a2 + b2;
|
||||
s2 = hi + lo;
|
||||
lo -= s2 - hi;
|
||||
hi = s2;
|
||||
return hi + lo;
|
||||
}
|
||||
|
||||
/* 80-bit x87 log1p: y = 1+x as an exact double-double pair (yh,yl), then
|
||||
* log1p(x) = log(yh) + log(1 + yl/yh) = log(yh) + t with |t| <= 2^-64, so
|
||||
* 1+x never rounds away the small argument. */
|
||||
static long double
|
||||
vlibc_log1pl_core(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
long double yh;
|
||||
long double yl;
|
||||
long double t;
|
||||
|
||||
u.f = x;
|
||||
se = u.p.se;
|
||||
m = u.p.m;
|
||||
if (m == 0)
|
||||
return x; /* +-0 -> +-0 */
|
||||
if (se != 0U && m < 0x8000000000000000ULL)
|
||||
return __builtin_nanl(""); /* unnormal: invalid operand, glibc NaN */
|
||||
if ((se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (m == 0x8000000000000000ULL)
|
||||
return (se & 0x8000U) != 0U ? __builtin_nanl("") : x; /* -inf, +inf */
|
||||
return x; /* NaN */
|
||||
}
|
||||
if (x <= -1.0L)
|
||||
return x == -1.0L ? -__builtin_huge_vall() : __builtin_nanl("");
|
||||
if (x > -0x1p-65L && x < 0x1p-65L)
|
||||
{
|
||||
if (se == 0U && m >= 0x8000000000000000ULL)
|
||||
{ /* pseudo-denormal: glibc returns the (m, 1) encoding */
|
||||
u.p.se = 1;
|
||||
return u.f;
|
||||
}
|
||||
return x; /* |x| < 2^-65: log1p(x) == x */
|
||||
}
|
||||
yh = 1.0L + x;
|
||||
yl = x - (yh - 1.0L); /* exact residual of the 1+x rounding for |x| <= 1 */
|
||||
t = yl / yh;
|
||||
return vlibc_logl_core(yh) + t;
|
||||
}
|
||||
|
||||
long double
|
||||
log1pl(long double x)
|
||||
{
|
||||
if (x < -1.0L)
|
||||
SET_EDOM();
|
||||
else if (x == -1.0L)
|
||||
SET_ERANGE();
|
||||
return vlibc_log1pl_core(x);
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The signed exponent of x as a floating-point value (C23 7.12.6.6),
|
||||
* all three precisions: logb(x) is floor(log2 |x|) as a float/double/
|
||||
* long double. logb(±0) is -Inf and logb(±Inf) is +Inf, each raising
|
||||
* the division-by-zero/invalid exception through the hardware but
|
||||
* without an errno path in the representable domain; logb(NaN) returns
|
||||
* the NaN. Pure, so the header marks the functions const; the exponent
|
||||
* extraction is exactly the ilogb one (see ilogb.c) with the int result
|
||||
* converted back to the argument's precision.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bits 62..52 hold the exponent biased by 1023 and
|
||||
* bits 51..0 the fraction. A subnormal with fraction F is F * 2^-1074.
|
||||
*/
|
||||
double
|
||||
logb(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e == 0x7ff)
|
||||
{
|
||||
/* Inf maps to +Inf (both signs); NaN passes through. */
|
||||
return frac == 0 ? HUGE_VAL : x;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return -HUGE_VAL;
|
||||
}
|
||||
msb = 63 - __builtin_clzll(frac);
|
||||
return (double)(msb - 1074);
|
||||
}
|
||||
return (double)(e - 1023);
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bits 30..23 hold the exponent biased by 127 and
|
||||
* bits 22..0 the fraction; a subnormal with fraction F is F * 2^-149.
|
||||
*/
|
||||
float
|
||||
logbf(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
frac = bits & 0x7FFFFFU;
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e == 0xff)
|
||||
{
|
||||
return frac == 0 ? HUGE_VALF : x;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return -HUGE_VALF;
|
||||
}
|
||||
msb = 31 - __builtin_clz(frac);
|
||||
return (float)(msb - 149);
|
||||
}
|
||||
return (float)(e - 127);
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (explicit
|
||||
* integer bit) with a sign/exponent word se biased by 16383. A normal m
|
||||
* in [2^63, 2^64) sits in [2^(e-16383), 2^(e-16382)). A subnormal with
|
||||
* e == 0 is interpreted as if e were 1, i.e. m * 2^-16445; an unnormal
|
||||
* (e > 0 with m < 2^63) is m * 2^(e - 16446).
|
||||
*/
|
||||
long double
|
||||
logbl(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
int e;
|
||||
int msb;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e == 0x7fff)
|
||||
{
|
||||
return (p.m & 0x7FFFFFFFFFFFFFFFULL) == 0 ? HUGE_VALL : x;
|
||||
}
|
||||
if (p.m == 0)
|
||||
{
|
||||
return -HUGE_VALL;
|
||||
}
|
||||
if (e == 0)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return (long double)(msb - 16445);
|
||||
}
|
||||
if (p.m < 0x8000000000000000ULL)
|
||||
{
|
||||
msb = 63 - __builtin_clzll(p.m);
|
||||
return (long double)(msb + e - 16446);
|
||||
}
|
||||
return (long double)(e - 16383);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x in the current rounding direction,
|
||||
* returned as long (C23 7.12.9.7), all three precisions. A result
|
||||
* outside the range of long is a range error whose return value is
|
||||
* unspecified, so the header declares no const attribute; the test
|
||||
* corpus keeps |x| < 2^62 where every result is exact.
|
||||
*
|
||||
* GCC never folds the __builtin_lrint* forms on this target (external
|
||||
* lrint@PLT calls at every optimization level), so lrint is built as
|
||||
* rint-then-convert: __builtin_rint* folds to the in-line round-to-
|
||||
* nearest-even sequence (see rint.c), producing an exact integral value,
|
||||
* and the cast to long is then exact no matter which conversion
|
||||
* instruction GCC emits. The rint step honors the MXCSR/x87 rounding
|
||||
* mode, the only reachable one being the default round-to-nearest-even
|
||||
* (no <fenv.h> exists in vlibc yet).
|
||||
*/
|
||||
|
||||
/*
|
||||
* As lrint, for a float argument.
|
||||
*/
|
||||
long
|
||||
lrintf(float x)
|
||||
{
|
||||
return (long)__builtin_rintf(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As lrint, for a double argument.
|
||||
*/
|
||||
long
|
||||
lrint(double x)
|
||||
{
|
||||
return (long)__builtin_rint(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As lrint, for a long double argument.
|
||||
*/
|
||||
long
|
||||
lrintl(long double x)
|
||||
{
|
||||
return (long)__builtin_rintl(x);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The nearest integral value to x, halfway cases rounded away from zero,
|
||||
* returned as long (C23 7.12.9.9), all three precisions.
|
||||
*
|
||||
* On this LP64 target long is 64 bits wide, so lround is llround in every
|
||||
* observable way: the value is rounded half away from zero by round.c and
|
||||
* narrowed, and a NaN, an Inf, or any rounded magnitude >= 2^63 collapses
|
||||
* to LONG_MIN, exactly what the host glibc's hardware-conversion lround
|
||||
* returns for such arguments (measured on glibc 2.44; errno untouched).
|
||||
* The reasoning, the range test shape and the -2^63 == LONG_MIN boundary
|
||||
* subtlety are those documented in llround.c.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As lround, for a float argument.
|
||||
*/
|
||||
long
|
||||
lroundf(float x)
|
||||
{
|
||||
float r = roundf(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63f || r < -0x1p63f)
|
||||
{
|
||||
return LONG_MIN;
|
||||
}
|
||||
return (long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As lround, for a double argument.
|
||||
*/
|
||||
long
|
||||
lround(double x)
|
||||
{
|
||||
double r = round(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63 || r < -0x1p63)
|
||||
{
|
||||
return LONG_MIN;
|
||||
}
|
||||
return (long)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* As lround, for a long double argument.
|
||||
*/
|
||||
long
|
||||
lroundl(long double x)
|
||||
{
|
||||
long double r = roundl(x);
|
||||
|
||||
if (isnan(r) || r >= 0x1p63L || r < -0x1p63L)
|
||||
{
|
||||
return LONG_MIN;
|
||||
}
|
||||
return (long)r;
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
#ifndef VLIBC_MATH_IMPL_H
|
||||
#define VLIBC_MATH_IMPL_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* vlibc — private helpers shared by the src/math/ implementation files.
|
||||
*
|
||||
* This header is internal to the todo-39 arithmetic slices (frexp, ldexp,
|
||||
* modf, scalbn, scalbln, and later slices that need to scale a value by a
|
||||
* power of two); it is never installed and is not public API.
|
||||
*
|
||||
* Every real function below is spelled for all three precisions around one
|
||||
* unified representation of a finite value:
|
||||
*
|
||||
* value = m * 2^p
|
||||
*
|
||||
* where m is an integer significand that carries the explicit integer bit
|
||||
* (m in [2^52, 2^53) for double, [2^23, 2^24) for float, [2^63, 2^64) for
|
||||
* the x86 80-bit extended format) and p is the exact unbiased power of
|
||||
* two. Normalizing a subnormal input (and an 80-bit unnormal) into this
|
||||
* shape is an exact left shift, so the scaling logic that follows never
|
||||
* has to special-case the input class again.
|
||||
*
|
||||
* Scaling by 2^n only moves p: value = m * 2^(p + n). The result class
|
||||
* is read straight off the target exponent k = p + n, and a right shift
|
||||
* with round-to-nearest-even (vl_rne_u64) is needed only when the scaled
|
||||
* value lands in the subnormal range and some low significand bits must be
|
||||
* dropped.
|
||||
*
|
||||
* The format constants (mask widths, exponent biases) are the ones the
|
||||
* rounding-family files (round.c, rint.c, ilogb.c, ...) already document;
|
||||
* only the value interpretation changes here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Round m >> r to nearest, ties to even, returning the kept integer. r is
|
||||
* in [1, 64]; for r == 64 the entire significand is dropped and the only
|
||||
* values that can survive are those above the tie point (1 is returned),
|
||||
* while r > 64 can never round up because m < 2^64 is below the half-way
|
||||
* threshold. Callers guarantee m < 2^64 and r >= 1.
|
||||
*/
|
||||
static inline unsigned long long
|
||||
vl_rne_u64(unsigned long long m, int r)
|
||||
{
|
||||
unsigned long long kept;
|
||||
unsigned long long dropped;
|
||||
unsigned long long half;
|
||||
|
||||
if (r >= 64)
|
||||
{
|
||||
return (r > 64 || m <= (1ULL << 63)) ? 0 : 1;
|
||||
}
|
||||
kept = m >> r;
|
||||
dropped = m & ((1ULL << r) - 1ULL);
|
||||
half = 1ULL << (r - 1);
|
||||
if (dropped > half || (dropped == half && (kept & 1ULL) != 0))
|
||||
{
|
||||
kept++;
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale a double by 2^n. Bits 62..52 are the exponent biased by 1023,
|
||||
* bits 51..0 the fraction; the significand is 53 bits wide, so a normal
|
||||
* input has m = 2^52 | frac and p = ef - 1075. A subnormal (ef == 0,
|
||||
* frac != 0) with msb at position s (0..51) normalizes exactly to
|
||||
* m = frac << (52 - s) with p = s - 1126.
|
||||
*
|
||||
* Target k = p + n: k >= 972 overflows (the exact result exceeds DBL_MAX;
|
||||
* the flag is raised and +-Inf returned), k in [-1074, 971] is an exact
|
||||
* normal result, and k <= -1075 is subnormal-or-zero with r = -1074 - k
|
||||
* dropped bits; r >= 54 always rounds to zero, and a rounded-up field of
|
||||
* exactly 2^52 is the smallest normal (ef 1, frac 0), reached when the
|
||||
* exact value is the tie just above the largest subnormal.
|
||||
*/
|
||||
static inline double
|
||||
vl_scale2_d(double x, int n, int *overflowed) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long sign;
|
||||
unsigned long long ef;
|
||||
unsigned long long frac;
|
||||
unsigned long long m;
|
||||
unsigned long long f;
|
||||
int s;
|
||||
int p;
|
||||
int k;
|
||||
int r;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1ULL << 63);
|
||||
ef = (bits >> 52) & 0x7ff;
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
if (ef == 0x7ff)
|
||||
{
|
||||
return x; /* +-Inf and NaN pass through unchanged */
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return x; /* +-0 stays +-0 for any n */
|
||||
}
|
||||
s = 63 - __builtin_clzll(frac);
|
||||
m = frac << (52 - s);
|
||||
p = s - 1126;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = frac | (1ULL << 52);
|
||||
p = (int)ef - 1075;
|
||||
}
|
||||
|
||||
k = p + n;
|
||||
if (k >= 972)
|
||||
{
|
||||
if (overflowed != NULL)
|
||||
{
|
||||
*overflowed = 1;
|
||||
}
|
||||
bits = sign | (0x7ffULL << 52);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if (k >= -1074)
|
||||
{
|
||||
bits = sign | ((unsigned long long)(k + 1075) << 52) | (m - (1ULL << 52));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
r = -1074 - k;
|
||||
if (r >= 54)
|
||||
{
|
||||
bits = sign;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
f = vl_rne_u64(m, r);
|
||||
if (f == 0)
|
||||
{
|
||||
bits = sign;
|
||||
}
|
||||
else if (f == (1ULL << 52))
|
||||
{
|
||||
bits = sign | (1ULL << 52); /* smallest normal, reached by rounding up */
|
||||
}
|
||||
else
|
||||
{
|
||||
bits = sign | f;
|
||||
}
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale a float by 2^n. Bits 30..23 are the exponent biased by 127, bits
|
||||
* 22..0 the fraction; a normal input has m = 2^23 | frac and p = ef - 150.
|
||||
* A subnormal (ef == 0, frac != 0) with msb at s (0..22) normalizes to
|
||||
* m = frac << (23 - s) with p = s - 172.
|
||||
*
|
||||
* k >= 105 overflows, k in [-149, 104] is an exact normal result, and
|
||||
* k <= -150 is subnormal-or-zero with r = -149 - k; r >= 25 always rounds
|
||||
* to zero and a rounded-up field of exactly 2^23 is the smallest normal.
|
||||
*/
|
||||
static inline float
|
||||
vl_scale2_f(float x, int n, int *overflowed) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int sign;
|
||||
unsigned int ef;
|
||||
unsigned int frac;
|
||||
unsigned int m;
|
||||
unsigned int f;
|
||||
int s;
|
||||
int p;
|
||||
int k;
|
||||
int r;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1U << 31);
|
||||
ef = (bits >> 23) & 0xff;
|
||||
frac = bits & 0x7FFFFFU;
|
||||
|
||||
if (ef == 0xff)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
s = 31 - __builtin_clz(frac);
|
||||
m = frac << (23 - s);
|
||||
p = s - 172;
|
||||
}
|
||||
else
|
||||
{
|
||||
m = frac | (1U << 23);
|
||||
p = (int)ef - 150;
|
||||
}
|
||||
|
||||
k = p + n;
|
||||
if (k >= 105)
|
||||
{
|
||||
if (overflowed != NULL)
|
||||
{
|
||||
*overflowed = 1;
|
||||
}
|
||||
bits = sign | (0xffU << 23);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if (k >= -149)
|
||||
{
|
||||
bits = sign | ((unsigned int)(k + 150) << 23) | (m - (1U << 23));
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
r = -149 - k;
|
||||
if (r >= 25)
|
||||
{
|
||||
bits = sign;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
f = (unsigned int)vl_rne_u64((unsigned long long)m, r);
|
||||
if (f == 0)
|
||||
{
|
||||
bits = sign;
|
||||
}
|
||||
else if (f == (1U << 23))
|
||||
{
|
||||
bits = sign | (1U << 23);
|
||||
}
|
||||
else
|
||||
{
|
||||
bits = sign | f;
|
||||
}
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scale an x86 80-bit extended value by 2^n. The significand m is 64 bits
|
||||
* wide with an explicit integer bit; se holds the sign (bit 15) and the
|
||||
* exponent biased by 16383 (bits 14..0). A canonical normal has m in
|
||||
* [2^63, 2^64) and p = ef - 16446 (value = m * 2^(ef - 16383 - 63)). A
|
||||
* subnormal (ef == 0, m != 0) — and, defensively, an unnormal (ef > 0 with
|
||||
* m < 2^63) — is interpreted as if the exponent were max(ef, 1) and
|
||||
* normalizes exactly by shifting m left until its msb sits at bit 63.
|
||||
*
|
||||
* k >= 16321 overflows, k in [-16445, 16320] is an exact normal result
|
||||
* (for the subnormal minimum, m = 2^63 with ef' = 1), and k <= -16446 is
|
||||
* subnormal-or-zero with r = -16445 - k; r >= 65 always rounds to zero.
|
||||
* The 64-bit significand makes r == 64 the deepest meaningful shift: only
|
||||
* m above 2^63 then rounds up (to the smallest subnormal, 2^-16445).
|
||||
*/
|
||||
static inline long double
|
||||
vl_scale2_ld(long double x, int n, int *overflowed) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long m;
|
||||
unsigned long long f;
|
||||
unsigned short sign;
|
||||
int ef;
|
||||
int s;
|
||||
int pwr;
|
||||
int k;
|
||||
int r;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
m = p.m;
|
||||
sign = (unsigned short)(p.se & 0x8000);
|
||||
ef = p.se & 0x7fff;
|
||||
|
||||
if (ef == 0x7fff)
|
||||
{
|
||||
return x; /* +-Inf and NaN pass through unchanged */
|
||||
}
|
||||
if (m == 0)
|
||||
{
|
||||
return x; /* +-0 (and degenerate empty encodings) stay put */
|
||||
}
|
||||
if (m < 0x8000000000000000ULL)
|
||||
{
|
||||
s = 63 - __builtin_clzll(m);
|
||||
m <<= (63 - s);
|
||||
pwr = (ef == 0 ? 1 : ef) + s - 16509;
|
||||
}
|
||||
else
|
||||
{
|
||||
pwr = ef - 16446;
|
||||
}
|
||||
|
||||
k = pwr + n;
|
||||
if (k >= 16321)
|
||||
{
|
||||
if (overflowed != NULL)
|
||||
{
|
||||
*overflowed = 1;
|
||||
}
|
||||
p.se = (unsigned short)(sign | 0x7fff);
|
||||
p.m = 0x8000000000000000ULL;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if (k >= -16445)
|
||||
{
|
||||
p.se = (unsigned short)(sign | (unsigned short)(k + 16446));
|
||||
p.m = m;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
r = -16445 - k;
|
||||
if (r >= 65)
|
||||
{
|
||||
p.se = sign;
|
||||
p.m = 0;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
f = vl_rne_u64(m, r);
|
||||
p.se = sign;
|
||||
p.m = f;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_MATH_IMPL_H */
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Split x into an integral part stored in *iptr and a fractional part
|
||||
* returned, both carrying x's sign (C23 7.12.6.5): modf(-1.5, &i) puts
|
||||
* -1.0 in i and returns -0.5. The integral part is x truncated toward
|
||||
* zero. modf(±0, &i) stores ±0 and returns ±0; modf(±Inf, &i) stores
|
||||
* ±Inf and returns ±0 with x's sign; modf(NaN, &i) stores the NaN and
|
||||
* returns it; a subnormal |x| < 1 stores ±0 (x's sign) and returns x.
|
||||
*
|
||||
* The implementation is a bit-level truncation (the same "clear the
|
||||
* fractional mantissa bits" shape as trunc.c): a value with |x| < 1 has
|
||||
* integral part ±0; a value too large to carry a fraction (|x| >= 2^52,
|
||||
* >= 2^23, >= 2^63 for the three formats) is its own integral part; the
|
||||
* values in between have their low (fraction) bits masked off. The
|
||||
* fractional part is then x - i, an exact subtraction — x and i share the
|
||||
* significand's leading bits, so the difference is a representable
|
||||
* multiple of the common unit in the last place. When the subtraction is
|
||||
* an exact zero the fraction is ±0 with x's sign, which the hardware
|
||||
* would otherwise always report as +0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. A normal with exponent field e in
|
||||
* [1023, 1074] has its low (1075 - e) fraction bits below the binary
|
||||
* point; e >= 1075 means |x| >= 2^52, an integer already.
|
||||
*/
|
||||
double
|
||||
modf(double x, double *iptr)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long sign;
|
||||
unsigned long long ef;
|
||||
unsigned long long frac;
|
||||
unsigned long long ibits;
|
||||
double i;
|
||||
double f;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1ULL << 63);
|
||||
ef = (bits >> 52) & 0x7ff;
|
||||
frac = bits & 0xFFFFFFFFFFFFFULL;
|
||||
|
||||
if (ef == 0x7ff)
|
||||
{
|
||||
*iptr = x;
|
||||
if (frac != 0)
|
||||
{
|
||||
return x; /* NaN: both parts are the NaN */
|
||||
}
|
||||
return sign != 0 ? -0.0 : 0.0; /* ±Inf: fraction ±0 of x's sign */
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*iptr = x; /* ±0 */
|
||||
return x;
|
||||
}
|
||||
*iptr = sign != 0 ? -0.0 : 0.0; /* subnormal: |x| < 1 */
|
||||
return x;
|
||||
}
|
||||
if (ef >= 1075)
|
||||
{
|
||||
*iptr = x; /* |x| >= 2^52: already integral */
|
||||
return sign != 0 ? -0.0 : 0.0;
|
||||
}
|
||||
if (ef < 1023)
|
||||
{
|
||||
*iptr = sign != 0 ? -0.0 : 0.0; /* 0 < |x| < 1 */
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - (int)ef;
|
||||
ibits = bits & ~((1ULL << shift) - 1ULL);
|
||||
__builtin_memcpy(&i, &ibits, sizeof i);
|
||||
*iptr = i;
|
||||
f = x - i;
|
||||
if (f == 0.0)
|
||||
{
|
||||
return sign != 0 ? -0.0 : 0.0;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. For e in [127, 149] the low (150 - e)
|
||||
* fraction bits are fractional; e >= 150 means |x| >= 2^23, integral.
|
||||
*/
|
||||
float
|
||||
modff(float x, float *iptr)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int sign;
|
||||
unsigned int ef;
|
||||
unsigned int frac;
|
||||
unsigned int ibits;
|
||||
float i;
|
||||
float f;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
sign = bits & (1U << 31);
|
||||
ef = (bits >> 23) & 0xff;
|
||||
frac = bits & 0x7FFFFFU;
|
||||
|
||||
if (ef == 0xff)
|
||||
{
|
||||
*iptr = x;
|
||||
if (frac != 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return sign != 0 ? -0.0f : 0.0f;
|
||||
}
|
||||
if (ef == 0)
|
||||
{
|
||||
if (frac == 0)
|
||||
{
|
||||
*iptr = x;
|
||||
return x;
|
||||
}
|
||||
*iptr = sign != 0 ? -0.0f : 0.0f;
|
||||
return x;
|
||||
}
|
||||
if (ef >= 150)
|
||||
{
|
||||
*iptr = x;
|
||||
return sign != 0 ? -0.0f : 0.0f;
|
||||
}
|
||||
if (ef < 127)
|
||||
{
|
||||
*iptr = sign != 0 ? -0.0f : 0.0f;
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - (int)ef;
|
||||
ibits = bits & ~((1U << shift) - 1U);
|
||||
__builtin_memcpy(&i, &ibits, sizeof i);
|
||||
*iptr = i;
|
||||
f = x - i;
|
||||
if (f == 0.0f)
|
||||
{
|
||||
return sign != 0 ? -0.0f : 0.0f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m with an explicit
|
||||
* integer bit and a sign/exponent word se biased by 16383. For e in
|
||||
* [16383, 16445] the low (16446 - e) bits of m are fractional; e >= 16446
|
||||
* means |x| >= 2^63, integral.
|
||||
*/
|
||||
long double
|
||||
modfl(long double x, long double *iptr)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long ibits;
|
||||
long double i;
|
||||
long double f;
|
||||
int ef;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
ef = p.se & 0x7fff;
|
||||
|
||||
if (ef == 0x7fff)
|
||||
{
|
||||
*iptr = x;
|
||||
if (p.m != 0x8000000000000000ULL)
|
||||
{
|
||||
return x; /* NaN */
|
||||
}
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L; /* ±Inf */
|
||||
}
|
||||
if (p.m == 0)
|
||||
{
|
||||
*iptr = x; /* ±0 */
|
||||
return x;
|
||||
}
|
||||
if (ef >= 16446)
|
||||
{
|
||||
*iptr = x;
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L;
|
||||
}
|
||||
if (ef < 16383)
|
||||
{
|
||||
*iptr = (p.se & 0x8000) != 0 ? -0.0L : 0.0L; /* subnormal: |x| < 1 */
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - ef;
|
||||
ibits = p.m & ~((1ULL << shift) - 1ULL);
|
||||
p.m = ibits;
|
||||
__builtin_memcpy(&i, &p, sizeof i);
|
||||
*iptr = i;
|
||||
f = x - i;
|
||||
if (f == 0.0L)
|
||||
{
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 0.0L;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x in the current rounding direction (C23
|
||||
* 7.12.9.5), all three precisions, guaranteed never to raise the inexact
|
||||
* exception. No <fenv.h> exists in vlibc yet, so the only reachable
|
||||
* rounding mode is the hardware default, round-to-nearest-even; the
|
||||
* functions must still fold to correct results once <fenv.h> lands, and
|
||||
* the const attribute promises no exception and no errno path.
|
||||
*
|
||||
* GCC never folds the __builtin_nearbyint forms on this target (the
|
||||
* roundsd expansion needs SSE4.1, absent from the default -march, so
|
||||
* every __builtin_nearbyint call becomes an external nearbyint@PLT call
|
||||
* at every optimization level), so each function below rounds directly
|
||||
* on the IEEE 754 bit pattern with round-to-nearest-even: drop the low
|
||||
* (frac-bits) of the significand; a dropped part above half an ulp steps
|
||||
* the kept significand up, a dropped part of exactly half an ulp steps
|
||||
* up only when the kept LSB is odd (ties to even). The step is a plain
|
||||
* integer add that carries into the exponent field when the kept
|
||||
* fraction is all ones, which renormalizes exactly; nothing here touches
|
||||
* a rounding mode or raises an exception. nearbyint(±0) is ±0,
|
||||
* nearbyint(-0.5) is -0.0 (tie to even zero), and ±Inf/NaN pass through.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. A finite value with exponent e has
|
||||
* fractional bits only when e < 1075; for e in [1023, 1074] exactly the
|
||||
* low (1075 - e) bits of the fraction word are the fractional part. The
|
||||
* significand is 53 bits wide (implicit 1 plus the 52-bit fraction), so
|
||||
* in the e == 1023 binade the tie-even test looks at the implicit bit:
|
||||
* the only half-way value there is 1.5, which rounds up to 2.
|
||||
*/
|
||||
static double
|
||||
nearbyint_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long kept;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~(1ULL << 63)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 1022)
|
||||
{
|
||||
/* [1/2, 1): 0.5 itself is a tie toward even zero; anything
|
||||
* above it rounds to ±1. */
|
||||
if ((bits & 0xFFFFFFFFFFFFFULL) == 0)
|
||||
{
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1ULL << 63)) | 0x3FF0000000000000ULL;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = bits & ((1ULL << shift) - 1ULL);
|
||||
kept = bits & ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && (shift == 52 || ((kept >> shift) & 1ULL) != 0)))
|
||||
{
|
||||
kept += 1ULL << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction; the significand is 24 bits wide. For
|
||||
* e in [127, 149] the low (150 - e) fraction bits are fractional, and in
|
||||
* the e == 127 binade the tie-even test looks at the implicit bit.
|
||||
*/
|
||||
static float
|
||||
nearbyint_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int kept;
|
||||
unsigned int frac;
|
||||
unsigned int half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~(1U << 31)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 126)
|
||||
{
|
||||
if ((bits & 0x7FFFFFU) == 0)
|
||||
{
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1U << 31)) | 0x3F800000U;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
half = 1U << (shift - 1);
|
||||
frac = bits & ((1U << shift) - 1U);
|
||||
kept = bits & ~((1U << shift) - 1U);
|
||||
if (frac > half || (frac == half && (shift == 23 || ((kept >> shift) & 1U) != 0)))
|
||||
{
|
||||
kept += 1U << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit, so the tie-even test is always m's kept LSB) and a
|
||||
* sign/exponent word se. A value m * 2^(e - 16446) has fractional bits
|
||||
* only when e < 16446; for e in [16383, 16445] the low (16446 - e) bits
|
||||
* of m are fractional. The step may overflow m when the kept significand
|
||||
* is all ones; the carry then moves the value to the next binade.
|
||||
*/
|
||||
static long double
|
||||
nearbyint_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 16382)
|
||||
{
|
||||
if (p.m == 0x8000000000000000ULL)
|
||||
{
|
||||
/* Exactly 0.5: tie toward even zero. */
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | 16383;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = p.m & ((1ULL << shift) - 1ULL);
|
||||
p.m &= ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && ((p.m >> shift) & 1ULL) != 0))
|
||||
{
|
||||
p.m += 1ULL << shift;
|
||||
if (p.m == 0)
|
||||
{
|
||||
/* Kept significand was all ones: carry to the next binade. */
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | (unsigned short)(e + 1);
|
||||
}
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As nearbyint, for a float argument.
|
||||
*/
|
||||
float
|
||||
nearbyintf(float x)
|
||||
{
|
||||
return nearbyint_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As nearbyint, for a double argument.
|
||||
*/
|
||||
double
|
||||
nearbyint(double x)
|
||||
{
|
||||
return nearbyint_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As nearbyint, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
nearbyintl(long double x)
|
||||
{
|
||||
return nearbyint_ld(x);
|
||||
}
|
||||
+1347
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y with n = x/y rounded to the nearest integer,
|
||||
* ties to even (C23 7.12.10.2), all three precisions.
|
||||
*
|
||||
* The implementation mirrors the host glibc 2.44 remainder, which is a
|
||||
* two-stage reduction: the exact fmod(x, y + y) first clears every whole
|
||||
* multiple of 2y, leaving a residual below 2y (the doubling is finite by
|
||||
* construction of the branch below), and the residual is then rounded to
|
||||
* the nearest multiple of y -- at most one subtraction can still be wrong,
|
||||
* so comparing the doubled residual against y decides the final n exactly,
|
||||
* and each subtraction is exact (Sterbenz), making the result exact. The
|
||||
* two stages run in x's magnitude and the sign of x is applied at the
|
||||
* end, matching glibc.
|
||||
*
|
||||
* For |y| so large that y + y would overflow (|y| >= 2^1023 for double,
|
||||
* >= 2^127 for float, >= 2^16383 for the 80-bit format) the fmod stage is
|
||||
* skipped and the same rounding logic runs against y/2 -- with x below
|
||||
* 2y at those magnitudes, at most two y subtractions can be needed, which
|
||||
* the doubled-compare decision still performs exactly. Domain errors
|
||||
* return a NaN (the classic indefinite pattern) and set errno to EDOM in
|
||||
* the library build, matching the measured host behavior.
|
||||
*/
|
||||
|
||||
/* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. */
|
||||
static double
|
||||
remainder_d(double x, double y)
|
||||
{
|
||||
const unsigned long long dbl_inf = 0x7FF0000000000000ULL;
|
||||
unsigned long long xw;
|
||||
unsigned long long yw;
|
||||
unsigned long long hx;
|
||||
unsigned long long hy;
|
||||
unsigned long long sx;
|
||||
double v;
|
||||
double yh;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 63;
|
||||
hx = xw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
hy = yw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
|
||||
v = fabs(x);
|
||||
y = fabs(y);
|
||||
if (hy < 0x7FE0000000000000ULL)
|
||||
{
|
||||
v = fmod(v, y + y); /* now v < 2y */
|
||||
if (v + v > y)
|
||||
{
|
||||
v -= y;
|
||||
if (v + v >= y)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0)
|
||||
{
|
||||
v = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* x not finite or y a NaN */
|
||||
if (hx >= dbl_inf || hy > dbl_inf)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
xw = 0xFFF8000000000000ULL;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
yh = 0.5 * y;
|
||||
if (v > yh)
|
||||
{
|
||||
v -= y;
|
||||
if (v >= yh)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0)
|
||||
{
|
||||
v = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sx)
|
||||
{
|
||||
v = -v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. */
|
||||
static float
|
||||
remainder_f(float x, float y)
|
||||
{
|
||||
const unsigned int flt_inf = 0x7F800000U;
|
||||
unsigned int xw;
|
||||
unsigned int yw;
|
||||
unsigned int hx;
|
||||
unsigned int hy;
|
||||
unsigned int sx;
|
||||
float v;
|
||||
float yh;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 31;
|
||||
hx = xw & 0x7FFFFFFFU;
|
||||
hy = yw & 0x7FFFFFFFU;
|
||||
|
||||
v = fabsf(x);
|
||||
y = fabsf(y);
|
||||
if (hy < 0x7F000000U)
|
||||
{
|
||||
v = fmodf(v, y + y); /* now v < 2y */
|
||||
if (v + v > y)
|
||||
{
|
||||
v -= y;
|
||||
if (v + v >= y)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0f)
|
||||
{
|
||||
v = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* x not finite or y a NaN */
|
||||
if (hx >= flt_inf || hy > flt_inf)
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
xw = 0xFFC00000U;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
yh = 0.5f * y;
|
||||
if (v > yh)
|
||||
{
|
||||
v -= y;
|
||||
if (v >= yh)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0f)
|
||||
{
|
||||
v = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sx)
|
||||
{
|
||||
v = -v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the
|
||||
* integer bit is explicit) and a sign/exponent word se in bytes 8..9 with
|
||||
* the sign in bit 15 and the exponent biased by 16383 in bits 14..0.
|
||||
*/
|
||||
struct remainder_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static long double
|
||||
remainder_l(long double x, long double y)
|
||||
{
|
||||
struct remainder_ld_word p;
|
||||
struct remainder_ld_word q;
|
||||
int sx;
|
||||
int ex;
|
||||
int ey;
|
||||
long double v;
|
||||
long double yh;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
sx = (int)(p.se >> 15);
|
||||
ex = p.se & 0x7fff;
|
||||
ey = q.se & 0x7fff;
|
||||
|
||||
v = fabsl(x);
|
||||
y = fabsl(y);
|
||||
if (ey < 0x7ffe)
|
||||
{
|
||||
v = fmodl(v, y + y); /* now v < 2y */
|
||||
if (v + v > y)
|
||||
{
|
||||
v -= y;
|
||||
if (v + v >= y)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0L)
|
||||
{
|
||||
v = 0.0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* x not finite or y a NaN */
|
||||
if (ex == 0x7fff || (ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
#ifdef HAVE_CONFIG_H
|
||||
errno = EDOM;
|
||||
#endif
|
||||
p.m = 0xC000000000000000ULL;
|
||||
p.se = 0xFFFF;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
yh = 0.5L * y;
|
||||
if (v > yh)
|
||||
{
|
||||
v -= y;
|
||||
if (v >= yh)
|
||||
{
|
||||
v -= y;
|
||||
}
|
||||
else if (v == 0.0L)
|
||||
{
|
||||
v = 0.0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sx)
|
||||
{
|
||||
v = -v;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* remainder for a float argument.
|
||||
*/
|
||||
float
|
||||
remainderf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remainder_f(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* remainder for a double argument.
|
||||
*/
|
||||
double
|
||||
remainder(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remainder_d(x, y);
|
||||
}
|
||||
|
||||
/*
|
||||
* remainder for a long double argument.
|
||||
*/
|
||||
long double
|
||||
remainderl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remainder_l(x, y);
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y with n = x/y rounded to nearest, ties to
|
||||
* even (C23 7.12.10.2), plus the signed low bits of the integer quotient
|
||||
* n stored through quo (C23 7.12.10.3), all three precisions.
|
||||
*
|
||||
* The structure mirrors the host glibc remainder/remquo implementation
|
||||
* (glibc 2.44, measured byte-for-byte), because remquo's quotient is not
|
||||
* the full n: glibc tracks only the three lowest quotient bits and still
|
||||
* returns a remainder in the exact IEEE rounding, so the two functions
|
||||
* share one reduce-then-round shape. x is first reduced by fmod(x, 8y)
|
||||
* whenever |y| is small enough for 8y to be finite, which makes |x| < 8y
|
||||
* and therefore bounds x/y below 8; the tracked quotient cquo collects
|
||||
* the one 4y subtraction, the one 2y subtraction and the final rounding's
|
||||
* up-to-two y subtractions, which together hold the three lowest bits of
|
||||
* n. The final compare against y/2 (using doubled compares when y is so
|
||||
* small that halving could lose precision) rounds x to the nearest
|
||||
* multiple of y, ties to even via the natural 0.5 boundary, and each
|
||||
* subtraction that fires is exact (Sterbenz), so the returned remainder
|
||||
* is exact.
|
||||
*
|
||||
* The x86-64 host collapses the remainder and quotient signs the same
|
||||
* way the code below does: the magnitude result is computed from |x| and
|
||||
* |y| and then negated when x < 0, and *quo carries the sign of x/y.
|
||||
* Domain errors -- |y| == 0, x not finite, y a NaN -- return a NaN (the
|
||||
* classic indefinite pattern) and leave *quo untouched, as glibc does.
|
||||
* Like glibc, remquo never touches errno.
|
||||
*/
|
||||
|
||||
/* The double format: bit 63 the sign, bits 62..52 the exponent biased by
|
||||
* 1023, bits 51..0 the fraction. */
|
||||
static double
|
||||
remquo_d(double x, double y, int *quo)
|
||||
{
|
||||
const unsigned long long dbl_inf = 0x7FF0000000000000ULL;
|
||||
unsigned long long xw;
|
||||
unsigned long long yw;
|
||||
unsigned long long hx;
|
||||
unsigned long long hy;
|
||||
unsigned long long sx;
|
||||
unsigned long long sy;
|
||||
double ax;
|
||||
double ay;
|
||||
double yh;
|
||||
int cquo;
|
||||
int qneg;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 63;
|
||||
sy = yw >> 63;
|
||||
qneg = (int)(sx ^ sy);
|
||||
hx = xw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
hy = yw & 0x7FFFFFFFFFFFFFFFULL;
|
||||
|
||||
if (hy == 0 || hx >= dbl_inf || hy > dbl_inf)
|
||||
{
|
||||
xw = 0xFFF8000000000000ULL;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (hx == hy)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0;
|
||||
}
|
||||
|
||||
ax = fabs(x);
|
||||
ay = fabs(y);
|
||||
if (hy <= 0x7FBFFFFFFFFFFFFFULL)
|
||||
{
|
||||
ax = fmod(ax, 8.0 * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (hy <= 0x7FCFFFFFFFFFFFFFULL && ax >= 4.0 * ay)
|
||||
{
|
||||
ax -= 4.0 * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (hy <= 0x7FDFFFFFFFFFFFFFULL && ax >= 2.0 * ay)
|
||||
{
|
||||
ax -= 2.0 * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (hy < 0x0020000000000000ULL)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5 * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0)
|
||||
{
|
||||
ax = 0.0;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/* The float format: bit 31 the sign, bits 30..23 the exponent biased by
|
||||
* 127, bits 22..0 the fraction. */
|
||||
static float
|
||||
remquo_f(float x, float y, int *quo)
|
||||
{
|
||||
const unsigned int flt_inf = 0x7F800000U;
|
||||
unsigned int xw;
|
||||
unsigned int yw;
|
||||
unsigned int hx;
|
||||
unsigned int hy;
|
||||
unsigned int sx;
|
||||
unsigned int sy;
|
||||
float ax;
|
||||
float ay;
|
||||
float yh;
|
||||
int cquo;
|
||||
int qneg;
|
||||
|
||||
__builtin_memcpy(&xw, &x, sizeof xw);
|
||||
__builtin_memcpy(&yw, &y, sizeof yw);
|
||||
sx = xw >> 31;
|
||||
sy = yw >> 31;
|
||||
qneg = (int)(sx ^ sy);
|
||||
hx = xw & 0x7FFFFFFFU;
|
||||
hy = yw & 0x7FFFFFFFU;
|
||||
|
||||
if (hy == 0 || hx >= flt_inf || hy > flt_inf)
|
||||
{
|
||||
xw = 0xFFC00000U;
|
||||
__builtin_memcpy(&x, &xw, sizeof xw);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (hx == hy)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0f;
|
||||
}
|
||||
|
||||
ax = fabsf(x);
|
||||
ay = fabsf(y);
|
||||
if (hy <= 0x7DFFFFFFU)
|
||||
{
|
||||
ax = fmodf(ax, 8.0f * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (hy <= 0x7E7FFFFFU && ax >= 4.0f * ay)
|
||||
{
|
||||
ax -= 4.0f * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (hy <= 0x7EFFFFFFU && ax >= 2.0f * ay)
|
||||
{
|
||||
ax -= 2.0f * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (hy < 0x01000000U)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5f * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0f)
|
||||
{
|
||||
ax = 0.0f;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64-bit significand m in bytes 0..7 (the
|
||||
* integer bit is explicit) and a sign/exponent word se in bytes 8..9 with
|
||||
* the sign in bit 15 and the exponent biased by 16383 in bits 14..0.
|
||||
*/
|
||||
struct remquo_ld_word
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
};
|
||||
|
||||
static long double
|
||||
remquo_l(long double x, long double y, int *quo)
|
||||
{
|
||||
struct remquo_ld_word p;
|
||||
struct remquo_ld_word q;
|
||||
int ex;
|
||||
int ey;
|
||||
int qneg;
|
||||
int sx;
|
||||
int sy;
|
||||
long double ax;
|
||||
long double ay;
|
||||
long double yh;
|
||||
int cquo;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
__builtin_memcpy(&q, &y, sizeof q);
|
||||
sx = (int)(p.se >> 15);
|
||||
sy = (int)(q.se >> 15);
|
||||
qneg = sx ^ sy;
|
||||
ex = p.se & 0x7fff;
|
||||
ey = q.se & 0x7fff;
|
||||
|
||||
if ((q.m == 0 && ey == 0) || ex == 0x7fff ||
|
||||
(ey == 0x7fff && (q.m & 0x7FFFFFFFFFFFFFFFULL) != 0))
|
||||
{
|
||||
p.m = 0xC000000000000000ULL;
|
||||
p.se = 0xFFFF;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
if (ex == ey && p.m == q.m)
|
||||
{
|
||||
*quo = qneg ? -1 : 1;
|
||||
return x * 0.0L;
|
||||
}
|
||||
|
||||
ax = fabsl(x);
|
||||
ay = fabsl(y);
|
||||
if (ey <= 0x7ffb)
|
||||
{
|
||||
ax = fmodl(ax, 8.0L * ay); /* now ax < 8 * ay */
|
||||
}
|
||||
|
||||
cquo = 0;
|
||||
if (ey <= 0x7ffc && ax >= 4.0L * ay)
|
||||
{
|
||||
ax -= 4.0L * ay;
|
||||
cquo += 4;
|
||||
}
|
||||
if (ey <= 0x7ffd && ax >= 2.0L * ay)
|
||||
{
|
||||
ax -= 2.0L * ay;
|
||||
cquo += 2;
|
||||
}
|
||||
|
||||
if (ey < 0x0002)
|
||||
{
|
||||
if (ax + ax > ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax + ax >= ay)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yh = 0.5L * ay;
|
||||
if (ax > yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
if (ax >= yh)
|
||||
{
|
||||
ax -= ay;
|
||||
cquo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*quo = qneg ? -cquo : cquo;
|
||||
if (ax == 0.0L)
|
||||
{
|
||||
ax = 0.0L;
|
||||
}
|
||||
if (sx)
|
||||
{
|
||||
ax = -ax;
|
||||
}
|
||||
return ax;
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a float argument.
|
||||
*/
|
||||
float
|
||||
remquof(float x, float y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_f(x, y, quo);
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a double argument.
|
||||
*/
|
||||
double
|
||||
remquo(double x, double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_d(x, y, quo);
|
||||
}
|
||||
|
||||
/*
|
||||
* remquo for a long double argument.
|
||||
*/
|
||||
long double
|
||||
remquol(long double x, long double y, int *quo) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return remquo_l(x, y, quo);
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x in the current rounding direction (C23
|
||||
* 7.12.9.4), all three precisions. rint differs from nearbyint only in
|
||||
* that it may raise the inexact exception; never raising it is also
|
||||
* conforming, and the const attribute promises no errno path.
|
||||
*
|
||||
* The __builtin_rint forms are tempting (they fold to in-line code on
|
||||
* this target), but GCC warns -Winfinite-recursion when the enclosing
|
||||
* function carries the same name as the library symbol the builtin would
|
||||
* fall back to (a function named rint whose body is __builtin_rint),
|
||||
* which the -Wall -Wextra -pedantic build gate forbids. Each function is
|
||||
* therefore implemented directly on the IEEE 754 bit pattern with
|
||||
* round-to-nearest-even, exactly like nearbyint (see nearbyint.c): no
|
||||
* <fenv.h> exists in vlibc yet, so the hardware default round-to-nearest-
|
||||
* even is the only reachable rounding mode. rint(±0) is ±0, rint(-0.5)
|
||||
* is -0.0 (tie to even zero), and ±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. For e in [1023, 1074] the low
|
||||
* (1075 - e) bits of the fraction word are the fractional part; the
|
||||
* significand is 53 bits wide, so in the e == 1023 binade the tie-even
|
||||
* test looks at the implicit bit (the only half-way value there, 1.5,
|
||||
* rounds up to 2).
|
||||
*/
|
||||
static double
|
||||
rint_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long kept;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~(1ULL << 63)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 1022)
|
||||
{
|
||||
/* [1/2, 1): 0.5 itself is a tie toward even zero; anything
|
||||
* above it rounds to ±1. */
|
||||
if ((bits & 0xFFFFFFFFFFFFFULL) == 0)
|
||||
{
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1ULL << 63)) | 0x3FF0000000000000ULL;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = bits & ((1ULL << shift) - 1ULL);
|
||||
kept = bits & ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && (shift == 52 || ((kept >> shift) & 1ULL) != 0)))
|
||||
{
|
||||
kept += 1ULL << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction; the significand is 24 bits wide. For
|
||||
* e in [127, 149] the low (150 - e) fraction bits are fractional, and in
|
||||
* the e == 127 binade the tie-even test looks at the implicit bit.
|
||||
*/
|
||||
static float
|
||||
rint_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int kept;
|
||||
unsigned int frac;
|
||||
unsigned int half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~(1U << 31)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 126)
|
||||
{
|
||||
if ((bits & 0x7FFFFFU) == 0)
|
||||
{
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits = (bits & (1U << 31)) | 0x3F800000U;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
half = 1U << (shift - 1);
|
||||
frac = bits & ((1U << shift) - 1U);
|
||||
kept = bits & ~((1U << shift) - 1U);
|
||||
if (frac > half || (frac == half && (shift == 23 || ((kept >> shift) & 1U) != 0)))
|
||||
{
|
||||
kept += 1U << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit, so the tie-even test is always m's kept LSB) and a
|
||||
* sign/exponent word se. For e in [16383, 16445] the low (16446 - e)
|
||||
* bits of m are fractional. The step may overflow m when the kept
|
||||
* significand is all ones; the carry then moves the value to the next
|
||||
* binade.
|
||||
*/
|
||||
static long double
|
||||
rint_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 16382)
|
||||
{
|
||||
if (p.m == 0x8000000000000000ULL)
|
||||
{
|
||||
/* Exactly 0.5: tie toward even zero. */
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | 16383;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = p.m & ((1ULL << shift) - 1ULL);
|
||||
p.m &= ~((1ULL << shift) - 1ULL);
|
||||
if (frac > half || (frac == half && ((p.m >> shift) & 1ULL) != 0))
|
||||
{
|
||||
p.m += 1ULL << shift;
|
||||
if (p.m == 0)
|
||||
{
|
||||
/* Kept significand was all ones: carry to the next binade. */
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | (unsigned short)(e + 1);
|
||||
}
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As rint, for a float argument.
|
||||
*/
|
||||
float
|
||||
rintf(float x)
|
||||
{
|
||||
return rint_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As rint, for a double argument.
|
||||
*/
|
||||
double
|
||||
rint(double x)
|
||||
{
|
||||
return rint_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As rint, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
rintl(long double x)
|
||||
{
|
||||
return rint_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x, halfway cases rounded away from zero
|
||||
* (C23 7.12.9.6), all three precisions.
|
||||
*
|
||||
* GCC never folds the __builtin_round forms on this target (no SSE4.1 in
|
||||
* the default -march, so the roundsd expansion is unavailable and every
|
||||
* __builtin_round call becomes an external round@PLT call at every
|
||||
* optimization level), so each function below rounds directly on the
|
||||
* IEEE 754 bit pattern. A negative argument with a nonzero dropped
|
||||
* fraction must round away from zero, so the rounding is applied to the
|
||||
* magnitude (sign bit untouched): drop the fractional bits, and when the
|
||||
* dropped part is >= half an ulp step the kept significand up by one.
|
||||
* The step is a plain integer add that carries into the exponent field
|
||||
* when the kept fraction is all ones, which renormalizes exactly.
|
||||
* round(±0) is ±0, round(-0.5) is -1.0 (half away from zero), and ±Inf
|
||||
* and NaN pass through unchanged. Pure: no rounding mode consulted and no
|
||||
* exception raised.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The double format: bit 63 is the sign, bits 62..52 the exponent biased
|
||||
* by 1023, bits 51..0 the fraction. A finite value with exponent e has
|
||||
* fractional bits only when e - 1023 < 52, i.e. e < 1075; for e in
|
||||
* [1023, 1074] exactly the low (1075 - e) bits of the fraction word are
|
||||
* the fractional part.
|
||||
*/
|
||||
static double
|
||||
round_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
unsigned long long kept;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
/* |x| >= 2^52 is already integral; Inf (e == 0x7ff) and NaN pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* |x| < 1: round(±0) is ±0 and any other value with |x| >= 1/2 goes
|
||||
* to ±1 while smaller magnitudes collapse to ±0, keeping the sign. */
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~(1ULL << 63)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 1022)
|
||||
{
|
||||
bits = (bits & (1ULL << 63)) | 0x3FF0000000000000ULL;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = bits & ((1ULL << shift) - 1ULL);
|
||||
kept = bits & ~((1ULL << shift) - 1ULL);
|
||||
if (frac >= half)
|
||||
{
|
||||
kept += 1ULL << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The float format: bit 31 is the sign, bits 30..23 the exponent biased
|
||||
* by 127, bits 22..0 the fraction. Fraction bits exist exactly when the
|
||||
* exponent e is in [127, 149]; the low (150 - e) bits are fractional.
|
||||
*/
|
||||
static float
|
||||
round_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
unsigned int kept;
|
||||
unsigned int frac;
|
||||
unsigned int half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~(1U << 31)) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 126)
|
||||
{
|
||||
bits = (bits & (1U << 31)) | 0x3F800000U;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
half = 1U << (shift - 1);
|
||||
frac = bits & ((1U << shift) - 1U);
|
||||
kept = bits & ~((1U << shift) - 1U);
|
||||
if (frac >= half)
|
||||
{
|
||||
kept += 1U << shift;
|
||||
}
|
||||
__builtin_memcpy(&x, &kept, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* The x86 80-bit extended format: 64 significand bits m (the integer bit
|
||||
* is explicit) in bytes 0..7 and a sign/exponent word se in bytes 8..9,
|
||||
* with the sign in bit 15 and the exponent (biased by 16383) in bits
|
||||
* 14..0. A value m * 2^(e - 16446) has fractional bits only when
|
||||
* e - 16383 < 63, i.e. e < 16446. The half-away step may overflow the
|
||||
* 64-bit m when the kept significand is all ones; the carry then moves
|
||||
* the value to the next binade (e + 1, integer bit alone).
|
||||
*/
|
||||
static long double
|
||||
round_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
unsigned long long half;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e == 16382)
|
||||
{
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | 16383;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
shift = 16446 - e;
|
||||
half = 1ULL << (shift - 1);
|
||||
frac = p.m & ((1ULL << shift) - 1ULL);
|
||||
p.m &= ~((1ULL << shift) - 1ULL);
|
||||
if (frac >= half)
|
||||
{
|
||||
p.m += 1ULL << shift;
|
||||
if (p.m == 0)
|
||||
{
|
||||
/* Kept significand was all ones: carry to the next binade. */
|
||||
p.m = 0x8000000000000000ULL;
|
||||
p.se = (p.se & 0x8000) | (unsigned short)(e + 1);
|
||||
}
|
||||
}
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As round, for a float argument.
|
||||
*/
|
||||
float
|
||||
roundf(float x)
|
||||
{
|
||||
return round_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As round, for a double argument.
|
||||
*/
|
||||
double
|
||||
round(double x)
|
||||
{
|
||||
return round_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As round, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
roundl(long double x)
|
||||
{
|
||||
return round_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "math_impl.h"
|
||||
|
||||
/*
|
||||
* x * FLT_RADIX^n with FLT_RADIX 2 (C23 7.12.6.6), all three precisions
|
||||
* and both exponent-argument types. scalbn takes an int n, scalbln a long
|
||||
* n; otherwise the two families are semantically identical to ldexp (they
|
||||
* share the exact same scale cores and overflow-to-+-Inf-with-ERANGE
|
||||
* behavior, and subnormal inputs are handled exactly like subnormal
|
||||
* outputs). The scalbln functions clamp the long exponent to +-20000
|
||||
* first: any magnitude beyond that saturates every result to +-Inf or +-0
|
||||
* in all three formats, and clamping keeps the arithmetic inside int range
|
||||
* with no shift by a huge count.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As ldexp (see ldexp.c) for a double x and an int n.
|
||||
*/
|
||||
double
|
||||
scalbn(double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
double r = vl_scale2_d(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
float
|
||||
scalbnf(float x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
float r = vl_scale2_f(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
long double
|
||||
scalbnl(long double x, int n)
|
||||
{
|
||||
int overflowed = 0;
|
||||
long double r = vl_scale2_ld(x, n, &overflowed);
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
if (overflowed)
|
||||
{
|
||||
errno = ERANGE;
|
||||
}
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* The scalbln family: as scalbn with the exponent given as a long.
|
||||
* n is first clamped into [-20000, 20000]; anything beyond saturates every
|
||||
* precision's range (the largest meaningful long-double exponent is below
|
||||
* 16446 in magnitude), so no precision is lost by the clamp.
|
||||
*/
|
||||
double
|
||||
scalbln(double x, long n)
|
||||
{
|
||||
if (n > 20000)
|
||||
{
|
||||
n = 20000;
|
||||
}
|
||||
else if (n < -20000)
|
||||
{
|
||||
n = -20000;
|
||||
}
|
||||
return scalbn(x, (int)n);
|
||||
}
|
||||
|
||||
float
|
||||
scalblnf(float x, long n)
|
||||
{
|
||||
if (n > 20000)
|
||||
{
|
||||
n = 20000;
|
||||
}
|
||||
else if (n < -20000)
|
||||
{
|
||||
n = -20000;
|
||||
}
|
||||
return scalbnf(x, (int)n);
|
||||
}
|
||||
|
||||
long double
|
||||
scalblnl(long double x, long n)
|
||||
{
|
||||
if (n > 20000)
|
||||
{
|
||||
n = 20000;
|
||||
}
|
||||
else if (n < -20000)
|
||||
{
|
||||
n = -20000;
|
||||
}
|
||||
return scalbnl(x, (int)n);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* sqrt is the one function IEEE 754 requires to be correctly rounded. The
|
||||
* sqrtsd/sqrtss/fsqrt hardware instructions are; inline asm keeps the call
|
||||
* self-contained (GCC's __builtin_sqrt would emit a libm call for NaN
|
||||
* inputs and a false recursion warning inside a function named sqrt). */
|
||||
double
|
||||
sqrt(double x)
|
||||
{
|
||||
double y;
|
||||
|
||||
if (x < 0.0)
|
||||
SET_EDOM();
|
||||
__asm__("sqrtsd %1, %0" : "=x"(y) : "x"(x));
|
||||
return y;
|
||||
}
|
||||
|
||||
float
|
||||
sqrtf(float x)
|
||||
{
|
||||
float y;
|
||||
|
||||
if (x < 0.0f)
|
||||
SET_EDOM();
|
||||
__asm__("sqrtss %1, %0" : "=x"(y) : "x"(x));
|
||||
return y;
|
||||
}
|
||||
|
||||
long double
|
||||
sqrtl(long double x)
|
||||
{
|
||||
if (x < 0.0L)
|
||||
SET_EDOM();
|
||||
__asm__("fsqrt" : "+t"(x));
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Integral value nearest to x in the direction of zero (C23 7.12.9.3),
|
||||
* all three precisions.
|
||||
*
|
||||
* GCC does not fold the __builtin_trunc forms on this target (see
|
||||
* floor.c), so each function below simply clears the fractional mantissa
|
||||
* bits on the IEEE 754 bit pattern. Rounding toward zero never rounds, so
|
||||
* no rounding mode or exception is involved; trunc(-0.3) is -0.0 (the
|
||||
* sign bit survives), and ±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
static double
|
||||
trunc_d(double x)
|
||||
{
|
||||
unsigned long long bits;
|
||||
int e;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
/* |x| >= 2^52 is already integral; Inf (e == 0x7ff) and NaN pass
|
||||
* through unchanged as well. */
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
/* |x| < 1: truncation is ±0, keeping the sign bit. */
|
||||
bits &= 1ULL << 63;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
bits &= ~((1ULL << (1075 - e)) - 1ULL);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
static float
|
||||
trunc_f(float x)
|
||||
{
|
||||
unsigned int bits;
|
||||
int e;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
bits &= 1U << 31;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
bits &= ~((1U << (150 - e)) - 1U);
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
|
||||
static long double
|
||||
trunc_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
int e;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
/* |x| < 1: truncation is ±0, keeping the sign bit. */
|
||||
p.m = 0;
|
||||
p.se &= 0x8000;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
p.m &= ~((1ULL << (63 - (e - 16383))) - 1ULL);
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* As trunc, for a float argument.
|
||||
*/
|
||||
float
|
||||
truncf(float x)
|
||||
{
|
||||
return trunc_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As trunc, for a double argument.
|
||||
*/
|
||||
double
|
||||
trunc(double x)
|
||||
{
|
||||
return trunc_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As trunc, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
truncl(long double x)
|
||||
{
|
||||
return trunc_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* pathconf() / fpathconf(): configurable limits and options of the
|
||||
* filesystem holding a path or an open descriptor.
|
||||
*
|
||||
* Both functions first validate the object with statfs/fstatfs (so a
|
||||
* missing path or a closed descriptor yields -1 with errno from the
|
||||
* syscall layer) and then answer from the resulting filesystem data plus
|
||||
* the fixed value table below. Only _PC_NAME_MAX and the three
|
||||
* block-size-derived keys are truly per-filesystem; the rest are vlibc's
|
||||
* fixed Linux values (see the pathconf() documentation in
|
||||
* include/unistd.h). Unknown keys and keys whose option this filesystem
|
||||
* does not provide return -1 WITHOUT setting errno.
|
||||
*
|
||||
* The name argument (one of the _PC_* keys) is forwarded to the value
|
||||
* table unchanged; POSIX does not define per-file-type answers for the
|
||||
* keys vlibc supports, so the object type is not inspected beyond the
|
||||
* statfs validation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Linux struct statfs (kernel ABI, x86_64; linux/statfs.h). Layout and
|
||||
* sizes are LP64 facts: f_type..f_ffree are longs, f_fsid is an int pair,
|
||||
* then f_namelen/f_frsize/f_flags/f_spare[4] complete the 120 bytes the
|
||||
* kernel copies out. Only f_bsize and f_namelen are read here.
|
||||
*/
|
||||
struct vlibc_statfs
|
||||
{
|
||||
long f_type;
|
||||
long f_bsize;
|
||||
long f_blocks;
|
||||
long f_bfree;
|
||||
long f_bavail;
|
||||
long f_files;
|
||||
long f_ffree;
|
||||
int f_fsid[2];
|
||||
long f_namelen;
|
||||
long f_frsize;
|
||||
long f_flags;
|
||||
long f_spare[4];
|
||||
};
|
||||
|
||||
/* Answer key name from the filesystem data in fs; -1 for unknown keys and
|
||||
* for keys whose option this filesystem does not provide, without setting
|
||||
* errno in either case. */
|
||||
static long
|
||||
pathconf_value(const struct vlibc_statfs *fs, int name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case _PC_LINK_MAX:
|
||||
return 32000;
|
||||
case _PC_MAX_CANON:
|
||||
case _PC_MAX_INPUT:
|
||||
return 255;
|
||||
case _PC_NAME_MAX:
|
||||
return fs->f_namelen;
|
||||
case _PC_PATH_MAX:
|
||||
case _PC_PIPE_BUF:
|
||||
return 4096;
|
||||
case _PC_CHOWN_RESTRICTED:
|
||||
case _PC_NO_TRUNC:
|
||||
case _PC_2_SYMLINKS:
|
||||
return 1;
|
||||
case _PC_VDISABLE:
|
||||
return 0;
|
||||
case _PC_FILESIZEBITS:
|
||||
return 64;
|
||||
case _PC_REC_MIN_XFER_SIZE:
|
||||
case _PC_REC_XFER_ALIGN:
|
||||
case _PC_ALLOC_SIZE_MIN:
|
||||
return fs->f_bsize;
|
||||
default:
|
||||
/* _PC_ASYNC_IO, _PC_SYNC_IO, _PC_PRIO_IO, _PC_SOCK_MAXBUF,
|
||||
* _PC_REC_INCR_XFER_SIZE, _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX
|
||||
* and unknown keys. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long
|
||||
pathconf(const char *path, int name)
|
||||
{
|
||||
struct vlibc_statfs fs = {0};
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_statfs, (long)path, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return pathconf_value(&fs, name);
|
||||
}
|
||||
|
||||
long
|
||||
fpathconf(int fildes, int name) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct vlibc_statfs fs = {0};
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_fstatfs, fildes, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return pathconf_value(&fs, name);
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sysconf(): system limits and options.
|
||||
*
|
||||
* Every supported key returns its value directly (see the sysconf()
|
||||
* documentation in include/unistd.h for the full table). Unknown keys and
|
||||
* genuinely unsupported options return -1 WITHOUT setting errno — a call
|
||||
* must be able to probe arbitrary _SC_* values without clobbering a
|
||||
* caller's errno (the QA contract of todo 32).
|
||||
*
|
||||
* The fixed x86_64 clock-tick rate, defined ONCE here: USER_HZ is 100 on
|
||||
* every x86_64 Linux kernel. todo 35's times() reports in exactly these
|
||||
* ticks and consumes this constant rather than re-defining it.
|
||||
*/
|
||||
#define VLIBC_CLK_TCK 100
|
||||
|
||||
/* Kernel-UAPI values local to this file (include/fcntl.h owns the public
|
||||
* AT_FDCWD / O_RDONLY spellings; asm-generic/auxvec.h owns AT_PAGESZ). */
|
||||
#define VLIBC_SYSCONF_AT_FDCWD (-100)
|
||||
#define VLIBC_SYSCONF_AT_NULL 0
|
||||
#define VLIBC_SYSCONF_AT_PAGESZ 6
|
||||
#define VLIBC_SYSCONF_O_RDONLY 0
|
||||
#define VLIBC_SYSCONF_RLIMIT_NOFILE 7
|
||||
|
||||
/*
|
||||
* Linux struct sysinfo (kernel ABI, x86_64; linux/sysinfo.h). The totalram/
|
||||
* freeram/... fields are memory sizes counted in mem_unit-byte units
|
||||
* (current kernels report mem_unit == 1 with sizes in bytes). procs is the
|
||||
* number of live PROCESSES, not a CPU count — CPU counts come from
|
||||
* sched_getaffinity, not from here.
|
||||
*/
|
||||
struct vlibc_sysinfo
|
||||
{
|
||||
long uptime;
|
||||
unsigned long loads[3];
|
||||
unsigned long totalram;
|
||||
unsigned long freeram;
|
||||
unsigned long sharedram;
|
||||
unsigned long bufferram;
|
||||
unsigned long totalswap;
|
||||
unsigned long freeswap;
|
||||
unsigned short procs;
|
||||
unsigned short pad;
|
||||
unsigned long totalhigh;
|
||||
unsigned long freehigh;
|
||||
unsigned int mem_unit;
|
||||
};
|
||||
|
||||
/* RLIMIT_NOFILE as returned by prlimit64: current and maximum limits. */
|
||||
struct vlibc_rlimit
|
||||
{
|
||||
unsigned long cur;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
/*
|
||||
* The memory page size, read from the AT_PAGESZ entry of the auxiliary
|
||||
* vector. The vlibc startup code (todo 3) does not save the auxv, so it is
|
||||
* re-read from /proc/self/auxv on first use and cached; a static cache is
|
||||
* safe because no threads exist yet (todo 45 must revisit). On any failure
|
||||
* — auxv unreadable or AT_PAGESZ absent — the x86_64 true page size 4096
|
||||
* is used as the documented fallback (this is what AT_PAGESZ reports on
|
||||
* every x86_64 kernel anyway; the value is never hardcoded as the primary
|
||||
* source).
|
||||
*/
|
||||
static long
|
||||
query_pagesize(void)
|
||||
{
|
||||
static long cached;
|
||||
static int done;
|
||||
|
||||
if (!done)
|
||||
{
|
||||
unsigned char buf[1024] = {0};
|
||||
long fd;
|
||||
long n;
|
||||
|
||||
done = 1;
|
||||
cached = 4096;
|
||||
fd = __syscall3(SYS_openat, VLIBC_SYSCONF_AT_FDCWD, (long)"/proc/self/auxv",
|
||||
VLIBC_SYSCONF_O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
n = __syscall3(SYS_read, fd, (long)buf, (long)sizeof buf);
|
||||
__syscall1(SYS_close, fd);
|
||||
for (long off = 0; n >= 16 && off <= n - 16; off += 16)
|
||||
{
|
||||
unsigned long type;
|
||||
unsigned long value;
|
||||
|
||||
/* The auxv is a sequence of (type, value) unsigned long pairs. */
|
||||
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
__builtin_memcpy(&type, buf + off, sizeof type);
|
||||
__builtin_memcpy(&value, buf + off + sizeof type, sizeof value);
|
||||
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
if (type == VLIBC_SYSCONF_AT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (type == VLIBC_SYSCONF_AT_PAGESZ)
|
||||
{
|
||||
cached = (long)value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
/*
|
||||
* The open-descriptor limit: the RLIMIT_NOFILE soft limit via prlimit64.
|
||||
* prlimit64 cannot usefully fail on the calling process, but the raw
|
||||
* result is checked anyway so no errno write happens on the fallback path
|
||||
* (the probe must stay errno-clean: it may run under a seccomp filter).
|
||||
*/
|
||||
static long
|
||||
query_open_max(void)
|
||||
{
|
||||
struct vlibc_rlimit rlim = {0};
|
||||
long r;
|
||||
|
||||
r = __syscall4(SYS_prlimit64, 0, VLIBC_SYSCONF_RLIMIT_NOFILE, 0, (long)&rlim);
|
||||
if (r < 0 || rlim.cur == 0 || rlim.cur == ~0UL)
|
||||
{
|
||||
return 1024;
|
||||
}
|
||||
return (long)rlim.cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* The online processor count, from the calling process's CPU affinity
|
||||
* mask via sched_getaffinity(pid 0): the kernel returns the task's allowed
|
||||
* CPUs, which is the count of CPUs this process can actually run on. The
|
||||
* configured count is reported as the same value (a cpuset could hide
|
||||
* CPUs, but on a system without hotplug the configured and online counts
|
||||
* agree; glibc/musl make the same simplification for the affinity path).
|
||||
*/
|
||||
static long
|
||||
query_nprocs(void)
|
||||
{
|
||||
unsigned long mask[16] = {0}; /* 1024 bits, enough for any CPU count */
|
||||
long count = 0;
|
||||
long r;
|
||||
|
||||
r = __syscall3(SYS_sched_getaffinity, 0, (long)sizeof mask, (long)mask);
|
||||
if (r < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (r > (long)sizeof mask)
|
||||
{
|
||||
r = (long)sizeof mask;
|
||||
}
|
||||
for (long i = 0; i < r / (long)sizeof(unsigned long); i++)
|
||||
{
|
||||
count += (long)__builtin_popcountl(mask[i]);
|
||||
}
|
||||
return count > 0 ? count : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Physical (avail == 0) or available (avail != 0) memory in pages, from
|
||||
* SYS_sysinfo. Sizes arrive in mem_unit-byte units, so the byte count is
|
||||
* totalram * mem_unit (overflow is not a concern: the product stays far
|
||||
* below 2^64 for any real machine). A sysinfo failure is reported as -1,
|
||||
* which the caller treats like an unsupported key.
|
||||
*/
|
||||
static long
|
||||
query_mem_pages(int avail)
|
||||
{
|
||||
struct vlibc_sysinfo si = {0};
|
||||
unsigned long unit;
|
||||
unsigned long bytes;
|
||||
long r;
|
||||
|
||||
r = __syscall1(SYS_sysinfo, (long)&si);
|
||||
if (r < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
unit = si.mem_unit != 0 ? si.mem_unit : 1;
|
||||
bytes = (avail != 0 ? si.freeram : si.totalram) * unit;
|
||||
return (long)(bytes / (unsigned long)query_pagesize());
|
||||
}
|
||||
|
||||
long
|
||||
sysconf(int name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case _SC_ARG_MAX:
|
||||
return 2097152;
|
||||
case _SC_CHILD_MAX:
|
||||
return 65535;
|
||||
case _SC_CLK_TCK:
|
||||
return VLIBC_CLK_TCK;
|
||||
case _SC_NGROUPS_MAX:
|
||||
return 65536;
|
||||
case _SC_OPEN_MAX:
|
||||
return query_open_max();
|
||||
case _SC_PAGESIZE:
|
||||
case _SC_PAGE_SIZE:
|
||||
return query_pagesize();
|
||||
case _SC_NPROCESSORS_CONF:
|
||||
case _SC_NPROCESSORS_ONLN:
|
||||
return query_nprocs();
|
||||
case _SC_PHYS_PAGES:
|
||||
return query_mem_pages(0);
|
||||
case _SC_AVPHYS_PAGES:
|
||||
return query_mem_pages(1);
|
||||
case _SC_SYMLOOP_MAX:
|
||||
return 40;
|
||||
case _SC_STREAM_MAX:
|
||||
return 16;
|
||||
case _SC_TZNAME_MAX:
|
||||
return 6;
|
||||
case _SC_HOST_NAME_MAX:
|
||||
return 64;
|
||||
case _SC_LOGIN_NAME_MAX:
|
||||
return 256;
|
||||
case _SC_GETPW_R_SIZE_MAX:
|
||||
case _SC_IOV_MAX:
|
||||
return 1024;
|
||||
case _SC_LINE_MAX:
|
||||
return 2048;
|
||||
case _SC_RE_DUP_MAX:
|
||||
return 32767;
|
||||
case _SC_COLL_WEIGHTS_MAX:
|
||||
return 255;
|
||||
case _SC_EXPR_NEST_MAX:
|
||||
return 32;
|
||||
case _SC_XOPEN_VERSION:
|
||||
return 700;
|
||||
case _SC_JOB_CONTROL:
|
||||
case _SC_SAVED_IDS:
|
||||
case _SC_REGEXP:
|
||||
case _SC_SHELL:
|
||||
case _SC_XOPEN_UNIX:
|
||||
case _SC_XOPEN_CRYPT:
|
||||
case _SC_XOPEN_ENH_I18N:
|
||||
case _SC_XOPEN_SHM:
|
||||
return 1;
|
||||
case _SC_VERSION:
|
||||
case _SC_2_VERSION:
|
||||
case _SC_THREADS:
|
||||
case _SC_SEMAPHORES:
|
||||
case _SC_SHARED_MEMORY_OBJECTS:
|
||||
case _SC_MESSAGE_PASSING:
|
||||
case _SC_MONOTONIC_CLOCK:
|
||||
case _SC_TIMERS:
|
||||
case _SC_SYNCHRONIZED_IO:
|
||||
case _SC_THREAD_SAFE_FUNCTIONS:
|
||||
case _SC_THREAD_ATTR_STACKADDR:
|
||||
case _SC_THREAD_ATTR_STACKSIZE:
|
||||
case _SC_THREAD_PRIORITY_SCHEDULING:
|
||||
case _SC_THREAD_PRIO_INHERIT:
|
||||
case _SC_THREAD_PRIO_PROTECT:
|
||||
case _SC_THREAD_PROCESS_SHARED:
|
||||
case _SC_REALTIME_SIGNALS:
|
||||
case _SC_2_C_BIND:
|
||||
case _SC_2_C_DEV:
|
||||
case _SC_2_FORT_DEV:
|
||||
case _SC_2_FORT_RUN:
|
||||
case _SC_2_LOCALEDEF:
|
||||
case _SC_2_SW_DEV:
|
||||
case _SC_2_UPE:
|
||||
return 200809L;
|
||||
default:
|
||||
/* Unknown key or unsupported option: -1, errno untouched. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* vlibc — openlog/syslog/closelog/vsyslog/setlogmask.
|
||||
*
|
||||
* syslog is XSI/BSD, not POSIX.1-2008 base, so the whole file is gated at
|
||||
* level 2 (whole-file-L2 rule); at level 1 this TU compiles empty.
|
||||
*
|
||||
* Wire format: each message is one SOCK_DGRAM datagram to the /dev/log
|
||||
* socket, shaped like
|
||||
*
|
||||
* <PRI>ident[pid]: message
|
||||
*
|
||||
* where PRI is the three-digit decimal priority code = facility | severity
|
||||
* (the facility constants in <syslog.h> are already code << 3, so the OR is
|
||||
* the wire value), ident and [pid] appear only when openlog received a
|
||||
* non-NULL ident / the LOG_PID option, and the datagram carries no
|
||||
* timestamp — the syslog daemon adds its own (glibc sends none either). The
|
||||
* message text is the caller's format expanded by vsnprintf (the library's
|
||||
* own, from src/stdio), with one syslog-specific addition: "%m" is replaced
|
||||
* by the strerror(errno) text before vsnprintf sees the format, because the
|
||||
* printf core deliberately has no %m conversion.
|
||||
*
|
||||
* The socket is opened lazily on the first syslog call (or immediately when
|
||||
* openlog gets LOG_NDELAY) and closed by closelog. Every failure — a socket
|
||||
* that cannot be created, a sendto that bounces (no daemon listening gives
|
||||
* ECONNREFUSED at once for an unconnected datagram socket, so nothing here
|
||||
* blocks), a console that cannot be opened — is silent, per the POSIX rule
|
||||
* that syslog must not terminate the process. sendto takes the destination
|
||||
* address per call, so a daemon restart that closes the daemon's socket
|
||||
* needs no reconnect; a single reopen-on-ENOTCONN retry is kept as a guard
|
||||
* for sockets that were implicitly connected.
|
||||
*
|
||||
* This is process-global state, deliberately not thread-synchronized: the
|
||||
* POSIX text does not require syslog to be thread-safe, and matching the
|
||||
* reference implementations' unsynchronized behavior keeps the hot path
|
||||
* free of locks.
|
||||
*/
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* The /dev/log address: the classic BSD syslog datagram sink on Linux. */
|
||||
#define LOG_SOCK_PATH "/dev/log"
|
||||
#define LOG_CONSOLE_PATH "/dev/console"
|
||||
|
||||
/* Kernel ABI constants this TU needs but no public header provides yet. */
|
||||
#define LOG_AF_UNIX 1 /* struct sockaddr family for AF_UNIX */
|
||||
#define LOG_SOCK_DGRAM 2 /* SOCK_DGRAM */
|
||||
|
||||
/*
|
||||
* struct sockaddr_un layout for Linux x86_64, declared locally: the socket
|
||||
* layer (todo 57) does not exist yet, and pulling in a system header would
|
||||
* break the clean-room, host-header-free build.
|
||||
*/
|
||||
struct log_sockaddr
|
||||
{
|
||||
unsigned short sun_family;
|
||||
char sun_path[108];
|
||||
};
|
||||
|
||||
/* Bounded copy of the openlog ident; 256 covers every real tag. */
|
||||
static char log_ident[256];
|
||||
|
||||
/* The openlog option flags (LOG_PID/LOG_CONS/...). */
|
||||
static int log_option;
|
||||
|
||||
/* The facility (code << 3) used when a message carries no facility of its
|
||||
* own; POSIX's default when openlog was never called is LOG_USER. */
|
||||
static int log_facility = LOG_USER;
|
||||
|
||||
/* The setlogmask mask: one bit per severity. Default: everything (0xff). */
|
||||
static int log_mask = LOG_UPTO(LOG_DEBUG);
|
||||
|
||||
/* The /dev/log socket, or -1 while closed. */
|
||||
static int log_fd = -1;
|
||||
|
||||
/*
|
||||
* Open the /dev/log datagram socket unless one is already open. Failures are
|
||||
* silent and leave log_fd at -1; the next message simply tries again.
|
||||
*/
|
||||
static void
|
||||
log_open(void)
|
||||
{
|
||||
long r;
|
||||
|
||||
if (log_fd >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
r = __syscall3(SYS_socket, LOG_AF_UNIX, LOG_SOCK_DGRAM, 0);
|
||||
if (r >= 0)
|
||||
{
|
||||
log_fd = (int)r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the len bytes at s to standard error with a raw write; ignore errors. */
|
||||
static void
|
||||
log_write_stderr(const char *s, size_t len)
|
||||
{
|
||||
(void)__syscall3(SYS_write, 2, (long)s, (long)len);
|
||||
}
|
||||
|
||||
/*
|
||||
* LOG_CONS fallback: write the message to /dev/console. The console is opened
|
||||
* O_WRONLY|O_NOCTTY so a session without a controlling terminal is not given
|
||||
* one, and is closed again right away — the file is not kept.
|
||||
*/
|
||||
static void
|
||||
log_write_console(const char *s, size_t len)
|
||||
{
|
||||
long fd;
|
||||
|
||||
fd = __syscall4(SYS_openat, AT_FDCWD, (long)LOG_CONSOLE_PATH, (long)(O_WRONLY | O_NOCTTY), 0);
|
||||
if (fd < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
(void)__syscall3(SYS_write, fd, (long)s, (long)len);
|
||||
(void)__syscall3(SYS_write, fd, (long)"\n", 1);
|
||||
(void)__syscall1(SYS_close, fd);
|
||||
}
|
||||
|
||||
/* Render v in decimal into dst; return the number of characters written. */
|
||||
static size_t
|
||||
log_put_dec(char *dst, unsigned long v)
|
||||
{
|
||||
char tmp[20];
|
||||
size_t n = 0;
|
||||
|
||||
do
|
||||
{
|
||||
tmp[n++] = (char)('0' + (int)(v % 10));
|
||||
v /= 10;
|
||||
} while (v != 0);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
dst[i] = tmp[n - 1 - i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
openlog(const char *ident, int option, int facility)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (ident == NULL)
|
||||
{
|
||||
log_ident[0] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; ident[i] != '\0' && i + 1 < sizeof(log_ident); i++)
|
||||
{
|
||||
log_ident[i] = ident[i];
|
||||
}
|
||||
log_ident[i] = '\0';
|
||||
}
|
||||
log_option = option;
|
||||
log_facility = facility;
|
||||
if ((log_option & LOG_NDELAY) != 0)
|
||||
{
|
||||
log_open();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
closelog(void)
|
||||
{
|
||||
if (log_fd >= 0)
|
||||
{
|
||||
(void)__syscall1(SYS_close, log_fd);
|
||||
log_fd = -1;
|
||||
}
|
||||
log_ident[0] = '\0';
|
||||
log_option = 0;
|
||||
}
|
||||
|
||||
int
|
||||
setlogmask(int maskpri)
|
||||
{
|
||||
int old = log_mask;
|
||||
|
||||
log_mask = maskpri;
|
||||
return old;
|
||||
}
|
||||
|
||||
void
|
||||
vsyslog(int priority, const char *format, va_list ap)
|
||||
{
|
||||
int errno_saved = errno;
|
||||
int pr = priority & LOG_PRIMASK;
|
||||
int facility = priority & LOG_FACMASK;
|
||||
char expanded[1024];
|
||||
char message[1024];
|
||||
char line[1400];
|
||||
char *o = line;
|
||||
const char *const oend = line + sizeof(line) - 1;
|
||||
const char *es = NULL; /* strerror text, resolved on first "%m" */
|
||||
size_t msglen;
|
||||
size_t linelen;
|
||||
long r;
|
||||
int retried = 0;
|
||||
|
||||
/* The mask filters on severity alone; facility never suppresses. */
|
||||
if ((log_mask & LOG_MASK(pr)) == 0)
|
||||
{
|
||||
errno = errno_saved;
|
||||
return;
|
||||
}
|
||||
if (facility == 0)
|
||||
{
|
||||
facility = log_facility;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand "%m" into the strerror text, doubling any "%" inside that text
|
||||
* so vsnprintf prints it literally; every other character — including
|
||||
* the other conversion specifiers and their argument parsing — passes
|
||||
* through untouched, so the va_list still lines up. "%%m" is a literal
|
||||
* "%m", not a substitution.
|
||||
*/
|
||||
{
|
||||
const char *src = format;
|
||||
char *dst = expanded;
|
||||
const char *const dstend = expanded + sizeof(expanded) - 1;
|
||||
|
||||
while (*src != '\0' && dst < dstend)
|
||||
{
|
||||
if (src[0] == '%' && src[1] == '%')
|
||||
{
|
||||
if (dst + 1 >= dstend)
|
||||
{
|
||||
break;
|
||||
}
|
||||
dst[0] = '%';
|
||||
dst[1] = '%';
|
||||
dst += 2;
|
||||
src += 2;
|
||||
}
|
||||
else if (src[0] == '%' && src[1] == 'm')
|
||||
{
|
||||
if (es == NULL)
|
||||
{
|
||||
es = strerror(errno_saved);
|
||||
}
|
||||
for (const char *q = es; *q != '\0' && dst < dstend - 1; q++)
|
||||
{
|
||||
if (*q == '%')
|
||||
{
|
||||
dst[0] = '%';
|
||||
dst[1] = '%';
|
||||
dst += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = *q;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
src += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[0] = *src;
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
msglen = (size_t)vsnprintf(message, sizeof(message), expanded, ap);
|
||||
|
||||
/* "<facility|severity>" — three digits, zero-padded (values <= 191). */
|
||||
{
|
||||
int code = facility | pr;
|
||||
|
||||
*o++ = '<';
|
||||
*o++ = (char)('0' + code / 100);
|
||||
*o++ = (char)('0' + (code / 10) % 10);
|
||||
*o++ = (char)('0' + code % 10);
|
||||
*o++ = '>';
|
||||
}
|
||||
|
||||
for (size_t i = 0; log_ident[i] != '\0' && o < oend; i++)
|
||||
{
|
||||
*o++ = log_ident[i];
|
||||
}
|
||||
|
||||
if ((log_option & LOG_PID) != 0)
|
||||
{
|
||||
long pid = __syscall0(SYS_getpid);
|
||||
char digits[20];
|
||||
size_t n;
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
pid = 0;
|
||||
}
|
||||
n = log_put_dec(digits, (unsigned long)pid);
|
||||
if (o < oend)
|
||||
{
|
||||
*o++ = '[';
|
||||
}
|
||||
for (size_t i = 0; i < n && o < oend; i++)
|
||||
{
|
||||
*o++ = digits[i];
|
||||
}
|
||||
if (o < oend)
|
||||
{
|
||||
*o++ = ']';
|
||||
}
|
||||
}
|
||||
|
||||
if (o + 1 < oend)
|
||||
{
|
||||
*o++ = ':';
|
||||
*o++ = ' ';
|
||||
}
|
||||
if (msglen > sizeof(message) - 1)
|
||||
{
|
||||
msglen = sizeof(message) - 1;
|
||||
}
|
||||
for (size_t i = 0; i < msglen && o < oend; i++)
|
||||
{
|
||||
*o++ = message[i];
|
||||
}
|
||||
*o = '\0';
|
||||
linelen = (size_t)(o - line);
|
||||
|
||||
if ((log_option & LOG_PERROR) != 0)
|
||||
{
|
||||
log_write_stderr(line, linelen);
|
||||
log_write_stderr("\n", 1);
|
||||
}
|
||||
|
||||
if (log_fd < 0)
|
||||
{
|
||||
log_open();
|
||||
}
|
||||
if (log_fd >= 0)
|
||||
{
|
||||
struct log_sockaddr sa = {.sun_family = LOG_AF_UNIX, .sun_path = LOG_SOCK_PATH};
|
||||
|
||||
do
|
||||
{
|
||||
r = __syscall6(SYS_sendto, log_fd, (long)line, (long)linelen, 0, (long)&sa,
|
||||
(long)sizeof(sa));
|
||||
if (r >= 0 || retried != 0 || -r != ENOTCONN)
|
||||
{
|
||||
break;
|
||||
}
|
||||
/* Stale after a daemon restart: drop and recreate the socket once. */
|
||||
(void)__syscall1(SYS_close, log_fd);
|
||||
log_fd = -1;
|
||||
log_open();
|
||||
retried = 1;
|
||||
} while (log_fd >= 0);
|
||||
|
||||
if (r < 0 && (log_option & LOG_CONS) != 0)
|
||||
{
|
||||
log_write_console(line, linelen);
|
||||
}
|
||||
}
|
||||
else if ((log_option & LOG_CONS) != 0)
|
||||
{
|
||||
log_write_console(line, linelen);
|
||||
}
|
||||
|
||||
errno = errno_saved;
|
||||
}
|
||||
|
||||
void
|
||||
syslog(int priority, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vsyslog(priority, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* uname/gethostname/sethostname/gethostid: system identification.
|
||||
*
|
||||
* Everything here is XSI or BSD — none of it is POSIX.1-2008 base — so the
|
||||
* whole file is gated at level 2 (whole-file-L2 rule; the wiring pass puts
|
||||
* it in VLIBC_LEVEL2_SRCS, and at level 1 this TU compiles empty).
|
||||
*
|
||||
* gethostname and sethostname are declared in <unistd.h>, gethostid too;
|
||||
* uname and struct utsname live in <sys/utsname.h>. struct utsname is the
|
||||
* kernel's struct new_utsname layout (six 65-byte arrays), so the kernel
|
||||
* writes it in place through SYS_uname.
|
||||
*/
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Fill the buffer with the kernel's UTS namespace strings (the "name"
|
||||
* information of the running kernel and host). syscall_ret reports 0 on
|
||||
* success and -1 with EFAULT when the buffer is not writable.
|
||||
*/
|
||||
int
|
||||
uname(struct utsname *buf)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_uname, (long)buf));
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the NUL-terminated nodename (the host name) into name. The
|
||||
* nodename is read from the kernel through uname() into a stack buffer, so
|
||||
* nothing is allocated or leaked. When the name does not fit in len bytes
|
||||
* — including its terminator — -1 is returned with ENAMETOOLONG and name
|
||||
* is left untouched (glibc semantics; the POSIX text leaves the truncation
|
||||
* case implementation-defined).
|
||||
*
|
||||
* The attribute keeps GCC from recognizing the length loop as a strlen
|
||||
* call or the copy loop as memcpy (house idiom, see src/string): a
|
||||
* libc-owned symbol must not be pulled into an unplanned dependency.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) int
|
||||
gethostname(char *name, size_t len)
|
||||
{
|
||||
struct utsname u = {0}; /* kernel-written; zeroed for the analyzer */
|
||||
size_t n = 0;
|
||||
|
||||
if (uname(&u) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (u.nodename[n] != '\0' && n < sizeof(u.nodename))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
if (n + 1 > len)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = 0; i <= n; i++)
|
||||
{
|
||||
name[i] = u.nodename[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the kernel's host name (the uts nodename) to the len bytes at name.
|
||||
* Requires CAP_SYS_ADMIN in the initial user namespace. syscall_ret
|
||||
* reports 0 on success and -1 with EPERM/EINVAL/EFAULT otherwise.
|
||||
*/
|
||||
int
|
||||
sethostname(const char *name, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_sethostname, (long)name, (long)len));
|
||||
}
|
||||
|
||||
/*
|
||||
* The 32-bit host identifier. vlibc returns 0, matching musl: the classic
|
||||
* value was a hash of the host's primary address obtained through
|
||||
* gethostbyname, a lookup this library deliberately does not perform, and
|
||||
* POSIX marks the interface obsolescent [OB].
|
||||
*/
|
||||
long
|
||||
gethostid(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* The mmap family over the raw x86_64 syscalls. Every wrapper is a thin
|
||||
* pass-through: SYS_mmap, SYS_munmap, SYS_mprotect, SYS_msync, SYS_mlock,
|
||||
* SYS_munlock, SYS_mlockall, SYS_munlockall, SYS_madvise. Arguments are
|
||||
* cast to long because that is the shape of the syscall register file (the
|
||||
* wrappers in arch/x86_64/syscall_arch.h take long arguments).
|
||||
*
|
||||
* mmap is the one member that cannot use syscall_ret(): its success value
|
||||
* is an address, which syscall_ret() would truncate to int. The kernel
|
||||
* result is therefore checked against the -4095 MAX_ERRNO bound directly
|
||||
* (the same test malloc.c applies to its private mmap) and the full pointer
|
||||
* is preserved on success. The other members funnel their result through
|
||||
* syscall_ret(), which returns the value on success and -1 with errno set
|
||||
* on error.
|
||||
*
|
||||
* Level 2 adds madvise (the raw Linux interface behind posix_madvise) and
|
||||
* the POSIX shared-memory pair. shm_open/shm_unlink live on real files
|
||||
* under /dev/shm (see <sys/mman.h> for the naming rationale and the
|
||||
* deviation from POSIX's leading-slash requirement): the object name is
|
||||
* validated, rendered as "/dev/shm/" + name into a local buffer, and handed
|
||||
* to SYS_openat / SYS_unlinkat directly — no inter-object dependency on
|
||||
* the unistd wrappers, the same self-containment creat.c uses.
|
||||
*/
|
||||
|
||||
/* Length of the "/dev/shm/" prefix (VLIBC_SHM_PREFIX_LEN bytes + NUL). */
|
||||
#define VLIBC_SHM_PREFIX "/dev/shm/"
|
||||
#define VLIBC_SHM_PREFIX_LEN (sizeof(VLIBC_SHM_PREFIX) - 1)
|
||||
|
||||
/* Longest object name: /dev/shm is a tmpfs, whose NAME_MAX is 255. */
|
||||
#define VLIBC_SHM_NAME_MAX 255
|
||||
#define VLIBC_SHM_PATH_MAX (VLIBC_SHM_PREFIX_LEN + VLIBC_SHM_NAME_MAX + 1)
|
||||
|
||||
/*
|
||||
* Map len bytes starting at offset off of the file descriptor fildes into
|
||||
* the process address space and return the mapping address. On failure
|
||||
* MAP_FAILED is returned with errno set. len is rounded up to whole pages
|
||||
* by the kernel; addr is a hint unless MAP_FIXED is given.
|
||||
*/
|
||||
void *
|
||||
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
|
||||
{
|
||||
long r = __syscall6(SYS_mmap, (long)addr, (long)len, prot, flags, fildes, (long)off);
|
||||
|
||||
if ((uintptr_t)r > (uintptr_t)-4096)
|
||||
{
|
||||
errno = (int)-r; /* the kernel returned -errno */
|
||||
return MAP_FAILED;
|
||||
}
|
||||
return (void *)r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the mapping at addr covering len bytes; return 0, or -1 with errno
|
||||
* set. addr must be page-aligned.
|
||||
*/
|
||||
int
|
||||
munmap(void *addr, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_munmap, (long)addr, (long)len));
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the protection of the pages at addr covering len bytes to prot;
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
mprotect(void *addr, size_t len, int prot)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_mprotect, (long)addr, (long)len, prot));
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the pages at addr covering len bytes of a MAP_SHARED mapping to
|
||||
* (or invalidate them from) the underlying object per flags; return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
msync(void *addr, size_t len, int flags)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_msync, (long)addr, (long)len, flags));
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock the pages at addr covering len bytes into memory so they are never
|
||||
* paged out; return 0, or -1 with errno set. munlock undoes the lock.
|
||||
*/
|
||||
int
|
||||
mlock(const void *addr, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_mlock, (long)addr, (long)len));
|
||||
}
|
||||
|
||||
int
|
||||
munlock(const void *addr, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_munlock, (long)addr, (long)len));
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock all pages mapped by the process into memory per flags (MCL_CURRENT,
|
||||
* MCL_FUTURE); return 0, or -1 with errno set. munlockall undoes the lock.
|
||||
*/
|
||||
int
|
||||
mlockall(int flags)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_mlockall, flags));
|
||||
}
|
||||
|
||||
int
|
||||
munlockall(void)
|
||||
{
|
||||
return syscall_ret(__syscall0(SYS_munlockall));
|
||||
}
|
||||
|
||||
/*
|
||||
* Give the kernel the POSIX_MADV_* advice about the pages at addr covering
|
||||
* len bytes; return 0, or -1 with errno set. The advice values (0-4) match
|
||||
* the Linux MADV_* values, so the call is a direct SYS_madvise.
|
||||
*/
|
||||
int
|
||||
posix_madvise(void *addr, size_t len, int advice)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_madvise, (long)addr, (long)len, advice));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Linux madvise: give the kernel the MADV_* advice about the pages at addr
|
||||
* covering len bytes; return 0, or -1 with errno set. XSI — posix_madvise
|
||||
* is the POSIX base form; this exposes the full Linux MADV_* set.
|
||||
*/
|
||||
int
|
||||
madvise(void *addr, size_t len, int advice)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_madvise, (long)addr, (long)len, advice));
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate name (no empty names, no '/', tmpfs NAME_MAX bound) and render
|
||||
* the /dev/shm path into buf (capacity VLIBC_SHM_PATH_MAX). Returns 0 and
|
||||
* fills buf on success; -1 with errno set (EINVAL, ENAMETOOLONG) on error.
|
||||
*/
|
||||
static int
|
||||
vlibc_shm_path(char *buf, const char *name)
|
||||
{
|
||||
size_t i;
|
||||
size_t n;
|
||||
|
||||
for (n = 0; name[n] != '\0'; n++)
|
||||
{
|
||||
if (name[n] == '/')
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (n == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (n > VLIBC_SHM_NAME_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < VLIBC_SHM_PREFIX_LEN; i++)
|
||||
{
|
||||
buf[i] = VLIBC_SHM_PREFIX[i];
|
||||
}
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
buf[VLIBC_SHM_PREFIX_LEN + i] = name[i];
|
||||
}
|
||||
buf[VLIBC_SHM_PREFIX_LEN + n] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open (creating with mode when oflag contains O_CREAT) the POSIX shared
|
||||
* memory object name and return a descriptor, or -1 with errno set. The
|
||||
* object is a real file under /dev/shm and persists until shm_unlink. See
|
||||
* <sys/mman.h> on the naming deviation.
|
||||
*/
|
||||
int
|
||||
shm_open(const char *name, int oflag, mode_t mode)
|
||||
{
|
||||
char path[VLIBC_SHM_PATH_MAX];
|
||||
|
||||
if (vlibc_shm_path(path, name) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return syscall_ret(__syscall4(SYS_openat, AT_FDCWD, (long)path, oflag, (long)mode));
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the shared memory object name from /dev/shm; return 0, or -1 with
|
||||
* errno set. Open descriptors of the object stay valid until closed.
|
||||
*/
|
||||
int
|
||||
shm_unlink(const char *name)
|
||||
{
|
||||
char path[VLIBC_SHM_PATH_MAX];
|
||||
|
||||
if (vlibc_shm_path(path, name) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return syscall_ret(__syscall3(SYS_unlinkat, AT_FDCWD, (long)path, 0));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,433 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <pwd.h>
|
||||
|
||||
/*
|
||||
* vlibc — password database (todo 37).
|
||||
*
|
||||
* getpwnam/getpwuid scan /etc/passwd with a fresh fopen per call (no
|
||||
* persistent cursor); getpwent walks one FILE kept open between calls,
|
||||
* which setpwent rewinds and endpwent closes. Every line is read with
|
||||
* getline, one at a time — the whole file is never cached.
|
||||
*
|
||||
* Storage model: two static buffers back every non-reentrant result. A
|
||||
* successful call returns a struct whose string members point into a
|
||||
* private result copy; the getline scan that produced the match ran in a
|
||||
* separate buffer, so a caller may feed the returned record's own name
|
||||
* back into a later lookup (the getpwnam(getpwuid(x)->pw_name)
|
||||
* round-trip) without the scan clobbering its key. The result copy is
|
||||
* replaced by the next call to any of getpwnam/getpwuid/getpwent (or a
|
||||
* realloc of the copy), matching POSIX result-lifetime rules. The _r
|
||||
* forms never use this static storage for their result: the caller's
|
||||
* struct and buffer receive the record, and the caller's *result is how
|
||||
* the outcome is reported.
|
||||
*
|
||||
* Record syntax: seven ':'-separated fields, name:passwd:uid:gid:gecos:
|
||||
* dir:shell. The last field (shell) keeps any further ':' characters as
|
||||
* data. Blank or comment-style lines and records whose uid/gid are not
|
||||
* decimal numbers are skipped like malformed input.
|
||||
*/
|
||||
|
||||
/* The database path; not overridable (no NSS, no shadow, POSIX only). */
|
||||
#define PWD_PATH "/etc/passwd"
|
||||
|
||||
/* Non-reentrant result: struct plus the private copy it points into. */
|
||||
static struct passwd pw_result;
|
||||
static char *pw_out;
|
||||
static size_t pw_out_cap;
|
||||
|
||||
/* The getline scan buffer shared by every lookup and stream walk. */
|
||||
static char *pw_line;
|
||||
static size_t pw_cap;
|
||||
|
||||
/* The persistent getpwent stream; NULL when closed. */
|
||||
static FILE *pw_stream;
|
||||
|
||||
/*
|
||||
* Parse the leading digit string of s as an unsigned id. Accepts only
|
||||
* plain decimal digits and rejects empty fields and overflow; returns 0
|
||||
* on success.
|
||||
*/
|
||||
static int
|
||||
pw_parse_id(const char *s, unsigned *out)
|
||||
{
|
||||
unsigned long v = 0;
|
||||
const char *p;
|
||||
|
||||
if (*s == '\0')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (p = s; *p != '\0'; p++)
|
||||
{
|
||||
if (*p < '0' || *p > '9')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
v = v * 10 + (unsigned long)(*p - '0');
|
||||
if (v > 0xffffffffUL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*out = (unsigned)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the line (newline already stripped) into its seven fields in
|
||||
* place, replacing the first six ':' separators with NULs. On success the
|
||||
* struct fields point into line and 0 is returned; a record with fewer
|
||||
* than six separators or a non-numeric uid/gid yields -1.
|
||||
*/
|
||||
static int
|
||||
pw_split(struct passwd *pw, char *line)
|
||||
{
|
||||
char *fields[7];
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
p = line;
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
fields[i] = p;
|
||||
p = strchr(p, ':');
|
||||
if (p == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
p++;
|
||||
}
|
||||
fields[6] = p;
|
||||
if (pw_parse_id(fields[2], &pw->pw_uid) != 0 || pw_parse_id(fields[3], &pw->pw_gid) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
pw->pw_name = fields[0];
|
||||
pw->pw_passwd = fields[1];
|
||||
pw->pw_gecos = fields[4];
|
||||
pw->pw_dir = fields[5];
|
||||
pw->pw_shell = fields[6];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Strip a trailing newline (and CR) from the line just read by getline. */
|
||||
static void
|
||||
pw_chomp(char *line, ssize_t len)
|
||||
{
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
||||
{
|
||||
line[--len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make line — a chomped, already-split record in the scan buffer — the
|
||||
* non-reentrant result: copy it into the private result buffer and
|
||||
* re-split it there, so the returned struct's strings point into storage
|
||||
* the scanner never touches again. Returns the result struct, or NULL if
|
||||
* the result copy could not be grown.
|
||||
*/
|
||||
static struct passwd *
|
||||
pw_publish(char *line)
|
||||
{
|
||||
size_t len = strlen(line) + 1;
|
||||
|
||||
if (len > pw_out_cap)
|
||||
{
|
||||
char *nb = (char *)realloc(pw_out, len);
|
||||
|
||||
if (nb == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pw_out = nb;
|
||||
pw_out_cap = len;
|
||||
}
|
||||
strcpy(pw_out, line);
|
||||
if (pw_split(&pw_result, pw_out) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &pw_result;
|
||||
}
|
||||
|
||||
/* Match predicate over a parsed record, keyed by name or uid. */
|
||||
typedef int (*pw_matcher)(const struct passwd *pw, const void *key);
|
||||
|
||||
static int
|
||||
pw_match_name(const struct passwd *pw, const void *key)
|
||||
{
|
||||
return strcmp(pw->pw_name, (const char *)key) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pw_match_uid(const struct passwd *pw, const void *key)
|
||||
{
|
||||
return pw->pw_uid == *(const uid_t *)key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-reentrant lookup core: open the database, scan with getline until
|
||||
* the predicate matches, and hand back a pointer into the shared static
|
||||
* line buffer. NULL means "no such entry" (or an unopenable database).
|
||||
*/
|
||||
static struct passwd *
|
||||
pw_find(pw_matcher match, const void *key)
|
||||
{
|
||||
FILE *f = fopen(PWD_PATH, "r");
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) == 0 && match(&cur, key))
|
||||
{
|
||||
(void)fclose(f);
|
||||
return pw_publish(pw_line);
|
||||
}
|
||||
}
|
||||
(void)fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant serializer: copy the parsed record into the caller's struct
|
||||
* and buffer. Returns 0 on success, or ERANGE (as the POSIX _r functions
|
||||
* return, never touching errno) when the record needs more than bufsize
|
||||
* bytes.
|
||||
*/
|
||||
static int
|
||||
pw_store(const struct passwd *in, struct passwd *pw, char *buf, size_t bufsize)
|
||||
{
|
||||
const char *fields[5] = {
|
||||
in->pw_name, in->pw_passwd, in->pw_gecos, in->pw_dir, in->pw_shell,
|
||||
};
|
||||
char *p = buf;
|
||||
size_t need = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
need += strlen(fields[i]) + 1;
|
||||
}
|
||||
if (bufsize < need)
|
||||
{
|
||||
return ERANGE;
|
||||
}
|
||||
pw->pw_uid = in->pw_uid;
|
||||
pw->pw_gid = in->pw_gid;
|
||||
pw->pw_name = p;
|
||||
p += strlen(pw->pw_name) + 1;
|
||||
pw->pw_passwd = p;
|
||||
p += strlen(pw->pw_passwd) + 1;
|
||||
pw->pw_gecos = p;
|
||||
p += strlen(pw->pw_gecos) + 1;
|
||||
pw->pw_dir = p;
|
||||
p += strlen(pw->pw_dir) + 1;
|
||||
pw->pw_shell = p;
|
||||
strcpy(pw->pw_shell, in->pw_shell);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct passwd *
|
||||
getpwnam(const char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return pw_find(pw_match_name, name);
|
||||
}
|
||||
|
||||
struct passwd *
|
||||
getpwuid(uid_t uid)
|
||||
{
|
||||
return pw_find(pw_match_uid, &uid);
|
||||
}
|
||||
|
||||
struct passwd *
|
||||
getpwent(void)
|
||||
{
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
pw_stream = fopen(PWD_PATH, "r");
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, pw_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) == 0)
|
||||
{
|
||||
return pw_publish(pw_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setpwent(void)
|
||||
{
|
||||
if (pw_stream != NULL)
|
||||
{
|
||||
rewind(pw_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
endpwent(void)
|
||||
{
|
||||
if (pw_stream != NULL)
|
||||
{
|
||||
(void)fclose(pw_stream);
|
||||
pw_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant lookup core: scan for the first matching record and serialize
|
||||
* it into the caller's storage. Returns 0 with *result pointing at the
|
||||
* filled struct on success, 0 with *result NULL when no record matches,
|
||||
* ERANGE when the record does not fit bufsize, and EINVAL for a NULL
|
||||
* result pointer.
|
||||
*/
|
||||
static int
|
||||
pw_find_r(pw_matcher match, const void *key, struct passwd *pw, char *buf, size_t bufsize,
|
||||
struct passwd **result)
|
||||
{
|
||||
FILE *f;
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || pw == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
f = fopen(PWD_PATH, "r");
|
||||
if (f == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) != 0 || !match(&cur, key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = pw_store(&cur, pw, buf, bufsize);
|
||||
(void)fclose(f);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = pw;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
(void)fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
if (result != NULL)
|
||||
{
|
||||
*result = NULL;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
return pw_find_r(pw_match_name, name, pw, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result)
|
||||
{
|
||||
return pw_find_r(pw_match_uid, &uid, pw, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getpwent_r(struct passwd *pw, char *buf, size_t bufsize, struct passwd **result)
|
||||
{
|
||||
struct passwd cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || pw == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
pw_stream = fopen(PWD_PATH, "r");
|
||||
if (pw_stream == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&pw_line, &pw_cap, pw_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
pw_chomp(pw_line, got);
|
||||
if (pw_split(&cur, pw_line) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = pw_store(&cur, pw, buf, bufsize);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = pw;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* getpriority (todo 31).
|
||||
*
|
||||
* The kernel never returns the raw nice value from SYS_getpriority: to
|
||||
* keep successful results from colliding with its negative -errno error
|
||||
* encoding, it reports 20 - nice (1..40 for nice -20..19). Recover the
|
||||
* nice value with the inverse translation. Errors arrive as negative
|
||||
* -errno values and go through syscall_ret, which stores errno and
|
||||
* returns -1.
|
||||
*
|
||||
* A returned -1 is therefore either an error (errno set) or the
|
||||
* legitimate priority of a process running at nice -1 (errno untouched) —
|
||||
* the caller distinguishes them by clearing errno before the call, per the
|
||||
* POSIX convention documented in <sys/resource.h>.
|
||||
*/
|
||||
int
|
||||
getpriority(int which, id_t who)
|
||||
{
|
||||
long r;
|
||||
|
||||
r = __syscall2(SYS_getpriority, (long)which, (long)who);
|
||||
if (r < 0)
|
||||
{
|
||||
return syscall_ret(r);
|
||||
}
|
||||
return (int)(20 - r);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* getrlimit/getrlimit64 (todo 31).
|
||||
*
|
||||
* x86_64 has no getrlimit syscall; the prlimit64 ABI serves the read with
|
||||
* (pid = 0, resource, new_limit = NULL, old_limit = &rlim). struct rlimit
|
||||
* is two 64-bit words here — the same layout as the kernel's struct
|
||||
* rlimit64 — so the record passes through without conversion.
|
||||
*
|
||||
* getrlimit64 is a name-only distinction on this architecture (rlim_t is
|
||||
* already 64 bits); it exists as a separate exported symbol so LFS callers
|
||||
* that reference it link cleanly.
|
||||
*/
|
||||
int
|
||||
getrlimit(int resource, struct rlimit *rlim)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_prlimit64, 0, (long)resource, 0, (long)rlim));
|
||||
}
|
||||
|
||||
int
|
||||
getrlimit64(int resource, struct rlimit *rlim)
|
||||
{
|
||||
return getrlimit(resource, rlim);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* getrusage (todo 31).
|
||||
*
|
||||
* Thin SYS_getrusage wrapper: (who, &usage). struct rusage is the kernel's
|
||||
* own record layout on x86_64 (two timevals + fourteen longs, verified in
|
||||
* <sys/resource.h>), so the kernel's copy-to-user writes the public struct
|
||||
* without translation. The kernel rejects an unknown who with EINVAL.
|
||||
*/
|
||||
int
|
||||
getrusage(int who, struct rusage *usage)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_getrusage, (long)who, (long)usage));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* nice (todo 31).
|
||||
*
|
||||
* Built on getpriority/setpriority over PRIO_PROCESS for the calling
|
||||
* process (the plan's design): read the current nice value, add inc, write
|
||||
* the sum back. The kernel clamps the result into PRIO_MIN..PRIO_MAX and
|
||||
* rejects a raise (a decrease of the nice value) without privilege.
|
||||
*
|
||||
* The new nice value is returned, as POSIX requires; that value may be -1
|
||||
* only when the process already runs at nice -1, which getpriority reports
|
||||
* with errno untouched, so the -1 error sentinel is distinguishable the
|
||||
* same way getpriority documents it.
|
||||
*/
|
||||
int
|
||||
nice(int inc)
|
||||
{
|
||||
int prio;
|
||||
|
||||
prio = getpriority(PRIO_PROCESS, 0);
|
||||
if (prio == -1)
|
||||
{
|
||||
/* -1 is a valid priority too; only a nonzero errno marks the read
|
||||
* as a genuine failure (getpriority leaves errno alone on
|
||||
* success). Callers may clear errno first to disambiguate. */
|
||||
if (errno != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (setpriority(PRIO_PROCESS, 0, prio + inc) != 0)
|
||||
{
|
||||
return -1; /* errno already set */
|
||||
}
|
||||
return getpriority(PRIO_PROCESS, 0);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* setpriority (todo 31).
|
||||
*
|
||||
* Thin SYS_setpriority wrapper: (which, who, prio). The kernel clamps
|
||||
* out-of-range nice values to PRIO_MIN..PRIO_MAX instead of rejecting
|
||||
* them, so the only failure paths are an invalid which (EINVAL), an
|
||||
* unknown target (ESRCH) and a denied priority raise (EPERM/EACCES); the
|
||||
* latter applies when the call would lower the target's nice value below
|
||||
* its current one without privilege.
|
||||
*/
|
||||
int
|
||||
setpriority(int which, id_t who, int prio)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_setpriority, (long)which, (long)who, (long)prio));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* setrlimit/setrlimit64 (todo 31).
|
||||
*
|
||||
* The write side of the prlimit64 ABI: (pid = 0, resource,
|
||||
* new_limit = &rlim, old_limit = NULL). struct rlimit is the kernel's
|
||||
* struct rlimit64 layout on x86_64, so the record passes through without
|
||||
* conversion; the kernel rejects a soft limit above the hard limit with
|
||||
* EINVAL and a hard-limit raise without CAP_SYS_RESOURCE with EPERM.
|
||||
*
|
||||
* setrlimit64 is a name-only distinction on this architecture (rlim_t is
|
||||
* already 64 bits); it exists as a separate exported symbol for LFS
|
||||
* callers.
|
||||
*/
|
||||
int
|
||||
setrlimit(int resource, const struct rlimit *rlim)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_prlimit64, 0, (long)resource, (long)rlim, 0));
|
||||
}
|
||||
|
||||
int
|
||||
setrlimit64(int resource, const struct rlimit *rlim)
|
||||
{
|
||||
return setrlimit(resource, rlim);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* abort: raise SIGABRT with default disposition, then die regardless.
|
||||
*
|
||||
* The sequence follows POSIX: first the blocked mask is cleared for
|
||||
* SIGABRT (a blocked SIGABRT would silently pend and abort would look
|
||||
* like success), then SIGABRT is raised with whatever disposition is
|
||||
* installed. If that raise returned, the disposition was caught (handler
|
||||
* that returned) or ignored — so the disposition is reset to SIG_DFL and
|
||||
* the raise repeated, which terminates the process. If even the default
|
||||
* raise returned (impossible in a healthy kernel), _exit with 128+6
|
||||
* (= 134, the shell convention for death-by-SIGABRT) is the last resort.
|
||||
* All of it is raw syscalls and the functions in this directory: abort
|
||||
* must work from a half-initialized runtime and never touch stdio.
|
||||
*/
|
||||
|
||||
__attribute__((noreturn)) void
|
||||
abort(void)
|
||||
{
|
||||
sigset_t unblock;
|
||||
struct sigaction dfl;
|
||||
|
||||
/* Make sure SIGABRT is not blocked, then try the installed action. */
|
||||
sigemptyset(&unblock);
|
||||
sigaddset(&unblock, SIGABRT);
|
||||
sigprocmask(SIG_UNBLOCK, &unblock, NULL);
|
||||
raise(SIGABRT);
|
||||
|
||||
/* Returned: caught by a returning handler, or ignored. Go default. */
|
||||
dfl.sa_handler = SIG_DFL;
|
||||
dfl.sa_flags = 0;
|
||||
sigemptyset(&dfl.sa_mask);
|
||||
sigaction(SIGABRT, &dfl, NULL);
|
||||
raise(SIGABRT);
|
||||
|
||||
/* Still here: the kernel failed to deliver. Die with the SIGABRT code. */
|
||||
__syscall1(SYS_exit_group, 134);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* alarm: schedule a one-shot SIGALRM in seconds seconds.
|
||||
*
|
||||
* Pure SYS_alarm pass-through: the kernel arms the private per-process
|
||||
* alarm timer, returns the seconds remaining on any alarm it just
|
||||
* replaced (0 when none was pending), and treats 0 as "cancel the pending
|
||||
* alarm". The raw result is always non-negative, so syscall_ret cannot
|
||||
* mistake it for an error.
|
||||
*/
|
||||
|
||||
unsigned
|
||||
alarm(unsigned seconds)
|
||||
{
|
||||
return (unsigned)syscall_ret(__syscall1(SYS_alarm, (long)seconds));
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* kill/killpg: process- and process-group-directed signals over SYS_kill.
|
||||
*
|
||||
* kill passes its pid straight to the kernel, which interprets it exactly
|
||||
* as POSIX specifies: pid > 0 is a single process, pid == 0 the caller's
|
||||
* process group, pid == -1 every process the caller may signal, and
|
||||
* pid < -1 the process group -pid. killpg(pgrp, sig) is equivalent to
|
||||
* kill(-pgrp, sig): a negative pgrp is not a valid group id and is
|
||||
* rejected with EINVAL here (mirroring musl) rather than inverted into a
|
||||
* positive-pid send by accident.
|
||||
*/
|
||||
|
||||
int
|
||||
kill(pid_t pid, int sig)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_kill, (long)pid, sig));
|
||||
}
|
||||
|
||||
int
|
||||
killpg(pid_t pgrp, int sig)
|
||||
{
|
||||
if (pgrp < 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return kill(-pgrp, sig);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/*
|
||||
* pause: sleep until a signal is caught.
|
||||
*
|
||||
* pause() must wait with nothing blocked, so it is sigsuspend on an empty
|
||||
* mask. When a caught signal's handler returns, sigsuspend reports -1
|
||||
* with errno EINTR (never restarted by the kernel), which is exactly what
|
||||
* POSIX specifies for pause. A signal whose default action terminates the
|
||||
* process makes pause never return.
|
||||
*/
|
||||
|
||||
int
|
||||
pause(void)
|
||||
{
|
||||
sigset_t empty = 0;
|
||||
|
||||
return sigsuspend(&empty);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* psignal/psiginfo: print a signal diagnostic to stderr (XSI).
|
||||
*
|
||||
* Both write the message argument s (when present), ": ", and
|
||||
* strsignal()'s description of the signal, then a newline, directly to
|
||||
* fd 2 with raw SYS_write — no stdio, so the calls work even when the
|
||||
* runtime is only partially up. psignal takes the signal number;
|
||||
* psiginfo reads si_signo out of a filled siginfo (a NULL si prints only
|
||||
* the message prefix).
|
||||
*/
|
||||
|
||||
/* Write the NUL-terminated string s to fd 2. */
|
||||
static void
|
||||
write_stderr(const char *s)
|
||||
{
|
||||
if (s != NULL)
|
||||
{
|
||||
__syscall3(SYS_write, 2, (long)s, (long)strlen(s));
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the shared "<s>: <description>\n" shape to stderr. */
|
||||
static void
|
||||
signal_message(const char *s, const char *description)
|
||||
{
|
||||
write_stderr(s);
|
||||
if (s != NULL && s[0] != '\0')
|
||||
{
|
||||
write_stderr(": ");
|
||||
}
|
||||
write_stderr(description);
|
||||
write_stderr("\n");
|
||||
}
|
||||
|
||||
void
|
||||
psignal(int sig, const char *s)
|
||||
{
|
||||
signal_message(s, strsignal(sig));
|
||||
}
|
||||
|
||||
void
|
||||
psiginfo(const siginfo_t *si, const char *s)
|
||||
{
|
||||
if (si == NULL)
|
||||
{
|
||||
signal_message(s, "");
|
||||
return;
|
||||
}
|
||||
signal_message(s, strsignal(si->si_signo));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* raise: deliver sig to the calling thread.
|
||||
*
|
||||
* POSIX requires raise to be thread-directed (the calling thread must
|
||||
* receive the signal even when sibling threads of the same process block
|
||||
* it), so this rides SYS_tgkill with the calling thread's pid and tid,
|
||||
* not the process-directed SYS_kill. The pid/tid come from raw syscalls:
|
||||
* they are kernel reads, and gettid also has no thread-library
|
||||
* dependency. The library is single-threaded today, but a process->
|
||||
* directed raise would already be the wrong thread once threads exist.
|
||||
*/
|
||||
|
||||
int
|
||||
raise(int sig)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall3(SYS_tgkill, __syscall0(SYS_getpid), __syscall0(SYS_gettid), (long)sig));
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigaction: install or query a signal disposition over SYS_rt_sigaction.
|
||||
*
|
||||
* The kernel's x86_64 sigaction ABI is NOT the POSIX struct layout:
|
||||
*
|
||||
* POSIX (public, struct sigaction): handler, mask, flags(int), restorer
|
||||
* kernel (rt_sigaction argument): handler, flags(64-bit), restorer,
|
||||
* mask
|
||||
*
|
||||
* and the kernel copies a full 32-byte word for the flags slot, so the
|
||||
* upper 32 bits of that slot must be zero (they hold the padding of the
|
||||
* int-typed public flags). This file therefore marshals between a
|
||||
* zeroed kernel-layout struct and the public struct, field by field.
|
||||
*
|
||||
* The kernel does not fall back to the vDSO return stub on x86_64: when a
|
||||
* real (non-SIG_DFL/SIG_IGN) handler is installed it MUST carry
|
||||
* SA_RESTORER plus a sa_restorer trampoline, or delivery crashes when the
|
||||
* handler returns. The trampoline below is the classic two-instruction
|
||||
* sequence (mov $SYS_rt_sigreturn; syscall) — the same shape glibc and
|
||||
* musl ship — entered with the stack already positioned at the kernel's
|
||||
* rt_sigframe, so it must be naked: any prologue would shift the stack
|
||||
* and make rt_sigreturn restore garbage. SA_RESTORER is masked out of the
|
||||
* flags reported through oact so callers only ever see the POSIX bits.
|
||||
*
|
||||
* No signal validity checking happens here: the kernel rejects out-of
|
||||
* range numbers (EINVAL) and attempts to catch SIGKILL/SIGSTOP, and this
|
||||
* wrapper is a pure pass-through of its results.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The kernel-layout sigaction (see above). sa_restorer and the trampoline
|
||||
* are only attached for real handlers.
|
||||
*/
|
||||
struct vlibc_kernel_sigaction
|
||||
{
|
||||
void (*vlibc_handler)(int); /* offset 0: handler or sa_sigaction */
|
||||
unsigned long vlibc_flags; /* offset 8: full 64-bit flags word */
|
||||
void (*vlibc_restorer)(void); /* offset 16: return trampoline */
|
||||
unsigned long vlibc_mask; /* offset 24: blocked-during-handler set */
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct vlibc_kernel_sigaction) == 32, "kernel sigaction must be 32 bytes");
|
||||
_Static_assert(offsetof(struct vlibc_kernel_sigaction, vlibc_flags) == 8,
|
||||
"kernel flags must sit at offset 8");
|
||||
_Static_assert(offsetof(struct vlibc_kernel_sigaction, vlibc_restorer) == 16,
|
||||
"kernel restorer must sit at offset 16");
|
||||
_Static_assert(offsetof(struct vlibc_kernel_sigaction, vlibc_mask) == 24,
|
||||
"kernel mask must sit at offset 24");
|
||||
|
||||
/*
|
||||
* The rt_sigreturn trampoline every real handler returns through. Naked:
|
||||
* it is entered by the handler's ret with the stack already pointing at
|
||||
* the kernel's rt_sigframe, so no prologue/epilogue may run.
|
||||
*/
|
||||
static void __attribute__((naked, noreturn))
|
||||
vlibc_restore_rt(void)
|
||||
{
|
||||
__asm__ volatile("movq $15, %rax\n\tsyscall");
|
||||
}
|
||||
|
||||
int
|
||||
sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact)
|
||||
{
|
||||
struct vlibc_kernel_sigaction kact;
|
||||
struct vlibc_kernel_sigaction kold;
|
||||
struct vlibc_kernel_sigaction *kactp;
|
||||
long r;
|
||||
|
||||
if (act != NULL)
|
||||
{
|
||||
/* Marshal the public struct into the kernel layout (see top). */
|
||||
kactp = &kact;
|
||||
kact.vlibc_handler = act->sa_handler;
|
||||
kact.vlibc_flags = (unsigned long)act->sa_flags;
|
||||
kact.vlibc_restorer = NULL;
|
||||
kact.vlibc_mask = (unsigned long)act->sa_mask;
|
||||
if (act->sa_handler != SIG_DFL && act->sa_handler != SIG_IGN)
|
||||
{
|
||||
kact.vlibc_flags |= (unsigned long)SA_RESTORER;
|
||||
kact.vlibc_restorer = vlibc_restore_rt;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kactp = NULL;
|
||||
}
|
||||
|
||||
r = __syscall4(SYS_rt_sigaction, sig, (long)kactp, oact != NULL ? (long)&kold : 0L,
|
||||
(long)sizeof(sigset_t));
|
||||
if (r < 0)
|
||||
{
|
||||
return syscall_ret(r);
|
||||
}
|
||||
|
||||
if (oact != NULL)
|
||||
{
|
||||
/* Marshal the kernel layout back into the public struct. */
|
||||
oact->sa_handler = kold.vlibc_handler;
|
||||
oact->sa_mask = (sigset_t)kold.vlibc_mask;
|
||||
oact->sa_flags = (int)(kold.vlibc_flags & ~(unsigned long)SA_RESTORER);
|
||||
oact->sa_restorer = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* sigaltstack: install or query the alternate signal stack over
|
||||
* SYS_sigaltstack (ss, oss). The kernel's stack_t — base pointer, flags
|
||||
* int, size — is byte-identical to the public stack_t, so it passes
|
||||
* through unmodified. A handler installed with SA_ONSTACK runs on the
|
||||
* alternate stack; the kernel restores the ordinary stack on return.
|
||||
*/
|
||||
|
||||
int
|
||||
sigaltstack(const stack_t *restrict ss, stack_t *restrict oss)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_sigaltstack, (long)ss, (long)oss));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* siginterrupt: toggle the SA_RESTART bit of sig's disposition.
|
||||
*
|
||||
* sig is the XSI way to switch between System V semantics (flag nonzero:
|
||||
* interrupted syscalls return EINTR — SA_RESTART cleared) and BSD
|
||||
* semantics (flag zero: syscalls restart — SA_RESTART set) for one
|
||||
* signal. The current action is read, its SA_RESTART bit updated, and the
|
||||
* action reinstalled through sigaction, which re-attaches the return
|
||||
* trampoline as needed.
|
||||
*/
|
||||
|
||||
int
|
||||
siginterrupt(int sig, int flag)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
if (sigaction(sig, NULL, &sa) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (flag != 0)
|
||||
{
|
||||
sa.sa_flags &= ~SA_RESTART;
|
||||
}
|
||||
else
|
||||
{
|
||||
sa.sa_flags |= SA_RESTART;
|
||||
}
|
||||
return sigaction(sig, &sa, NULL);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
/*
|
||||
* signal: the System V convenience, defined on top of sigaction with BSD
|
||||
* restart semantics. It installs handler for sig with SA_RESTART (syscalls
|
||||
* interrupted by the signal are restarted) and an empty supplementary
|
||||
* mask, then returns the previous disposition. SIG_DFL/SIG_IGN pass
|
||||
* through unchanged. Errors are reported as SIG_ERR with errno set.
|
||||
*/
|
||||
|
||||
void (*signal(int sig, void (*handler)(int)))(int)
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct sigaction old;
|
||||
|
||||
sa.sa_handler = handler;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
if (sigaction(sig, &sa, &old) == -1)
|
||||
{
|
||||
return SIG_ERR;
|
||||
}
|
||||
return old.sa_handler;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigprocmask/sigpending: the blocked-mask pair over the rt_* syscalls.
|
||||
*
|
||||
* sigprocmask rides SYS_rt_sigprocmask (how, set, oldset, sigsetsize).
|
||||
* The kernel reads the one-word sigset when set is non-NULL and applies
|
||||
* it per how; a NULL set with SIG_BLOCK is the POSIX-blessed way to query
|
||||
* the current mask (how is then ignored). sigpending rides SYS_rt_sigpending
|
||||
* (set, sigsetsize) and reports the signals that are BOTH blocked and
|
||||
* pending. Both are pure pass-throughs: syscall_ret turns the kernel's
|
||||
* -errno convention into -1 + errno.
|
||||
*/
|
||||
|
||||
int
|
||||
sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall4(SYS_rt_sigprocmask, how, (long)set, (long)oldset, (long)sizeof(sigset_t)));
|
||||
}
|
||||
|
||||
int
|
||||
sigpending(sigset_t *set)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_rt_sigpending, (long)set, (long)sizeof(sigset_t)));
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigqueue: send sig with an attached sigval payload to process pid over
|
||||
* SYS_rt_sigqueueinfo (pid, sig, siginfo *).
|
||||
*
|
||||
* The kernel validates and forwards a full 128-byte siginfo; the sender
|
||||
* must place the payload where the kernel's SI_QUEUE union reads it:
|
||||
* si_code at offset 8 (a negative value — SI_QUEUE is -1 — because the
|
||||
* kernel refuses si_code >= 0 with EPERM: only the kernel may send with a
|
||||
* positive code) and the sigval at offset 24 (after the pid/uid words the
|
||||
* kernel reserves for the receiving side). The private struct below pins
|
||||
* exactly that; si_signo is set for completeness though the kernel
|
||||
* overwrites it with the sig argument.
|
||||
*/
|
||||
struct vlibc_rt_sigqueue_info
|
||||
{
|
||||
int si_signo; /* 0 */
|
||||
int si_errno; /* 4 */
|
||||
int si_code; /* 8 */
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
union sigval si_value; /* 24 */
|
||||
};
|
||||
int vlibc_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct vlibc_rt_sigqueue_info) == 128,
|
||||
"rt_sigqueueinfo needs a full 128-byte siginfo");
|
||||
_Static_assert(offsetof(struct vlibc_rt_sigqueue_info, si_code) == 8,
|
||||
"si_code must sit at offset 8");
|
||||
|
||||
int
|
||||
sigqueue(pid_t pid, int sig, const union sigval value)
|
||||
{
|
||||
struct vlibc_rt_sigqueue_info si;
|
||||
|
||||
si.si_signo = sig;
|
||||
si.si_errno = 0;
|
||||
si.si_code = -1; /* SI_QUEUE; negative per the kernel's from-user check */
|
||||
si.si_pid = 0;
|
||||
si.si_uid = 0;
|
||||
si.si_value = value;
|
||||
|
||||
return syscall_ret(__syscall3(SYS_rt_sigqueueinfo, (long)pid, sig, (long)&si));
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* sigemptyset/sigfillset/sigaddset/sigdelset/sigismember: the sigset_t
|
||||
* manipulation API over the single 64-bit word kernel sigset (signal N
|
||||
* is bit N-1). No syscalls are involved; only the out-of-range checks
|
||||
* report EINVAL. Signals 1..64 are addressable (SIGRTMAX == 64), so the
|
||||
* shift for the highest signal is 1UL << 63.
|
||||
*/
|
||||
|
||||
/* True when sig names a usable signal (1..NSIG-1, i.e. 1..64). */
|
||||
static int
|
||||
vlibc_sig_valid(int sig)
|
||||
{
|
||||
return sig >= 1 && sig < NSIG;
|
||||
}
|
||||
|
||||
int
|
||||
sigemptyset(sigset_t *set)
|
||||
{
|
||||
*set = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigfillset(sigset_t *set)
|
||||
{
|
||||
*set = ~(sigset_t)0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigaddset(sigset_t *set, int sig)
|
||||
{
|
||||
if (!vlibc_sig_valid(sig))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*set |= (sigset_t)1 << (sig - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigdelset(sigset_t *set, int sig)
|
||||
{
|
||||
if (!vlibc_sig_valid(sig))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
*set &= ~((sigset_t)1 << (sig - 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigismember(const sigset_t *set, int sig)
|
||||
{
|
||||
if (!vlibc_sig_valid(sig))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return (int)((*set >> (sig - 1)) & 1);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigsuspend: atomically swap in mask and sleep until a signal is caught.
|
||||
*
|
||||
* SYS_rt_sigsuspend (mask, sigsetsize) installs mask for the duration of
|
||||
* the wait and restores the caller's previous mask when the catching
|
||||
* handler returns; it always returns -EINTR then (it is never restarted),
|
||||
* which syscall_ret reports as -1 with errno EINTR. A signal whose
|
||||
* default action terminates or stops the process makes sigsuspend never
|
||||
* return, exactly as POSIX requires.
|
||||
*/
|
||||
|
||||
int
|
||||
sigsuspend(const sigset_t *mask)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_rt_sigsuspend, (long)mask, (long)sizeof(sigset_t)));
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sigwait/sigwaitinfo/sigtimedwait: synchronous signal consumption over
|
||||
* SYS_rt_sigtimedwait (set, siginfo *, timeout, sigsetsize). The syscall
|
||||
* dequeues one of the signals in set (consuming it, so no handler ever
|
||||
* runs for it), returns its number, and — when info is non-NULL — copies
|
||||
* the full 128-byte kernel siginfo through it.
|
||||
*
|
||||
* The kernel's timespec timeout is relative; NULL waits indefinitely.
|
||||
* EINTR (a handler for some OTHER signal ran) is retried by sigwait —
|
||||
* whose contract is to return 0 with the signal stored, or an error
|
||||
* NUMBER (never -1/errno). sigwaitinfo/sigtimedwait instead surface the
|
||||
* kernel result directly via syscall_ret: the signal number, or -1 with
|
||||
* errno set (EAGAIN on timeout).
|
||||
*/
|
||||
|
||||
int
|
||||
sigwait(const sigset_t *restrict set, int *restrict sig)
|
||||
{
|
||||
long r;
|
||||
|
||||
do
|
||||
{
|
||||
r = __syscall4(SYS_rt_sigtimedwait, (long)set, 0L, 0L, (long)sizeof(sigset_t));
|
||||
} while (r == -EINTR);
|
||||
|
||||
if (r < 0)
|
||||
{
|
||||
return (int)-r; /* error number, per the sigwait contract */
|
||||
}
|
||||
*sig = (int)r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sigwaitinfo(const sigset_t *restrict set, siginfo_t *restrict info)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall4(SYS_rt_sigtimedwait, (long)set, (long)info, 0L, (long)sizeof(sigset_t)));
|
||||
}
|
||||
|
||||
int
|
||||
sigtimedwait(const sigset_t *restrict set, siginfo_t *restrict info,
|
||||
const struct timespec *restrict timeout)
|
||||
{
|
||||
return syscall_ret(__syscall4(SYS_rt_sigtimedwait, (long)set, (long)info, (long)timeout,
|
||||
(long)sizeof(sigset_t)));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* ualarm: schedule a SIGALRM in usecs microseconds, repeating every
|
||||
* interval microseconds (0 = one-shot), over SYS_setitimer(ITIMER_REAL).
|
||||
*
|
||||
* The kernel itimerval layout matches vlibc's struct itimerval from
|
||||
* <sys/time.h> exactly (two timevals of long seconds + long microseconds),
|
||||
* so the public struct passes through unmodified. The previous alarm is
|
||||
* returned in microseconds, converted from the old it_value the kernel
|
||||
* reports; -1 surfaces syscall failures (useconds_t is unsigned, so -1 is
|
||||
* the all-ones value, the standard error spelling for this XSI call).
|
||||
*/
|
||||
|
||||
useconds_t
|
||||
ualarm(useconds_t usecs, useconds_t interval)
|
||||
{
|
||||
struct itimerval new_timer;
|
||||
struct itimerval old_timer;
|
||||
|
||||
new_timer.it_interval.tv_sec = (time_t)(interval / 1000000);
|
||||
new_timer.it_interval.tv_usec = (suseconds_t)(interval % 1000000);
|
||||
new_timer.it_value.tv_sec = (time_t)(usecs / 1000000);
|
||||
new_timer.it_value.tv_usec = (suseconds_t)(usecs % 1000000);
|
||||
|
||||
if (syscall_ret(__syscall3(SYS_setitimer, ITIMER_REAL, (long)&new_timer, (long)&old_timer)) ==
|
||||
-1)
|
||||
{
|
||||
return (useconds_t)-1;
|
||||
}
|
||||
return (useconds_t)old_timer.it_value.tv_sec * 1000000 + (useconds_t)old_timer.it_value.tv_usec;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -74,6 +74,9 @@
|
||||
|
||||
#include "../internal/errno.h" /* VLIBC_TCB_ERRNO_OFF */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define VLIBC_TCB_SELF_OFF 0
|
||||
|
||||
/* VLIBC_TCB_ERRNO_OFF (8) is consumed from src/internal/errno.h. */
|
||||
@@ -84,4 +87,31 @@
|
||||
|
||||
#define VLIBC_TCB_SIZE 24
|
||||
|
||||
/*
|
||||
* New-thread TLS geometry (todo 44). All four are set by init_main_tls()
|
||||
* in src/start/tls.c and read by src/thread/tls.c (__vlibc_tls_setup):
|
||||
*
|
||||
* __vlibc_static_tls_size PT_TLS p_memsz of the main executable — the
|
||||
* one DEFAULT-VISIBILITY EXPORTED symbol of
|
||||
* the group: #62 grows it on dlopen, #45 reads
|
||||
* it when sizing new-thread TLS blocks.
|
||||
* __vlibc_static_tls_filesz PT_TLS p_filesz (.tdata part).
|
||||
* __vlibc_static_tls_align PT_TLS p_align (16 when there is no PT_TLS).
|
||||
* __vlibc_static_tls_template the PRIMAL TLS image address: the mapped
|
||||
* .tdata at p_vaddr, never the main thread's
|
||||
* LIVE TLS block (which mutates). In the
|
||||
* file-fallback path it is the main block
|
||||
* itself — the only copy that exists then.
|
||||
*
|
||||
* The three hidden ones keep their attribute on the definitions (as with
|
||||
* environ's hidden alias in src/start/environ.c); __vlibc_static_tls_size
|
||||
* deliberately carries none so it stays an exported ABI object.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
extern size_t __vlibc_static_tls_size;
|
||||
extern uintptr_t __vlibc_static_tls_template;
|
||||
extern size_t __vlibc_static_tls_filesz;
|
||||
extern size_t __vlibc_static_tls_align;
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_INTERNAL_START_TCB_H */
|
||||
|
||||
@@ -76,6 +76,17 @@ struct elf64_phdr
|
||||
uint64_t p_align;
|
||||
};
|
||||
|
||||
/*
|
||||
* New-thread TLS geometry, consumed by src/thread/tls.c (todo 44) — see
|
||||
* tcb.h for the full contract. __vlibc_static_tls_size is the EXPORTED
|
||||
* default-visibility object (no attribute!); the other three are internal
|
||||
* and hidden.
|
||||
*/
|
||||
size_t __vlibc_static_tls_size = 0;
|
||||
hidden uintptr_t __vlibc_static_tls_template = 0;
|
||||
hidden size_t __vlibc_static_tls_filesz = 0;
|
||||
hidden size_t __vlibc_static_tls_align = 16;
|
||||
|
||||
/*
|
||||
* Round v up to a multiple of a. a must be a power of two, which ELF
|
||||
* p_align of a PT_TLS segment always is.
|
||||
@@ -253,6 +264,13 @@ init_main_tls(char **envp)
|
||||
align = 16;
|
||||
}
|
||||
|
||||
/* Record the geometry for new-thread TLS blocks (#44/#45) BEFORE the
|
||||
* template address is known: size/filesz/align are PT_TLS facts, the
|
||||
* template address depends on the mmap below. */
|
||||
__vlibc_static_tls_size = memsz;
|
||||
__vlibc_static_tls_filesz = filesz;
|
||||
__vlibc_static_tls_align = align;
|
||||
|
||||
/*
|
||||
* Single allocation: TLS image at the base, TCB at tp. tp uses exactly
|
||||
* p_align (never bumped): the linker bakes tpoff against
|
||||
@@ -273,6 +291,12 @@ init_main_tls(char **envp)
|
||||
tp = base + block;
|
||||
image = (char *)base;
|
||||
|
||||
/* The PRIMAL image is the new-thread template: the still-pristine
|
||||
* .tdata at p_vaddr when mapped, else this block (the file-fallback
|
||||
* path leaves no other copy). Never the main thread's live TLS — its
|
||||
* mutations must not leak into threads created later. */
|
||||
__vlibc_static_tls_template = mapped ? vaddr : base;
|
||||
|
||||
if (filesz > 0)
|
||||
{
|
||||
copy_tls_image(image, offset, vaddr, filesz, mapped);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Linux struct statfs (kernel ABI, x86_64; linux/statfs.h). Layout and
|
||||
* sizes are LP64 facts: f_type..f_ffree are longs, f_fsid is an int pair,
|
||||
* then f_namelen/f_frsize/f_flags/f_spare[4] complete the 120 bytes the
|
||||
* kernel copies out (the same layout src/misc/pathconf.c pins for
|
||||
* pathconf()/fpathconf()). The signed kernel longs are non-negative on
|
||||
* every real filesystem, so they convert straight into the unsigned POSIX
|
||||
* counts below.
|
||||
*/
|
||||
struct vlibc_kstatfs
|
||||
{
|
||||
long f_type;
|
||||
long f_bsize;
|
||||
long f_blocks;
|
||||
long f_bfree;
|
||||
long f_bavail;
|
||||
long f_files;
|
||||
long f_ffree;
|
||||
int f_fsid[2];
|
||||
long f_namelen;
|
||||
long f_frsize;
|
||||
long f_flags;
|
||||
long f_spare[4];
|
||||
};
|
||||
|
||||
/* Map one kernel statfs record into the portable POSIX statvfs view. */
|
||||
static void
|
||||
statvfs_fill(struct statvfs *out, const struct vlibc_kstatfs *in)
|
||||
{
|
||||
out->f_bsize = (unsigned long)in->f_bsize;
|
||||
out->f_frsize = in->f_frsize != 0 ? (unsigned long)in->f_frsize : (unsigned long)in->f_bsize;
|
||||
out->f_blocks = (unsigned long)in->f_blocks;
|
||||
out->f_bfree = (unsigned long)in->f_bfree;
|
||||
out->f_bavail = (unsigned long)in->f_bavail;
|
||||
out->f_files = (unsigned long)in->f_files;
|
||||
out->f_ffree = (unsigned long)in->f_ffree;
|
||||
out->f_favail = (unsigned long)in->f_ffree;
|
||||
/* Reconstruct the kernel's 64-bit fsid (val[0] holds the low word on
|
||||
* the little-endian x86_64; the same packing glibc's statvfs reports). */
|
||||
out->f_fsid = ((unsigned long)(unsigned int)in->f_fsid[1] << 32) |
|
||||
(unsigned long)(unsigned int)in->f_fsid[0];
|
||||
out->f_flag = (unsigned long)(in->f_flags & (ST_RDONLY | ST_NOSUID));
|
||||
out->f_namemax = (unsigned long)in->f_namelen;
|
||||
}
|
||||
|
||||
/*
|
||||
* statvfs()/fstatvfs(): filesystem statistics in the portable POSIX shape,
|
||||
* over SYS_statfs/SYS_fstatfs (which share the same kernel struct and the
|
||||
* same mapping). The kernel returns 0 on success after filling all 120
|
||||
* bytes; failures come back as -errno and are translated by syscall_ret.
|
||||
*/
|
||||
int
|
||||
statvfs(const char *restrict path, struct statvfs *restrict buf)
|
||||
{
|
||||
struct vlibc_kstatfs fs;
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_statfs, (long)path, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
statvfs_fill(buf, &fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fstatvfs(int fildes, struct statvfs *buf)
|
||||
{
|
||||
struct vlibc_kstatfs fs;
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_fstatfs, fildes, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
statvfs_fill(buf, &fs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
+1536
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/times.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* times(): process CPU-time accounting in CLK_TCK ticks.
|
||||
*
|
||||
* Everything here is XSI — times() is [CX] in POSIX.1-2008, NOT base — so
|
||||
* the whole file is gated at level 2 (whole-file-L2 rule; the wiring pass
|
||||
* puts it in VLIBC_LEVEL2_SRCS, and at level 1 this TU compiles empty).
|
||||
*
|
||||
* SYS_times writes a struct __kernel_tms, which on x86_64 is exactly four
|
||||
* longs (tms_utime, tms_stime, tms_cutime, tms_cstime) — byte-for-byte the
|
||||
* public struct tms from <sys/times.h> (clock_t is long here). The kernel
|
||||
* therefore fills the caller's struct in place; no private mirror is
|
||||
* needed. The syscall's return value is the tick counter since boot, which
|
||||
* is clock_t (long) — a value that can legitimately exceed INT_MAX on a
|
||||
* long-lived host — so the error translation is inlined below with the same
|
||||
* semantics as syscall_ret() instead of routing the full-width counter
|
||||
* through it (house idiom, see src/unistd/lseek.c).
|
||||
*
|
||||
* The tick unit is CLK_TCK (100 per second on x86_64), the fixed USER_HZ
|
||||
* constant reported by sysconf(_SC_CLK_TCK) and defined ONCE in
|
||||
* src/misc/sysconf.c; times() consumes that unit rather than re-defining
|
||||
* it. clock_t here is therefore the raw kernel tick count, matching POSIX
|
||||
* XSI (where the clock_t unit is CLK_TCK); this deliberately does NOT
|
||||
* rescale to the 1 MHz clock() unit that <time.h>'s clock() uses (see
|
||||
* src/time/clock.c, which reads the same SYS_times through its own struct
|
||||
* tms mirror and scales by CLOCKS_PER_SEC / 100).
|
||||
*/
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Fill *buf with the CPU times of the calling process (tms_utime,
|
||||
* tms_stime) and of its waited-for children (tms_cutime, tms_cstime) in
|
||||
* CLK_TCK ticks, and return the number of clock ticks since boot. On
|
||||
* failure — buf pointing outside the address space — return (clock_t)-1
|
||||
* with errno set; per POSIX times() has no other error returns.
|
||||
*/
|
||||
clock_t
|
||||
times(struct tms *buf)
|
||||
{
|
||||
long r = __syscall1(SYS_times, (long)buf);
|
||||
|
||||
if (r < 0 && r > -4096)
|
||||
{
|
||||
errno = (int)-r;
|
||||
return (clock_t)-1;
|
||||
}
|
||||
return (clock_t)r;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,248 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <termios.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* vlibc — terminal attributes (todo 29).
|
||||
*
|
||||
* The ten POSIX base functions plus the three level-2 extras, all thin
|
||||
* pass-throughs to the kernel tty ioctls over SYS_ioctl. struct termios
|
||||
* (include/termios.h) is byte-identical to the x86_64 kernel termios, so
|
||||
* the ioctl argument pointer is passed straight through: the kernel copies
|
||||
* exactly sizeof(struct termios) bytes in each direction.
|
||||
*
|
||||
* The ioctl request numbers are Linux-only kernel-UAPI facts
|
||||
* (asm-generic/ioctls.h), kept private here — they are the future
|
||||
* <sys/ioctl.h>'s names, not <termios.h>'s (POSIX exposes only the
|
||||
* actions/queues/actions constants, which the header defines).
|
||||
*
|
||||
* The cf* speed helpers never touch the kernel: they only re-encode the
|
||||
* caller's c_cflag CBAUD/CIBAUD bit fields, which the kernel's termios
|
||||
* layer itself uses (a later tcsetattr carries the bits verbatim). The
|
||||
* CIBAUD field (input speed) is shifted IBSHIFT = 16 bits above CBAUD and
|
||||
* is not part of any public constant set, so it is transcribed locally.
|
||||
*
|
||||
* Return-value convention follows syscall_ret for every I/O function
|
||||
* (0 or positive on success, -1 with errno on failure).
|
||||
*/
|
||||
|
||||
/* Kernel tty ioctls (asm-generic/ioctls.h on x86_64). */
|
||||
#define VLIBC_TERMIOS_TCGETS 0x5401
|
||||
#define VLIBC_TERMIOS_TCSETS 0x5402
|
||||
#define VLIBC_TERMIOS_TCSETSW 0x5403
|
||||
#define VLIBC_TERMIOS_TCSETSF 0x5404
|
||||
#define VLIBC_TERMIOS_TCSBRK 0x5409
|
||||
#define VLIBC_TERMIOS_TCXONC 0x540a
|
||||
#define VLIBC_TERMIOS_TCFLSH 0x540b
|
||||
#define VLIBC_TERMIOS_TIOCGSID 0x5429
|
||||
|
||||
/* Kernel input-baud encoding (asm-generic/termbits.h): CIBAUD = CBAUD << 16. */
|
||||
#define VLIBC_TERMIOS_CIBAUD 0x100f0000
|
||||
#define VLIBC_TERMIOS_IBSHIFT 16
|
||||
|
||||
/*
|
||||
* tcgetattr: read the terminal attributes of the terminal open on fildes.
|
||||
* The kernel returns -ENOTTY when fildes is not a terminal; syscall_ret
|
||||
* converts that to -1 with errno ENOTTY.
|
||||
*/
|
||||
int
|
||||
tcgetattr(int fildes, struct termios *termios_p)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_TERMIOS_TCGETS, (long)termios_p));
|
||||
}
|
||||
|
||||
/*
|
||||
* tcsetattr: write the terminal attributes. optional_actions selects the
|
||||
* ioctl: TCSANOW applies immediately (TCSETS), TCSADRAIN waits for output
|
||||
* to drain (TCSETSW), TCSAFLUSH additionally discards unread input
|
||||
* (TCSETSF). Anything else is EINVAL before the kernel is consulted.
|
||||
*/
|
||||
int
|
||||
tcsetattr(int fildes, int optional_actions, // NOLINT(bugprone-easily-swappable-parameters)
|
||||
const struct termios *termios_p)
|
||||
{
|
||||
long request;
|
||||
|
||||
if (optional_actions == TCSANOW)
|
||||
{
|
||||
request = VLIBC_TERMIOS_TCSETS;
|
||||
}
|
||||
else if (optional_actions == TCSADRAIN)
|
||||
{
|
||||
request = VLIBC_TERMIOS_TCSETSW;
|
||||
}
|
||||
else if (optional_actions == TCSAFLUSH)
|
||||
{
|
||||
request = VLIBC_TERMIOS_TCSETSF;
|
||||
}
|
||||
else
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return syscall_ret(__syscall3(SYS_ioctl, fildes, request, (long)termios_p));
|
||||
}
|
||||
|
||||
/*
|
||||
* tcsendbreak: transmit a break condition. The kernel sends a break of
|
||||
* implementation-defined duration; POSIX leaves the meaning of a nonzero
|
||||
* duration to the implementation, so the argument is ignored.
|
||||
*/
|
||||
int
|
||||
tcsendbreak(int fildes, int duration) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
(void)duration;
|
||||
return syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_TERMIOS_TCSBRK, 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* tcdrain: wait until all output written to the terminal has been
|
||||
* transmitted. The kernel implements this as the TCSBRK ioctl with
|
||||
* argument 1 (as opposed to tcsendbreak's argument 0).
|
||||
*/
|
||||
int
|
||||
tcdrain(int fildes)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_TERMIOS_TCSBRK, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* tcflush: discard pending input, output, or both per queue_selector.
|
||||
* The kernel validates the selector (EINVAL on anything but TCIFLUSH/
|
||||
* TCOFLUSH/TCIOFLUSH).
|
||||
*/
|
||||
int
|
||||
tcflush(int fildes, int queue_selector) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_TERMIOS_TCFLSH, queue_selector));
|
||||
}
|
||||
|
||||
/*
|
||||
* tcflow: suspend/restart transmission or send a STOP/START character per
|
||||
* action. The kernel validates the action (EINVAL on anything but
|
||||
* TCOOFF/TCOON/TCIOFF/TCION).
|
||||
*/
|
||||
int
|
||||
tcflow(int fildes, int action) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
return syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_TERMIOS_TCXONC, action));
|
||||
}
|
||||
|
||||
/*
|
||||
* cfgetispeed: the input baud rate as stored in *termios_p. The kernel
|
||||
* encodes it in the CIBAUD field (c_cflag bits IBSHIFT..); when that field
|
||||
* is clear the input rate is defined to follow the output rate and the
|
||||
* stored value is B0.
|
||||
*/
|
||||
speed_t
|
||||
cfgetispeed(const struct termios *termios_p)
|
||||
{
|
||||
return (speed_t)((termios_p->c_cflag & VLIBC_TERMIOS_CIBAUD) >> VLIBC_TERMIOS_IBSHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
* cfgetospeed: the output baud rate as stored in *termios_p (the CBAUD
|
||||
* field, which already spans the CBAUDEX extension bits).
|
||||
*/
|
||||
speed_t
|
||||
cfgetospeed(const struct termios *termios_p)
|
||||
{
|
||||
return termios_p->c_cflag & CBAUD;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfsetispeed: store speed in the CIBAUD field of *termios_p. Only values
|
||||
* representable in the CBAUD encoding are valid (B0 clears the field,
|
||||
* meaning "input rate follows the output rate").
|
||||
*/
|
||||
int
|
||||
cfsetispeed(struct termios *termios_p, speed_t speed)
|
||||
{
|
||||
if ((speed & ~CBAUD) != 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
termios_p->c_cflag &= ~VLIBC_TERMIOS_CIBAUD;
|
||||
termios_p->c_cflag |= (speed & CBAUD) << VLIBC_TERMIOS_IBSHIFT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfsetospeed: store speed in the CBAUD field of *termios_p. Only values
|
||||
* representable in the CBAUD encoding (B0..B38400, B57600..B4000000) are
|
||||
* valid.
|
||||
*/
|
||||
int
|
||||
cfsetospeed(struct termios *termios_p, speed_t speed)
|
||||
{
|
||||
if ((speed & ~CBAUD) != 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
termios_p->c_cflag &= ~CBAUD;
|
||||
termios_p->c_cflag |= speed & CBAUD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* Level 2 (muslmimic): BSD/glibc extensions and XSI [CX] additions. */
|
||||
|
||||
/*
|
||||
* cfmakeraw: switch *termios_p to raw mode. Only the termios flags are
|
||||
* touched — speeds, character size beyond CS8, and the c_cc array are the
|
||||
* caller's business (POSIX does not define the function, so its exact
|
||||
* effect is a glibc-compatible choice).
|
||||
*/
|
||||
void
|
||||
cfmakeraw(struct termios *termios_p)
|
||||
{
|
||||
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
termios_p->c_oflag &= ~OPOST;
|
||||
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
termios_p->c_cflag &= ~(CSIZE | PARENB);
|
||||
termios_p->c_cflag |= CS8;
|
||||
}
|
||||
|
||||
/*
|
||||
* cfsetspeed: set both the input and the output baud rate to speed. Both
|
||||
* fields are written explicitly (cfsetospeed then cfsetispeed), so the
|
||||
* structure round-trips cfgetospeed/cfgetispeed and the kernel decodes the
|
||||
* same rate on both sides.
|
||||
*/
|
||||
int
|
||||
cfsetspeed(struct termios *termios_p, speed_t speed)
|
||||
{
|
||||
if (cfsetospeed(termios_p, speed) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return cfsetispeed(termios_p, speed);
|
||||
}
|
||||
|
||||
/*
|
||||
* tcgetsid: the session id of the session whose controlling terminal is
|
||||
* the terminal open on fildes. The kernel fills an int via the TIOCGSID ioctl
|
||||
* and fails with -ENOTTY when the terminal has no session.
|
||||
*/
|
||||
pid_t
|
||||
tcgetsid(int fildes)
|
||||
{
|
||||
int sid = 0; /* written by TIOCGSID; zeroed for the static analyzers */
|
||||
|
||||
if (syscall_ret(__syscall3(SYS_ioctl, fildes, VLIBC_TERMIOS_TIOCGSID, (long)&sid)) < 0)
|
||||
{
|
||||
return (pid_t)-1;
|
||||
}
|
||||
return (pid_t)sid;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "../internal/futex.h"
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* vlibc — futex implementation (todo 44).
|
||||
*
|
||||
* The raw seam returns exactly what SYS_futex left in rax (0/positive on
|
||||
* success, negative errno on failure); the mapped wrappers translate that
|
||||
* through syscall_ret() into the house convention (0/value or -1 with
|
||||
* errno set). __futex_wake maps a negative cnt to INT_MAX before the
|
||||
* syscall: the kernel's futex_wake_mark stops scanning when the count it
|
||||
* has already woken exceeds nr_wake, so -1 would stop after ONE waiter,
|
||||
* while INT_MAX is the "wake all" sentinel musl uses (after musl
|
||||
* src/thread/__futex.c + pthread_impl.h's __wake).
|
||||
*/
|
||||
|
||||
long
|
||||
__futex(volatile int *addr, int op, int val, void *ts)
|
||||
{
|
||||
return __syscall4(SYS_futex, (long)addr, op, val, (long)ts);
|
||||
}
|
||||
|
||||
int
|
||||
__futex_wait(volatile int *addr, int val, int priv)
|
||||
{
|
||||
long r = __futex(addr, FUTEX_WAIT | (priv ? FUTEX_PRIVATE : 0), val, 0);
|
||||
|
||||
/* The kernel reports a value mismatch as -EAGAIN: the wake condition
|
||||
* already holds, nothing to wait for. That is success for every
|
||||
* futex consumer, not an error (musl's __timedwait applies the same
|
||||
* rule). */
|
||||
if (r == -EAGAIN)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return syscall_ret(r);
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
int
|
||||
__futex_timedwait(volatile int *addr, int val, int priv, int clk, const struct timespec *at)
|
||||
{
|
||||
int op = FUTEX_WAIT_BITSET | (priv ? FUTEX_PRIVATE : 0);
|
||||
|
||||
if (clk == CLOCK_REALTIME)
|
||||
{
|
||||
op |= FUTEX_CLOCK_REALTIME;
|
||||
}
|
||||
|
||||
long r = __syscall6(SYS_futex, (long)addr, op, val, (long)at, 0, FUTEX_BITSET_MATCH_ANY);
|
||||
if (r == -EAGAIN)
|
||||
{
|
||||
return 0; /* value already differs: condition holds, see above */
|
||||
}
|
||||
return syscall_ret(r);
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
int
|
||||
__futex_wake(volatile int *addr, int cnt, int priv)
|
||||
{
|
||||
if (cnt < 0)
|
||||
{
|
||||
cnt = 0x7fffffff; /* wake-all sentinel, see the file comment */
|
||||
}
|
||||
return syscall_ret(__futex(addr, FUTEX_WAKE | (priv ? FUTEX_PRIVATE : 0), cnt, 0));
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#ifndef VLIBC_THREAD_PTHREAD_IMPL_H
|
||||
#define VLIBC_THREAD_PTHREAD_IMPL_H
|
||||
|
||||
/*
|
||||
* vlibc — internal threading substrate (todo 44).
|
||||
*
|
||||
* Everything the pthread layer (#45 and later) needs that is NOT itself a
|
||||
* public pthread_* API: the TCB accessor, the thread-pointer installer, the
|
||||
* new-thread TLS block builder, the futex seam, and the shared static-TLS
|
||||
* size symbol. The public pthread_* functions are built by #45 on top of
|
||||
* exactly these primitives; nothing here is exported from the shared
|
||||
* library (every function is hidden, and __vlibc_static_tls_size — the one
|
||||
* symbol with default visibility — is a data object, not an API).
|
||||
*
|
||||
* Ownership notes for the follow-up todos:
|
||||
*
|
||||
* - #45 defines pthread_t and the public pthread_self(); its
|
||||
* implementation simply returns __pthread_self(). musl's
|
||||
* cancellation-aware __syscall_cp/syscall_cp.s seam is NOT provided
|
||||
* here: cancellation points are #45's concept, and a syscall_cp here
|
||||
* would have to reference a __cancel symbol that does not exist yet.
|
||||
* - #46-#48 use __futex_wait/__futex_timedwait/__futex_wake and the
|
||||
* atomic.h extension for their lock words.
|
||||
* - #45's pthread_create maps a fresh stack + TLS block, calls
|
||||
* __vlibc_tls_setup(base) to copy the template and get the TP, and
|
||||
* installs it in the new thread via __set_thread_area().
|
||||
*
|
||||
* TP discipline (authoritative layout in src/start/tcb.h): the thread
|
||||
* pointer points AT the TCB (ELF TLS variant II), slot 0 holds the TCB's
|
||||
* own address, and static TLS data live at negative offsets. Every
|
||||
* primitive here respects that contract and never re-derives it.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../internal/atomic.h"
|
||||
#include "../internal/futex.h"
|
||||
#include "../internal/libc.h"
|
||||
#include "../start/tcb.h"
|
||||
|
||||
/*
|
||||
* The names below live in the implementation-reserved namespace (the
|
||||
* __pthread_self/__set_thread_area pair by musl parity); the waiver covers
|
||||
* exactly those, nothing else.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* Internal TCB accessor: returns the calling thread's TCB address, which
|
||||
* is the thread pointer itself (slot 0 of the TCB holds exactly that
|
||||
* value; __builtin_thread_pointer() reads %fs:0). Distinct and non-null
|
||||
* for every thread whose FS base was installed by init_main_tls() or
|
||||
* __set_thread_area(). The PUBLIC pthread_self() is #45's symbol; this
|
||||
* one stays hidden so the two can never collide at the ABI level.
|
||||
*/
|
||||
hidden void *
|
||||
__pthread_self(void);
|
||||
|
||||
/*
|
||||
* Install the calling thread's thread pointer via arch_prctl(ARCH_SET_FS).
|
||||
* Must run IN the thread whose FS base is being set (the arch_prctl
|
||||
* operates on the current task). Returns the raw syscall result: 0 on
|
||||
* success, a negative errno on failure — the caller decides how to report
|
||||
* it (there is no errno write here). Implemented in assembly
|
||||
* (src/thread/x86_64/__set_thread_area.s), after musl's file of the same
|
||||
* name.
|
||||
*/
|
||||
hidden long
|
||||
__set_thread_area(void *tp);
|
||||
|
||||
/*
|
||||
* Initialize a fresh TCB at tp exactly as the authoritative tcb.h layout
|
||||
* dictates: self pointer at slot 0, errno slot zeroed, and the per-thread
|
||||
* fenv reservation reset to the x86 defaults (x87 control word 0x037F,
|
||||
* MXCSR 0x1F80) — the same values init_main_tls() writes for the main
|
||||
* thread, so #42's fenv functions (which read/write the slot through the
|
||||
* fixed offsets) start from a defined state in every thread.
|
||||
*/
|
||||
hidden void
|
||||
__vlibc_tcb_init(void *tp);
|
||||
|
||||
/*
|
||||
* Build a new thread's static TLS block at base: copy the main
|
||||
* executable's PT_TLS image from the stored template, zero the .tbss
|
||||
* tail, initialize the TCB, and return the thread pointer. base must be a
|
||||
* page-aligned region of at least round_up(__vlibc_static_tls_size,
|
||||
* align) + VLIBC_TCB_SIZE bytes; the caller (#45's pthread_create) owns
|
||||
* the mapping (stack + guard + TLS in one block, munmap'd at thread
|
||||
* exit). The placement formula is exactly the one the linker bakes into
|
||||
* every %fs-relative TLS offset — see tcb.h, and never round the
|
||||
* alignment differently.
|
||||
*/
|
||||
hidden void *
|
||||
__vlibc_tls_setup(void *base);
|
||||
|
||||
/*
|
||||
* Size in bytes of the main executable's PT_TLS (p_memsz, i.e. .tdata +
|
||||
* .tbss) for the STATIC no-ld.so case. Default visibility ON PURPOSE:
|
||||
* this is exported ABI. init_main_tls() (src/start/tls.c) initializes it
|
||||
* from the auxv program headers at startup; #62's dynamic loader is the
|
||||
* sole runtime updater (it grows it on each successful dlopen), and #45
|
||||
* reads it when sizing new-thread TLS blocks. In a static build there is
|
||||
* no dlopen, so the startup value is the final one.
|
||||
*/
|
||||
extern size_t __vlibc_static_tls_size;
|
||||
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_THREAD_PTHREAD_IMPL_H */
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "pthread_impl.h"
|
||||
|
||||
/*
|
||||
* vlibc — internal thread self-access (todo 44).
|
||||
*
|
||||
* The TCB lives at the thread pointer itself (variant II; the authoritative
|
||||
* layout in src/start/tcb.h), and slot 0 of the TCB holds exactly that
|
||||
* address because __builtin_thread_pointer() reads %fs:0. __pthread_self()
|
||||
* is therefore a single load: it returns the calling thread's TCB address,
|
||||
* distinct and non-null for every thread whose FS base was installed by
|
||||
* init_main_tls() (main thread) or __set_thread_area() (new threads).
|
||||
*
|
||||
* This symbol is INTERNAL and hidden. The public pthread_self() belongs to
|
||||
* todo 45 and is its own exported symbol; its implementation delegates to
|
||||
* this accessor once pthread_t is defined there. Keeping the two names
|
||||
* separate means this substrate cannot collide with #45's public ABI.
|
||||
*/
|
||||
|
||||
void *
|
||||
__pthread_self(void)
|
||||
{
|
||||
return __builtin_thread_pointer();
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pthread_impl.h"
|
||||
|
||||
/*
|
||||
* vlibc — new-thread TLS block builder (todo 44).
|
||||
*
|
||||
* The counterpart of init_main_tls() (src/start/tls.c) for THREADS: the
|
||||
* caller (#45's pthread_create) maps a fresh block, this file turns it into
|
||||
* a working per-thread TLS area. Two halves:
|
||||
*
|
||||
* - __vlibc_tcb_init(tp): fill the TCB fields at a fresh thread pointer.
|
||||
* The fenv reservation is reset to the x86 defaults (x87 control word
|
||||
* 0x037F, MXCSR 0x1F80) so #42's fenv functions observe a defined
|
||||
* state in every thread — the same values init_main_tls() writes for
|
||||
* the main thread. All offsets are the authoritative constants from
|
||||
* src/start/tcb.h, never re-derived.
|
||||
*
|
||||
* - __vlibc_tls_setup(base): place the TLS image + TCB in base per the
|
||||
* linker's placement contract (tp = base + round_up(memsz, align),
|
||||
* with align EXACT — see tcb.h) and return the TP.
|
||||
*
|
||||
* The image source is the stored PRIMAL template (the executable's
|
||||
* mapped .tdata at p_vaddr, recorded by init_main_tls in
|
||||
* __vlibc_static_tls_template), NOT the main thread's live TLS block:
|
||||
* copying the live block would leak the main thread's mutations into
|
||||
* every new thread. The .tbss tail is zeroed explicitly.
|
||||
*
|
||||
* Thread pointer installation is deliberately NOT done here:
|
||||
* __set_thread_area() must run in the new thread itself, so #45 calls it
|
||||
* from the thread's entry point after __vlibc_tls_setup() returns.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Round v up to a multiple of a. a must be a power of two, which the ELF
|
||||
* p_align of a PT_TLS segment (and the no-TLS fallback of 16) always is.
|
||||
*/
|
||||
static size_t
|
||||
round_up(size_t v, size_t a)
|
||||
{
|
||||
return (v + a - 1) & ~(a - 1);
|
||||
}
|
||||
|
||||
void
|
||||
__vlibc_tcb_init(void *tp)
|
||||
{
|
||||
char *t = tp;
|
||||
|
||||
((uintptr_t *)t)[VLIBC_TCB_SELF_OFF / sizeof(uintptr_t)] = (uintptr_t)tp;
|
||||
*(int *)(t + VLIBC_TCB_ERRNO_OFF) = 0;
|
||||
*(uint16_t *)(t + VLIBC_TCB_FENV_X87CW_OFF) = 0x037F; /* x87 reset default */
|
||||
*(uint32_t *)(t + VLIBC_TCB_FENV_MXCSR_OFF) = 0x1F80; /* MXCSR reset default */
|
||||
}
|
||||
|
||||
/*
|
||||
* memcpy/memset from a raw template address: the insecureAPI check and the
|
||||
* integer-to-pointer pessimization warning do not apply to a libc copying
|
||||
* its own TLS image (the template is a stored address by design; boundary
|
||||
* checks are the placement contract in tcb.h, not memcpy_s). The whole
|
||||
* function is waived.
|
||||
*/
|
||||
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,performance-no-int-to-ptr)
|
||||
void *
|
||||
__vlibc_tls_setup(void *base)
|
||||
{
|
||||
char *block = base;
|
||||
char *tp = block + round_up(__vlibc_static_tls_size, __vlibc_static_tls_align);
|
||||
|
||||
if (__vlibc_static_tls_filesz > 0)
|
||||
{
|
||||
memcpy(block, (const void *)__vlibc_static_tls_template, __vlibc_static_tls_filesz);
|
||||
}
|
||||
if (__vlibc_static_tls_size > __vlibc_static_tls_filesz)
|
||||
{
|
||||
memset(block + __vlibc_static_tls_filesz, 0,
|
||||
__vlibc_static_tls_size - __vlibc_static_tls_filesz);
|
||||
}
|
||||
__vlibc_tcb_init(tp);
|
||||
return tp;
|
||||
}
|
||||
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,performance-no-int-to-ptr)
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* vlibc — x86_64 thread-pointer installer (todo 44).
|
||||
*
|
||||
* __set_thread_area(tp): install tp as the calling thread's FS base via
|
||||
* arch_prctl(ARCH_SET_FS). After this, %fs-relative TLS offsets resolve
|
||||
* against tp exactly as the linker baked them (see src/start/tcb.h for the
|
||||
* placement contract), and __builtin_thread_pointer() / %fs:0 returns tp.
|
||||
*
|
||||
* Must be executed IN the thread whose FS base is being set: arch_prctl
|
||||
* operates on the current task, so pthread_create (#45) calls this from the
|
||||
* new thread's entry point, never from the parent.
|
||||
*
|
||||
* Return convention: the raw syscall result is left in %eax — 0 on success,
|
||||
* a negative errno on failure. No errno write happens here; the caller maps
|
||||
* the result however its context needs (this mirrors musl's
|
||||
* src/thread/x86_64/__set_thread_area.s, which also returns raw).
|
||||
*
|
||||
* arch_prctl codes (kernel UAPI asm/prctl.h): ARCH_SET_FS 0x1002,
|
||||
* SYS_arch_prctl 158. Kept as literals because there is no internal header
|
||||
* for them yet; src/start/tls.c defines the same values locally.
|
||||
*/
|
||||
|
||||
.text
|
||||
.global __set_thread_area
|
||||
.hidden __set_thread_area
|
||||
.type __set_thread_area,@function
|
||||
__set_thread_area:
|
||||
mov %rdi,%rsi
|
||||
movl $0x1002,%edi
|
||||
movl $158,%eax
|
||||
syscall
|
||||
ret
|
||||
.size __set_thread_area,.-__set_thread_area
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* struct tms layout for SYS_times: four process-CPU clock-tick counters,
|
||||
* in kernel order (x86_64: four longs; the kernel ABI names it __kernel_tms).
|
||||
* It is private to this file; <sys/times.h> (a later todo) will own the
|
||||
* public definition.
|
||||
*/
|
||||
struct tms
|
||||
{
|
||||
clock_t tms_utime; /* user CPU time */
|
||||
clock_t tms_stime; /* system CPU time */
|
||||
clock_t tms_cutime; /* user CPU time of waited-for children */
|
||||
clock_t tms_cstime; /* system CPU time of waited-for children */
|
||||
};
|
||||
|
||||
/*
|
||||
* clock: the calling process's CPU time in CLOCKS_PER_SEC units.
|
||||
*
|
||||
* SYS_times reports CPU time in USER_HZ ticks (100 per second on x86_64),
|
||||
* so the tick counts are scaled by CLOCKS_PER_SEC / 100 == 10000 to the
|
||||
* 1 MHz clock() unit. On failure SYS_times returns -1 and clock() reports
|
||||
* (clock_t)-1 with errno set.
|
||||
*/
|
||||
clock_t
|
||||
clock(void)
|
||||
{
|
||||
struct tms t;
|
||||
|
||||
if (syscall_ret(__syscall1(SYS_times, (long)&t)) < 0)
|
||||
{
|
||||
return (clock_t)-1;
|
||||
}
|
||||
return (clock_t)((t.tms_utime + t.tms_stime) * (CLOCKS_PER_SEC / 100));
|
||||
}
|
||||
|
||||
/*
|
||||
* clock_gettime/clock_settime/clock_getres: raw kernel clock reads and
|
||||
* writes over SYS_clock_gettime(228)/SYS_clock_settime(227)/
|
||||
* SYS_clock_getres(229). The clock ids (CLOCK_REALTIME and friends) are
|
||||
* kernel ABI values and pass through unchanged; the struct timespec
|
||||
* arguments are the kernel's own layout.
|
||||
*/
|
||||
int
|
||||
clock_gettime(clockid_t clockid, struct timespec *tp)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_clock_gettime, (long)clockid, (long)tp));
|
||||
}
|
||||
|
||||
int
|
||||
clock_settime(clockid_t clockid, const struct timespec *tp)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_clock_settime, (long)clockid, (long)tp));
|
||||
}
|
||||
|
||||
int
|
||||
clock_getres(clockid_t clockid, struct timespec *res)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_clock_getres, (long)clockid, (long)res));
|
||||
}
|
||||
|
||||
/*
|
||||
* clock_getcpuclockid: the clock id that measures a given process's CPU
|
||||
* time.
|
||||
*
|
||||
* The calling process (pid 0 or our own pid) is always reported as
|
||||
* CLOCK_PROCESS_CPUTIME_ID. Any other pid is encoded with the kernel's
|
||||
* CPUCLOCK_PID scheme — (~pid << 3) | 2 — and validated by probing that
|
||||
* clock with SYS_clock_getres, which the kernel answers with ESRCH when no
|
||||
* such process exists. That probe failing is the only errno write this
|
||||
* file performs on its own; every other path leaves errno to syscall_ret.
|
||||
*/
|
||||
int
|
||||
clock_getcpuclockid(pid_t pid, clockid_t *clock_id)
|
||||
{
|
||||
pid_t self = (pid_t)syscall_ret(__syscall0(SYS_getpid));
|
||||
clockid_t id;
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
pid = self;
|
||||
}
|
||||
if (pid == self)
|
||||
{
|
||||
*clock_id = CLOCK_PROCESS_CPUTIME_ID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Linux CPUCLOCK_PID encoding of a process clock id. */
|
||||
id = (clockid_t)((~(unsigned)pid << 3) | 2u);
|
||||
|
||||
/* Probe the encoded id; the kernel rejects unknown pids with ESRCH. */
|
||||
{
|
||||
struct timespec res;
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_clock_getres, (long)id, (long)&res)) < 0)
|
||||
{
|
||||
errno = ESRCH;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*clock_id = id;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* asctime/ctime: the fixed-format "Www Mmm dd hh:mm:ss yyyy\n" rendering
|
||||
* (26 bytes including the terminating NUL). Both functions are thin layers
|
||||
* over a single buffer writer: asctime renders a broken-down time directly,
|
||||
* ctime renders localtime(t). Per POSIX the conversion fails (NULL) when
|
||||
* the calendar year lies outside [1000, 9999], which is also what keeps the
|
||||
* fixed 26-byte layout exact.
|
||||
*/
|
||||
|
||||
static const char *const wd_abbr[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
static const char *const mon_abbr[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
|
||||
/*
|
||||
* Render tm into buf (26 bytes: 24 + '\n' + '\0'). Returns buf, or NULL
|
||||
* when the year is outside the representable range (the buffer is then
|
||||
* left untouched, matching the POSIX contract).
|
||||
*/
|
||||
static char *
|
||||
asctime_write(const struct tm *tm, char *buf)
|
||||
{
|
||||
int year = tm->tm_year + 1900;
|
||||
int wd = tm->tm_wday;
|
||||
int mo = tm->tm_mon;
|
||||
int mday = tm->tm_mday;
|
||||
const char *s;
|
||||
char *p = buf;
|
||||
|
||||
if (year < 1000 || year > 9999)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (wd < 0)
|
||||
{
|
||||
wd = 7 - (-wd) % 7;
|
||||
if (wd == 7)
|
||||
{
|
||||
wd = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wd %= 7;
|
||||
}
|
||||
if (mo < 0)
|
||||
{
|
||||
mo = 12 - (-mo) % 12;
|
||||
if (mo == 12)
|
||||
{
|
||||
mo = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mo %= 12;
|
||||
}
|
||||
|
||||
s = wd_abbr[wd];
|
||||
while (*s != '\0')
|
||||
{
|
||||
*p++ = *s++;
|
||||
}
|
||||
*p++ = ' ';
|
||||
s = mon_abbr[mo];
|
||||
while (*s != '\0')
|
||||
{
|
||||
*p++ = *s++;
|
||||
}
|
||||
*p++ = ' ';
|
||||
if (mday < 10)
|
||||
{
|
||||
*p++ = ' ';
|
||||
}
|
||||
*p++ = (char)('0' + (mday / 10) % 10);
|
||||
*p++ = (char)('0' + mday % 10);
|
||||
*p++ = ' ';
|
||||
*p++ = (char)('0' + (tm->tm_hour / 10) % 10);
|
||||
*p++ = (char)('0' + tm->tm_hour % 10);
|
||||
*p++ = ':';
|
||||
*p++ = (char)('0' + (tm->tm_min / 10) % 10);
|
||||
*p++ = (char)('0' + tm->tm_min % 10);
|
||||
*p++ = ':';
|
||||
*p++ = (char)('0' + (tm->tm_sec / 10) % 10);
|
||||
*p++ = (char)('0' + tm->tm_sec % 10);
|
||||
*p++ = ' ';
|
||||
*p++ = (char)('0' + (year / 1000) % 10);
|
||||
*p++ = (char)('0' + (year / 100) % 10);
|
||||
*p++ = (char)('0' + (year / 10) % 10);
|
||||
*p++ = (char)('0' + year % 10);
|
||||
*p++ = '\n';
|
||||
*p = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *
|
||||
asctime(const struct tm *tm) // NOLINT(concurrency-mt-unsafe)
|
||||
{
|
||||
static char asctime_buf[26];
|
||||
|
||||
return asctime_write(tm, asctime_buf);
|
||||
}
|
||||
|
||||
char *
|
||||
ctime(const time_t *timer)
|
||||
{
|
||||
return asctime(localtime(timer));
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
char *
|
||||
asctime_r(const struct tm *tm, char *buf)
|
||||
{
|
||||
return asctime_write(tm, buf);
|
||||
}
|
||||
|
||||
char *
|
||||
ctime_r(const time_t *timer, char *buf)
|
||||
{
|
||||
struct tm tmv;
|
||||
|
||||
if (localtime_r(timer, &tmv) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return asctime_write(&tmv, buf);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* getitimer: read the current setting of interval timer which (ITIMER_REAL,
|
||||
* ITIMER_VIRTUAL or ITIMER_PROF), via SYS_getitimer. struct itimerval is
|
||||
* layout-identical to the kernel's two-timeval structure on x86_64, so the
|
||||
* buffer passes straight through and the kernel reports whether the timer
|
||||
* is armed (it_value nonzero) and its period (it_interval). An unknown
|
||||
* which or an invalid value pointer is rejected by the kernel (EINVAL /
|
||||
* EFAULT), reported as -1 with errno set.
|
||||
*/
|
||||
int
|
||||
getitimer(int which, struct itimerval *value)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_getitimer, which, (long)value));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* gettimeofday: the current wall-clock time, via SYS_gettimeofday.
|
||||
*
|
||||
* The kernel writes the two-field struct timeval (tv_sec, tv_usec) that is
|
||||
* layout-identical to the public struct timeval on x86_64, so the pointer
|
||||
* passes straight through. The tz argument is deliberately ignored: the
|
||||
* historic second parameter is obsolete, POSIX.1-2008 removed struct
|
||||
* timezone, and no caller may rely on it being filled — NULL is passed to
|
||||
* the kernel, which then leaves any timezone record untouched. tv may also
|
||||
* be NULL, in which case the kernel writes nothing and 0 is returned.
|
||||
*/
|
||||
int
|
||||
gettimeofday(struct timeval *restrict tv, void *restrict tz)
|
||||
{
|
||||
(void)tz;
|
||||
return syscall_ret(__syscall2(SYS_gettimeofday, (long)tv, 0));
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,188 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "time_impl.h"
|
||||
|
||||
/*
|
||||
* Civil <-> serial conversions (mktime/timegm/__fill_tm).
|
||||
*
|
||||
* days_from_civil/civil_from_days are Howard Hinnant's proleptic-Gregorian
|
||||
* algorithms (the "chrono-Compatible Low-Level Date Algorithms" pair): the
|
||||
* era-400 formulation keeps the year arithmetic exact for the full 32-bit
|
||||
* int year range and the division by 146097 is compile-time constant.
|
||||
* days_from_civil counts days since 1970-01-01 (negative before that);
|
||||
* civil_from_days is its exact inverse and yields a 1-based month.
|
||||
*
|
||||
* floor_div/floor_mod give Euclidean division so that the day/time math
|
||||
* stays correct for negative inputs (pre-1970 timestamps, out-of-range
|
||||
* struct tm fields): floor_mod always returns a non-negative remainder.
|
||||
*/
|
||||
|
||||
static long long
|
||||
floor_div(long long a, long long b)
|
||||
{
|
||||
long long q = a / b;
|
||||
long long r = a % b;
|
||||
|
||||
if (r != 0 && ((r < 0) != (b < 0)))
|
||||
{
|
||||
q -= 1;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
static long long
|
||||
floor_mod(long long a, long long b)
|
||||
{
|
||||
long long r = a % b;
|
||||
|
||||
if (r != 0 && ((r < 0) != (b < 0)))
|
||||
{
|
||||
r += b;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters,bugprone-reserved-identifier)
|
||||
long long
|
||||
days_from_civil(int y, int m, int d)
|
||||
{
|
||||
long long yy = y;
|
||||
|
||||
yy -= (m <= 2);
|
||||
{
|
||||
long long era = (yy >= 0 ? yy : yy - 399) / 400;
|
||||
unsigned int yoe = (unsigned int)(yy - era * 400); /* [0, 399] */
|
||||
unsigned int doy = (153u * (unsigned int)(m + (m > 2 ? -3 : 9)) + 2u) / 5u +
|
||||
(unsigned int)d - 1u; /* [0, 365] */
|
||||
unsigned int doe = yoe * 365u + yoe / 4u - yoe / 100u + doy; /* [0, 146096] */
|
||||
|
||||
return era * 146097 + (long long)doe - 719468;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
civil_from_days(long long days, int *y, int *m, int *d)
|
||||
{
|
||||
long long z = days + 719468;
|
||||
long long era = (z >= 0 ? z : z - 146096) / 146097;
|
||||
unsigned int doe = (unsigned int)(z - era * 146097); /* [0, 146096] */
|
||||
unsigned int yoe = (doe - doe / 1460u + doe / 36524u - doe / 146096u) / 365u; /* [0, 399] */
|
||||
unsigned int doy = doe - (365u * yoe + yoe / 4u - yoe / 100u); /* [0, 365] */
|
||||
unsigned int mp = (5u * doy + 2u) / 153u; /* [0, 11] */
|
||||
unsigned int dd = doy - (153u * mp + 2u) / 5u + 1u; /* [1, 31] */
|
||||
unsigned int mm = mp < 10u ? mp + 3u : mp - 9u; /* [1, 12] */
|
||||
|
||||
*y = (int)(yoe + era * 400) + (int)(mm <= 2u);
|
||||
*m = (int)mm;
|
||||
*d = (int)dd;
|
||||
}
|
||||
|
||||
/*
|
||||
* __fill_tm: materialize a struct tm from a local civil time expressed as
|
||||
* seconds (the pseudo-UTC frame every TZ computation uses), the offset in
|
||||
* effect, the DST flag, and the zone abbreviation to store in tm_zone.
|
||||
* The caller supplies the fully resolved values; this function only splits
|
||||
* local_sec into the civil fields.
|
||||
*/
|
||||
void
|
||||
__fill_tm(long long local_sec, long gmtoff, int isdst, const char *zone, struct tm *out)
|
||||
{
|
||||
long long days = floor_div(local_sec, 86400);
|
||||
long long tod = local_sec - days * 86400; /* [0, 86400) */
|
||||
int y;
|
||||
int m;
|
||||
int d;
|
||||
|
||||
civil_from_days(days, &y, &m, &d);
|
||||
out->tm_sec = (int)(tod % 60);
|
||||
out->tm_min = (int)((tod / 60) % 60);
|
||||
out->tm_hour = (int)(tod / 3600);
|
||||
out->tm_mday = d;
|
||||
out->tm_mon = m - 1;
|
||||
out->tm_year = y - 1900;
|
||||
out->tm_wday = (int)floor_mod(days + 4, 7); /* 1970-01-01 was a Thursday */
|
||||
out->tm_yday = (int)(days - days_from_civil(y, 1, 1));
|
||||
out->tm_isdst = isdst;
|
||||
out->tm_gmtoff = gmtoff;
|
||||
out->tm_zone = zone;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters,bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* tm_civil_seconds: the struct tm fields reduced to one linear "local
|
||||
* civil" second count. Out-of-range fields fold in naturally: the month is
|
||||
* renormalized into the year with Euclidean division, the day is carried as
|
||||
* an offset from the first of the month, and the time of day just adds on
|
||||
* (hours/minutes/seconds may be anything and spill across day boundaries).
|
||||
*/
|
||||
static long long
|
||||
tm_civil_seconds(const struct tm *tm)
|
||||
{
|
||||
int y = tm->tm_year + 1900 + (int)floor_div((long long)tm->tm_mon, 12);
|
||||
int m = (int)floor_mod((long long)tm->tm_mon, 12) + 1;
|
||||
long long days = days_from_civil(y, m, 1) + (long long)tm->tm_mday - 1;
|
||||
|
||||
return days * 86400 + (long long)tm->tm_hour * 3600 + (long long)tm->tm_min * 60 +
|
||||
(long long)tm->tm_sec;
|
||||
}
|
||||
|
||||
/*
|
||||
* mktime: interpret the broken-down fields as LOCAL civil time and return
|
||||
* the corresponding UTC epoch. The DST regime is chosen from tm_isdst:
|
||||
* positive forces daylight time, zero forces standard time, and negative
|
||||
* (the "I don't know" value localtime fills) defers to the zone rules.
|
||||
* POSIX requires the struct to be overwritten with the normalized fields,
|
||||
* so __fill_tm runs on the resolved local time before returning. Overflow
|
||||
* leaves the struct untouched and reports (time_t)-1 with errno EOVERFLOW.
|
||||
*/
|
||||
time_t
|
||||
mktime(struct tm *tm)
|
||||
{
|
||||
const struct tz_state *st;
|
||||
long long local = tm_civil_seconds(tm);
|
||||
long off;
|
||||
int isdst = 0;
|
||||
long long epoch;
|
||||
|
||||
__tzset_lazy();
|
||||
st = __tz_state();
|
||||
off = tz_offset_for_local(st, local, tm->tm_isdst, &isdst);
|
||||
if (__builtin_sub_overflow(local, (long long)off, &epoch))
|
||||
{
|
||||
errno = EOVERFLOW;
|
||||
return (time_t)-1;
|
||||
}
|
||||
__fill_tm(local, off, isdst, isdst ? st->dst_name : st->std_name, tm);
|
||||
return (time_t)epoch;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* timegm: mktime interpreted as UTC — the same normalization, with a zero
|
||||
* offset and a fixed "UTC" zone, so no timezone state is consulted.
|
||||
*/
|
||||
time_t
|
||||
timegm(struct tm *tm)
|
||||
{
|
||||
long long local = tm_civil_seconds(tm);
|
||||
long long epoch;
|
||||
|
||||
if (__builtin_sub_overflow(local, 0, &epoch))
|
||||
{
|
||||
errno = EOVERFLOW;
|
||||
return (time_t)-1;
|
||||
}
|
||||
__fill_tm(local, 0, 0, "UTC", tm);
|
||||
return (time_t)epoch;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user