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 )