From 7fb126722499cbd6b1ef9abd32717108ce6c1a7e Mon Sep 17 00:00:00 2001 From: Odweta Date: Thu, 7 Dec 2023 20:03:18 +0100 Subject: [PATCH] initial commit --- Makefile | 31 ++++ README.md | 32 ++++ demo.c | 85 +++++++++++ include/file.h | 24 +++ include/vector.h | 54 +++++++ src/file.c | 105 +++++++++++++ src/vector.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++ test.txt | 6 + 8 files changed, 722 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 demo.c create mode 100644 include/file.h create mode 100644 include/vector.h create mode 100644 src/file.c create mode 100644 src/vector.c create mode 100644 test.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..709a535 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +CC := gcc +CFLAGS := -Wall -Werror +LIBS := -lvector -lfile +LIBSPATH := -L/usr/lib/odweta/ +INCLUDEDIR := -I/usr/include/odweta/ +PATCH := -Wl,-rpath=/usr/lib/odweta/ + +.PHONY: default install + +default: demo + +demo: demo.c libvector.so libfile.so + sudo $(CC) $(CFLAGS) -o $@ $< $(PATCH) $(INCLUDEDIR) $(LIBSPATH) $(LIBS) + +libvector.so: src/vector.c + sudo $(CC) -c -fpic $< -o /usr/lib/odweta/vector.o + sudo $(CC) -shared -o /usr/lib/odweta/$@ /usr/lib/odweta/vector.o + +libfile.so: src/file.c + sudo $(CC) -c -fpic $< -o /usr/lib/odweta/file.o + sudo $(CC) -shared -o /usr/lib/odweta/$@ /usr/lib/odweta/file.o + +install: + sudo mkdir -p /usr/include/odweta/ + sudo mkdir -p /usr/lib/odweta/ + sudo cp src/vector.c src/file.c include/vector.h include/file.h /usr/include/odweta/ + sudo make libvector.so libfile.so + sudo ldconfig # update ldconfig cache + +clean: + sudo rm -f main /usr/lib/odweta/vector.o /usr/lib/odweta/file.o /usr/lib/odweta/libvector.so /usr/lib/odweta/libfile.so diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a0148b --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# OdwetaLibC + +I got bored with having to use complicated checking and stuff when working with files and arrays in C, so I implemented a new `file_t` struct which can load the contents of a file into a dynamically allocated array (yes, there is an implmentation of those here as well... (`vector_t` struct)). The struct also stores the number of lines the file has. + +## Files + +#### What works so far: + +1. Reading files into a 2D vector +2. Converting a file into a string + +#### TODO: + +1. File writing logic +2. File appending logic + ++ You tell me + +## Vectors (dynamic arrays) + +#### What works so far: + +1. Dynamically allocated arrays (vectors) +2. Converting a vector into a string (has to be a `char` vector) +3. Popping the last value of a vector and returning it +4. Getting and settings a value of a vector on a given index + +#### TODO: + +1. Insertion of a vector into another vector + ++ You tell me diff --git a/demo.c b/demo.c new file mode 100644 index 0000000..7ae2030 --- /dev/null +++ b/demo.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) { + printf("demonstration of the power of dynamically allocated arrays in c:\n-----------\n\n"); + + printf("initializing an integer vector\n"); + vector_t *v = vectorInit(INT); + + printf("vector after initialization\n"); + vectorPrint(v); + + printf("pushing an element of value 1 into vector\n"); + int element = 1; + vectorPush(v, &element); + vectorPrint(v); + + printf("pushing another element (value 2) into vector\n"); + element = 2; + vectorPush(v, &element); + vectorPrint(v); + + printf("inserting 3 at index 1\n"); + element = 3; + vectorInsertAt(v, 1, &element); + vectorPrint(v); + + printf("popping the last value from the vector\n"); + int popped = *((int *)vectorPop(v)); + printf("popped: %d\n", popped); + printf("vector after popping\n"); + vectorPrint(v); + + printf("initializing a second vector of type vector_t (multidimensional vector => vector of vectors)\n"); + vector_t *w = vectorInit(VECTOR); + + printf("vector after initialization\n"); + vectorPrint(w); + + printf("pushing the first vector into the newly created one\n"); + vectorPush(w, v); + vectorPrint(w); + + printf("initializing a char vector (variable-length string) with value \"Hello, world!\"\n"); + vector_t *charVector = vectorInit(CHAR); + char *word = "Hello, world!"; + for (int i = 0; i < strlen(word); i++) { + vectorPush(charVector, &word[i]); + } + printf("char vector as string\n"); + vectorPrintAsString(charVector); + + printf("char vector converted into a regular string\n"); + char *newWord = vectorToString(charVector); + printf("new word: %s\n", newWord); + + printf("setting a value in the char vector on index 10 to \"\" (nothing, effectively deleting the character, although the size of the vector stays the same!)\n"); + vectorSetAt(charVector, 10, ""); // should change the vector to "Hello, word!" (size stays the same!) + vectorPrintAsString(charVector); + + printf("files section\n---------------\n"); + printf("initializing a file called test.txt with mode \"r\" (reading it)\n"); + file_t* f = fileInit("test.txt", "r"); + + printf("printing the file contents\n"); + filePrint(f); + printf("file length (line count): %d\n", f->length); + printf("file as vector\n"); + vectorPrint(f->lines); + printf("file to string\n"); + char* strFile = fileToString(f); + printf("%s", strFile); + + printf("cleaning up vectors\n"); + vectorCleanup(v); + vectorCleanup(w); + vectorCleanup(charVector); + + printf("cleaning up files\n"); + fileCleanup(f); + + return 0; +} diff --git a/include/file.h b/include/file.h new file mode 100644 index 0000000..a810e73 --- /dev/null +++ b/include/file.h @@ -0,0 +1,24 @@ +#ifndef FILE_H +#define FILE_H + +#include +#include + +typedef struct { + FILE* descriptor; // the file descriptor + int length; // line count + vector_t* lines; // 2d array of chars +} file_t; + +file_t* fileInit(char* path, char* mode); // initialization function + // TODO: when there is not a '\n' at the end of a file, the last line is not loaded :( + +void fileCleanup(file_t* f); // just to be memory safe... + +int fileCountLines(file_t* f); // + +void filePrint(file_t* f); + +char* fileToString(file_t* f); + +#endif \ No newline at end of file diff --git a/include/vector.h b/include/vector.h new file mode 100644 index 0000000..55dc37f --- /dev/null +++ b/include/vector.h @@ -0,0 +1,54 @@ +#ifndef VECTOR_H +#define VECTOR_H + +typedef long unsigned int size_t; + +typedef enum { + INT, + FLOAT, + CHAR, + VECTOR // allows for multi-dimensional arrays +} dataType_e; + +typedef struct { + dataType_e type; + size_t size; + size_t capacity; + size_t elementSize; + void* data; +} vector_t; + +vector_t* +vectorInit(dataType_e dataType); // size gets determined by the dataType and the capacity starts at 1 and x1.5 every time the vector gets filled up + +void +vectorReset(vector_t* v); // sets the vector to its original state + +void* +vectorPop(vector_t* v); // remove one value from the end of the vector and return it + +void +vectorPush(vector_t* v, void* element); // push one value at the end of the vector + +void +vectorCleanup(vector_t* v); // freeing pointers + +void +vectorPrint(vector_t* v); // shows the vector + +void +vectorPrintAsString(vector_t* v); // show the vector as a string if the v->type is CHAR + +void +vectorInsertAt(vector_t* v, int index, void* element); // inserts an element 'element' at index 'index' + +void +vectorSetAt(vector_t* v, int index, void* element); // set an element's value at a given index + +char* +vectorToString(vector_t* v); // convert the vector into a regular string + +void* +vectorGetAt(vector_t* v, int index); // get an element at the given index + +#endif \ No newline at end of file diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..6378e99 --- /dev/null +++ b/src/file.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +int +fileCountLines(file_t* f) { + char ch; + int lines = 0; + + while ((ch = fgetc(f->descriptor)) != EOF) { + if (ch == '\n') { + lines++; + } + } + + rewind(f->descriptor); + + return lines; +} + +void +_fileLoadLines(file_t* f) { + int lineLength; + char ch; + vector_t* line = vectorInit(CHAR); + + for (int i = 0; i < f->length; i++) { + while ((ch = fgetc(f->descriptor)) != '\n' && ch != EOF) { + vectorPush(line, &ch); + } + + // Skip the newline character if it exists + if (ch == '\n') { + char* empty = ""; + vectorPush(line, empty); + } + + vectorPush(f->lines, line); + line = vectorInit(CHAR); + } + + rewind(f->descriptor); +} + +char* +fileToString(file_t* f) { + return (char *)vectorToString(f->lines); +} + +file_t* +fileInit(char* path, char* mode) { + file_t* f = malloc(sizeof(file_t)); + if (f == NULL) { + perror("failed to allocate memory for the file\n"); + exit(EXIT_FAILURE); + } + f->lines = malloc(sizeof(vector_t)); + if (f->lines == NULL) { + perror("failed to allocate memory for the file contents vector\n"); + exit(EXIT_FAILURE); + } + int imode = 0; + + if (strcmp(mode, "r") == 0) { + imode = 0; + } else if (strcmp(mode, "w") == 0) { + imode = 1; + } else if (strcmp(mode, "a") == 0) { + imode = 2; + } else { + perror("only allowed modes for file opening are \"r\", \"w\" and \"a\"\n"); + exit(EXIT_FAILURE); + } + + f->descriptor = fopen(path, mode); + if (f->descriptor == NULL) { + fprintf(stderr, "unable to open file %s with mode %s\n", path, mode); + fileCleanup(f); + exit(EXIT_FAILURE); + } + + f->length = fileCountLines(f); + f->lines = vectorInit(VECTOR); // vector of vectors of chars = vector of strings + if (imode == 0) _fileLoadLines(f); + + return f; +} + +void +fileCleanup(file_t* f) { + vectorCleanup(f->lines); + fclose(f->descriptor); + free(f); +} + +void +filePrint(file_t* f) { + vector_t* line = vectorInit(CHAR); + for (int i = 0; i < f->length; i++) { + line = (vector_t *)vectorGetAt(f->lines, i); + vectorPrintAsString(line); + } +} \ No newline at end of file diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..ac90b21 --- /dev/null +++ b/src/vector.c @@ -0,0 +1,385 @@ +#include +#include +#include +#include + +void +vectorCleanup(vector_t* v) { + free(v->data); + free(v); +} + +int printNewline = 0; + +void +vectorReset(vector_t* v) { + while (v->size > 0) { + vectorPop(v); + } +} + +void +_printVectorRecursive(vector_t* v, int depth) { + int i; + fprintf(stdout, "["); + for (i = 0; i < v->size; i++) { + switch (v->type) { + case INT: + fprintf(stdout, "%d", *((int *)(v->data) + i)); + break; + case FLOAT: + fprintf(stdout, "%f", *((float *)(v->data) + i)); + break; + case CHAR: + fprintf(stdout, "%c", *((char *)(v->data) + i)); + break; + case VECTOR: + // Recursively print each level of the vector + _printVectorRecursive((vector_t *)(v->data + i * v->elementSize), depth + 1); + break; + default: + perror("tried to print unsupported data type\n"); + exit(EXIT_FAILURE); + } + + if (i < v->size - 2 && i != v->size - 1) { + fprintf(stdout, ", "); + } + } + fprintf(stdout, "]"); + if (depth != 0) { + fprintf(stdout, "\n"); + } +} + +void +vectorPrint(vector_t* v) { + fprintf(stdout, "["); + int i; + for (i = 0; i < v->size; i++) { + switch (v->type) { + case INT: + fprintf(stdout, "%d", *((int *)(v->data) + i)); + break; + case FLOAT: + fprintf(stdout, "%f", *((float *)(v->data) + i)); + break; + case CHAR: + fprintf(stdout, "%c", *((char *)(v->data) + i)); + break; + case VECTOR: + // Print an nD vector + _printVectorRecursive((vector_t *)(v->data + i * v->elementSize), 0); + break; + default: + perror("tried to print unsupported data type\n"); + exit(EXIT_FAILURE); + } + + if (i < v->size - 1) { + fprintf(stdout, ", "); + } + } + fprintf(stdout, "]"); + if (printNewline == 0) { + fprintf(stdout, "\n"); + } else { + fprintf(stdout, "]"); + } +} + +void +_printVectorAsStringRecursive(vector_t* v, int depth) { + int i; + for (i = 0; i < v->size; i++) { + switch (v->type) { + case CHAR: + fprintf(stdout, "%c", *((char *)(v->data) + i)); + break; + case VECTOR: + // Recursively print each level of the vector + _printVectorAsStringRecursive((vector_t *)(v->data + i * v->elementSize), depth + 1); + break; + default: + perror("tried to print a non-char type vector as string\n"); + exit(EXIT_FAILURE); + } + } + if (depth != 0) fprintf(stdout, "\n"); // do not print the last additional new line +} + +void +vectorPrintAsString(vector_t* v) { + switch (v->type) { + case CHAR: + for (int i = 0; i < v->size; i++) { + fprintf(stdout, "%c", *((char *)(v->data) + i)); + } + fprintf(stdout, "\n"); + break; + case VECTOR: + _printVectorAsStringRecursive(v, 0); + break; + default: + perror("tried to print a non-char type vector as string\n"); + exit(EXIT_FAILURE); + } +} + +vector_t* +vectorInit(dataType_e type) { + vector_t *v = malloc(sizeof(vector_t)); + if (v == NULL) { + perror("failed to allocate memory for the vector\n"); + exit(EXIT_FAILURE); + } + + v->type = type; + v->size = 0; + v->capacity = 1; + switch (v->type) { + case INT: + v->elementSize = sizeof(int); + break; + case FLOAT: + v->elementSize = sizeof(float); + break; + case CHAR: + v->elementSize = sizeof(char); + break; + case VECTOR: + v->elementSize = sizeof(vector_t); + break; + default: + perror("specify a valid vector type\n"); + exit(EXIT_FAILURE); + } + v->data = malloc(v->elementSize * v->capacity); + + if (v->data == NULL) { + perror("failed to allocate memory for vector data\n"); + exit(EXIT_FAILURE); + } + + return v; +} + +void +vectorPush(vector_t* v, void* element) { + switch (v->type) { + case INT: + *((int *)(v->data) + v->size) = *((int *)element); + break; + case FLOAT: + *((float *)(v->data) + v->size) = *((float *)element); + break; + case CHAR: + *((char *)(v->data) + v->size) = *((char *)element); + break; + case VECTOR: + // Instead of reallocating memory for vector_t pointers, allocate memory for the vector data + if (v->size == 0) { + // Allocate memory for the vector data for the first time + v->data = malloc(v->elementSize * v->capacity); + } else { + // Reallocate memory for the vector data + v->data = realloc(v->data, v->elementSize * v->capacity); + } + if (v->data == NULL) { + perror("failed to allocate memory for vector data\n"); + exit(EXIT_FAILURE); + } + + // Copy the vector data from the element to the vector + memcpy((vector_t *)(v->data) + v->size, (vector_t *)element, sizeof(vector_t)); + + break; + default: + perror("specify a valid vector type\n"); + exit(EXIT_FAILURE); + } + + v->size += 1; + if (v->size == v->capacity) { + v->capacity *= 2; + } +} + +void* +vectorPop(vector_t* v) { + // capacity is always a power of 2, so a rounding error cannot happen i guess _/-(shi)-\_ + + switch (v->type) { + case INT: + int *iretval = (int *)(v->data) + v->size-1; + v->size -= 1; + if (v->size <= v->capacity / 2) v->capacity /= 2; + v->data = realloc(v->data, v->elementSize * v->capacity); + if (v->capacity == 0) v->capacity = 1; + return iretval; + case FLOAT: + float *fretval = (float *)(v->data) + v->size-1; + v->size -= 1; + if (v->size <= v->capacity / 2) v->capacity /= 2; + if (v->capacity == 0) v->capacity = 1; + v->data = realloc(v->data, v->elementSize * v->capacity); + return fretval; + case CHAR: + char *cretval = (char *)(v->data) + v->size-1; + v->size -= 1; + if (v->size <= v->capacity / 2) v->capacity /= 2; + if (v->capacity == 0) v->capacity = 1; + v->data = realloc(v->data, v->elementSize * v->capacity); + return cretval; + case VECTOR: + vector_t *vretval = (vector_t *)(v->data) + v->size-1; + v->size -= 1; + if (v->size <= v->capacity / 2) v->capacity /= 2; + if (v->capacity == 0) v->capacity = 1; + v->data = realloc(v->data, v->elementSize * v->capacity); + return vretval; + default: + perror("specify a valid vector type\n"); + exit(EXIT_FAILURE); + } +} + +char* +vectorToString(vector_t* v) { + char* retString; + int totalSize = 0; + + // Calculate the total size + for (int i = 0; i < v->size; i++) { + if (v->type == CHAR) { + totalSize += 1; // For a single character + } else if (v->type == VECTOR) { + vector_t* innerVector = (vector_t *)vectorGetAt(v, i); + char* innerString = vectorToString(innerVector); + totalSize += strlen(innerString); + + // Add newline between vectors (except the last one) + if (i < v->size - 1) { + totalSize += 1; + } + + free(innerString); // Free the memory allocated for innerString + } + } + + // Allocate memory for the string + retString = (char *)malloc((totalSize + 1 + 1) * sizeof(char)); // +1 for null terminator; +1 for ending '\n' + + // Copy the elements to the string + int index = 0; + for (int i = 0; i < v->size; i++) { + if (v->type == CHAR) { + retString[index++] = *((char *)vectorGetAt(v, i)); + } else if (v->type == VECTOR) { + vector_t* innerVector = (vector_t *)vectorGetAt(v, i); + char* innerString = vectorToString(innerVector); + strcpy(retString + index, innerString); + index += strlen(innerString); + + // Add newline between vectors (including the last one) + if (i < v->size) { + retString[index++] = '\n'; + } + + free(innerString); // Free the memory allocated for innerString + } + } + + retString[index] = '\0'; // Null-terminate the string + + return retString; +} + +void* +vectorGetAt(vector_t* v, int index) { + switch (v->type) { + case INT: + return (int *)(v->data) + index; + case FLOAT: + return (float *)(v->data) + index; + case CHAR: + return (char *)(v->data) + index; + case VECTOR: + return (vector_t *)(v->data) + index; + default: + perror("specify a valid vector type\n"); + exit(EXIT_FAILURE); + } +} + +void +vectorSetAt(vector_t* v, int index, void* element) { + if (index >= v->size) { + perror("index out of range\n"); + exit(EXIT_FAILURE); + } + + switch (v->type) { + case INT: + *((int *)(v->data) + index) = *((int *)element); + break; + case FLOAT: + *((float *)(v->data) + index) = *((float *)element); + break; + case CHAR: + *((char *)(v->data) + index) = *((char *)element); + break; + case VECTOR: + *((vector_t *)(v->data) + index) = *((vector_t *)element); + break; + default: + perror("specify a valid vector type\n"); + exit(EXIT_FAILURE); + } +} + +void +vectorInsertAt(vector_t* v, int index, void* element) { + if (index >= v->size) { + perror("index out of range\n"); + exit(EXIT_FAILURE); + } + + v->size += 1; + if (v->size >= v->capacity) { + v->capacity *= 2; + v->data = realloc(v->data, v->elementSize * v->capacity); + } + + switch (v->type) { + case INT: + // move all elements to the right by one + for (int i = v->size-1; i > index; i--) { + *((int *)(v->data) + i) = *((int *)(v->data) + i-1); + } + // set the element at the given index + *((int *)(v->data) + index) = *((int *)element); + break; + case FLOAT: + for (int i = v->size-1; i > index; i--) { + *((float *)(v->data) + i) = *((float *)(v->data) + i-1); + } + *((float *)(v->data) + index) = *((float *)element); + break; + case CHAR: + for (int i = v->size-1; i > index; i--) { + *((char *)(v->data) + i) = *((char *)(v->data) + i-1); + } + *((char *)(v->data) + index) = *((char *)element); + break; + case VECTOR: + for (int i = v->size-1; i > index; i--) { + *((vector_t *)(v->data) + i) = *((vector_t *)(v->data) + i-1); + } + *((vector_t *)(v->data) + index) = *((vector_t *)element); + break; + default: + perror("specify a valid vector type\n"); + exit(EXIT_FAILURE); + } +} \ No newline at end of file diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..c009799 --- /dev/null +++ b/test.txt @@ -0,0 +1,6 @@ +this !1@#% +is a big +mega such large + +test +and