Compare commits

...

14 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
10 changed files with 560 additions and 49 deletions

3
.gitignore vendored
View File

@@ -54,3 +54,6 @@ dkms.conf
# Ignore /build directory from cmake
build/
# Ignore the .cache/ directory that clangd gernates
.cache/

View File

@@ -2,10 +2,16 @@ 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_library(quantum SHARED ${quantum-src}memory/zone.c)
add_executable(test_stuff ${test_stuff-src}main.c)
target_link_libraries(test_stuff quantum)

View File

@@ -1,7 +1,7 @@
#pragma once
#include <inttypes.h>
#include <assert.h>
#include <inttypes.h>
typedef int16_t i16;
typedef uint16_t u16;
@@ -12,12 +12,11 @@ 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);
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__);

View File

@@ -1,15 +1,24 @@
#include <stdio.h> // printf()
#include <stdlib.h> // malloc() free()
#include <stdio.h> // printf()
#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;
Zone * zoneCreate(size_t sizeBytes) {
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
@@ -23,15 +32,39 @@ Zone * zoneCreate(size_t sizeBytes) {
// 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) {
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) {
printf("Could not allocate, not enough space.");
QERROR("Could not allocate, not enough space.");
return NULL;
}
@@ -39,27 +72,43 @@ void * zoneAlloc(Zone *zone, size_t sizeBytes) {
zone_header->cur_size += sizeBytes;
printf("Zone Header Information:\n");
printf("Current Size: %u\n", zone_header->cur_size);
printf("Total Capacity: %u\n", zone_header->capacity);
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;
printf("Zone Header Information:\n");
printf("Current Size: %u\n", zone_header->cur_size);
printf("Total Capacity: %u\n", zone_header->capacity);
}
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
void *del_mem = (ZoneHeader *)zone - sizeof(ZoneHeader);
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);

View File

@@ -1,13 +1,11 @@
#pragma once
#include <stddef.h> // size_t definition
#include <stddef.h> // size_t definition
#include "../defines.h"
typedef struct Zone Zone;
typedef struct _Zone Zone;
Zone * zoneCreate(size_t sizeBytes);
void * zoneAlloc(Zone *zone, size_t sizeBytes);
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

@@ -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,30 +1,35 @@
#include <defines.h>
#include <logger/logger.h>
#include <memory/zone.h>
#include <stdio.h>
int main() {
printf("Size of i16: %i\n", sizeof(i16));
printf("Size of u16: %i\n", sizeof(u16));
printf("Size of i32: %i\n", sizeof(i32));
printf("Size of u32: %i\n", sizeof(u32));
printf("Size of i64: %i\n", sizeof(i64));
printf("Size of u64: %i\n", sizeof(u64));
printf("Size of f32: %i\n", sizeof(f32));
printf("Size of f64: %i\n", sizeof(f64));
printf("\nCreating new zone 4K in size...\n");
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);
void *test_alloc = zoneAlloc(test_zone, 4);
void *test_alloc2 = zoneAlloc(test_zone, 4);
u64 val1 = 420;
u32 val2 = 169;
u32 val3 = 80085;
printf("Address of test_zone: %p\n", (void *)test_zone);
printf("Address of test_alloc: %p\n", test_alloc);
printf("Address of test_alloc2: %p\n", test_alloc2);
u64 *zVal1 = (u64 *)zoneAlloc(test_zone, sizeof(val1));
*zVal1 = val1;
zoneClear(test_zone);
zoneFree(zVal1, test_zone, sizeof(zVal1));
zoneDestroy(test_zone);
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;
}