Files
regexx/examples/large_file_search.c
T

103 lines
3.8 KiB
C
Raw Normal View History

/* large_file_search - demonstrates Input_from_file's mmap-backed reading
* (README.md "Memory footprint") on a file large enough that the
* difference between mapping it and copying it into a fresh malloc'd
* buffer is not theoretical. The example generates its own input (so the
* program is self-contained and does not depend on a file existing on the
* machine it runs on), searches it for a pattern that does not occur
* anywhere (the worst case for a search: every position is tried, since
* none of them succeeds early), and reports both the elapsed time and,
* where /proc is available, the process's peak resident memory, so the
* two numbers can be compared directly against the measured figures in
* README.md rather than taken on faith.
*/
#define _POSIX_C_SOURCE 199309L
#include "regexx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
static double now_seconds(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
}
static void print_peak_rss(void) {
FILE *f = fopen("/proc/self/status", "r");
if (!f) { printf("(peak RSS: /proc not available on this platform)\n"); return; }
char line[256];
while (fgets(line, sizeof line, f)) {
if (strncmp(line, "VmHWM:", 6) == 0) {
printf("peak RSS: %s", line + 6);
break;
}
}
fclose(f);
}
int main(void) {
const char *path = "/tmp/regexx_large_file_search_example.dat";
size_t size = 100u * 1024 * 1024; /* 100MB: large enough to be a real
* measurement, small enough to run
* in a few seconds on ordinary
* hardware. */
printf("Generating a %zu MB file of non-matching text at %s ...\n", size / (1024 * 1024), path);
{
FILE *f = fopen(path, "wb");
if (!f) { perror("fopen"); return 1; }
/* Repeating, harmless filler text: never contains the digit
* sequence the search pattern below looks for. */
static const char chunk[] =
"the quick brown fox jumps over the lazy dog. ";
size_t chunklen = sizeof(chunk) - 1;
size_t written = 0;
while (written < size) {
size_t n = fwrite(chunk, 1, chunklen, f);
if (n == 0) break;
written += n;
}
fclose(f);
}
const char *pattern = "\\d{10,}"; /* ten or more consecutive digits: not present */
PatternError err; memset(&err, 0, sizeof err);
Pattern *pat = re_compile(pattern, strlen(pattern), ASCII, &err);
if (!pat) {
fprintf(stderr, "compile error: %s\n", err.msg);
PatternError_free(&err);
remove(path);
return 1;
}
Input *in = Input_from_file(path, &err);
if (!in) {
fprintf(stderr, "cannot read file: %s\n", err.msg);
PatternError_free(&err);
Pattern_free(pat);
remove(path);
return 1;
}
printf("Searching (Input_from_file mmaps this file rather than copying it,\n"
"so this process's own heap never holds a private copy of the 100MB\n"
"file content itself; this pattern has no backreference, lookaround,\n"
"or atomic group, so it also runs on the Pike VM, README.md \"The\n"
"Pike VM\", which needs none of the backtracking engine's own search\n"
"tables, README.md \"Memory footprint\", at all)...\n");
Match m; memset(&m, 0, sizeof m);
double t0 = now_seconds();
int rc = Pattern_search(pat, in, 0, -1, &m);
double t1 = now_seconds();
printf("result: rc=%d (0 = no match, as expected) in %.3fs\n", rc, t1 - t0);
print_peak_rss();
if (rc == 1) Match_free(&m);
Input_free(in);
Pattern_free(pat);
remove(path);
printf("(temporary file removed)\n");
return 0;
}