NAME

File::Raw - Fast IO operations using direct system calls

SYNOPSIS

use File::Raw qw(import);

# Slurp entire file
my $content = file_slurp('/path/to/file');

# Write to file
file_spew('/path/to/file', $content);

# Append to file
file_append('/path/to/file', "more data\n");

# Get all lines as array
my $lines = file_lines('/path/to/file');

# Process lines efficiently with callback (line via $_)
file_each_line('/path/to/file', sub {
    print "Line: $_\n";
});

# Memory-mapped file access
my $mmap = file_mmap_open('/path/to/file');
my $data = $mmap->data;  # Zero-copy access
$mmap->close;

# Line iterator (memory efficient)
my $iter = file_lines_iter('/path/to/file');
while (!$iter->eof) {
    my $line = $iter->next;
    # process line
}
$iter->close;

# File stat operations
my $size   = file_size('/path/to/file');
my $mtime  = file_mtime('/path/to/file');
my $exists = file_exists('/path/to/file');

# Type checks
file_is_file('/path/to/file');
file_is_dir('/path/to/dir');
file_is_readable('/path/to/file');
file_is_writable('/path/to/file');

DESCRIPTION

Fast IO operations using direct system calls, bypassing PerlIO overhead. This module provides significantly faster file operations compared to Perl's built-in IO functions.

Performance

The module uses:

  • Direct read(2)/write(2) syscalls

  • Pre-allocated buffers based on file size

  • Memory-mapped file access for zero-copy reads

  • Efficient line iteration without loading entire file

  • MULTICALL optimization for callback-based functions

FUNCTIONS

All functions are available with a file_ prefix when imported, e.g. file_slurp, file_spew, etc.

use File::Raw qw(slurp spew);

file_spew('/path/to/file', "data");  # Write data
my $content = file_slurp('/path/to/file');  # Read data

slurp

my $content = File::Raw::slurp($path);

Read entire file into a scalar. Returns undef on error. Pre-allocates the buffer based on file size for optimal performance.

slurp_raw

my $content = File::Raw::slurp_raw($path);

Same as slurp, explicit binary mode.

spew

my $ok = File::Raw::spew($path, $data);

Write data to file (creates or truncates). Returns true on success.

append

my $ok = File::Raw::append($path, $data);

Append data to file. Returns true on success.

lines

my $lines = File::Raw::lines($path);

Returns arrayref of all lines (without newlines).

each_line

File::Raw::each_line($path, sub {
    print "Line: $_\n";  # line available via $_
});

Process each line with a callback. Memory efficient - doesn't load entire file into memory.

lines_iter

my $iter = File::Raw::lines_iter($path);
while (!$iter->eof) {
    my $line = $iter->next;
}
$iter->close;

Returns a line iterator object for memory-efficient line processing.

Note: For maximum performance, prefer each_line() which uses MULTICALL optimization and is significantly faster. Use lines_iter() when you need iterator control (e.g., early exit, multiple iterators).

mmap_open

my $mmap = File::Raw::mmap_open($path);
my $mmap = File::Raw::mmap_open($path, 1);  # writable

Memory-map a file. Returns a File::Raw::mmap object.

File::Raw::mmap methods

data() - Returns the mapped content as a scalar (zero-copy)
sync() - Flush changes to disk (for writable maps)
close() - Unmap the file

size

my $bytes = File::Raw::size($path);

Returns file size in bytes, or -1 on error.

mtime

my $epoch = File::Raw::mtime($path);

Returns modification time as epoch seconds, or -1 on error.

exists

if (File::Raw::exists($path)) { ... }

Returns true if path exists.

is_file

if (File::Raw::is_file($path)) { ... }

Returns true if path is a regular file.

is_dir

if (File::Raw::is_dir($path)) { ... }

Returns true if path is a directory.

is_readable

if (File::Raw::is_readable($path)) { ... }

Returns true if path is readable.

is_writable

if (File::Raw::is_writable($path)) { ... }

Returns true if path is writable.

stat

my $st = File::Raw::stat($path);

Returns a hashref with all file attributes in one syscall. This is much more efficient than calling multiple individual functions.

my $st = File::Raw::stat($path);
# $st = {
#     size    => 12345,
#     mtime   => 1234567890,
#     atime   => 1234567890,
#     ctime   => 1234567890,
#     mode    => 0644,      # Permission bits only
#     is_file => 1,
#     is_dir  => '',
#     dev     => 16777233,
#     ino     => 12345,
#     nlink   => 1,
#     uid     => 501,
#     gid     => 20,
# }

Returns undef if stat fails.

File::Raw caches the last stat result for performance. When you call multiple stat-like functions on the same file (size, mtime, is_readable, etc.), only the first call hits the filesystem.

clear_stat_cache

File::Raw::clear_stat_cache();        # Clear entire cache
File::Raw::clear_stat_cache($path);   # Clear cache for specific path

Clear the internal stat cache. Use this when an external process may have modified a file, or to force a fresh stat on the next call.

The cache is automatically invalidated when you use File::Raw functions that modify files (spew, append, unlink, touch, chmod, etc.), but external modifications require manual cache clearing.

my $size1 = File::Raw::size($file);  # Cached stat
# External process modifies $file...
File::Raw::clear_stat_cache($file);  # Clear cache
my $size2 = File::Raw::size($file);  # Fresh stat

copy

my $ok = File::Raw::copy($src, $dst);

Copy a file. Uses native copy functions (copyfile on macOS, sendfile on Linux) for optimal performance. Returns true on success.

move

my $ok = File::Raw::move($src, $dst);

Move/rename a file. Uses rename() for same-filesystem moves, falls back to copy+unlink for cross-device moves. Returns true on success.

my $ok = File::Raw::unlink($path);

Delete a file. Returns true on success.

touch

my $ok = File::Raw::touch($path);

Create an empty file or update timestamps. Returns true on success.

mkdir

my $ok = File::Raw::mkdir($path);
my $ok = File::Raw::mkdir($path, $mode);

Create a directory. Default mode is 0755. Returns true on success.

rmdir

my $ok = File::Raw::rmdir($path);

Remove an empty directory. Returns true on success.

readdir

my $entries = File::Raw::readdir($path);

Returns arrayref of directory entries (excludes . and ..).

basename

my $name = File::Raw::basename($path);

Returns the filename portion of a path.

dirname

my $dir = File::Raw::dirname($path);

Returns the directory portion of a path.

extname

my $ext = File::Raw::extname($path);

Returns the file extension including the dot (e.g., ".txt").

join

my $path = File::Raw::join($part1, $part2, ...);

Join path components with the appropriate separator. Handles leading/trailing slashes correctly.

my $path = File::Raw::join('/usr', 'local', 'bin');
# Returns: /usr/local/bin

atime

my $epoch = File::Raw::atime($path);

Returns access time as epoch seconds, or -1 on error.

ctime

my $epoch = File::Raw::ctime($path);

Returns inode change time as epoch seconds, or -1 on error.

mode

my $mode = File::Raw::mode($path);

Returns the file permission bits (e.g., 0644), or -1 on error.

if (File::Raw::is_link($path)) { ... }

Returns true if path is a symbolic link.

is_executable

if (File::Raw::is_executable($path)) { ... }

Returns true if path is executable.

chmod

my $ok = File::Raw::chmod($path, $mode);

Change file permissions. Returns true on success.

File::Raw::chmod($path, 0755);
my $lines = File::Raw::head($path);      # First 10 lines
my $lines = File::Raw::head($path, 20);  # First 20 lines

Returns arrayref of first N lines (default 10).

tail

my $lines = File::Raw::tail($path);      # Last 10 lines
my $lines = File::Raw::tail($path, 20);  # Last 20 lines

Returns arrayref of last N lines (default 10).

atomic_spew

my $ok = File::Raw::atomic_spew($path, $data);

Write data to a temporary file then atomically rename. This ensures the file is never in a partial state. Returns true on success.

grep_lines

my $lines = File::Raw::grep_lines($path, \&predicate);
my $lines = File::Raw::grep_lines($path, 'not_blank');

Filter lines matching a predicate. The predicate can be a coderef or a registered predicate name.

Built-in predicates: blank, not_blank, empty, not_empty, comment, not_comment

# Using coderef
my $lines = File::Raw::grep_lines($path, sub { /pattern/ });

# Using built-in predicate
my $lines = File::Raw::grep_lines($path, 'not_blank');

count_lines

my $count = File::Raw::count_lines($path);
my $count = File::Raw::count_lines($path, \&predicate);
my $count = File::Raw::count_lines($path, 'not_blank');

Count lines in a file. Optionally filter by predicate.

find_line

my $line = File::Raw::find_line($path, \&predicate);
my $line = File::Raw::find_line($path, 'not_blank');

Find the first line matching a predicate. Returns undef if not found.

map_lines

my $results = File::Raw::map_lines($path, \&transform);

Transform each line with a callback, returns arrayref of results.

my $lengths = File::Raw::map_lines($path, sub { length($_) });

register_line_callback

File::Raw::register_line_callback($name, \&predicate);

Register a custom predicate for use with grep_lines, count_lines, etc.

File::Raw::register_line_callback('has_error', sub { /ERROR/ });
my $errors = File::Raw::grep_lines($path, 'has_error');

list_line_callbacks

my $names = File::Raw::list_line_callbacks();

Returns arrayref of registered predicate names.

HOOKS

File::Raw supports read/write hooks for data transformation.

register_read_hook

File::Raw::register_read_hook(\&hook);

Register a hook that transforms data after reading.

File::Raw::register_read_hook(sub {
    my ($path, $data) = @_;
    return uc($data);  # uppercase all content
});

register_write_hook

File::Raw::register_write_hook(\&hook);

Register a hook that transforms data before writing.

clear_hooks

File::Raw::clear_hooks($phase);

Clear all hooks for a phase. Phase can be: read, write, open, close.

has_hooks

if (File::Raw::has_hooks('read')) { ... }

Check if hooks are registered for a phase.

IMPORT STYLE

use File::Raw qw(:all);              # Import all functions as file_*
use File::Raw qw(slurp spew lines);  # Import specific functions
use File::Raw qw(import);            # Same as :all (backwards compat)

When imported, the functions are installed with `file_` prefix and use custom ops for maximum performance (eliminating function call overhead).

use File::Raw qw(slurp spew);

my $content = file_slurp($path);
file_spew($path, $data);

Available imports: slurp, slurp_raw, spew, append, atomic_spew, lines, exists, size, mtime, atime, ctime, mode, is_file, is_dir, is_link, is_readable, is_writable, is_executable, unlink, mkdir, rmdir, touch, copy, move, chmod, readdir, basename, dirname, extname, clear_stat_cache.

PERFORMANCE NOTES

Platform Optimizations

  • macOS: Uses copyfile() for native file copying

  • Linux: Uses sendfile() for zero-copy file transfer

  • Linux/BSD: Uses posix_fadvise() to hint sequential reads

When to use File::Raw::stat

If you need multiple attributes from a file (size, mtime, is_file, etc.), use File::Raw::stat() instead of calling individual functions:

# SLOW: 5 syscalls
my $size    = File::Raw::size($path);
my $mtime   = File::Raw::mtime($path);
my $is_file = File::Raw::is_file($path);

# FAST: 1 syscall
my $st = File::Raw::stat($path);
my ($size, $mtime, $is_file) = @{$st}{qw(size mtime is_file)};

XS API

File::Raw exposes a C API via include/file_hooks.h that other XS modules can use to register read/write hooks at the C level, avoiding Perl callback overhead. The shared object is loaded with RTLD_GLOBAL so symbols are visible to subsequently loaded XS modules.

Types

FileHookPhase

Enum identifying the hook phase:

FILE_HOOK_PHASE_READ    /* After reading, before returning to Perl */
FILE_HOOK_PHASE_WRITE   /* Before writing to disk */
FILE_HOOK_PHASE_OPEN    /* Before opening file */
FILE_HOOK_PHASE_CLOSE   /* After closing file */
FileHookPriority

Predefined priority levels (lower runs first):

FILE_HOOK_PRIORITY_FIRST    =    0
FILE_HOOK_PRIORITY_EARLY    =  100
FILE_HOOK_PRIORITY_NORMAL   =  500
FILE_HOOK_PRIORITY_LATE     =  900
FILE_HOOK_PRIORITY_LAST     = 1000
FileHookContext

Struct passed to C hook callbacks:

typedef struct {
    const char   *path;       /* File path */
    SV           *data;       /* Data SV (may be modified in place) */
    FileHookPhase phase;      /* Which phase */
    void         *user_data;  /* User-provided context */
    int           cancel;     /* Set to 1 to cancel operation */
} FileHookContext;
file_hook_func

C hook function signature:

typedef SV* (*file_hook_func)(pTHX_ FileHookContext *ctx);

Return the (possibly modified) SV, or NULL to cancel the operation.

Functions

file_register_hook_c
int file_register_hook_c(pTHX_
                         FileHookPhase phase,
                         const char *name,
                         file_hook_func func,
                         int priority,
                         void *user_data);

Register a named C hook for a given phase. Returns 1 on success. Call during module initialisation only (not thread-safe).

file_unregister_hook
int file_unregister_hook(pTHX_ FileHookPhase phase, const char *name);

Remove a hook by name. Returns 1 if found and removed.

file_has_hooks
int file_has_hooks(FileHookPhase phase);

Fast check for registered hooks. Use before allocating a context.

file_run_hooks
SV* file_run_hooks(pTHX_ FileHookPhase phase, const char *path, SV *data);

Execute all hooks for a phase in priority order. Returns the transformed SV, or NULL if a hook cancelled the operation.

file_set_read_hook / file_set_write_hook
void file_set_read_hook(pTHX_ file_hook_func func, void *user_data);
void file_set_write_hook(pTHX_ file_hook_func func, void *user_data);

Simple single-hook registration (overwrites any existing hook for the phase). Pass NULL to clear.

file_get_read_hook / file_get_write_hook
file_hook_func file_get_read_hook(void);
file_hook_func file_get_write_hook(void);

Return the currently registered hook function, or NULL.

Convenience Macros

FILE_HAS_READ_HOOK()           /* true if a read hook is set */
FILE_HAS_WRITE_HOOK()          /* true if a write hook is set */
FILE_RUN_READ_HOOK(path, sv)   /* run hook or return sv unchanged */
FILE_RUN_WRITE_HOOK(path, sv)  /* run hook or return sv unchanged */

Example (XS module)

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <file_hooks.h>

static SV* my_rot13_hook(pTHX_ FileHookContext *ctx) {
    STRLEN len;
    char *p = SvPV(ctx->data, len);
    STRLEN i;
    for (i = 0; i < len; i++) {
        if (p[i] >= 'a' && p[i] <= 'z')
            p[i] = 'a' + (p[i] - 'a' + 13) % 26;
        else if (p[i] >= 'A' && p[i] <= 'Z')
            p[i] = 'A' + (p[i] - 'A' + 13) % 26;
    }
    return ctx->data;
}

MODULE = MyModule  PACKAGE = MyModule

BOOT:
    file_set_read_hook(aTHX_ my_rot13_hook, NULL);

AUTHOR

LNATION <email@lnation.org>

BUGS

Please report any bugs or feature requests to bug-file-fast at rt.cpan.org, or through the web interface at https://rt.cpan.org/NoAuth/ReportBug.html?Queue=File-Fast. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc File::Raw

You can also look for information at:

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION <email@lnation.org>.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)