diff --git a/crates/axio/src/lib.rs b/crates/axio/src/lib.rs index c001120707..eaab3d06b3 100644 --- a/crates/axio/src/lib.rs +++ b/crates/axio/src/lib.rs @@ -251,7 +251,7 @@ where } /// Struct for poll result. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct PollState { /// Object can be read now. pub readable: bool, diff --git a/ulib/c_libax/include/dirent.h b/ulib/c_libax/include/dirent.h new file mode 100644 index 0000000000..549e500b1a --- /dev/null +++ b/ulib/c_libax/include/dirent.h @@ -0,0 +1,41 @@ +#ifndef _DIRENT_H +#define _DIRENT_H + +#include + +struct dirstream { + long long tell; + int fd; + int buf_pos; + int buf_end; + int lock[1]; + char buf[2048]; +}; + +typedef struct dirstream DIR; + +struct dirent { + ino_t d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +int closedir(DIR *); +DIR *fdopendir(int); +int dirfd(DIR *); + +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 +#define IFTODT(x) ((x) >> 12 & 017) +#define DTTOIF(x) ((x) << 12) + +#endif //_DIRENT_H diff --git a/ulib/c_libax/include/libgen.h b/ulib/c_libax/include/libgen.h new file mode 100644 index 0000000000..7c7fd9c6d2 --- /dev/null +++ b/ulib/c_libax/include/libgen.h @@ -0,0 +1,15 @@ +#ifndef _LIBGEN_H +#define _LIBGEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +char *dirname(char *); +char *basename(char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ulib/c_libax/include/stdio.h b/ulib/c_libax/include/stdio.h index c9bdfe7ccc..bdbcfc31c1 100644 --- a/ulib/c_libax/include/stdio.h +++ b/ulib/c_libax/include/stdio.h @@ -42,6 +42,7 @@ extern FILE *const stderr; #if defined(AX_CONFIG_ALLOC) && defined(AX_CONFIG_FS) FILE *fopen(const char *filename, const char *mode); +char *fgets(char *__restrict, int, FILE *__restrict); #endif int fflush(FILE *); diff --git a/ulib/c_libax/include/stdlib.h b/ulib/c_libax/include/stdlib.h index 11c9c0d4d4..6b7567afc4 100644 --- a/ulib/c_libax/include/stdlib.h +++ b/ulib/c_libax/include/stdlib.h @@ -16,6 +16,12 @@ void *realloc(void *memblock, size_t size); _Noreturn void abort(void); char *getenv(const char *name); +#ifdef AX_CONFIG_FP_SIMD +float strtof(const char *__restrict, char **__restrict); +double strtod(const char *__restrict, char **__restrict); +long double strtold(const char *__restrict, char **__restrict); +#endif + long strtol(const char *__restrict, char **__restrict, int); unsigned long strtoul(const char *nptr, char **endptr, int base); long long strtoll(const char *nptr, char **endptr, int base); diff --git a/ulib/c_libax/include/string.h b/ulib/c_libax/include/string.h index 0882603ab9..6a3dcd987b 100644 --- a/ulib/c_libax/include/string.h +++ b/ulib/c_libax/include/string.h @@ -20,8 +20,13 @@ char *strncat(char *restrict d, const char *restrict s, size_t n); int strcmp(const char *l, const char *r); int strncmp(const char *l, const char *r, size_t n); +int strcoll(const char *, const char *); + size_t strcspn(const char *s1, const char *s2); size_t strspn(const char *s, const char *c); +char *strpbrk(const char *, const char *); + +char *__strchrnul(const char *, int); char *strrchr(const char *str, int c); char *strchr(const char *str, int c); diff --git a/ulib/c_libax/include/sys/epoll.h b/ulib/c_libax/include/sys/epoll.h new file mode 100644 index 0000000000..3364523c47 --- /dev/null +++ b/ulib/c_libax/include/sys/epoll.h @@ -0,0 +1,56 @@ +#ifndef _SYS_EPOLL_H +#define _SYS_EPOLL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define EPOLL_CLOEXEC O_CLOEXEC +#define EPOLL_NONBLOCK O_NONBLOCK + +enum EPOLL_EVENTS { __EPOLL_DUMMY }; +#define EPOLLIN 0x001 +#define EPOLLPRI 0x002 +#define EPOLLOUT 0x004 +#define EPOLLRDNORM 0x040 +#define EPOLLNVAL 0x020 +#define EPOLLRDBAND 0x080 +#define EPOLLWRNORM 0x100 +#define EPOLLWRBAND 0x200 +#define EPOLLMSG 0x400 +#define EPOLLERR 0x008 +#define EPOLLHUP 0x010 +#define EPOLLRDHUP 0x2000 +#define EPOLLEXCLUSIVE (1U << 28) +#define EPOLLWAKEUP (1U << 29) +#define EPOLLONESHOT (1U << 30) +#define EPOLLET (1U << 31) + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 + +typedef union epoll_data { + void *ptr; + int fd; + uint32_t u32; + uint64_t u64; +} epoll_data_t; + +struct epoll_event { + uint32_t events; + epoll_data_t data; +}; + +int epoll_create(int __size); +int epoll_ctl(int, int, int, struct epoll_event *); +int epoll_wait(int, struct epoll_event *, int, int); + +#ifdef __cplusplus +} +#endif + +#endif //_SYS_EPOLL_H diff --git a/ulib/c_libax/include/time.h b/ulib/c_libax/include/time.h index e810462d4b..7eacf6c629 100644 --- a/ulib/c_libax/include/time.h +++ b/ulib/c_libax/include/time.h @@ -34,4 +34,8 @@ time_t time(time_t *t); int clock_gettime(clockid_t _clk, struct timespec *ts); int nanosleep(const struct timespec *requested_time, struct timespec *remaining); +#ifdef AX_CONFIG_FP_SIMD +double difftime(time_t, time_t); +#endif + #endif diff --git a/ulib/c_libax/include/unistd.h b/ulib/c_libax/include/unistd.h index 370cd6c1a2..7ca722f2f3 100644 --- a/ulib/c_libax/include/unistd.h +++ b/ulib/c_libax/include/unistd.h @@ -185,4 +185,6 @@ long int sysconf(int name); #define _SC_THREAD_ROBUST_PRIO_INHERIT 247 #define _SC_THREAD_ROBUST_PRIO_PROTECT 248 +extern char **__environ; + #endif diff --git a/ulib/c_libax/src/dirent.c b/ulib/c_libax/src/dirent.c new file mode 100644 index 0000000000..9cbdd7785d --- /dev/null +++ b/ulib/c_libax/src/dirent.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef AX_CONFIG_ALLOC +int closedir(DIR *dir) +{ + int ret = close(dir->fd); + free(dir); + return ret; +} + +DIR *fdopendir(int fd) +{ + DIR *dir; + struct stat st; + + if (fstat(fd, &st) < 0) { + return 0; + } + if (fcntl(fd, F_GETFL) & O_PATH) { + errno = EBADF; + return 0; + } + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return 0; + } + if (!(dir = calloc(1, sizeof(*dir)))) { + return 0; + } + + fcntl(fd, F_SETFD, FD_CLOEXEC); + dir->fd = fd; + return dir; +} + +#endif + +int dirfd(DIR *d) +{ + return d->fd; +} diff --git a/ulib/c_libax/src/epoll.c b/ulib/c_libax/src/epoll.c new file mode 100644 index 0000000000..3bb7d74f87 --- /dev/null +++ b/ulib/c_libax/src/epoll.c @@ -0,0 +1,19 @@ +#include +#include + +#include + +int epoll_create(int size) +{ + return ax_epoll_create(size); +} + +int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) +{ + return ax_epoll_ctl(fd, op, fd2, ev); +} + +int epoll_wait(int fd, struct epoll_event *ev, int cnt, int to) +{ + return ax_epoll_wait(fd, ev, cnt, to); +} diff --git a/ulib/c_libax/src/libgen.c b/ulib/c_libax/src/libgen.c new file mode 100644 index 0000000000..fbce881545 --- /dev/null +++ b/ulib/c_libax/src/libgen.c @@ -0,0 +1,33 @@ +#include +#include + +char *dirname(char *s) +{ + size_t i; + if (!s || !*s) + return "."; + i = strlen(s) - 1; + for (; s[i] == '/'; i--) + if (!i) + return "/"; + for (; s[i] != '/'; i--) + if (!i) + return "."; + for (; s[i] == '/'; i--) + if (!i) + return "/"; + s[i + 1] = 0; + return s; +} + +char *basename(char *s) +{ + size_t i; + if (!s || !*s) + return "."; + i = strlen(s) - 1; + for (; i && s[i] == '/'; i--) s[i] = 0; + for (; i && s[i - 1] != '/'; i--) + ; + return s + i; +} diff --git a/ulib/c_libax/src/math.c b/ulib/c_libax/src/math.c index 0d72a6c702..1a4f2f97ce 100644 --- a/ulib/c_libax/src/math.c +++ b/ulib/c_libax/src/math.c @@ -93,4 +93,11 @@ double floor(double x) return x + y - 1; return x + y; } -#endif \ No newline at end of file + +// TODO +long double __extenddftf2(double a) +{ + return 0; +} + +#endif diff --git a/ulib/c_libax/src/stdio.c b/ulib/c_libax/src/stdio.c index a7b31615f2..ab5ddd89fc 100644 --- a/ulib/c_libax/src/stdio.c +++ b/ulib/c_libax/src/stdio.c @@ -204,4 +204,28 @@ FILE *fopen(const char *filename, const char *mode) return f; } +char *fgets(char *restrict s, int n, FILE *restrict f) +{ + if (n == 0) + return NULL; + if (n == 1) { + *s = '\0'; + return s; + } + + int cnt = 0; + while (cnt < n - 1) { + char c; + if (ax_read(f->fd, (void *)&c, 1) > 0) { + if (c != '\n') + s[cnt++] = c; + else + break; + } else + break; + } + s[cnt] = '\0'; + return s; +} + #endif diff --git a/ulib/c_libax/src/stdlib.c b/ulib/c_libax/src/stdlib.c index a1952456d2..2f6338a312 100644 --- a/ulib/c_libax/src/stdlib.c +++ b/ulib/c_libax/src/stdlib.c @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -60,10 +61,13 @@ _Noreturn void abort(void) __builtin_unreachable(); } -// TODO: char *getenv(const char *name) { - unimplemented(); + size_t l = __strchrnul(name, '=') - name; + if (l && !name[l] && __environ) + for (char **e = __environ; *e; e++) + if (!strncmp(name, *e, l) && l[*e] == '=') + return *e + l + 1; return 0; } @@ -377,3 +381,21 @@ long long atoll(const char *s) while (isdigit(*s)) n = 10 * n - (*s++ - '0'); return neg ? n : -n; } + +#ifdef AX_CONFIG_FP_SIMD +float strtof(const char *restrict s, char **restrict p) +{ + return ax_strtof(s, p); +} + +double strtod(const char *restrict s, char **restrict p) +{ + return ax_strtod(s, p); +} + +// TODO: precision may not be enough +long double strtold(const char *restrict s, char **restrict p) +{ + return (long double)strtod(s, p); +} +#endif diff --git a/ulib/c_libax/src/string.c b/ulib/c_libax/src/string.c index 962a033e4f..1caa713719 100644 --- a/ulib/c_libax/src/string.c +++ b/ulib/c_libax/src/string.c @@ -131,6 +131,11 @@ int strncmp(const char *_l, const char *_r, size_t n) return *l - *r; } +int strcoll(const char *l, const char *r) +{ + return strcmp(l, r); +} + #define BITOP(a, b, op) \ ((a)[(size_t)(b) / (8 * sizeof *(a))] op(size_t) 1 << ((size_t)(b) % (8 * sizeof *(a)))) #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -173,6 +178,23 @@ size_t strspn(const char *s, const char *c) return s - a; } +char *strpbrk(const char *s, const char *b) +{ + s += strcspn(s, b); + return *s ? (char *)s : 0; +} + +char *__strchrnul(const char *s, int c) +{ + c = (unsigned char)c; + if (!c) + return (char *)s + strlen(s); + + for (; *s && *(unsigned char *)s != c; s++) + ; + return (char *)s; +} + char *strchr(const char *s, int c) { while (*s != c && *s != '\0') s++; diff --git a/ulib/c_libax/src/time.c b/ulib/c_libax/src/time.c index 5eab00d1e1..60779081fd 100644 --- a/ulib/c_libax/src/time.c +++ b/ulib/c_libax/src/time.c @@ -177,3 +177,10 @@ int nanosleep(const struct timespec *req, struct timespec *rem) { return ax_nanosleep(req, rem); } + +#ifdef AX_CONFIG_FP_SIMD +double difftime(time_t t1, time_t t0) +{ + return t1 - t0; +} +#endif diff --git a/ulib/c_libax/src/unistd.c b/ulib/c_libax/src/unistd.c index 432035d835..90b70a4001 100644 --- a/ulib/c_libax/src/unistd.c +++ b/ulib/c_libax/src/unistd.c @@ -6,6 +6,8 @@ #include #include +char **__environ = 0; + // TODO: uid_t geteuid(void) { diff --git a/ulib/libax/Cargo.toml b/ulib/libax/Cargo.toml index 166f34141b..6832a84e9e 100644 --- a/ulib/libax/Cargo.toml +++ b/ulib/libax/Cargo.toml @@ -47,6 +47,9 @@ net = ["alloc", "axruntime/net", "dep:axdriver", "dep:axnet"] # Pipe pipe = ["alloc"] +# Epoll +epoll = ["alloc"] + # Display display = ["axruntime/display", "dep:axdriver", "dep:axdisplay"] diff --git a/ulib/libax/build.rs b/ulib/libax/build.rs index b1348509cf..3d31f9c5c2 100644 --- a/ulib/libax/build.rs +++ b/ulib/libax/build.rs @@ -30,6 +30,7 @@ fn main() { "sys/socket.h".into(), "sys/select.h".into(), "sys/time.h".into(), + "sys/epoll.h".into(), ], includes: vec!["axconfig.h".into()], header: Some("/* Generated by cbindgen and build.rs, DO NOT edit! */".into()), @@ -46,6 +47,8 @@ fn main() { rename_type(&mut config, "sockaddr"); rename_type(&mut config, "timespec"); rename_type(&mut config, "timeval"); + rename_type(&mut config, "epoll_event"); + cbindgen::generate_with_config(crate_dir, config) .expect("Unable to generate rust->c bindings") .write_to_file(out_file); @@ -56,8 +59,18 @@ fn main() { let include_dir = crate_dir.join("../c_libax/include"); let allow_types = [ - "stat", "size_t", "ssize_t", "off_t", "mode_t", "O_*", "sock.*", "FILE", "jmp_buf", - "fd.*", "timeval", + "stat", + "size_t", + "ssize_t", + "off_t", + "mode_t", + "O_*", + "sock.*", + "FILE", + "jmp_buf", + "fd.*", + "timeval", + "epoll_event", ]; let allow_vars = [ "O_.*", diff --git a/ulib/libax/ctypes.h b/ulib/libax/ctypes.h index 4271fc62b4..9253ac8007 100644 --- a/ulib/libax/ctypes.h +++ b/ulib/libax/ctypes.h @@ -1,14 +1,14 @@ #include +#include +#include +#include +#include #include -#include -#include +#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include +#include #include -#include - diff --git a/ulib/libax/src/cbindings/epoll.rs b/ulib/libax/src/cbindings/epoll.rs new file mode 100644 index 0000000000..16e291cca1 --- /dev/null +++ b/ulib/libax/src/cbindings/epoll.rs @@ -0,0 +1,278 @@ +use super::ctypes; +use super::fd_ops::FileLike; +use crate::cbindings::fd_ops::{add_file_like, get_file_like}; +use crate::debug; +use alloc::collections::{BTreeMap, BTreeSet}; +use alloc::sync::Arc; +use axerrno::{LinuxError, LinuxResult}; +use axsync::Mutex; +use core::ffi::c_int; + +pub struct EPollCtlOp; +impl EPollCtlOp { + const ADD: i32 = 1; /* Add a file descriptor to the interface. */ + const DEL: i32 = 2; /* Remove a file descriptor from the interface. */ + const MOD: i32 = 3; /* Change file descriptor epoll_event structure. */ +} + +#[derive(Clone, Copy)] +pub struct EpollData { + ptr: u64, +} + +#[derive(Clone)] +pub struct EpollEvent { + pub events: u32, /* Epoll events */ + pub data: EpollData, /* User data variable */ +} + +#[allow(dead_code)] +impl EpollEvent { + pub const EPOLLIN: u32 = 0x001; + pub const EPOLLOUT: u32 = 0x004; + pub const EPOLLERR: u32 = 0x008; + pub const EPOLLHUP: u32 = 0x010; + + pub const EPOLLPRI: u32 = 0x002; + pub const EPOLLRDNORM: u32 = 0x040; + pub const EPOLLRDBAND: u32 = 0x080; + pub const EPOLLWRNORM: u32 = 0x100; + pub const EPOLLWRBAND: u32 = 0x200; + pub const EPOLLMSG: u32 = 0x400; + pub const EPOLLRDHUP: u32 = 0x2000; + pub const EPOLLEXCLUSIVE: u32 = 1 << 28; + pub const EPOLLWAKEUP: u32 = 1 << 29; + pub const EPOLLONESHOT: u32 = 1 << 30; + pub const EPOLLET: u32 = 1 << 31; + + pub fn contains(&self, events: u32) -> bool { + (self.events & events) != 0 + } +} + +pub struct EpollInstance { + pub events: Mutex>, + pub ready_list: Mutex>, + pub new_ctl_list: Mutex>, +} + +impl Clone for EpollInstance { + fn clone(&self) -> Self { + EpollInstance::new(0) + } +} + +impl EpollInstance { + // TODO: parse flags + pub fn new(_flags: usize) -> Self { + Self { + events: Mutex::new(BTreeMap::new()), + ready_list: Default::default(), + new_ctl_list: Default::default(), + } + } + + pub fn control(&self, op: usize, fd: usize, event: &EpollEvent) -> LinuxResult { + match op as i32 { + EPollCtlOp::ADD => { + self.events.lock().insert(fd, event.clone()); + self.new_ctl_list.lock().insert(fd); + } + EPollCtlOp::MOD => { + let mut events = self.events.lock(); + if events.get(&fd).is_some() { + events.remove(&fd); + events.insert(fd, event.clone()); + self.new_ctl_list.lock().insert(fd); + } else { + return Err(LinuxError::EPERM); + } + } + EPollCtlOp::DEL => { + let mut events = self.events.lock(); + if events.get(&fd).is_some() { + events.remove(&fd); + } else { + return Err(LinuxError::EPERM); + } + } + _ => { + return Err(LinuxError::EPERM); + } + } + Ok(0) + } +} + +impl FileLike for EpollInstance { + fn read(&self, _buf: &mut [u8]) -> LinuxResult { + Err(LinuxError::ENOSYS) + } + + fn write(&self, _buf: &[u8]) -> LinuxResult { + Err(LinuxError::ENOSYS) + } + + fn stat(&self) -> LinuxResult { + let st_mode = 0o600u32; // rw------- + Ok(ctypes::stat { + st_ino: 1, + st_nlink: 1, + st_mode, + ..Default::default() + }) + } + + fn into_any(self: Arc) -> alloc::sync::Arc { + self + } + + fn poll(&self) -> LinuxResult { + Err(LinuxError::ENOSYS) + } + + fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + Ok(()) + } +} + +/// `ax_epoll_create()` creates a new epoll instance. +/// +/// `ax_epoll_create()` returns a file descriptor referring to the new epoll instance. +#[no_mangle] +pub unsafe extern "C" fn ax_epoll_create(size: c_int) -> c_int { + debug!("ax_epoll_create <= {}", size); + ax_call_body!(ax_epoll_create, { + if size < 0 { + return Err(LinuxError::EINVAL); + } + let epoll_instance = EpollInstance::new(0); + add_file_like(Arc::new(epoll_instance)) + }) +} + +fn c_event_to_rust(ev: *mut ctypes::epoll_event) -> EpollEvent { + unsafe { + EpollEvent { + events: (*ev).events, + data: EpollData { + ptr: core::mem::transmute::((*ev).data), + }, + } + } +} + +/// Control interface for an epoll file descriptor +#[no_mangle] +pub unsafe extern "C" fn ax_epoll_ctl( + fd: c_int, + op: c_int, + fd2: c_int, + ev: *mut ctypes::epoll_event, +) -> c_int { + debug!("ax_epoll_ctl <= fd: {} op: {} fd2: {}", fd, op, fd2); + ax_call_body!(ax_epoll_ctl, { + let ep = get_file_like(fd)?; + let ret = ep + .into_any() + .downcast::() + .map_err(|_| LinuxError::EINVAL)? + .control(op as usize, fd as usize, &c_event_to_rust(ev))? as c_int; + Ok(ret) + }) +} + +/// `ax_epoll_wait()` waits for events on the epoll instance referred to by the file descriptor epfd. +#[no_mangle] +pub unsafe extern "C" fn ax_epoll_wait( + fd: c_int, + events: *mut ctypes::epoll_event, + cnt: c_int, + to: c_int, +) -> c_int { + debug!("ax_epoll_wait <= fd: {}, cnt: {}, to: {}", fd, cnt, to); + + ax_call_body!(ax_epoll_wait, { + let epoll_instance = { + get_file_like(fd) + .unwrap() + .into_any() + .downcast::() + .map_err(|_| LinuxError::EINVAL)? + }; + + { + epoll_instance.ready_list.lock().clear(); + epoll_instance + .ready_list + .lock() + .extend(epoll_instance.new_ctl_list.lock().clone()); + epoll_instance.new_ctl_list.lock().clear(); + } + + // Add already active events into ready_list + for (k, v) in epoll_instance.events.lock().iter() { + if !v.contains(EpollEvent::EPOLLET) { + match get_file_like(*k as i32) { + Err(_) => { + return Err(LinuxError::EINVAL); + } + Ok(file_like) => { + let status = file_like.poll()?; + if status.writable || status.readable { + let mut ready_list = epoll_instance.ready_list.lock(); + ready_list.insert(*k); + } + } + } + } + } + + let start_time = crate::time::Instant::now(); + loop { + let ready_list = { epoll_instance.ready_list.lock().clone() }; + let mut events_num = 0usize; + + for infd in ready_list.iter() { + let mut status: axio::PollState = Default::default(); + { + match get_file_like(*infd as i32) { + Err(e) => return Err(e), + Ok(file_like) => { + let status_ = file_like.poll()?; + status.writable = status_.writable; + status.readable = status_.readable; + } + } + } + + { + let epollevent = epoll_instance.events.lock().get_mut(infd).cloned().unwrap(); + + if status.readable && epollevent.contains(EpollEvent::EPOLLIN) { + (*events.add(events_num)).events = EpollEvent::EPOLLIN; + (*events.add(events_num)).data.u64_ = epollevent.data.ptr; + events_num += 1; + } + + if status.writable && epollevent.contains(EpollEvent::EPOLLOUT) { + (*events.add(events_num)).events = EpollEvent::EPOLLOUT; + (*events.add(events_num)).data.u64_ = epollevent.data.ptr; + events_num += 1; + } + } + } + + epoll_instance.ready_list.lock().clear(); + if events_num > 0 { + return Ok(events_num as c_int); + } + + let total_time = start_time.elapsed().as_micros(); + let to = to as usize; + if to < (1 << 31) && total_time > to as u128 { + return Ok(0); + } + } + }) +} diff --git a/ulib/libax/src/cbindings/mod.rs b/ulib/libax/src/cbindings/mod.rs index 741b15273d..22cb163e30 100644 --- a/ulib/libax/src/cbindings/mod.rs +++ b/ulib/libax/src/cbindings/mod.rs @@ -11,6 +11,8 @@ mod ctypes; #[macro_use] mod utils; +#[cfg(feature = "epoll")] +mod epoll; #[cfg(feature = "alloc")] mod fd_ops; #[cfg(feature = "fs")] @@ -23,6 +25,8 @@ mod malloc; mod pipe; #[cfg(feature = "net")] mod socket; +#[cfg(feature = "fp_simd")] +mod str; #[cfg(feature = "multitask")] mod thread; @@ -80,6 +84,12 @@ pub use self::pipe::ax_pipe; #[cfg(feature = "alloc")] pub use self::io_mpx::ax_select; +#[cfg(feature = "fp_simd")] +pub use self::str::{ax_strtod, ax_strtof}; + +#[cfg(feature = "epoll")] +pub use self::epoll::{ax_epoll_create, ax_epoll_ctl, ax_epoll_wait}; + pub use self::errno::ax_errno_string; pub use self::stdio::{ax_print_str, ax_println_str}; pub use self::sys::ax_sysconf; diff --git a/ulib/libax/src/cbindings/str.rs b/ulib/libax/src/cbindings/str.rs new file mode 100644 index 0000000000..79a5edcae7 --- /dev/null +++ b/ulib/libax/src/cbindings/str.rs @@ -0,0 +1,24 @@ +use core::ffi::{c_char, c_double, c_float, c_int}; + +pub(crate) fn ax_isspace(c: c_int) -> c_int { + c_int::from( + c == c_int::from(b' ') + || c == c_int::from(b'\t') + || c == c_int::from(b'\n') + || c == c_int::from(b'\r') + || c == 0x0b + || c == 0x0c, + ) +} + +/// `strtod` implementation +#[no_mangle] +pub unsafe extern "C" fn ax_strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double { + strto_float_impl!(c_double, s, endptr) +} + +/// `strtof`implementation +#[no_mangle] +pub unsafe extern "C" fn ax_strtof(s: *const c_char, endptr: *mut *mut c_char) -> c_float { + strto_float_impl!(c_float, s, endptr) +} diff --git a/ulib/libax/src/cbindings/utils.rs b/ulib/libax/src/cbindings/utils.rs index c1d5970211..9bd315e634 100644 --- a/ulib/libax/src/cbindings/utils.rs +++ b/ulib/libax/src/cbindings/utils.rs @@ -46,3 +46,112 @@ macro_rules! ax_call_body_no_debug { } }}; } + +macro_rules! strto_float_impl { + ($type:ident, $s:expr, $endptr:expr) => {{ + let mut s = $s; + let endptr = $endptr; + + // TODO: Handle named floats: NaN, Inf... + + while ax_isspace(*s as c_int) != 0 { + s = s.offset(1); + } + + let mut result: $type = 0.0; + let mut radix = 10; + + let result_sign = match *s as u8 { + b'-' => { + s = s.offset(1); + -1.0 + } + b'+' => { + s = s.offset(1); + 1.0 + } + _ => 1.0, + }; + + if *s as u8 == b'0' && *s.offset(1) as u8 == b'x' { + s = s.offset(2); + radix = 16; + } + + while let Some(digit) = (*s as u8 as char).to_digit(radix) { + result *= radix as $type; + result += digit as $type; + s = s.offset(1); + } + + if *s as u8 == b'.' { + s = s.offset(1); + + let mut i = 1.0; + while let Some(digit) = (*s as u8 as char).to_digit(radix) { + i *= radix as $type; + result += digit as $type / i; + s = s.offset(1); + } + } + + let s_before_exponent = s; + + let exponent = match (*s as u8, radix) { + (b'e' | b'E', 10) | (b'p' | b'P', 16) => { + s = s.offset(1); + + let is_exponent_positive = match *s as u8 { + b'-' => { + s = s.offset(1); + false + } + b'+' => { + s = s.offset(1); + true + } + _ => true, + }; + + // Exponent digits are always in base 10. + if (*s as u8 as char).is_digit(10) { + let mut exponent_value = 0; + + while let Some(digit) = (*s as u8 as char).to_digit(10) { + exponent_value *= 10; + exponent_value += digit; + s = s.offset(1); + } + + let exponent_base = match radix { + 10 => 10u128, + 16 => 2u128, + _ => unreachable!(), + }; + + if is_exponent_positive { + Some(exponent_base.pow(exponent_value) as $type) + } else { + Some(1.0 / (exponent_base.pow(exponent_value) as $type)) + } + } else { + // Exponent had no valid digits after 'e'/'p' and '+'/'-', rollback + s = s_before_exponent; + None + } + } + _ => None, + }; + + // Return pointer should be *mut + if !endptr.is_null() { + *endptr = s as *mut _; + } + + if let Some(exponent) = exponent { + result_sign * result * exponent + } else { + result_sign * result + } + }}; +}