Skip to content

Instantly share code, notes, and snippets.

@TheDcoder
Last active June 14, 2020 14:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheDcoder/c7edb8a1cb3c63b110adfc9c0615b4b6 to your computer and use it in GitHub Desktop.
Save TheDcoder/c7edb8a1cb3c63b110adfc9c0615b4b6 to your computer and use it in GitHub Desktop.
readfile - Read the contents of a text file into a dynamically allocated buffer efficiently
/*
* MIT License
*
* Copyright (c) 2020 Damon Harris <TheDcoder@protonmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef READ_FILE_BUFFER_SIZE
#define READ_FILE_BUFFER_SIZE 1024
#endif
struct ReadFileBufferNode {
char buffer[READ_FILE_BUFFER_SIZE];
size_t data_len;
struct ReadFileBufferNode *next;
};
/// @brief Read the contents of a text file into a dynamically allocated buffer efficiently
/// @details This function reads the file in chunks which are stored in a linked list, and then all of the chunks are concatenated
/// into a single buffer which perfectly fits the whole string.
/// @author Damon Harris (TheDcoder@protonmail.com)
/// @param [in] file Pointer to the FILE to read.
/// @returns If success, pointer to dynamically allocated buffer containing the full contents of the file, NULL otherwise in case of an error.
char *readfile(FILE *file) {
// Define the final buffer
char *final_buffer = NULL;
size_t final_size = 0;
// Define and allocate the initial node
struct ReadFileBufferNode *initial_node = malloc(sizeof(struct ReadFileBufferNode));
if (!initial_node) return NULL;
// Read the contents of file in chunks
struct ReadFileBufferNode *curr_node = initial_node;
struct ReadFileBufferNode *next_node;
while (true) {
// Copy the current chunk
size_t bytes_read = fread(curr_node->buffer, 1, READ_FILE_BUFFER_SIZE, file);
curr_node->data_len = bytes_read;
final_size += bytes_read;
if (bytes_read < READ_FILE_BUFFER_SIZE) {
// Check if we have an error
if (ferror(file)) goto cleanup;
// Mark this node as final
curr_node->next = NULL;
// Break the loop
break;
}
// Allocate the next buffer node
next_node = malloc(sizeof(struct ReadFileBufferNode));
if (!next_node) goto cleanup;
curr_node->next = next_node;
curr_node = next_node;
}
// Allocate the buffer
final_buffer = malloc(final_size + 1);
if (!final_buffer) goto cleanup;
final_buffer[final_size] = '\0';
// Copy data into the final buffer
curr_node = initial_node;
char *curr_chunk = final_buffer;
do {
memcpy(curr_chunk, curr_node->buffer, curr_node->data_len);
curr_chunk += curr_node->data_len;
curr_node = curr_node->next;
} while (curr_node);
// Free all nodes
cleanup:
curr_node = initial_node;
do {
next_node = curr_node->next;
free(curr_node);
curr_node = next_node;
} while (curr_node);
// Return the final buffer
return final_buffer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment