mirror of
https://gitlab.com/80486DX2-66/gists
synced 2025-05-31 08:31:41 +05:30
C: categorize files, update .gitignore
This commit is contained in:
396
c-programming/experiments/reverse-ramdisk.c
Normal file
396
c-programming/experiments/reverse-ramdisk.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* reverse-ramdisk.c
|
||||
*
|
||||
* C programming idea: Handling temporary files like memory allocations
|
||||
* (allocating -> creating empty file, using -> locking for R/W,
|
||||
* freeing -> deleting).
|
||||
*
|
||||
* 20% AI, 80% human (the code is tested and reviewed)
|
||||
*
|
||||
* Warning: The current result is quick and dirty. Not for educational or
|
||||
* production purposes.
|
||||
*
|
||||
* GCC/Clang/TCC: Compile with -DTEST to set macro TEST as defined, with
|
||||
* -DDEBUG to enable debug mode
|
||||
*
|
||||
* To-Do: Test: Fix the bug with invalid next size to reallocate
|
||||
* To-Do: Test: Automate the test verification
|
||||
*
|
||||
* Author: Intel A80486DX2-66
|
||||
* License: Creative Commons Zero 1.0 Universal
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if _POSIX_VERSION >= 200112L
|
||||
# include <fcntl.h>
|
||||
# include <unistd.h>
|
||||
# define IS_POSIX 1
|
||||
#else
|
||||
# define IS_POSIX 0
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
# define LINE_FAIL(x) printf("failed on line %d\n", __LINE__ + x)
|
||||
#else
|
||||
# define LINE_FAIL(x)
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG) || defined(TEST)
|
||||
# define DBG_PRINT(...) do { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
#else
|
||||
# define DBG_PRINT(...)
|
||||
#endif
|
||||
|
||||
#define RETREAT(s) do { \
|
||||
perror(s); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
bool locked;
|
||||
int ID;
|
||||
char* file_path;
|
||||
#if IS_POSIX
|
||||
int file;
|
||||
#else
|
||||
FILE* file;
|
||||
#endif
|
||||
} TempFile;
|
||||
|
||||
TempFile* temp_files = NULL;
|
||||
size_t num_temp_files = 0;
|
||||
|
||||
int tf_alloc(size_t n, size_t type_size);
|
||||
int tf_free(int ID);
|
||||
int tf_write(int ID, size_t offset, void* src, size_t data_size);
|
||||
int tf_read(int ID, size_t offset, void* dest, size_t data_size);
|
||||
|
||||
int tf_alloc(size_t n, size_t type_size) {
|
||||
DBG_PRINT("tf_alloc(%zu, %zu)\n", n, type_size);
|
||||
|
||||
// Create an empty file
|
||||
size_t len_digit;
|
||||
if (num_temp_files == 0)
|
||||
len_digit = 1;
|
||||
else
|
||||
len_digit = (size_t) floor(log10((double) num_temp_files)) + 1;
|
||||
size_t file_path_len = len_digit + strlen("tf_.tmp");
|
||||
char* file_path = malloc((file_path_len + 1) * sizeof(char));
|
||||
if (file_path == NULL) {
|
||||
LINE_FAIL(-2);
|
||||
return -1;
|
||||
}
|
||||
int res = snprintf(file_path, file_path_len + 1, "tf_%" PRIuMAX ".tmp",
|
||||
(uintmax_t) num_temp_files);
|
||||
if ((size_t) res != file_path_len) {
|
||||
LINE_FAIL(-2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if IS_POSIX
|
||||
int file = open(file_path, O_RDWR | O_CREAT);
|
||||
if (file == -1) {
|
||||
#else
|
||||
FILE* file = fopen(file_path, "w+b");
|
||||
if (file == NULL) {
|
||||
#endif
|
||||
LINE_FAIL(-2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate memory for the TempFile struct
|
||||
TempFile* temp_file = malloc(sizeof(TempFile));
|
||||
if (temp_file == NULL) {
|
||||
LINE_FAIL(-2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Assign the ID, file path, file handler
|
||||
temp_file->locked = false;
|
||||
temp_file->ID = num_temp_files;
|
||||
temp_file->file_path = strdup(file_path);
|
||||
temp_file->file = file;
|
||||
|
||||
// Allocate/reallocate memory for the temp_files structure
|
||||
if (temp_files == NULL)
|
||||
temp_files = malloc(sizeof(TempFile));
|
||||
else
|
||||
temp_files = realloc(temp_files, num_temp_files * sizeof(TempFile));
|
||||
if (temp_files == NULL) {
|
||||
LINE_FAIL(-2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add the temp file to the array
|
||||
temp_files[num_temp_files++] = *temp_file;
|
||||
|
||||
return temp_file->ID;
|
||||
}
|
||||
|
||||
int tf_free(int ID) {
|
||||
DBG_PRINT("tf_free(%d)\n", ID);
|
||||
|
||||
size_t index = (size_t) ID;
|
||||
|
||||
if (temp_files[index].locked) {
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
temp_files[index].locked = true;
|
||||
|
||||
#if IS_POSIX
|
||||
close(temp_files[index].file);
|
||||
#else
|
||||
fclose(temp_files[index].file);
|
||||
#endif
|
||||
|
||||
// Delete the file
|
||||
if (remove(temp_files[index].file_path) != 0) {
|
||||
LINE_FAIL(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(temp_files[index].file_path);
|
||||
|
||||
// Shift the remaining temp files in the array
|
||||
for (size_t i = index; i < num_temp_files - 1; i++)
|
||||
temp_files[i] = temp_files[i + 1];
|
||||
|
||||
// Reallocate memory for the temp_files array
|
||||
if (--num_temp_files > 0) {
|
||||
DBG_PRINT("num_temp_files = %zu\n", num_temp_files);
|
||||
if ((temp_files = realloc(temp_files, num_temp_files
|
||||
* sizeof(TempFile))) == NULL) {
|
||||
LINE_FAIL(-2);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
temp_files[index].locked = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tf_write(int ID, size_t offset, void* src, size_t data_size) {
|
||||
DBG_PRINT("tf_write(%d, %zu, %p, %zu)\n", ID, offset, src, data_size);
|
||||
|
||||
size_t index = (size_t) ID;
|
||||
|
||||
if (temp_files[index].locked) {
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
temp_files[index].locked = true;
|
||||
|
||||
#if IS_POSIX
|
||||
// Check file handler for -1
|
||||
int file = temp_files[index].file;
|
||||
if (file == -1)
|
||||
#else
|
||||
// Check file handler for NULL
|
||||
FILE* file = temp_files[index].file;
|
||||
if (file == NULL)
|
||||
#endif
|
||||
return -1;
|
||||
|
||||
// Set the position
|
||||
#if IS_POSIX
|
||||
if (lseek(file, offset, SEEK_SET) == -1) {
|
||||
#else
|
||||
if (fseek(file, offset, SEEK_SET) == -1) {
|
||||
#endif
|
||||
LINE_FAIL(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write the data to the file
|
||||
#if IS_POSIX
|
||||
ssize_t
|
||||
#else
|
||||
size_t
|
||||
#endif
|
||||
bytes_written =
|
||||
#if IS_POSIX
|
||||
write(file, src, data_size);
|
||||
#else
|
||||
fwrite(src, 1, data_size, file);
|
||||
#endif
|
||||
|
||||
if (
|
||||
#if IS_POSIX
|
||||
(size_t)
|
||||
#endif
|
||||
bytes_written != data_size) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if IS_POSIX
|
||||
if (fsync(file) == -1) {
|
||||
LINE_FAIL(-1);
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
fflush(file);
|
||||
#endif
|
||||
|
||||
temp_files[index].locked = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tf_read(int ID, size_t offset, void* dest, size_t data_size) {
|
||||
DBG_PRINT("tf_read(%d, %zu, %p, %zu)\n", ID, offset, dest, data_size);
|
||||
|
||||
size_t index = (size_t) ID;
|
||||
|
||||
if (temp_files[index].locked) {
|
||||
errno = EBUSY;
|
||||
return -1;
|
||||
}
|
||||
temp_files[index].locked = true;
|
||||
|
||||
#if IS_POSIX
|
||||
int file = temp_files[index].file;
|
||||
if (file == -1)
|
||||
#else
|
||||
FILE* file = temp_files[index].file;
|
||||
if (file == NULL)
|
||||
#endif
|
||||
return -1;
|
||||
|
||||
// Read the data from the file
|
||||
void* src = malloc(data_size);
|
||||
if (src == NULL) {
|
||||
#if IS_POSIX
|
||||
close(file);
|
||||
#else
|
||||
fclose(file);
|
||||
#endif
|
||||
LINE_FAIL(-7);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(src, 0, data_size); // clear destination
|
||||
// Set the position
|
||||
#if IS_POSIX
|
||||
if (lseek(file, offset, SEEK_SET) == -1) {
|
||||
#else
|
||||
if (fseek(file, offset, SEEK_SET) == -1) {
|
||||
#endif
|
||||
LINE_FAIL(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// read bytes
|
||||
#if IS_POSIX
|
||||
ssize_t bytes_read = read(file, src, data_size);
|
||||
#else
|
||||
size_t bytes_read = fread(src, 1, data_size, file);
|
||||
#endif
|
||||
memcpy(dest, src, data_size);
|
||||
|
||||
free(src); // Free the allocated memory
|
||||
|
||||
if (
|
||||
#if IS_POSIX
|
||||
(size_t)
|
||||
#endif
|
||||
bytes_read != data_size) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Read: ID = %d, src = %p, size = %zu -> '",
|
||||
ID, dest, data_size);
|
||||
for (size_t i = 0; i < data_size; i++)
|
||||
printf("0x%02" PRIX8 "%c", *((uint8_t*)((uint8_t*)dest + i)),
|
||||
i == (data_size - 1) ? '\'' : ' ');
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
temp_files[index].locked = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
int main(void) {
|
||||
DBG_PRINT("started\n");
|
||||
|
||||
#define ARRAY_1_LEN 4
|
||||
#define ARRAY_2_LEN 16
|
||||
|
||||
int ID_1 = tf_alloc(ARRAY_1_LEN, sizeof(int));
|
||||
int ID_2 = tf_alloc(ARRAY_2_LEN, sizeof(uint16_t));
|
||||
if (ID_1 == -1 || ID_2 == -1)
|
||||
RETREAT("tf_alloc");
|
||||
DBG_PRINT("allocated memory\n");
|
||||
|
||||
int test_data_1[ARRAY_1_LEN] = {123, 456, 789, -123};
|
||||
DBG_PRINT("initialized array 1\n");
|
||||
|
||||
uint16_t test_data_2[ARRAY_2_LEN];
|
||||
for (size_t i = 0; i < ARRAY_2_LEN; i++)
|
||||
test_data_2[i] = 1 << i;
|
||||
DBG_PRINT("initialized array 2\n");
|
||||
|
||||
for (size_t i = 0; i < ARRAY_1_LEN; i++)
|
||||
if (tf_write(ID_1, i * sizeof(int), &test_data_1[i], sizeof(int)) == -1)
|
||||
RETREAT("tf_write");
|
||||
DBG_PRINT("wrote array 1\n");
|
||||
|
||||
for (size_t i = 0; i < ARRAY_2_LEN; i++)
|
||||
if (tf_write(ID_2, i * sizeof(uint16_t), &test_data_2[i],
|
||||
sizeof(uint16_t)) == -1)
|
||||
RETREAT("tf_write");
|
||||
DBG_PRINT("wrote array 2\n");
|
||||
|
||||
// round-trip
|
||||
test_data_1[0] = 111;
|
||||
test_data_1[1] = 222;
|
||||
test_data_1[2] = 333;
|
||||
test_data_1[3] = 444;
|
||||
DBG_PRINT("filled array 1 with garbage\n");
|
||||
|
||||
for (size_t i = 0; i < 16; i++)
|
||||
test_data_2[i] ^= 1;
|
||||
DBG_PRINT("filled array 2 with garbage\n");
|
||||
|
||||
for (size_t i = 0; i < ARRAY_1_LEN; i++)
|
||||
if (tf_read(ID_1, i * sizeof(int), &test_data_1[i], sizeof(int)) == -1)
|
||||
RETREAT("tf_read");
|
||||
DBG_PRINT("restored array 1\n");
|
||||
|
||||
for (size_t i = 0; i < ARRAY_2_LEN; i++)
|
||||
if (tf_read(ID_2, i * sizeof(uint16_t), &test_data_2[i],
|
||||
sizeof(uint16_t)) == -1)
|
||||
RETREAT("tf_read");
|
||||
DBG_PRINT("restored array 2\n");
|
||||
|
||||
DBG_PRINT("Values (1): ");
|
||||
for (size_t i = 0; i < ARRAY_1_LEN; i++)
|
||||
DBG_PRINT("%d%c", test_data_1[i], i == (ARRAY_1_LEN - 1) ? '\n' : ' ');
|
||||
DBG_PRINT("Values (2): ");
|
||||
for (size_t i = 0; i < ARRAY_2_LEN; i++)
|
||||
DBG_PRINT("%d%c", test_data_2[i], i == (ARRAY_2_LEN - 1) ? '\n' : ' ');
|
||||
|
||||
tf_free(ID_1);
|
||||
tf_free(ID_2);
|
||||
DBG_PRINT("freed both files\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user