Foundations
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
#include <c_File.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <io.h> // 提供 _get_osfhandle
|
||||
#include <direct.h>
|
||||
#define C_ACCESS(path) _access(path, 0)
|
||||
#define C_MAKE_DIR(path) _mkdir(path) // Windows 下创建目录
|
||||
#else
|
||||
#include <unistd.h> // 提供 fsync
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#define C_ACCESS(path) access(path, F_OK)
|
||||
#define C_MAKE_DIR(path) mkdir(path, 0755)
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
#if defined(C_PLATFORM_IS_64BIT)
|
||||
|
||||
// 跨平台 64 位 fseek 封装
|
||||
C_STATIC_FORCE_INLINE
|
||||
int fn_fseek(FILE* fp, long long offset, int whence) {
|
||||
#ifdef _WIN32
|
||||
return _fseeki64(fp, (__int64)offset, whence);
|
||||
#else
|
||||
// 在定义了 _FILE_OFFSET_BITS=64 的情况下,fseeko 接收 64 位的 off_t
|
||||
return fseeko(fp, (off_t)offset, whence);
|
||||
#endif
|
||||
}
|
||||
|
||||
// 跨平台 64 位 ftell 封装
|
||||
C_STATIC_FORCE_INLINE
|
||||
long long fn_tell(FILE* fp) {
|
||||
#ifdef _WIN32
|
||||
return (long long)_ftelli64(fp);
|
||||
#else
|
||||
return (long long)ftello(fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined(C_PLATFORM_IS_32BIT)
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
int fn_fseek(FILE* fp, long long offset, int whence) {
|
||||
return fseek(fp, (long)offset, whence);
|
||||
}
|
||||
|
||||
C_STATIC_FORCE_INLINE
|
||||
long long fn_tell(FILE* fp) {
|
||||
return (long long)ftell(fp);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
|
||||
c_err_t c_File_Open(c_File_t* file, const char* fileName, const char* mode) {
|
||||
if (!file || !fileName || !mode) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
file->fp = fopen(fileName, mode);
|
||||
if (!file->fp) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
void c_File_Close(c_File_t* file) {
|
||||
if (file && file->fp) {
|
||||
fclose(file->fp);
|
||||
file->fp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
c_err_t c_File_Read(c_File_t* file, void* buffer, c_size_t buffer_size, c_size_t *read_size) {
|
||||
if (!file || !file->fp || !buffer || buffer_size == 0) {
|
||||
if (read_size) *read_size = 0;
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
// 从文件流中读取 1 字节 * buffer_size
|
||||
size_t bytes_read = fread(buffer, 1, (size_t)buffer_size, file->fp);
|
||||
|
||||
if (read_size) {
|
||||
*read_size = (c_size_t)bytes_read;
|
||||
}
|
||||
|
||||
// 如果读取数量小于请求数量,需检查是出错还是到达文件末尾
|
||||
if (bytes_read < (size_t)buffer_size) {
|
||||
if (ferror(file->fp)) {
|
||||
return C_ERR_FAIL; // 发生读取错误
|
||||
}
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_File_Readline(c_File_t* file, void* buffer, c_size_t buffer_size, c_size_t *read_size) {
|
||||
if (!file || !file->fp || !buffer || buffer_size == 0) {
|
||||
if (read_size) *read_size = 0;
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
// fgets 会自动在末尾加上 '\0',并且最多读取 buffer_size - 1 个字符
|
||||
char* result = fgets((char*)buffer, (int)buffer_size, file->fp);
|
||||
|
||||
if (!result) {
|
||||
if (read_size) *read_size = 0;
|
||||
return C_ERR_FAIL; // 读取失败或到达文件末尾
|
||||
}
|
||||
|
||||
if (read_size) {
|
||||
*read_size = (c_size_t)strlen((char*)buffer);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_File_Write(c_File_t* file, void* buffer, c_size_t buffer_size, c_size_t *write_size) {
|
||||
if (!file || !file->fp || !buffer || buffer_size == 0) {
|
||||
if (write_size) *write_size = 0;
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
size_t bytes_written = fwrite(buffer, 1, (size_t)buffer_size, file->fp);
|
||||
|
||||
if (write_size) {
|
||||
*write_size = (c_size_t)bytes_written;
|
||||
}
|
||||
|
||||
// 如果实际写入字节数不等于预期,说明写入失败(如磁盘满)
|
||||
if (bytes_written < (size_t)buffer_size) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_File_Seek(c_File_t* file, long long position) {
|
||||
if (!file || !file->fp) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
const int result = fseek(file->fp, (long)position, SEEK_SET);
|
||||
return (result == 0) ? C_ERR_OK : C_ERR_FAIL;
|
||||
}
|
||||
|
||||
long long c_File_Size(c_File_t* file) {
|
||||
if (!file || !file->fp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 1. 保存当前文件指针的位置
|
||||
long long current_pos = fn_tell(file->fp);
|
||||
if (current_pos == -1LL) return 0;
|
||||
|
||||
// 2. 将文件指针移到末尾
|
||||
if (fn_fseek(file->fp, 0, SEEK_END) != C_ERR_OK) return 0;
|
||||
|
||||
// 3. 获取末尾位置即为文件大小
|
||||
long long size = fn_tell(file->fp);
|
||||
|
||||
// 4. 恢复原先的文件指针位置
|
||||
fn_fseek(file->fp, current_pos, SEEK_SET);
|
||||
|
||||
return (long long)((size < 0) ? 0 : size);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_File_Flush(c_File_t* file) {
|
||||
if (!file || !file->fp) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
// Step 1: 先刷新 C 库自带的软件层缓冲区(将数据推入 OS 内核队列)
|
||||
if (fflush(file->fp) != 0) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
// Step 2: 获取底层文件描述符,强行驱动硬件刷盘
|
||||
#ifdef _WIN32
|
||||
// 从 C 语言的 FILE* 提取 Windows 系统的文件句柄 HANDLE
|
||||
int fd = _fileno(file->fp);
|
||||
if (fd < 0) return C_ERR_FAIL;
|
||||
|
||||
HANDLE hFile = (HANDLE)_get_osfhandle(fd);
|
||||
if (hFile == INVALID_HANDLE_VALUE) return C_ERR_FAIL;
|
||||
|
||||
// 强行驱动机械硬盘/SSD硬件将内核缓存写入物理介质
|
||||
if (!FlushFileBuffers(hFile)) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
#else
|
||||
// Linux / macOS 平台直接提取文件描述符
|
||||
int fd = fileno(file->fp);
|
||||
if (fd < 0) return C_ERR_FAIL;
|
||||
|
||||
// 触发 POSIX 的 fsync 系统调用,阻塞直到硬件写入完毕
|
||||
if (fsync(fd) != 0) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_bool_t c_File_IsExist(const char* fileName) {
|
||||
if (!fileName) {
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
// 参数 0 代表 F_OK,即只检查文件是否存在
|
||||
// C_ACCESS 返回 0 表示文件存在且可访问,返回 -1 表示不存在或无权限
|
||||
if (C_ACCESS(fileName) == F_OK) {
|
||||
return C_TRUE;
|
||||
}
|
||||
|
||||
return C_FALSE;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/* */
|
||||
|
||||
c_err_t c_File_MkDirs(const char* path) {
|
||||
if (path == NULL || strlen(path) == 0) {
|
||||
return C_ERR_PARAM;
|
||||
}
|
||||
|
||||
char temp_path[512];
|
||||
snprintf(temp_path, sizeof(temp_path), "%s", path);
|
||||
size_t len = strlen(temp_path);
|
||||
|
||||
// 统一将 Windows 的 '\\' 转换为 '/' 方便逐级切分
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (temp_path[i] == '\\') {
|
||||
temp_path[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
// 确保路径以 '/' 结尾,方便循环逻辑处理
|
||||
if (temp_path[len - 1] != '/') {
|
||||
if (len + 1 < sizeof(temp_path)) {
|
||||
temp_path[len] = '/';
|
||||
temp_path[len + 1] = '\0';
|
||||
len++;
|
||||
} else {
|
||||
return C_ERR_FAIL; // 路径超长
|
||||
}
|
||||
}
|
||||
|
||||
// 逐级探测并创建目录
|
||||
for (int i = 0; i < len; i++) {
|
||||
// 每当遇到路径分隔符 '/' 时,尝试截断并创建当前层级目录
|
||||
if (temp_path[i] == '/' && i > 0) {
|
||||
temp_path[i] = '\0'; // 临时截断字符串
|
||||
|
||||
// 检查当前级目录是否存在,不存在则创建
|
||||
if (C_ACCESS(temp_path) != 0) {
|
||||
if (C_MAKE_DIR(temp_path) != 0) {
|
||||
return C_ERR_FAIL; // 创建失败
|
||||
}
|
||||
}
|
||||
|
||||
temp_path[i] = '/'; // 恢复分隔符
|
||||
}
|
||||
}
|
||||
|
||||
return C_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user