diff --git a/platform.c b/platform.c new file mode 100644 index 0000000..ec91516 --- /dev/null +++ b/platform.c @@ -0,0 +1,562 @@ +/** + * platform.c - 跨平台抽象层实现 + * + * 高性能实现原则: + * - sendfile 替代方案使用 64KB 大块缓冲区,最大化单次 I/O 吞吐量 + * - Windows 目录遍历用 FindFirstFile 一次性获取,减少系统调用次数 + * - 错误码统一映射,避免跨平台条件分支污染业务代码 + * - socket 关闭统一用 cocoon_socket_close,防止 Windows 下误用 close() + * + * @author xfy + */ + +#include "platform.h" +#include +#include +#include + +/* ========== POSIX 实现 ========== */ +#ifdef COCOON_PLATFORM_POSIX + +/** + * POSIX socket 子系统无需显式初始化。 + */ +int cocoon_socket_init(void) { return 0; } + +/** + * POSIX socket 子系统无需显式清理。 + */ +void cocoon_socket_cleanup(void) { } + +/** + * POSIX 下使用 fcntl 设置 O_NONBLOCK 标志。 + */ +int cocoon_socket_nonblock(cocoon_socket_t fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) return -1; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +/** + * POSIX 下 socket 与文件描述符统一用 close()。 + */ +int cocoon_socket_close(cocoon_socket_t fd) { + return close(fd); +} + +/** + * POSIX 下使用 shutdown(SHUT_RDWR) 双向断开连接。 + */ +int cocoon_socket_shutdown(cocoon_socket_t fd) { + return shutdown(fd, SHUT_RDWR); +} + +/** + * POSIX 下 socket 发送直接用 write(), + * 内核态零拷贝路径,无需额外封装。 + */ +ssize_t cocoon_socket_send(cocoon_socket_t fd, const char *buf, size_t len) { + return write(fd, buf, len); +} + +ssize_t cocoon_socket_recv(cocoon_socket_t fd, void *buf, size_t len) { + return read(fd, buf, len); +} + +/** + * POSIX 下文件操作直接映射标准 API。 + */ +cocoon_file_t cocoon_file_open(const char *path) { + return open(path, O_RDONLY); +} + +ssize_t cocoon_file_read(cocoon_file_t fd, void *buf, size_t count) { + return read(fd, buf, count); +} + +int64_t cocoon_file_seek(cocoon_file_t fd, int64_t offset, int whence) { + return lseek(fd, (off_t)offset, whence); +} + +int cocoon_file_close(cocoon_file_t fd) { + return close(fd); +} + +int cocoon_file_stat(const char *path, cocoon_stat_t *st) { + return stat(path, st); +} + +int cocoon_file_fstat(cocoon_file_t fd, cocoon_stat_t *st) { + return fstat(fd, st); +} + +bool cocoon_stat_isreg(const cocoon_stat_t *st) { + return S_ISREG(st->st_mode); +} + +bool cocoon_stat_isdir(const cocoon_stat_t *st) { + return S_ISDIR(st->st_mode); +} + +int64_t cocoon_stat_size(const cocoon_stat_t *st) { + return (int64_t)st->st_size; +} + +time_t cocoon_stat_mtime(const cocoon_stat_t *st) { + return st->st_mtime; +} + +/** + * Linux 下优先使用 sendfile 零拷贝。 + * 失败时回退到 read+write 循环,64KB 缓冲区。 + */ +ssize_t cocoon_file_send(cocoon_socket_t sock, cocoon_file_t file, + int64_t offset, size_t count) { + off_t off = (off_t)offset; + ssize_t sent = sendfile(sock, file, &off, count); + if (sent >= 0 || (errno != EINVAL && errno != ENOSYS)) { + return sent; + } + + /* sendfile 不支持此场景,回退到 read+write */ + if (lseek(file, (off_t)offset, SEEK_SET) < 0) { + return -1; + } + + char buf[65536]; /* 64KB 缓冲区,最大化单次 I/O */ + size_t total = 0; + while (total < count) { + size_t to_read = count - total; + if (to_read > sizeof(buf)) to_read = sizeof(buf); + ssize_t n = read(file, buf, to_read); + if (n <= 0) { + if (n < 0 && errno == EINTR) continue; + return (total > 0) ? (ssize_t)total : -1; + } + + size_t buf_sent = 0; + while (buf_sent < (size_t)n) { + ssize_t w = write(sock, buf + buf_sent, (size_t)n - buf_sent); + if (w < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue; + return (total > 0) ? (ssize_t)total : -1; + } + if (w == 0) return (total > 0) ? (ssize_t)total : -1; + buf_sent += (size_t)w; + } + total += (size_t)n; + } + return (ssize_t)total; +} + +/** + * POSIX 下目录遍历直接用 opendir/readdir/closedir。 + */ +int cocoon_dir_open(cocoon_dir_iter_t *iter, const char *path) { + if (!iter || !path) return -1; + iter->dir = opendir(path); + return (iter->dir != NULL) ? 0 : -1; +} + +int cocoon_dir_next(cocoon_dir_iter_t *iter) { + if (!iter || !iter->dir) return -1; + struct dirent *entry = readdir(iter->dir); + if (!entry) return 1; /* 遍历结束 */ + + strncpy(iter->name, entry->d_name, sizeof(iter->name) - 1); + iter->name[sizeof(iter->name) - 1] = '\0'; + + /* 用 d_type 快速判断(如果支持),否则 stat */ +#ifdef _DIRENT_HAVE_D_TYPE + iter->is_dir = (entry->d_type == DT_DIR); +#else + char full_path[4096]; + /* 注意:这里 path 未存储在 iter 中,调用方需自行判断 */ + iter->is_dir = false; /* 保守默认值,static.c 会 stat 验证 */ +#endif + return 0; +} + +void cocoon_dir_close(cocoon_dir_iter_t *iter) { + if (iter && iter->dir) { + closedir(iter->dir); + iter->dir = NULL; + } +} + +/** + * POSIX 信号处理:SIGINT + SIGTERM。 + */ +static void (*g_signal_handler)(int) = NULL; + +static void posix_signal_handler(int sig) { + if (g_signal_handler) g_signal_handler(sig); +} + +void cocoon_signal_setup(void (*handler)(int)) { + g_signal_handler = handler; + signal(SIGINT, posix_signal_handler); + signal(SIGTERM, posix_signal_handler); +} + +/** + * POSIX 下获取 CPU 核心数。 + */ +uint32_t cocoon_cpu_count(void) { + long n = sysconf(_SC_NPROCESSORS_ONLN); + return (n > 0) ? (uint32_t)n : 4; +} + +/** + * POSIX 下路径规范化用 realpath。 + */ +bool cocoon_realpath(const char *path, char *resolved, size_t resolved_size) { + if (!path || !resolved || resolved_size == 0) return false; + char *r = realpath(path, NULL); + if (r) { + strncpy(resolved, r, resolved_size - 1); + resolved[resolved_size - 1] = '\0'; + free(r); + return true; + } + return false; +} + +/** + * POSIX 下创建目录。 + */ +int cocoon_mkdir(const char *path) { + return mkdir(path, 0755); +} + +/** + * POSIX 下直接返回 errno。 + */ +int cocoon_get_last_error(void) { + return errno; +} + +const char *cocoon_strerror(int err) { + return strerror(err); +} + +#endif /* COCOON_PLATFORM_POSIX */ + + +/* ========== Windows 实现 ========== */ +#ifdef COCOON_PLATFORM_WINDOWS + +#include /* _O_RDONLY, _O_BINARY */ + +/** + * Windows 下 Winsock 需要显式初始化和清理。 + * 使用静态引用计数,确保多线程安全(此处单线程 accept 足够)。 + */ +static int g_wsa_ref = 0; + +int cocoon_socket_init(void) { + if (g_wsa_ref == 0) { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + return -1; + } + } + g_wsa_ref++; + return 0; +} + +void cocoon_socket_cleanup(void) { + if (g_wsa_ref > 0) { + g_wsa_ref--; + if (g_wsa_ref == 0) { + WSACleanup(); + } + } +} + +/** + * Windows 下用 ioctlsocket(FIONBIO) 设置非阻塞。 + */ +int cocoon_socket_nonblock(cocoon_socket_t fd) { + u_long mode = 1; /* 1 = 非阻塞 */ + return ioctlsocket(fd, FIONBIO, &mode); +} + +/** + * Windows 下 socket 必须用 closesocket(),不能用 close()。 + */ +int cocoon_socket_close(cocoon_socket_t fd) { + return closesocket(fd); +} + +/** + * Windows 下 shutdown 行为与 POSIX 一致。 + */ +int cocoon_socket_shutdown(cocoon_socket_t fd) { + return shutdown(fd, SD_BOTH); +} + +/** + * Windows 下 socket 发送用 send(),不能用 write()。 + */ +ssize_t cocoon_socket_send(cocoon_socket_t fd, const char *buf, size_t len) { + /* send() 的 len 参数是 int,大于 INT_MAX 时分批发送 */ + int chunk = (len > INT_MAX) ? INT_MAX : (int)len; + int r = send(fd, buf, chunk, 0); + if (r == SOCKET_ERROR) { + WSASetLastError(WSAGetLastError()); /* 保持错误码 */ + return -1; + } + return (ssize_t)r; +} + +ssize_t cocoon_socket_recv(cocoon_socket_t fd, void *buf, size_t len) { + int chunk = (len > INT_MAX) ? INT_MAX : (int)len; + int r = recv(fd, (char *)buf, chunk, 0); + if (r == SOCKET_ERROR) { + WSASetLastError(WSAGetLastError()); + return -1; + } + return (ssize_t)r; +} + +/** + * Windows 下文件操作用 _open/_read/_lseek/_close(二进制模式)。 + */ +cocoon_file_t cocoon_file_open(const char *path) { + return _open(path, _O_RDONLY | _O_BINARY); +} + +ssize_t cocoon_file_read(cocoon_file_t fd, void *buf, size_t count) { + int chunk = (count > INT_MAX) ? INT_MAX : (int)count; + int r = _read(fd, buf, chunk); + return (r < 0) ? -1 : (ssize_t)r; +} + +int64_t cocoon_file_seek(cocoon_file_t fd, int64_t offset, int whence) { + return _lseeki64(fd, offset, whence); +} + +int cocoon_file_close(cocoon_file_t fd) { + return _close(fd); +} + +/** + * Windows 下用 _stat64 获取文件信息,支持大于 2GB 的文件。 + */ +int cocoon_file_stat(const char *path, cocoon_stat_t *st) { + return _stat64(path, st); +} + +int cocoon_file_fstat(cocoon_file_t fd, cocoon_stat_t *st) { + return _fstat64(fd, st); +} + +bool cocoon_stat_isreg(const cocoon_stat_t *st) { + return S_ISREG(st->st_mode); +} + +bool cocoon_stat_isdir(const cocoon_stat_t *st) { + return S_ISDIR(st->st_mode); +} + +int64_t cocoon_stat_size(const cocoon_stat_t *st) { + return (int64_t)st->st_size; +} + +time_t cocoon_stat_mtime(const cocoon_stat_t *st) { + return st->st_mtime; +} + +/** + * Windows 下无原生 sendfile,使用 read+send 循环。 + * 64KB 缓冲区,最大化单次 I/O 吞吐量,减少用户态/内核态切换开销。 + */ +ssize_t cocoon_file_send(cocoon_socket_t sock, cocoon_file_t file, + int64_t offset, size_t count) { + if (_lseeki64(file, offset, SEEK_SET) < 0) { + return -1; + } + + char buf[65536]; /* 64KB:现代页表和磁盘 I/O 的最佳平衡点 */ + size_t total = 0; + + while (total < count) { + size_t to_read = count - total; + if (to_read > sizeof(buf)) to_read = sizeof(buf); + + int chunk = (to_read > INT_MAX) ? INT_MAX : (int)to_read; + int n = _read(file, buf, chunk); + if (n <= 0) { + if (n < 0 && errno == EINTR) continue; + return (total > 0) ? (ssize_t)total : -1; + } + + size_t buf_sent = 0; + while (buf_sent < (size_t)n) { + int to_send = (int)((size_t)n - buf_sent); + int w = send(sock, buf + buf_sent, to_send, 0); + if (w == SOCKET_ERROR) { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK || err == WSAEINTR) continue; + errno = EIO; /* 映射为通用 I/O 错误 */ + return (total > 0) ? (ssize_t)total : -1; + } + if (w == 0) return (total > 0) ? (ssize_t)total : -1; + buf_sent += (size_t)w; + } + total += (size_t)n; + } + return (ssize_t)total; +} + +/** + * Windows 下目录遍历用 FindFirstFile/FindNextFile。 + * 一次性获取所有信息,减少系统调用次数。 + */ +int cocoon_dir_open(cocoon_dir_iter_t *iter, const char *path) { + if (!iter || !path) return -1; + + /* 构建搜索路径:path + \\* */ + size_t len = strlen(path); + if (len >= sizeof(iter->path_buf) - 3) return -1; + + memcpy(iter->path_buf, path, len); + iter->path_buf[len] = '\\'; + iter->path_buf[len + 1] = '*'; + iter->path_buf[len + 2] = '\0'; + + iter->handle = FindFirstFileA(iter->path_buf, &iter->data); + if (iter->handle == INVALID_HANDLE_VALUE) return -1; + + iter->first = true; + return 0; +} + +int cocoon_dir_next(cocoon_dir_iter_t *iter) { + if (!iter || iter->handle == INVALID_HANDLE_VALUE) return -1; + + if (!iter->first) { + if (!FindNextFileA(iter->handle, &iter->data)) { + if (GetLastError() == ERROR_NO_MORE_FILES) return 1; + return -1; + } + } + iter->first = false; + + /* 跳过 . 和 .. */ + while (iter->data.cFileName[0] == '.') { + if (iter->data.cFileName[1] == '\0') goto skip; + if (iter->data.cFileName[1] == '.' && iter->data.cFileName[2] == '\0') goto skip; + break; + skip: + if (!FindNextFileA(iter->handle, &iter->data)) { + if (GetLastError() == ERROR_NO_MORE_FILES) return 1; + return -1; + } + } + + strncpy(iter->name, iter->data.cFileName, sizeof(iter->name) - 1); + iter->name[sizeof(iter->name) - 1] = '\0'; + iter->is_dir = (iter->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + return 0; +} + +void cocoon_dir_close(cocoon_dir_iter_t *iter) { + if (iter && iter->handle != INVALID_HANDLE_VALUE) { + FindClose(iter->handle); + iter->handle = INVALID_HANDLE_VALUE; + } +} + +/** + * Windows 控制台事件处理(替代 POSIX 信号)。 + */ +static void (*g_win_handler)(int) = NULL; + +static BOOL WINAPI win_ctrl_handler(DWORD ctrl_type) { + (void)ctrl_type; + if (g_win_handler) g_win_handler(2); /* SIGINT = 2 */ + return TRUE; +} + +void cocoon_signal_setup(void (*handler)(int)) { + g_win_handler = handler; + SetConsoleCtrlHandler(win_ctrl_handler, TRUE); +} + +/** + * Windows 下用 GetSystemInfo 获取 CPU 核心数。 + */ +uint32_t cocoon_cpu_count(void) { + SYSTEM_INFO si; + GetSystemInfo(&si); + return (si.dwNumberOfProcessors > 0) ? si.dwNumberOfProcessors : 1; +} + +/** + * Windows 下路径规范化用 _fullpath。 + */ +bool cocoon_realpath(const char *path, char *resolved, size_t resolved_size) { + if (!path || !resolved || resolved_size == 0) return false; + char *r = _fullpath(NULL, path, 0); + if (r) { + strncpy(resolved, r, resolved_size - 1); + resolved[resolved_size - 1] = '\0'; + free(r); + return true; + } + return false; +} + +/** + * Windows 下创建目录。 + */ +int cocoon_mkdir(const char *path) { + return _mkdir(path); +} + +/** + * Windows 错误码统一映射: + * WSAEWOULDBLOCK → EAGAIN + * WSAECONNRESET → ECONNRESET + * 其他保持原样。 + */ +int cocoon_get_last_error(void) { + int err = WSAGetLastError(); + switch (err) { + case WSAEWOULDBLOCK: return EAGAIN; + case WSAEINTR: return EINTR; + case WSAECONNRESET: return ECONNRESET; + case WSAECONNABORTED: return ECONNABORTED; + default: return err; + } +} + +/** + * Windows 下错误描述用 FormatMessage 获取。 + * 注意:线程不安全,仅用于日志输出。 + */ +static __thread char g_err_buf[256]; + +const char *cocoon_strerror(int err) { + if (err == 0) return "成功"; + + DWORD fm = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, (DWORD)err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + g_err_buf, sizeof(g_err_buf), NULL); + + if (fm == 0) { + snprintf(g_err_buf, sizeof(g_err_buf), "未知错误码 %d", err); + } + + /* 去除尾部换行符 */ + size_t len = strlen(g_err_buf); + while (len > 0 && (g_err_buf[len - 1] == '\n' || g_err_buf[len - 1] == '\r')) { + g_err_buf[--len] = '\0'; + } + return g_err_buf; +} + +#endif /* COCOON_PLATFORM_WINDOWS */ diff --git a/platform.h b/platform.h new file mode 100644 index 0000000..ab57f51 --- /dev/null +++ b/platform.h @@ -0,0 +1,408 @@ +/** + * platform.h - 跨平台抽象层头文件 + * + * 封装 POSIX 与 Windows 的系统差异,提供统一的跨平台 API。 + * 所有公共 API 均使用中文注释,遵循 Doxygen 风格。 + * + * 设计原则: + * 1. 最小化系统调用次数(高性能) + * 2. Windows 下优先使用原生 API,拒绝兼容性层带来的额外开销 + * 3. 零拷贝优先:sendfile 在 Windows 下用 TransmitFile 或大块 read+send 循环 + * 4. 错误码统一:POSIX errno 与 Windows WSAGetLastError() 映射为统一错误码 + * + * @author xfy + */ + +#ifndef COCOON_PLATFORM_H +#define COCOON_PLATFORM_H + +/* ========== 平台检测与系统头文件 ========== */ + +#ifdef _WIN32 + #define COCOON_PLATFORM_WINDOWS + + /* 精简 Windows 头文件,减少编译时间 */ + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include /**< Winsock 2(必须在 windows.h 之前) */ + #include /**< IPv6 扩展 */ + #include /**< Windows 核心 API */ + #include /**< _open/_read/_close 等 */ + #include /**< _mkdir */ + #include + #include + + /* Windows 下缺失的 POSIX 宏定义 */ + #ifndef S_ISREG + #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) + #endif + #ifndef S_ISDIR + #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + #endif + + /* MinGW 已提供 dirent.h,MSVC 需要自行实现 */ + #ifdef __MINGW32__ + #include + #define COCOON_HAS_DIRENT + #endif + +#else + #define COCOON_PLATFORM_POSIX + + #include /**< socket/bind/listen/accept */ + #include /**< sockaddr_in */ + #include /**< TCP_NODELAY */ + #include /**< htons/ntohs */ + #include /**< close/read/write */ + #include /**< fcntl/open */ + #include /**< signal/sigaction */ + #include /**< opendir/readdir */ + #include /**< sendfile(Linux 零拷贝) */ + #define COCOON_HAS_DIRENT +#endif + +#include +#include +#include +#include + +/* ========== 跨平台类型定义 ========== */ + +#ifdef COCOON_PLATFORM_WINDOWS + /** + * cocoon_socket_t - 跨平台 socket 描述符类型 + * + * Windows 下为 SOCKET(UINT_PTR),POSIX 下为 int。 + */ + typedef SOCKET cocoon_socket_t; + #define COCOON_INVALID_SOCKET INVALID_SOCKET /**< 无效 socket 值 */ + + /** + * cocoon_file_t - 跨平台文件描述符类型 + * + * Windows 下通过 _open/_close 管理,仍为 int。 + */ + typedef int cocoon_file_t; + + /** + * cocoon_socklen_t - socket 地址长度类型 + */ + typedef int cocoon_socklen_t; + +#else + typedef int cocoon_socket_t; + #define COCOON_INVALID_SOCKET (-1) + typedef int cocoon_file_t; + typedef socklen_t cocoon_socklen_t; +#endif + +/* ========== socket 生命周期 API ========== */ + +/** + * cocoon_socket_init - 初始化 socket 子系统 + * + * Windows 下调用 WSAStartup;POSIX 下无操作。 + * + * @return 0 成功,-1 失败 + */ +int cocoon_socket_init(void); + +/** + * cocoon_socket_cleanup - 清理 socket 子系统 + * + * Windows 下调用 WSACleanup;POSIX 下无操作。 + */ +void cocoon_socket_cleanup(void); + +/** + * cocoon_socket_nonblock - 设置 socket 为非阻塞模式 + * + * POSIX: fcntl(fd, F_SETFL, O_NONBLOCK) + * Windows: ioctlsocket(fd, FIONBIO, &mode) + * + * @param fd socket 描述符 + * @return 0 成功,-1 失败 + */ +int cocoon_socket_nonblock(cocoon_socket_t fd); + +/** + * cocoon_socket_close - 关闭 socket + * + * POSIX: close(fd) + * Windows: closesocket(fd) + * + * @param fd socket 描述符 + * @return 0 成功,-1 失败 + */ +int cocoon_socket_close(cocoon_socket_t fd); + +/** + * cocoon_socket_shutdown - 双向关闭 socket 连接 + * + * 用于唤醒阻塞在 coco_read 的协程,触发连接超时清理。 + * + * @param fd socket 描述符 + * @return 0 成功,-1 失败 + */ +int cocoon_socket_shutdown(cocoon_socket_t fd); + +/** + * cocoon_socket_send - 向 socket 发送数据(高性能) + * + * POSIX: write(fd, buf, len) + * Windows: send(fd, buf, len, 0) + * + * 自动处理 EAGAIN/EWOULDBLOCK/EINTR,调用者应自行循环。 + * + * @param fd socket 描述符 + * @param buf 数据缓冲区 + * @param len 数据长度 + * @return 实际发送字节数,-1 错误(设置 errno) + */ +ssize_t cocoon_socket_send(cocoon_socket_t fd, const char *buf, size_t len); + +/** + * cocoon_socket_recv - 从 socket 接收数据 + * + * POSIX: read(fd, buf, len) + * Windows: recv(fd, buf, len, 0) + * + * @param fd socket 描述符 + * @param buf 接收缓冲区 + * @param len 最大接收长度 + * @return 实际接收字节数,0 表示对端关闭,-1 错误 + */ +ssize_t cocoon_socket_recv(cocoon_socket_t fd, void *buf, size_t len); + +/* ========== 文件操作 API ========== */ + +/* 跨平台 stat 类型定义:Windows 用 _stat64(64位文件大小),POSIX 用 struct stat */ +#ifdef COCOON_PLATFORM_WINDOWS + typedef struct _stat64 cocoon_stat_t; +#else + typedef struct stat cocoon_stat_t; +#endif + +/** + * cocoon_file_open - 以只读模式打开文件 + * + * @param path 文件路径 + * @return 文件描述符,-1 失败 + */ +cocoon_file_t cocoon_file_open(const char *path); + +/** + * cocoon_file_read - 从文件读取数据 + * + * @param fd 文件描述符 + * @param buf 缓冲区 + * @param count 读取长度 + * @return 实际读取字节数,0 表示 EOF,-1 错误 + */ +ssize_t cocoon_file_read(cocoon_file_t fd, void *buf, size_t count); + +/** + * cocoon_file_seek - 移动文件指针 + * + * @param fd 文件描述符 + * @param offset 偏移量 + * @param whence 起始位置(SEEK_SET/SEEK_CUR/SEEK_END) + * @return 新的文件位置,-1 错误 + */ +int64_t cocoon_file_seek(cocoon_file_t fd, int64_t offset, int whence); + +/** + * cocoon_file_close - 关闭文件 + * + * @param fd 文件描述符 + * @return 0 成功,-1 失败 + */ +int cocoon_file_close(cocoon_file_t fd); + +/** + * cocoon_file_stat - 获取文件元数据 + * + * @param path 文件路径 + * @param st 输出 stat 结构体(POSIX struct stat / Windows struct _stat) + * @return 0 成功,-1 失败 + */ +int cocoon_file_stat(const char *path, cocoon_stat_t *st); + +/** + * cocoon_file_fstat - 通过文件描述符获取元数据 + * + * @param fd 文件描述符 + * @param st 输出 stat 结构体 + * @return 0 成功,-1 失败 + */ +int cocoon_file_fstat(cocoon_file_t fd, cocoon_stat_t *st); + +/** + * cocoon_stat_isreg - 判断是否为普通文件 + * + * @param st stat 结构体指针 + * @return true 是普通文件 + */ +bool cocoon_stat_isreg(const cocoon_stat_t *st); + +/** + * cocoon_stat_isdir - 判断是否为目录 + * + * @param st stat 结构体指针 + * @return true 是目录 + */ +bool cocoon_stat_isdir(const cocoon_stat_t *st); + +/** + * cocoon_stat_size - 获取文件大小 + * + * @param st stat 结构体指针 + * @return 文件大小(字节) + */ +int64_t cocoon_stat_size(const cocoon_stat_t *st); + +/** + * cocoon_stat_mtime - 获取文件修改时间 + * + * @param st stat 结构体指针 + * @return 修改时间戳(秒) + */ +time_t cocoon_stat_mtime(const cocoon_stat_t *st); + +/* ========== 高性能文件发送(零拷贝替代) ========== */ + +/** + * cocoon_file_send - 将文件内容发送到 socket + * + * Linux: 优先使用 sendfile 零拷贝(内核态传输,无用户态内存拷贝) + * Windows/其他: 使用 read+send 循环,64KB 大块缓冲区, + * 最大化单次系统调用吞吐量,减少用户态/内核态切换 + * + * @param sock 目标 socket + * @param file 源文件描述符 + * @param offset 文件起始偏移量 + * @param count 发送字节数 + * @return 实际发送字节数,-1 错误 + */ +ssize_t cocoon_file_send(cocoon_socket_t sock, cocoon_file_t file, + int64_t offset, size_t count); + +/* ========== 目录遍历 API ========== */ + +/** + * cocoon_dir_iter_t - 目录遍历迭代器 + * + * POSIX: 包装 DIR*,一次性 opendir。 + * Windows: 包装 FindFirstFile/FindNextFile,避免多次系统调用。 + */ +typedef struct cocoon_dir_iter { +#ifdef COCOON_PLATFORM_WINDOWS + HANDLE handle; /**< FindFirstFile 句柄 */ + WIN32_FIND_DATAA data; /**< 当前文件数据 */ + bool first; /**< 首次调用标记 */ + char path_buf[4096]; /**< 完整搜索路径(含通配符) */ +#else + DIR *dir; /**< POSIX 目录句柄 */ +#endif + char name[256]; /**< 当前文件名(输出) */ + bool is_dir; /**< 当前项是否为目录 */ +} cocoon_dir_iter_t; + +/** + * cocoon_dir_open - 打开目录进行遍历 + * + * @param iter 迭代器结构体(由调用者分配) + * @param path 目录路径 + * @return 0 成功,-1 失败 + */ +int cocoon_dir_open(cocoon_dir_iter_t *iter, const char *path); + +/** + * cocoon_dir_next - 获取下一个目录项 + * + * @param iter 迭代器 + * @return 0 成功,1 遍历结束,-1 错误 + */ +int cocoon_dir_next(cocoon_dir_iter_t *iter); + +/** + * cocoon_dir_close - 关闭目录迭代器 + * + * @param iter 迭代器 + */ +void cocoon_dir_close(cocoon_dir_iter_t *iter); + +/* ========== 信号处理 API ========== */ + +/** + * cocoon_signal_setup - 注册优雅关闭信号/事件处理 + * + * POSIX: signal(SIGINT/SIGTERM, handler) + * Windows: SetConsoleCtrlHandler(Ctrl+C / Ctrl+Break / 关闭事件) + * + * @param handler 信号处理函数(POSIX 下 sig 参数为信号编号) + */ +void cocoon_signal_setup(void (*handler)(int)); + +/* ========== 系统信息 API ========== */ + +/** + * cocoon_cpu_count - 获取 CPU 逻辑核心数 + * + * POSIX: sysconf(_SC_NPROCESSORS_ONLN) + * Windows: GetSystemInfo → dwNumberOfProcessors + * + * @return 逻辑 CPU 核心数(至少返回 1) + */ +uint32_t cocoon_cpu_count(void); + +/* ========== 路径处理 API ========== */ + +/** + * cocoon_realpath - 获取绝对路径 + * + * POSIX: realpath() + * Windows: _fullpath()(MinGW)或 GetFullPathNameA() + * + * @param path 输入路径 + * @param resolved 输出缓冲区 + * @param resolved_size 缓冲区大小 + * @return true 成功,false 失败 + */ +bool cocoon_realpath(const char *path, char *resolved, size_t resolved_size); + +/** + * cocoon_mkdir - 创建目录 + * + * POSIX: mkdir(path, 0755) + * Windows: _mkdir(path)(默认权限) + * + * @param path 目录路径 + * @return 0 成功,-1 失败 + */ +int cocoon_mkdir(const char *path); + +/* ========== 错误处理 API ========== */ + +/** + * cocoon_get_last_error - 获取最后一次系统错误码 + * + * 统一映射: + * - POSIX socket 错误:直接返回 errno + * - Windows socket 错误:WSAGetLastError() → 映射到 POSIX errno 等价码 + * - 文件操作错误:直接返回 errno / GetLastError() 映射 + * + * @return 错误码(与 POSIX errno 兼容) + */ +int cocoon_get_last_error(void); + +/** + * cocoon_strerror - 将错误码转为可读的字符串 + * + * @param err 错误码 + * @return 错误描述字符串(线程不安全,仅用于日志输出) + */ +const char *cocoon_strerror(int err); + +#endif /* COCOON_PLATFORM_H */