Skip to content

Commit

Permalink
Parse files from Ruby API using fread, not mmap
Browse files Browse the repository at this point in the history
  • Loading branch information
kddnewton committed Mar 6, 2024
1 parent 74c3ae0 commit 62d4376
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 2 deletions.
2 changes: 1 addition & 1 deletion ext/prism/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ file_options(int argc, VALUE *argv, pm_string_t *input, pm_options_t *options) {

const char * string_source = (const char *) pm_string_source(&options->filepath);

if (!pm_string_mapped_init(input, string_source)) {
if (!pm_string_file_init(input, string_source)) {
pm_options_free(options);

#ifdef _WIN32
Expand Down
11 changes: 11 additions & 0 deletions include/prism/util/pm_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ void pm_string_constant_init(pm_string_t *string, const char *source, size_t len
*/
PRISM_EXPORTED_FUNCTION bool pm_string_mapped_init(pm_string_t *string, const char *filepath);

/**
* Read the file indicated by the filepath parameter into source and load its
* contents and size into the given `pm_string_t`. The given `pm_string_t`
* should be freed using `pm_string_free` when it is no longer used.
*
* @param string The string to initialize.
* @param filepath The filepath to read.
* @return Whether or not the file was successfully read.
*/
PRISM_EXPORTED_FUNCTION bool pm_string_file_init(pm_string_t *string, const char *filepath);

/**
* Returns the memory size associated with the string.
*
Expand Down
104 changes: 103 additions & 1 deletion src/util/pm_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pm_string_constant_init(pm_string_t *string, const char *source, size_t length)
* `MapViewOfFile`, on POSIX systems that have access to `mmap` we'll use
* `mmap`, and on other POSIX systems we'll use `read`.
*/
bool
PRISM_EXPORTED_FUNCTION bool
pm_string_mapped_init(pm_string_t *string, const char *filepath) {
#ifdef _WIN32
// Open the file for reading.
Expand Down Expand Up @@ -143,6 +143,108 @@ pm_string_mapped_init(pm_string_t *string, const char *filepath) {
#endif
}

/**
* Read the file indicated by the filepath parameter into source and load its
* contents and size into the given `pm_string_t`. The given `pm_string_t`
* should be freed using `pm_string_free` when it is no longer used.
*/
PRISM_EXPORTED_FUNCTION bool
pm_string_file_init(pm_string_t *string, const char *filepath) {
#ifdef _WIN32
// Open the file for reading.
HANDLE file = CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (file == INVALID_HANDLE_VALUE) {
return false;
}

// Get the file size.
DWORD file_size = GetFileSize(file, NULL);
if (file_size == INVALID_FILE_SIZE) {
CloseHandle(file);
return false;
}

// If the file is empty, then we don't need to do anything else, we'll set
// the source to a constant empty string and return.
if (file_size == 0) {
CloseHandle(file);
const uint8_t source[] = "";
*string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
return true;
}

// Create a buffer to read the file into.
uint8_t *source = malloc(file_size);
if (source == NULL) {
CloseHandle(file);
return false;
}

// Read the contents of the file
DWORD bytes_read;
if (!ReadFile(file, source, file_size, &bytes_read, NULL)) {
CloseHandle(file);
return false;
}

// Check the number of bytes read
if (bytes_read != file_size) {
free(source);
CloseHandle(file);
return false;
}

CloseHandle(file);
*string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = (size_t) file_size };
return true;
#elif defined(_POSIX_MAPPED_FILES)
FILE *file = fopen(filepath, "rb");
if (file == NULL) {
return false;
}

fseek(file, 0, SEEK_END);
long file_size = ftell(file);

if (file_size == -1) {
fclose(file);
return false;
}

if (file_size == 0) {
fclose(file);
const uint8_t source[] = "";
*string = (pm_string_t) { .type = PM_STRING_CONSTANT, .source = source, .length = 0 };
return true;
}

size_t length = (size_t) file_size;
uint8_t *source = malloc(length);
if (source == NULL) {
fclose(file);
return false;
}

fseek(file, 0, SEEK_SET);
size_t bytes_read = fread(source, length, 1, file);
fclose(file);

if (bytes_read != 1) {
free(source);
return false;
}

*string = (pm_string_t) { .type = PM_STRING_OWNED, .source = source, .length = length };
return true;
#else
(void) string;
(void) filepath;
perror("pm_string_file_init is not implemented for this platform");
return false;
#endif
}

/**
* Returns the memory size associated with the string.
*/
Expand Down

0 comments on commit 62d4376

Please sign in to comment.