Compare commits

...

22 Commits

Author SHA1 Message Date
4567b1bfd5 Merge pull request 'Zone Freeing - No Fragmentation' (#12) from zone/real-free into main
Reviewed-on: #12
2024-02-18 19:26:19 +00:00
957ba0880d Zone Freeing - No Fragmentation
Now have the ability to keep track of freed zones and will now allocate
to those first before allocating new memory in a zone so that we do not
fragment our zones.
2024-02-18 14:23:34 -05:00
757d795749 Merge pull request 'Linked List Fixes' (#11) from list/fixes into main
Reviewed-on: #11
2024-02-15 22:05:05 +00:00
181ed5f732 Linked List Fixes
Fixed up where there was no error checking of position when inserting
into a position into the list. Also made it so that listCreate returns a
pointer to a list instead of a list so that we can move the definition
of List into the implementation file instead of the header.
2024-02-15 17:00:19 -05:00
1966ddf800 Merge pull request 'Linked List' (#10) from linked-list into main
Reviewed-on: #10
2024-02-15 01:14:27 +00:00
7d7d671dca Linked List
Added in the first support for a linked list type. It is called a List
type in the library. Have tested the functionality and so far all seems
good
2024-02-14 20:12:15 -05:00
3667b777f2 Merge pull request 'Log File' (#9) from logger/file into main
Reviewed-on: #9
2024-02-10 19:04:11 +00:00
446fa704cc Log File
Added in the ability to write logs to a log file as well. For now the
logs write to ./quantum.log. All new log entries append onto the end of
the log. Also added timestamps in front of all log entries that are
generated when the log entry is made.
2024-02-10 13:59:43 -05:00
78798a42fd Merge pull request 'Log Colors' (#8) from logger/colors into main
Reviewed-on: #8
2024-02-10 16:26:48 +00:00
75b5767e36 Log Colors
Added colored output to the logs. Also added the logs to zone for
outputting errors using our logger instead of printf().
2024-02-10 11:24:10 -05:00
7b32a08add Merge pull request 'Logging' (#7) from logging into main
Reviewed-on: #7
2024-02-10 14:52:46 +00:00
16e45e4675 Logging
Got a very basic logger up and working. Can be used with the Q[LEVEL]
macros. Still needs some work to add coloring to the console and to be
able to write logs out to files.
2024-02-10 09:50:02 -05:00
abbceb3443 Merge pull request 'Fix and Free' (#6) from zone/fix-and-free into main
Reviewed-on: #6
2024-02-08 22:53:00 +00:00
a13b6fcac5 Fix and Free
Added the ability to free data from a zone. Also made some fixes to make
sure that our numbers format properly in print statements.
2024-02-08 17:50:46 -05:00
9f9bcf215f Merge pull request 'Memory Zones' (#5) from zones into main
Reviewed-on: #5
2024-02-07 23:28:58 +00:00
1448d3336b Memory Zones
First draft of memory zone functionality. Can create a zone and allocate
you memory from the zone. Also has the ability to clear zones and to
free. There is no way yet to free anything that has been allocated on
the zones.
2024-02-07 18:25:13 -05:00
6f32314daa Merge pull request 'Added variables to CMAkeLists.txt' (#4) from build-patch into main
Reviewed-on: #4
2024-02-05 01:10:21 +00:00
ce8103fa81 Added variables to CMAkeLists.txt
Added in variables to CMakeLists.txt to make managing directories easier
in the future.
2024-02-04 20:09:22 -05:00
8927d62cf3 Merge pull request 'CMake Migration' (#3) from cmake-migration into main
Reviewed-on: #3
2024-02-05 00:16:33 +00:00
cfe045acc3 CMake Migration
Moved the build system over to CMake. This build system right now
replicates the same functionality as our bash scripts from earlier. Will
need a lot of refactoring to be more complete, but for now we are fully
on CMake.
2024-02-04 19:13:13 -05:00
9854118958 Merge pull request 'Build Script Tweaks' (#2) from build-patch into main
Reviewed-on: #2
2024-02-04 18:52:04 +00:00
0f0ecf0307 Build Script Tweaks
Changed location of the /build directory to be in the root to make for
easier cleaning of project. Added in a clean-all.sh script as well to
clean everything.

Clean Script

Added in a way to clean the build environment as well.
2024-02-04 13:49:39 -05:00
15 changed files with 652 additions and 77 deletions

7
.gitignore vendored
View File

@@ -52,5 +52,8 @@ Module.symvers
Mkfile.old
dkms.conf
# Ignore /bin directory
/bin
# Ignore /build directory from cmake
build/
# Ignore the .cache/ directory that clangd gernates
.cache/

18
CMakeLists.txt Normal file
View File

@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.28.1)
project(quantum-utils)
add_compile_options(-g)
set(quantum-src "./quantum/src/")
set(test_stuff-src "./test_stuff/src/")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_library(
quantum SHARED ${quantum-src}memory/zone.c ${quantum-src}logger/logger.c
${quantum-src}types/linked_list.c)
add_executable(test_stuff ${test_stuff-src}main.c)
target_link_libraries(test_stuff quantum)
target_include_directories(test_stuff PUBLIC ${quantum-src})

View File

@@ -1,29 +0,0 @@
#!/bin/bash
#
# Build script to build everything
set echo on
mkdir -p bin/
echo "Building all.."
pushd quantum
source build.sh
popd
ERRORLEVEL=$?
if [ $ERRORLEVEL -ne 0 ]; then
echo "Error:"$ERRORLEVEL && exit
fi
pushd test_stuff
source build.sh
popd
ERRORLEVEL=$?
if [ $ERRORLEVEL -ne 0 ]; then
echo "Error:"$ERRORLEVEL && exit
fi
echo "All assemblies built successfully!!!!"

View File

@@ -1,23 +0,0 @@
#!/bin/bash
#
# Build script for quantum
set echo on
mkdir -p build/
# Get a list of all the .c files.
cFilenames=$(find . -type f -name "*.c")
assembly="quantum"
objLocation="build/libquantum.o"
compilerFlags="-g -c -fpic"
includeFlags="-Isrc"
echo "Building $assembly..."
# First we need to compile down to .o files
gcc $cFilenames $compilerFlags -o $objLocation $includeFlags
compilerFlags="-g -shared"
gcc $objLocation $compilerFlags -o ../bin/libquantum.so $includeFlags

22
quantum/src/defines.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <assert.h>
#include <inttypes.h>
typedef int16_t i16;
typedef uint16_t u16;
typedef int32_t i32;
typedef uint32_t u32;
typedef int64_t i64;
typedef uint64_t u64;
typedef float f32;
typedef double f64;
static_assert(sizeof(i16) == 2, "");
static_assert(sizeof(u16) == 2, "");
static_assert(sizeof(i32) == 4, "");
static_assert(sizeof(u32) == 4, "");
static_assert(sizeof(i64) == 8, "");
static_assert(sizeof(u64) == 8, "");
static_assert(sizeof(f32) == 4, "");
static_assert(sizeof(f64) == 8, "");

View File

@@ -0,0 +1,84 @@
#include "logger.h"
#include "../defines.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
typedef enum level_color {
COLOR_FATAL,
COLOR_ERROR,
COLOR_WARN,
COLOR_INFO,
COLOR_DEBUG,
} level_color;
void send_to_console(const char *message, level_color color) {
const char *color_strings[] = {"41;97", "0;91", "0;93", "0;94", "0;92"};
printf("\033[%sm%s\033[0m\n", color_strings[color], message);
}
void send_to_error_log(const char *message) {
// First we need to open up our log file to append to it
FILE *log_file = fopen("./quantum.log", "a");
// Lets make sure we actually opened the file, and if not we return
if (log_file == NULL) {
send_to_console("[ERROR]: Could not open log file.", COLOR_ERROR);
return;
}
// Now we just need to write our message to the file
int chars_written = fprintf(log_file, "%s\n", message);
// fprintf returns a negative number on an error so let's check for that and
// display an error message
if (chars_written < 0) {
send_to_console("[ERROR]: Could not write to log file.", COLOR_ERROR);
}
// Now we need to make sure we close the file so that we do not leak
fclose(log_file);
}
void log_output(log_level level, const char *message, ...) {
const char *level_strings[] = {
"[FATAL]: ", "[ERROR]: ", "[WARN]: ", "[INFO]: ", "[DEBUG]: "};
// We are going to want to put a timestamp on our logs so lets get the current
// time and convert it into localtime
time_t cur_time = time(NULL);
struct tm local_time = *localtime(&cur_time);
// We are going to impose a 32K char limit on a single log entry
// we will hold this in format_message as a place to collect all
// the variadic arguments and process them together. We make sure
// that the memory is cleared to 0 before we start working on it.
const i32 msg_length = 32000;
char format_message[msg_length];
memset(format_message, 0, sizeof(format_message));
// Now we need to assemble the message from the message +
// the variadic arguments
__builtin_va_list arg_ptr;
va_start(arg_ptr, message);
vsnprintf(format_message, msg_length, message, arg_ptr);
va_end(arg_ptr);
// Now we prepend the message with a timestamp and our message level
char log_message[msg_length];
sprintf(log_message, "[%02d-%02d-%d %02d:%02d:%02d]%s%s",
local_time.tm_mon + 1, local_time.tm_mday, local_time.tm_year + 1900,
local_time.tm_hour, local_time.tm_min, local_time.tm_sec,
level_strings[level], format_message);
// Now we call the appropriate function depending on the level
if (level <= 1) {
send_to_console(log_message, level == FATAL ? COLOR_FATAL : COLOR_ERROR);
send_to_error_log(log_message);
} else {
send_to_console(log_message, (level_color)level);
}
}

View File

@@ -0,0 +1,17 @@
#pragma once
typedef enum log_level {
FATAL,
ERROR,
WARN,
INFO,
DEBUG,
} log_level;
void log_output(log_level level, const char *message, ...);
#define QFATAL(message, ...) log_output(FATAL, message, ##__VA_ARGS__);
#define QERROR(message, ...) log_output(ERROR, message, ##__VA_ARGS__);
#define QWARN(message, ...) log_output(WARN, message, ##__VA_ARGS__);
#define QINFO(message, ...) log_output(INFO, message, ##__VA_ARGS__);
#define QDEBUG(message, ...) log_output(DEBUG, message, ##__VA_ARGS__);

115
quantum/src/memory/zone.c Normal file
View File

@@ -0,0 +1,115 @@
#include <stdio.h> // printf()
#include <stdlib.h> // malloc() free()
#include <string.h> // memset()
#include "../defines.h"
#include "../logger/logger.h"
#include "../types/linked_list.h"
#include "zone.h"
typedef struct ZoneHeader {
u64 capacity;
u64 cur_size;
List *free_zones;
} ZoneHeader;
typedef struct FreeZone {
void *start_address;
u64 capacity;
} FreeZone;
Zone *zoneCreate(size_t sizeBytes) {
// First we need to get a block of memory from the OS
// This block needs to include the size of what we want to store
// plus the size of our ZoneHeader
void *mem_block = malloc(sizeBytes + sizeof(ZoneHeader));
// Now that we have the block let's get the addresses
// for where the header is, and the offset where the data starts
ZoneHeader *zone_header = (ZoneHeader *)mem_block;
Zone *zone_addr = (Zone *)(zone_header + sizeof(ZoneHeader));
// Initialize the values of the header
zone_header->capacity = sizeBytes;
zone_header->cur_size = 0;
zone_header->free_zones = listCreate();
return zone_addr;
}
void *zoneAlloc(Zone *zone, size_t sizeBytes) {
ZoneHeader *zone_header = (ZoneHeader *)zone - sizeof(ZoneHeader);
u64 cur_free_zones = listSize(zone_header->free_zones);
if (cur_free_zones > 0) {
for (u64 i = 1; i <= cur_free_zones; i++) {
FreeZone *curFreeZone =
(FreeZone *)listDataAt(zone_header->free_zones, i);
u64 freeZoneSize = curFreeZone->capacity;
if (freeZoneSize >= sizeBytes) {
void *ret_addr = curFreeZone->start_address;
curFreeZone->start_address += sizeBytes;
curFreeZone->capacity -= sizeBytes;
if (curFreeZone->capacity <= 0) {
listRemoveAt(zone_header->free_zones, i);
free(curFreeZone);
}
return ret_addr;
}
}
}
if (zone_header->cur_size + sizeBytes > zone_header->capacity) {
QERROR("Could not allocate, not enough space.");
return NULL;
}
void *new_mem = (char *)zone + zone_header->cur_size;
zone_header->cur_size += sizeBytes;
return new_mem;
}
void zoneFree(void *data, Zone *zone, size_t dataSize) {
// Lets first check to make sure we weren't given a NULL pointer
if (data == NULL)
return;
// First we need to set the memory starting at `data` and going to `dataSize`
// to 0
memset(data, 0, dataSize);
FreeZone *newFreeZone = malloc(sizeof(FreeZone));
ZoneHeader *zone_header = (ZoneHeader *)zone - sizeof(ZoneHeader);
newFreeZone->start_address = data;
newFreeZone->capacity = dataSize;
listPush(zone_header->free_zones, newFreeZone);
// Now we need to null the pointer
data = NULL;
}
void zoneClear(Zone *zone) {
ZoneHeader *zone_header = (ZoneHeader *)zone - sizeof(ZoneHeader);
zone_header->cur_size = 0;
}
void zoneDestroy(Zone *zone) {
// First we need to go back to the beginning of the header as we returned
// an offset for the user to use
ZoneHeader *del_mem = (ZoneHeader *)zone - sizeof(ZoneHeader);
// Next we need to destory our free_zones list
listDestroy(del_mem->free_zones);
// Free the memory
free(del_mem);
}

11
quantum/src/memory/zone.h Normal file
View File

@@ -0,0 +1,11 @@
#pragma once
#include <stddef.h> // size_t definition
typedef struct Zone Zone;
Zone *zoneCreate(size_t sizeBytes);
void *zoneAlloc(Zone *zone, size_t sizeBytes);
void zoneFree(void *data, Zone *zone, size_t dataSize);
void zoneClear(Zone *zone);
void zoneDestroy(Zone *zone);

View File

@@ -1,5 +0,0 @@
#include <stdio.h>
#include "test.h"
void hello_quantum() { printf("Hello from Quantum!\n"); }

View File

@@ -1 +0,0 @@
void hello_quantum();

View File

@@ -0,0 +1,332 @@
#include "linked_list.h"
#include "../defines.h"
#include "../logger/logger.h"
#include <stddef.h>
#include <stdlib.h>
typedef struct Node {
struct Node *next;
struct Node *prev;
void *data;
} Node;
typedef struct List {
Node *cur_head;
Node *cur_tail;
u64 cur_size;
} List;
List *listCreate() {
List *new_list = malloc(sizeof(List));
new_list->cur_size = 0;
new_list->cur_head = NULL;
new_list->cur_tail = NULL;
return new_list;
}
void listDestroy(List *list) {
// First lets make sure we don't have a null pointer passed in
if (list == NULL) {
QWARN("Null list passed to listDestroy().");
return;
}
Node *del_node = list->cur_tail;
Node *next_node;
while (del_node != NULL) {
// We want to set del_node to the next_node to free
next_node = del_node->prev;
// Then we want to free del_node
free(del_node);
// Finally we want to set del_node to the next_node to free
del_node = next_node;
}
// Now we null out the cur_head and cur_tail of our list
list->cur_tail = NULL;
list->cur_head = NULL;
// Now we free out the list and make it point to nothing
free(list);
}
u64 listSize(const List *list) {
if (list != NULL)
return list->cur_size;
return 0;
}
void *listPeek(List *list) {
if (list != NULL && list->cur_size > 0)
return list->cur_tail->data;
return NULL;
}
void *listPop(List *list) {
if (list == NULL) {
QWARN("Null list passed to listPop()");
return NULL;
}
Node *pop_node = list->cur_tail;
Node *prev_node = list->cur_head == list->cur_tail ? NULL : pop_node->prev;
void *data = pop_node->data;
if (prev_node != NULL)
prev_node->next = NULL;
list->cur_size--;
list->cur_tail = prev_node;
free(pop_node);
return data;
}
void listPush(List *list, void *data) {
// Allocate the space for our new node
Node *new_node = malloc(sizeof(Node));
// Make sure that we were able to allocate the space, and if not we write an
// error and return
if (new_node == NULL) {
QERROR("Could not allocate space to insert data into list.");
return;
}
// If the current size of our list is 0
if (list->cur_size == 0) {
// Set the head and tail pointer to the new node as we are about to have
// only 1 element
list->cur_tail = new_node;
list->cur_head = new_node;
// Set the next and previous of the new node to NULL as we will only
// have 1 element and nothing to point to
new_node->next = NULL;
new_node->prev = NULL;
} else {
// Set the next of new_node to NULL as it is the new tail,
// and also update prev of new_node to point to the current tail
new_node->next = NULL;
new_node->prev = list->cur_tail;
// Set the next of our current tail to point to the new tail
list->cur_tail->next = new_node;
}
// Set the data of the new node to the data we passed in
new_node->data = data;
// Update the list size
list->cur_size++;
// Set the list tail pointer to the new node
list->cur_tail = new_node;
}
void *listPeekFromFront(List *list) {
if (list != NULL && list->cur_size > 0)
return list->cur_head->data;
return NULL;
}
void *listPopFromFront(List *list) {
if (list == NULL) {
QERROR("Null list passed to listPopFromFront()");
return NULL;
}
Node *pop_node = list->cur_head;
Node *next_node = list->cur_head == list->cur_tail ? NULL : pop_node->next;
void *data = pop_node->data;
if (next_node != NULL)
next_node->next = NULL;
list->cur_size--;
list->cur_head = next_node;
free(pop_node);
return data;
}
void listPushFromFront(List *list, void *data) {
// Allocate the space for our new node
Node *new_node = malloc(sizeof(Node));
// Make sure that we were able to allocate the space, and if not we write an
// error and return
if (new_node == NULL) {
QERROR("Could not allocate space to insert data into list.");
return;
}
// If the current size of our list is 0
if (list->cur_size == 0) {
// Set the head and tail pointer to the new node as we are about to have
// only 1 element
list->cur_tail = new_node;
list->cur_head = new_node;
// Set the next and previous of the new node to NULL as we will only
// have 1 element and nothing to point to
new_node->next = NULL;
new_node->prev = NULL;
} else {
// Set the prev of new_node to NULL as it is the new head,
// and also update next of new_node to point to the current head
new_node->next = list->cur_head;
new_node->prev = NULL;
// Set the prev of our current head to point to the new head
list->cur_head->prev = new_node;
}
// Set the data of the new node to the data we passed in
new_node->data = data;
// Update the list size
list->cur_size++;
// Set the list head pointer to the new node
list->cur_head = new_node;
}
void *listDataAt(List *list, u64 pos) {
// Make sure our list isn't NULL or empty, if it is return NULL
if (list == NULL || list->cur_size == 0) {
QWARN("Null or empty list passed into listDataAt()");
return NULL;
}
// Next we want to make sure that our position is in range
if (pos < 1 || pos > list->cur_size) {
QERROR("Position given is out of range: %" PRId64, pos);
return NULL;
}
// This is the pointer to the node with the data we will be returning
Node *data_addr = list->cur_head;
// This is to keep track of what position in the list we are on
u64 counter = 1;
// While we arent at our position yet
while (counter != pos) {
// Get the next nodes address
data_addr = data_addr->next;
// And increment the counter
counter++;
}
// Return the data from the node
return data_addr->data;
}
void listInsertAt(List *list, u64 pos, void *data) {
// If our list is null return.
if (list == NULL) {
QWARN("A null list was passed into listInsertAt()");
return;
}
// Make sure we passed in a valid position
if (pos < 1 || pos > list->cur_size) {
// If we have elements in the list
if (list->cur_size != 0) {
// Then the position is actually out of bounds, so return.
QERROR("Position given is out of range: %" PRId64, pos);
return;
}
}
u64 counter = 1;
Node *insert_pos;
Node *prev_node;
Node *new_node = malloc(sizeof(Node));
if (list->cur_size == 0) {
insert_pos = new_node;
new_node->next = NULL;
new_node->prev = NULL;
pos = 1;
} else {
insert_pos = list->cur_head;
}
new_node->data = data;
while (counter != pos) {
insert_pos = insert_pos->next;
counter++;
}
if (insert_pos->prev != NULL)
prev_node = insert_pos->prev;
if (list->cur_size > 1) {
new_node->next = insert_pos;
new_node->prev = prev_node;
insert_pos->prev = new_node;
prev_node->next = new_node;
}
list->cur_size++;
}
void listRemoveAt(List *list, u64 pos) {
// If we were passed a null or empty list return
if (list == NULL || list->cur_size == 0) {
QWARN("A null list was passed into listRemoveAt()");
return;
}
// If we give a value that is outside of our list return
if (pos < 1 || pos > list->cur_size) {
QERROR("An invalid index was passed to listRemoveAt()");
return;
}
// This is the node we are going to be removing
Node *del_node = list->cur_head;
// This is a counter to keep our position in the list
u64 counter = 1;
// While we aren't at our position
while (counter != pos) {
// Set the node to the next node
del_node = del_node->next;
// And increase the counter
counter++;
}
// Now we are at the position we want to delete, so let's get the address of
// the next and previous nodes
Node *prev_node = del_node->prev;
Node *next_node = del_node->next;
// Now let's update their prev/next values accordingly
if (prev_node != NULL || next_node != NULL) {
prev_node->next = next_node;
next_node->prev = prev_node;
}
// Finally we can free del_node
free(del_node);
// Then we decrement the size of our list
list->cur_size--;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "../defines.h"
typedef struct List List;
List *listCreate();
void listDestroy(List *list);
u64 listSize(const List *list);
void *listPeek(List *list);
void *listPop(List *list);
void listPush(List *list, void *data);
void *listPeekFromFront(List *list);
void *listPopFromFront(List *list);
void listPushFromFront(List *list, void *data);
void *listDataAt(List *list, u64 pos);
void listInsertAt(List *list, u64 pos, void *data);
void listRemoveAt(List *list, u64 pos);

View File

@@ -1,15 +0,0 @@
#!/bin/bash
#Build script for test_stuff
set echo on
# Get a list of all the .c files.
cFilenames=$(find . -type f -name "*.c")
assembly="test_stuff"
compilerFlags="-g -fpic"
includeFlags="-Isrc -I../quantum/src/"
linkerFlags="-L../bin/ -lquantum -Wl,-rpath,."
echo "Building $assembly..."
gcc $cFilenames $compilerFlags -o ../bin/$assembly $includeFlags $linkerFlags

View File

@@ -1,7 +1,35 @@
#include <test.h>
#include <defines.h>
#include <logger/logger.h>
#include <memory/zone.h>
int main() {
hello_quantum();
QINFO("Size of i16: %" PRId16, sizeof(i16));
QINFO("Size of u16: %" PRIu16, sizeof(u16));
QINFO("Size of i32: %" PRId32, sizeof(i32));
QINFO("Size of u32: %" PRIu32, sizeof(u32));
QINFO("Size of i64: %" PRId64, sizeof(i64));
QINFO("Size of u64: %" PRIu64, sizeof(u64));
QINFO("Size of f32: %lu", sizeof(f32));
QINFO("Size of f64: %lu", sizeof(f64));
Zone *test_zone = zoneCreate(4096);
u64 val1 = 420;
u32 val2 = 169;
u32 val3 = 80085;
u64 *zVal1 = (u64 *)zoneAlloc(test_zone, sizeof(val1));
*zVal1 = val1;
zoneFree(zVal1, test_zone, sizeof(zVal1));
u32 *zVal2 = (u32 *)zoneAlloc(test_zone, sizeof(val2));
u32 *zVal3 = (u32 *)zoneAlloc(test_zone, sizeof(val3));
*zVal2 = val2;
*zVal3 = val3;
void *dont_care = zoneAlloc(test_zone, 64);
return 0;
}