Ancient
view release on metacpan or search on metacpan
xs/file/file.c view on Meta::CPAN
char *path; /* File path (for reopening) */
} LineIterEntry;
static LineIterEntry *g_iters = NULL;
static IV g_iters_size = 0;
static IV g_iters_count = 0;
static IV *g_free_iters = NULL;
static IV g_free_iters_size = 0;
static IV g_free_iters_count = 0;
/* ============================================
Initialization
============================================ */
static int file_initialized = 0;
/* Forward declaration for callback registry init */
static void file_init_callback_registry(pTHX);
static void file_init(pTHX) {
if (file_initialized) return;
g_mmaps_size = 16;
Newxz(g_mmaps, g_mmaps_size, MmapEntry);
g_free_mmaps_size = 16;
Newxz(g_free_mmaps, g_free_mmaps_size, IV);
g_iters_size = 16;
Newxz(g_iters, g_iters_size, LineIterEntry);
g_free_iters_size = 16;
Newxz(g_free_iters, g_free_iters_size, IV);
/* Initialize callback registry with built-in predicates */
file_init_callback_registry(aTHX);
file_initialized = 1;
}
/* ============================================
Fast slurp - read entire file into SV
============================================ */
static SV* file_slurp_internal(pTHX_ const char *path) {
int fd;
struct stat st;
SV *result;
char *buf;
ssize_t total = 0, n;
#ifdef _WIN32
int open_flags = O_RDONLY | O_BINARY;
#else
int open_flags = O_RDONLY;
#endif
fd = open(path, open_flags);
if (fd < 0) {
return &PL_sv_undef;
}
if (fstat(fd, &st) < 0) {
close(fd);
return &PL_sv_undef;
}
/* Pre-allocate exact size for regular files */
if (S_ISREG(st.st_mode) && st.st_size > 0) {
result = newSV(st.st_size + 1);
SvPOK_on(result);
buf = SvPVX(result);
/* Read in one shot if possible */
while (total < st.st_size) {
n = read(fd, buf + total, st.st_size - total);
if (n < 0) {
if (errno == EINTR) continue;
close(fd);
SvREFCNT_dec(result);
return &PL_sv_undef;
}
if (n == 0) break;
total += n;
}
buf[total] = '\0';
SvCUR_set(result, total);
} else {
/* Stream or unknown size - read in chunks */
size_t capacity = FILE_BUFFER_SIZE;
result = newSV(capacity);
SvPOK_on(result);
buf = SvPVX(result);
while (1) {
if (total >= (ssize_t)capacity - 1) {
capacity *= 2;
SvGROW(result, capacity);
buf = SvPVX(result);
}
n = read(fd, buf + total, capacity - total - 1);
if (n < 0) {
if (errno == EINTR) continue;
close(fd);
SvREFCNT_dec(result);
return &PL_sv_undef;
}
if (n == 0) break;
total += n;
}
buf[total] = '\0';
SvCUR_set(result, total);
}
close(fd);
/* Run read hooks if registered (lazy - just pointer check) */
if (g_file_read_hook || g_file_hooks[FILE_HOOK_PHASE_READ]) {
SV *hooked = file_run_hooks(aTHX_ FILE_HOOK_PHASE_READ, path, result);
if (!hooked) {
SvREFCNT_dec(result);
return &PL_sv_undef;
}
if (hooked != result) {
SvREFCNT_dec(result);
result = hooked;
}
}
return result;
}
/* ============================================
Fast slurp binary - same as slurp but explicit
(bypasses hooks - for raw binary data)
============================================ */
static SV* file_slurp_raw_internal(pTHX_ const char *path) {
int fd;
struct stat st;
SV *result;
char *buf;
ssize_t total = 0, n;
#ifdef _WIN32
int open_flags = O_RDONLY | O_BINARY;
#else
int open_flags = O_RDONLY;
#endif
fd = open(path, open_flags);
if (fd < 0) {
return &PL_sv_undef;
}
if (fstat(fd, &st) < 0) {
close(fd);
return &PL_sv_undef;
}
if (S_ISREG(st.st_mode) && st.st_size > 0) {
result = newSV(st.st_size + 1);
SvPOK_on(result);
buf = SvPVX(result);
while (total < st.st_size) {
n = read(fd, buf + total, st.st_size - total);
if (n < 0) {
if (errno == EINTR) continue;
close(fd);
SvREFCNT_dec(result);
return &PL_sv_undef;
}
if (n == 0) break;
total += n;
}
buf[total] = '\0';
SvCUR_set(result, total);
} else {
size_t capacity = FILE_BUFFER_SIZE;
result = newSV(capacity);
SvPOK_on(result);
buf = SvPVX(result);
while (1) {
if (total >= (ssize_t)capacity - 1) {
capacity *= 2;
SvGROW(result, capacity);
buf = SvPVX(result);
}
n = read(fd, buf + total, capacity - total - 1);
if (n < 0) {
if (errno == EINTR) continue;
close(fd);
SvREFCNT_dec(result);
return &PL_sv_undef;
}
if (n == 0) break;
total += n;
}
buf[total] = '\0';
SvCUR_set(result, total);
}
close(fd);
return result; /* No hooks for raw */
}
static SV* file_slurp_raw(pTHX_ const char *path) {
return file_slurp_raw_internal(aTHX_ path);
}
/* ============================================
xs/file/file.c view on Meta::CPAN
size_t file_size;
#ifdef _WIN32
HANDLE file_handle;
HANDLE map_handle;
LARGE_INTEGER size;
DWORD access = writable ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ;
DWORD share = FILE_SHARE_READ;
DWORD protect = writable ? PAGE_READWRITE : PAGE_READONLY;
DWORD map_access = writable ? FILE_MAP_WRITE : FILE_MAP_READ;
file_handle = CreateFileA(path, access, share, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (file_handle == INVALID_HANDLE_VALUE) {
return -1;
}
if (!GetFileSizeEx(file_handle, &size)) {
CloseHandle(file_handle);
return -1;
}
if (size.QuadPart == 0) {
CloseHandle(file_handle);
return -1;
}
file_size = (size_t)size.QuadPart;
map_handle = CreateFileMappingA(file_handle, NULL, protect, 0, 0, NULL);
if (map_handle == NULL) {
CloseHandle(file_handle);
return -1;
}
addr = MapViewOfFile(map_handle, map_access, 0, 0, 0);
if (addr == NULL) {
CloseHandle(map_handle);
CloseHandle(file_handle);
return -1;
}
idx = alloc_mmap_slot();
g_mmaps[idx].addr = addr;
g_mmaps[idx].len = file_size;
g_mmaps[idx].file_handle = file_handle;
g_mmaps[idx].map_handle = map_handle;
g_mmaps[idx].refcount = 1;
#else
int fd;
struct stat st;
int flags = writable ? O_RDWR : O_RDONLY;
int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ;
fd = open(path, flags);
if (fd < 0) {
return -1;
}
if (fstat(fd, &st) < 0) {
close(fd);
return -1;
}
if (st.st_size == 0) {
/* Can't mmap empty file */
close(fd);
return -1;
}
file_size = st.st_size;
addr = mmap(NULL, st.st_size, prot, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
close(fd);
return -1;
}
idx = alloc_mmap_slot();
g_mmaps[idx].addr = addr;
g_mmaps[idx].len = file_size;
g_mmaps[idx].fd = fd;
g_mmaps[idx].refcount = 1;
#endif
return idx;
}
static SV* file_mmap_get_sv(pTHX_ IV idx) {
MmapEntry *entry;
SV *sv;
if (idx < 0 || idx >= g_mmaps_count) {
return &PL_sv_undef;
}
entry = &g_mmaps[idx];
#ifdef _WIN32
if (!entry->addr) {
return &PL_sv_undef;
}
#else
if (!entry->addr || entry->addr == MAP_FAILED) {
return &PL_sv_undef;
}
#endif
/* Create an SV that points directly to the mapped memory */
sv = newSV(0);
SvUPGRADE(sv, SVt_PV);
SvPV_set(sv, (char*)entry->addr);
SvCUR_set(sv, entry->len);
SvLEN_set(sv, 0); /* Don't free this memory! */
SvPOK_on(sv);
SvREADONLY_on(sv);
return sv;
}
static void file_mmap_close(IV idx) {
xs/file/file.c view on Meta::CPAN
}
/* Move remaining data to start of buffer */
if (entry->buf_pos > 0) {
size_t remaining = entry->buf_len - entry->buf_pos;
if (remaining > 0) {
memmove(entry->buffer, entry->buffer + entry->buf_pos, remaining);
}
entry->buf_len = remaining;
entry->buf_pos = 0;
}
/* Expand buffer if needed */
if (entry->buf_len >= entry->buf_size - 1) {
entry->buf_size *= 2;
Renew(entry->buffer, entry->buf_size, char);
}
/* Read more data */
n = read(entry->fd, entry->buffer + entry->buf_len,
entry->buf_size - entry->buf_len - 1);
if (n < 0) {
if (errno == EINTR) continue;
return &PL_sv_undef;
}
if (n == 0) {
entry->eof = 1;
} else {
entry->buf_len += n;
}
}
}
static int file_lines_eof(IV idx) {
LineIterEntry *entry;
if (idx < 0 || idx >= g_iters_count) {
return 1;
}
entry = &g_iters[idx];
return entry->eof && entry->buf_pos >= entry->buf_len;
}
static void file_lines_close(IV idx) {
if (idx < 0 || idx >= g_iters_count) return;
LineIterEntry *entry = &g_iters[idx];
entry->refcount--;
if (entry->refcount <= 0) {
free_iter_slot(idx);
}
}
/* ============================================
Fast stat operations
============================================ */
static IV file_size_internal(const char *path) {
struct stat st;
if (stat(path, &st) < 0) {
return -1;
}
return st.st_size;
}
static int file_exists_internal(const char *path) {
struct stat st;
return stat(path, &st) == 0;
}
static int file_is_file_internal(const char *path) {
struct stat st;
if (stat(path, &st) < 0) return 0;
return S_ISREG(st.st_mode);
}
static int file_is_dir_internal(const char *path) {
struct stat st;
if (stat(path, &st) < 0) return 0;
return S_ISDIR(st.st_mode);
}
static int file_is_readable_internal(const char *path) {
return access(path, R_OK) == 0;
}
static int file_is_writable_internal(const char *path) {
return access(path, W_OK) == 0;
}
static IV file_mtime_internal(const char *path) {
struct stat st;
if (stat(path, &st) < 0) {
return -1;
}
return st.st_mtime;
}
static IV file_atime_internal(const char *path) {
struct stat st;
if (stat(path, &st) < 0) {
return -1;
}
return st.st_atime;
}
static IV file_ctime_internal(const char *path) {
struct stat st;
if (stat(path, &st) < 0) {
return -1;
}
return st.st_ctime;
}
static IV file_mode_internal(const char *path) {
struct stat st;
if (stat(path, &st) < 0) {
return -1;
}
return st.st_mode & 07777; /* Return permission bits only */
}
static int file_is_link_internal(const char *path) {
#ifdef _WIN32
/* Windows: check for reparse point */
DWORD attrs = GetFileAttributesA(path);
if (attrs == INVALID_FILE_ATTRIBUTES) return 0;
return (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
#else
struct stat st;
if (lstat(path, &st) < 0) return 0;
return S_ISLNK(st.st_mode);
#endif
}
static int file_is_executable_internal(const char *path) {
#ifdef _WIN32
/* Windows: check file extension */
const char *ext = strrchr(path, '.');
if (ext) {
if (_stricmp(ext, ".exe") == 0 || _stricmp(ext, ".bat") == 0 ||
_stricmp(ext, ".cmd") == 0 || _stricmp(ext, ".com") == 0) {
return 1;
}
}
return 0;
#else
return access(path, X_OK) == 0;
#endif
}
/* ============================================
File manipulation operations
============================================ */
static int file_unlink_internal(const char *path) {
#ifdef _WIN32
return _unlink(path) == 0;
#else
return unlink(path) == 0;
#endif
}
static int file_copy_internal(pTHX_ const char *src, const char *dst) {
int fd_src, fd_dst;
char *buffer;
ssize_t n_read, n_written, written;
struct stat st;
int result = 0;
#ifdef _WIN32
int open_flags_r = O_RDONLY | O_BINARY;
int open_flags_w = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY;
#else
int open_flags_r = O_RDONLY;
int open_flags_w = O_WRONLY | O_CREAT | O_TRUNC;
#endif
fd_src = open(src, open_flags_r);
if (fd_src < 0) return 0;
if (fstat(fd_src, &st) < 0) {
close(fd_src);
return 0;
}
fd_dst = open(dst, open_flags_w, st.st_mode & 07777);
if (fd_dst < 0) {
close(fd_src);
return 0;
}
Newx(buffer, FILE_BULK_BUFFER_SIZE, char);
while (1) {
n_read = read(fd_src, buffer, FILE_BULK_BUFFER_SIZE);
if (n_read < 0) {
if (errno == EINTR) continue;
goto cleanup;
}
if (n_read == 0) break;
written = 0;
while (written < n_read) {
n_written = write(fd_dst, buffer + written, n_read - written);
if (n_written < 0) {
if (errno == EINTR) continue;
goto cleanup;
}
written += n_written;
}
}
result = 1;
cleanup:
Safefree(buffer);
close(fd_src);
close(fd_dst);
return result;
}
static int file_move_internal(pTHX_ const char *src, const char *dst) {
/* Try rename first (fast path for same filesystem) */
if (rename(src, dst) == 0) {
return 1;
}
/* If EXDEV, copy then delete (cross-device move) */
if (errno == EXDEV) {
if (file_copy_internal(aTHX_ src, dst)) {
return file_unlink_internal(src);
}
}
return 0;
}
static int file_touch_internal(const char *path) {
#ifdef _WIN32
HANDLE h;
FILETIME ft;
( run in 1.302 second using v1.01-cache-2.11-cpan-e93a5daba3e )