diff --git a/.github/workflows/kanso-cd.yml b/.github/workflows/kanso-cd.yml index 8c82922..d2656c8 100644 --- a/.github/workflows/kanso-cd.yml +++ b/.github/workflows/kanso-cd.yml @@ -1,8 +1,5 @@ name: Kanso-CD on: - push: - branches: - - main workflow_dispatch: jobs: diff --git a/.github/workflows/kanso-ci.yml b/.github/workflows/kanso-ci.yml index 5344fc3..b37aaa0 100644 --- a/.github/workflows/kanso-ci.yml +++ b/.github/workflows/kanso-ci.yml @@ -1,5 +1,8 @@ name: Kanso-CI on: + push: + branches: + - main pull_request: workflow_dispatch: diff --git a/cmake/Toolchains/riscv-common.cmake b/cmake/Toolchains/riscv-common.cmake index bf23671..4b95197 100644 --- a/cmake/Toolchains/riscv-common.cmake +++ b/cmake/Toolchains/riscv-common.cmake @@ -19,3 +19,7 @@ endif() set(CMAKE_C_FLAGS "${_COMMON} -march=${_MARCH} -mabi=${_MABI} ${_MODEL}") set(CMAKE_ASM_FLAGS "--target=${COMPILE_TARGET} -march=${_MARCH} -mabi=${_MABI} -Wno-unused-command-line-argument -x assembler-with-cpp") +set(CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "-fuse-ld=lld") +set(CMAKE_C_LINK_FLAGS_INIT "-fuse-ld=lld") diff --git a/src/Common/Console.c b/src/Common/Console.c new file mode 100644 index 0000000..057eccd --- /dev/null +++ b/src/Common/Console.c @@ -0,0 +1,77 @@ +#include "Console.h" + +void FormatBoxedMessage(SpanChar destination, ReadOnlySpanChar message) +{ + auto upLeftCorner = String("┌"); + auto upRightCorner = String("┐"); + auto downLeftCorner = String("└"); + auto downRightCorner = String("┘"); + auto horizontalLine = String("─"); + auto verticalLine = String("│"); + + MemoryCopy(destination, upLeftCorner); + destination = SpanSliceFrom(destination, upLeftCorner.Length); + + for (uint32_t i = 0; i < message.Length + 2; i++) + { + MemoryCopy(destination, horizontalLine); + destination = SpanSliceFrom(destination, horizontalLine.Length); + } + + MemoryCopy(destination, upRightCorner); + destination = SpanSliceFrom(destination, upRightCorner.Length); + + MemoryCopy(destination, String("\n")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, verticalLine); + destination = SpanSliceFrom(destination, verticalLine.Length); + + MemoryCopy(destination, String(" ")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, message); + destination = SpanSliceFrom(destination, message.Length); + + MemoryCopy(destination, String(" ")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, verticalLine); + destination = SpanSliceFrom(destination, verticalLine.Length); + + MemoryCopy(destination, String("\n")); + destination = SpanSliceFrom(destination, 1); + + MemoryCopy(destination, downLeftCorner); + destination = SpanSliceFrom(destination, downLeftCorner.Length); + + for (uint32_t i = 0; i < message.Length + 2; i++) + { + MemoryCopy(destination, horizontalLine); + destination = SpanSliceFrom(destination, horizontalLine.Length); + } + + MemoryCopy(destination, downRightCorner); + destination = SpanSliceFrom(destination, downRightCorner.Length); + + // TODO: There is a problem here with null terminator not present +} + +void ConsoleSetForegroundColor(Color color) +{ + ConsolePrint(String("\x1b[38;2;%d;%d;%dm"), (int32_t)color.Red, (int32_t)color.Green, (int32_t)color.Blue); +} + +void ConsoleResetStyle() +{ + ConsolePrint(String("\x1b[0m")); + ConsoleSetForegroundColor(ConsoleColorNormal); +} + +void ConsolePrintBoxMessage(ReadOnlySpanChar message) +{ + // TODO: Use the stack memory arena here + auto boxedMessage = StackAlloc(char, 512); + FormatBoxedMessage(boxedMessage, message); + ConsolePrint(String("\n%s\n"), boxedMessage); +} diff --git a/src/Common/Console.h b/src/Common/Console.h new file mode 100644 index 0000000..70b77d5 --- /dev/null +++ b/src/Common/Console.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Memory.h" +#include "String.h" +#include "Types.h" + +const Color ConsoleColorNormal = { 212, 212, 212, 255 }; +const Color ConsoleColorHighlight = { 250, 250, 250, 255 }; +const Color ConsoleColorAccent = { 79, 193, 255, 255 }; +const Color ConsoleColorSuccess = { 106, 153, 85, 255 }; +const Color ConsoleColorWarning = { 255, 135, 100, 255 }; +const Color ConsoleColorError = { 255, 105, 105, 255 }; +const Color ConsoleColorInfo = { 220, 220, 170, 255 }; +const Color ConsoleColorAction = { 197, 134, 192, 255 }; +const Color ConsoleColorKeyword = { 86, 156, 214, 255 }; +const Color ConsoleColorNumeric = { 181, 206, 168, 255 }; + +void ConsolePrint(ReadOnlySpanChar message, ...); + +void ConsoleSetForegroundColor(Color color); +void ConsoleResetStyle(); + +void ConsolePrintBoxMessage(ReadOnlySpanChar message); diff --git a/src/Common/Memory.c b/src/Common/Memory.c index 7f2a210..724456f 100644 --- a/src/Common/Memory.c +++ b/src/Common/Memory.c @@ -1,15 +1,292 @@ #include "Memory.h" +#include "Console.h" +#include "System.h" +#include "Types.h" +#include "String.h" -void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value) +// TODO: This will need to be thread local +// TODO: Implement memory arena multi-threading + +MemoryError globalMemoryError = MemoryError_None; + +//--------------------------------------------------------------------------------------- +// MemoryArena +//--------------------------------------------------------------------------------------- + +#define STACK_MEMORY_ARENA_DEFAULT_SIZE MegaBytesToBytes(4) + +typedef struct MemoryArenaStorage { - (void)stride; - uint8_t* pointer = destination; - uint8_t byteValue = *(uint8_t*)value; + MemoryReservation MemoryReservation; + BitArray PageCommittedStatus; + SpanUint8 DataSpan; + uint8_t* CurrentPointer; + size_t CommittedBytes; + uint8_t StackLevel; + uint8_t StackMinAllocatedLevel; +} MemoryArenaStorage; - for (size_t i = 0; i < destinationLength; i++) +// TODO: This will need to be thread local +MemoryArenaStorage* globalStackMemoryArenaStorage = nullptr; +MemoryArenaStorage* globalStackMemoryArenaExtraStorage = nullptr; + +MemoryArenaStorage* CreateMemoryArenaStorage(size_t sizeInBytes) +{ + if (sizeInBytes == 0) + { + globalMemoryError = MemoryError_InvalidParameter; + return nullptr; + } + + auto systemInformation = SystemGetInformation(); + + auto dataPageCount = DivRoundUp(sizeInBytes, systemInformation.PageSize); + auto committedStatusBitArraySizeInBytes = AlignUp(DivRoundUp(dataPageCount, 8), alignof(size_t)); + + auto storageSize = AlignUp(sizeof(MemoryArenaStorage), alignof(size_t)); + auto headerSizeInBytes = storageSize + committedStatusBitArraySizeInBytes; + auto headerPageCount = DivRoundUp(headerSizeInBytes, systemInformation.PageSize); + + auto memoryReservation = MemoryReservePages(headerPageCount + dataPageCount); + + if (MemoryReservationIsEmpty(memoryReservation)) + { + return nullptr; + } + + MemoryCommitPages(&memoryReservation, 0, headerPageCount, MemoryAccess_ReadWrite); + + auto storage = (MemoryArenaStorage*)memoryReservation.BaseAddress; + *storage = (MemoryArenaStorage){}; + storage->MemoryReservation = memoryReservation; + storage->DataSpan = CreateSpan(uint8_t, (uint8_t*)memoryReservation.BaseAddress + headerSizeInBytes, sizeInBytes); + + auto committedStatusBitArrayData = SpanCast(size_t, CreateSpan(uint8_t, (uint8_t*)memoryReservation.BaseAddress + storageSize, committedStatusBitArraySizeInBytes)); + storage->PageCommittedStatus = CreateBitArrayWithBitCount(committedStatusBitArrayData, dataPageCount); + storage->CurrentPointer = storage->DataSpan.Pointer; + storage->StackLevel = 0; + storage->StackMinAllocatedLevel = UINT8_MAX; + + globalMemoryError = MemoryError_None; + + return storage; +} + +MemoryArenaStorage* GetMemoryArenaWorkingStorage(MemoryArena memoryArena) +{ + auto storage = memoryArena.Storage; + + if (memoryArena.StackLevel != storage->StackLevel) + { + if (!globalStackMemoryArenaExtraStorage) + { + // TODO: Replace the size by a constant depending on the boot phase + // TODO: Do an util method for bytes + globalStackMemoryArenaExtraStorage = CreateMemoryArenaStorage(STACK_MEMORY_ARENA_DEFAULT_SIZE); + } + + // TODO: Replace by a min function + storage->StackMinAllocatedLevel = memoryArena.StackLevel < storage->StackMinAllocatedLevel ? memoryArena.StackLevel : storage->StackMinAllocatedLevel; + return globalStackMemoryArenaExtraStorage; + } + + return storage; +} + +MemoryArena CreateMemoryArena(size_t sizeInBytes) +{ + auto storage = CreateMemoryArenaStorage(sizeInBytes); + + if (!storage) + { + return MEMORY_ARENA_EMPTY; + } + + return (MemoryArena) + { + .Storage = storage + }; +} + +bool MemoryArenaRelease(MemoryArena* memoryArena) +{ + if (MemoryArenaIsEmpty(*memoryArena)) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + MemoryRelease(&memoryArena->Storage->MemoryReservation); + + *memoryArena = MEMORY_ARENA_EMPTY; + globalMemoryError = MemoryError_None; + + return true; +} + +MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena) +{ + auto storage = memoryArena.Storage; + + return (MemoryArenaAllocationInfos) + { + .AllocatedBytes = storage->CurrentPointer - storage->DataSpan.Pointer, + .CommittedBytes = storage->CommittedBytes, + .MaximumSizeInBytes = storage->DataSpan.Length + }; +} + +SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes) +{ + auto span = MemoryArenaPushReserved(memoryArena, sizeInBytes); + + if (span.Pointer) { - pointer[i] = byteValue; + MemoryArenaCommit(memoryArena, span); } + + return span; +} + +SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes) +{ + sizeInBytes = AlignUp(sizeInBytes, sizeof(uintptr_t)); + + auto storage = GetMemoryArenaWorkingStorage(memoryArena); + auto allocationInfos = MemoryArenaGetAllocationInfos((MemoryArena){ .Storage = storage }); + + if (allocationInfos.AllocatedBytes + sizeInBytes > allocationInfos.MaximumSizeInBytes) + { + globalMemoryError = MemoryError_OutOfMemory; + return CreateSpan(uint8_t, nullptr, 0); + } + + auto span = CreateSpan(uint8_t, storage->CurrentPointer, sizeInBytes); + + storage->CurrentPointer += sizeInBytes; + globalMemoryError = MemoryError_None; + + return span; +} + +bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes) +{ + if ((size_t)(memoryArena.Storage->CurrentPointer - memoryArena.Storage->DataSpan.Pointer) < sizeInBytes) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + memoryArena.Storage->CurrentPointer -= sizeInBytes; + globalMemoryError = MemoryError_None; + + return true; +} + +void MemoryArenaClear(MemoryArena memoryArena) +{ + // TODO: Do nothing for stack + + memoryArena.Storage->CurrentPointer = memoryArena.Storage->DataSpan.Pointer; + globalMemoryError = MemoryError_None; +} + +bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range) +{ + auto storage = GetMemoryArenaWorkingStorage(memoryArena); + auto systemInformation = SystemGetInformation(); + + if (range.Pointer > storage->CurrentPointer || (range.Pointer + range.Length) > storage->CurrentPointer) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + auto headerPageCount = ((uintptr_t)storage->DataSpan.Pointer - (uintptr_t)storage->MemoryReservation.BaseAddress) / systemInformation.PageSize; + auto dataPageOffset = ((uintptr_t)range.Pointer - (uintptr_t)storage->DataSpan.Pointer) / systemInformation.PageSize; + + auto pageOffset = headerPageCount + dataPageOffset; + auto pageCount = DivRoundUp(range.Length, systemInformation.PageSize); + + for (uint32_t i = 0; i < pageCount; i++) + { + // TODO: optimize + if (!BitArrayIsSet(storage->PageCommittedStatus, dataPageOffset + i)) + { + MemoryCommitPages(&storage->MemoryReservation, pageOffset + i, 1, MemoryAccess_ReadWrite); + BitArraySet(storage->PageCommittedStatus, dataPageOffset + i); + storage->CommittedBytes += systemInformation.PageSize; + } + } + + globalMemoryError = MemoryError_None; + return true; +} + +MemoryArena GetStackMemoryArena() +{ + if (!globalStackMemoryArenaStorage) + { + // TODO: Replace the size by a constant depending on the boot phase + // TODO: Do an util method for bytes + globalStackMemoryArenaStorage = CreateMemoryArenaStorage(STACK_MEMORY_ARENA_DEFAULT_SIZE); + } + + globalStackMemoryArenaStorage->StackLevel++; + + return (MemoryArena) + { + .Storage = globalStackMemoryArenaStorage, + .StackStartPointer = globalStackMemoryArenaStorage->CurrentPointer, + .StackExtraStartPointer = globalStackMemoryArenaExtraStorage ? globalStackMemoryArenaExtraStorage->CurrentPointer : nullptr, + .StackLevel = globalStackMemoryArenaStorage->StackLevel + }; +} + +void ReleaseStackMemoryArena(MemoryArena* stackMemoryArena) +{ + if (!stackMemoryArena || !stackMemoryArena->Storage) + { + return; + } + + auto storage = stackMemoryArena->Storage; + + if (globalStackMemoryArenaExtraStorage && storage->StackMinAllocatedLevel >= stackMemoryArena->StackLevel) + { + size_t extraBytesToPop = (size_t)(globalStackMemoryArenaExtraStorage->CurrentPointer - stackMemoryArena->StackExtraStartPointer); + + if (extraBytesToPop) + { + // TODO: Do something cleaner? + MemoryArenaPop((MemoryArena){ .Storage = globalStackMemoryArenaExtraStorage }, extraBytesToPop); + } + + storage->StackMinAllocatedLevel = UINT8_MAX; + } + + storage->StackLevel--; + + auto bytesToPop = (size_t)(storage->CurrentPointer - stackMemoryArena->StackStartPointer); + + if (bytesToPop) + { + MemoryArenaPop(*stackMemoryArena, bytesToPop); + } + + stackMemoryArena->StackStartPointer = storage->CurrentPointer; + stackMemoryArena->StackExtraStartPointer = globalStackMemoryArenaExtraStorage ? globalStackMemoryArenaExtraStorage->CurrentPointer : nullptr; +} + +//--------------------------------------------------------------------------------------- +// General +//--------------------------------------------------------------------------------------- + +void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value) +{ + (void)stride; + uint8_t byteValue = *(uint8_t*)value; + __builtin_memset(destination, byteValue, destinationLength); } void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value) @@ -32,10 +309,7 @@ void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, // TODO: Check length (void)destinationLength; - for (size_t i = 0; i < sourceLength; i++) - { - ((uint8_t*)destination)[i] = ((uint8_t*)source)[i]; - } + __builtin_memcpy(destination, source, sourceLength); } void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength) @@ -54,6 +328,39 @@ void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLengt } } +void* MemoryConcatChar(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length) +{ + auto destination = MemoryArenaPush(memoryArena, source1Length + source2Length + 1); + + MemoryCopyByte(stride, destination.Pointer, source1Length, source1, source1Length); + MemoryCopyByte(stride, destination.Pointer + (source1Length * stride), source2Length, source2, source2Length); + + SpanAt(destination, source1Length + source2Length) = 0; + + return destination.Pointer; +} + +void* MemoryConcatByte(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length) +{ + auto destination = MemoryArenaPush(memoryArena, source1Length + source2Length); + + MemoryCopyByte(stride, destination.Pointer, source1Length, source1, source1Length); + MemoryCopyByte(stride, destination.Pointer + source1Length, source2Length, source2, source2Length); + + return destination.Pointer; +} + +void* MemoryConcatDefault(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length) +{ + auto destination = MemoryArenaPush(memoryArena, stride * (source1Length + source2Length)); + + MemoryCopyDefault(stride, destination.Pointer, source1Length, source1, source1Length); + MemoryCopyDefault(stride, destination.Pointer + (source1Length * stride), source2Length, source2, source2Length); + + return destination.Pointer; +} + + // TODO: Move that to the standard library size_t strlen(const char* string) @@ -68,7 +375,22 @@ size_t strlen(const char* string) return (size_t)(pointer - string); } -void memset(uint8_t* destination, uint8_t value, size_t sizeInBytes) +void* memset(uint8_t* destination, uint8_t value, size_t sizeInBytes) { - MemorySetByte(sizeof(uint8_t), destination, sizeInBytes, &value); + for (size_t i = 0; i < sizeInBytes; i++) + { + destination[i] = value; + } + + return destination; +} + +void* memcpy(uint8_t* destination, const uint8_t* source, size_t sizeInBytes) +{ + for (size_t i = 0; i < sizeInBytes; i++) + { + destination[i] = source[i]; + } + + return destination; } diff --git a/src/Common/Memory.h b/src/Common/Memory.h index 6205cef..ea02729 100644 --- a/src/Common/Memory.h +++ b/src/Common/Memory.h @@ -2,61 +2,125 @@ #include "Types.h" -#define MemoryAlignUp(value, align) __builtin_align_up(value, align) -#define MemoryIsAligned(value, align) __builtin_is_aligned(value, align) -#define MemoryOffsetOf(type, member) __builtin_offsetof(type, member) - - -#define DefineSpan(name, type) \ - typedef struct Span##name { type* Pointer; size_t Length; } Span##name; \ - typedef struct ReadOnlySpan##name { const type* Pointer; size_t Length; } ReadOnlySpan##name; \ - \ - static inline Span##name CreateSpan##name(type* pointer, size_t length) \ - { \ - return (Span##name) { .Pointer = pointer, .Length = length }; \ - } \ - \ - static inline ReadOnlySpan##name CreateReadOnlySpan##name(const type* pointer, size_t length) \ - { \ - return (ReadOnlySpan##name) { .Pointer = pointer, .Length = length }; \ - } \ - \ - static inline ReadOnlySpan##name ToReadOnlySpan##name(Span##name span) \ - { \ - return (ReadOnlySpan##name) { .Pointer = span.Pointer, .Length = span.Length }; \ - } - -#define DefineSpanStackAlloc(name, type, length) \ - (__extension__ ({ \ - static_assert((length) >= 0, "StackAlloc: length must be an integer-constant expression"); \ - type array[(length)]; \ - CreateSpan##name(array, (size_t)(length)); \ - })) - -DefineSpan(Char, char) -#define StackAllocChar(length) DefineSpanStackAlloc(Char, char, (length)) - -DefineSpan(Uint8, uint8_t) -#define StackAllocUint8(length) DefineSpanStackAlloc(Uint8, uint8_t, (length)) - -DefineSpan(Uint32, uint32_t) -#define StackAllocUint32(length) DefineSpanStackAlloc(Uint32, uint32_t, (length)) - -DefineSpan(Uint64, uint64_t) -#define StackAllocUint64(length) DefineSpanStackAlloc(Uint64, uint64_t, (length)) - -#define SpanSlice(span, offset, length) \ -( \ - (typeof(span)) \ - { \ - .Pointer = (span).Pointer + (offset), \ - .Length = (length) \ - } \ -) - -#define SpanSliceFrom(span, offset) SpanSlice((span), (offset), (span).Length - (offset)) - -// TODO: SpanGetItem? +//--------------------------------------------------------------------------------------- +// Error Handling +//--------------------------------------------------------------------------------------- + +typedef enum +{ + MemoryError_None, + MemoryError_InvalidParameter, + MemoryError_OutOfMemory +} MemoryError; + +// TODO: This will need to be thread local +extern MemoryError globalMemoryError; + +static inline MemoryError MemoryGetLastError() +{ + return globalMemoryError; +} + +//--------------------------------------------------------------------------------------- +// Utilities +//--------------------------------------------------------------------------------------- + +#define KiloBytesToBytes(value) (value) * 1024 +#define MegaBytesToBytes(value) KiloBytesToBytes((value)) * 1024 +#define GigaBytesToBytes(value) MegaBytesToBytes((value)) * 1024 + +//--------------------------------------------------------------------------------------- +// Memory Allocation +//--------------------------------------------------------------------------------------- + +typedef enum +{ + MemoryAccess_Read, + MemoryAccess_ReadWrite, + MemoryAccess_Execute, + MemoryAccess_ExecuteRead, + MemoryAccess_ExecuteReadWrite +} MemoryAccess; + +typedef struct +{ + void* BaseAddress; + size_t PageCount; +} MemoryReservation; + +typedef struct +{ + size_t CommittedPages; + size_t ReservedPages; +} MemoryAllocationInfos; + +#define MEMORY_RESERVATION_EMPTY ((MemoryReservation) { .BaseAddress = nullptr, .PageCount = 0 }) + +static inline bool MemoryReservationIsEmpty(MemoryReservation memoryReservation) +{ + return memoryReservation.BaseAddress == nullptr; +} + +MemoryAllocationInfos MemoryGetAllocationInfos(); + +MemoryReservation MemoryReservePages(size_t pageCount); +bool MemoryRelease(MemoryReservation* memoryReservation); + +bool MemoryCommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount, MemoryAccess access); +bool MemoryDecommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount); + +//--------------------------------------------------------------------------------------- +// Memory Arena +//--------------------------------------------------------------------------------------- + +struct MemoryArenaStorage; + +typedef struct +{ + struct MemoryArenaStorage* Storage; + uint8_t* StackStartPointer; + uint8_t* StackExtraStartPointer; + uint8_t StackLevel; +} MemoryArena; + +typedef struct +{ + size_t AllocatedBytes; + size_t CommittedBytes; + size_t MaximumSizeInBytes; +} MemoryArenaAllocationInfos; + +#define MEMORY_ARENA_EMPTY ((MemoryArena) { .Storage = nullptr }); + +static inline bool MemoryArenaIsEmpty(MemoryArena memoryArena) +{ + return memoryArena.Storage == nullptr; +} + +MemoryArena CreateMemoryArena(size_t sizeInBytes); +bool MemoryArenaRelease(MemoryArena* memoryArena); + +MemoryArenaAllocationInfos MemoryArenaGetAllocationInfos(MemoryArena memoryArena); + +SpanUint8 MemoryArenaPush(MemoryArena memoryArena, size_t sizeInBytes); +SpanUint8 MemoryArenaPushReserved(MemoryArena memoryArena, size_t sizeInBytes); + +#define MemoryArenaPushStruct(type, memoryArena) ((type*)MemoryArenaPush((memoryArena), sizeof(type)).Pointer) +#define MemoryArenaPushArray(type, memoryArena, count) CreateSpan(type, ((type*)MemoryArenaPush((memoryArena), sizeof(type) * (count)).Pointer), (count)) + +bool MemoryArenaPop(MemoryArena memoryArena, size_t sizeInBytes); +void MemoryArenaClear(MemoryArena memoryArena); + +bool MemoryArenaCommit(MemoryArena memoryArena, SpanUint8 range); + +MemoryArena GetStackMemoryArena(); +void ReleaseStackMemoryArena(MemoryArena* stackMemoryArena); + +#define StackMemoryArena(name) [[gnu::cleanup(ReleaseStackMemoryArena)]] MemoryArena name = GetStackMemoryArena(); + +//--------------------------------------------------------------------------------------- +// General +//--------------------------------------------------------------------------------------- void MemorySetByte(size_t stride, void* destination, size_t destinationLength, const void* value); void MemorySetDefault(size_t stride, void* destination, size_t destinationLength, const void* value); @@ -69,33 +133,32 @@ void MemorySetDefault(size_t stride, void* destination, size_t destinationLength )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, &(typeof(*(destination).Pointer)){ (value) }) +// TODO: Add Errors + void MemoryCopyByte(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); void MemoryCopyDefault(size_t stride, void* destination, size_t destinationLength, const void* source, size_t sourceLength); -#define _MemoryCopyDispatch(destination, source) \ +#define MemoryCopy(destination, source) \ _Generic((destination).Pointer, \ char*: MemoryCopyByte, \ uint8_t*: MemoryCopyByte, \ default: MemoryCopyDefault \ )(sizeof(*(destination).Pointer), (destination).Pointer, (destination).Length, (source).Pointer, (source).Length) - -#define _IS_READONLY_SPAN(span) \ - __builtin_types_compatible_p( \ - __typeof__((span).Pointer), \ - const __typeof__(*(span).Pointer) *) - -#define _ASSERT_READONLY_SPAN(src) \ - _Static_assert( _IS_READONLY_SPAN(src), \ - "MemoryCopy: source span must be read-only") - -#define MemoryCopy(destination, source) \ - do { \ - _ASSERT_READONLY_SPAN(source); \ - _MemoryCopyDispatch((destination), (source)); \ - } while (0) - - - -// TODO: Move that to the standard library -void memset(uint8_t* destination, uint8_t value, size_t sizeInBytes); +void* MemoryConcatChar(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length); +void* MemoryConcatByte(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length); +void* MemoryConcatDefault(MemoryArena memoryArena, size_t stride, const void* source1, size_t source1Length, const void* source2, size_t source2Length); + +// TODO: It works but the only drawback now is that if source1 is a ReadOnlySpan, it will create a ReadOnlySpan as a result +#define MemoryConcat(memoryArena, source1, source2) \ + (typeof(source1)) \ + { \ + .Pointer = _Generic((source1).Pointer, \ + char*: MemoryConcatChar, \ + const char*: MemoryConcatChar, \ + uint8_t*: MemoryConcatByte, \ + const uint8_t*: MemoryConcatByte, \ + default: MemoryConcatDefault \ + )(memoryArena, sizeof(*(source1).Pointer), (source1).Pointer, (source1).Length, (source2).Pointer, (source2).Length), \ + .Length = (source1).Length + (source2).Length \ + }; diff --git a/src/Common/String.c b/src/Common/String.c index 0fb4f1b..e6b51c0 100644 --- a/src/Common/String.c +++ b/src/Common/String.c @@ -1,9 +1,5 @@ #include "String.h" - -ReadOnlySpanChar String(const char* string) -{ - return CreateReadOnlySpanChar(string, __builtin_strlen(string)); -} +#include "Memory.h" bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) { @@ -14,7 +10,7 @@ bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) for (uint32_t i = 0; i < string1.Length; i++) { - if (string1.Pointer[i] != string2.Pointer[i]) + if (SpanAt(string1, i) != SpanAt(string2, i)) { return false; } @@ -23,17 +19,47 @@ bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2) return true; } -void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...) +ReadOnlySpanString StringSplit(MemoryArena memoryArena, ReadOnlySpanChar value, char separator) +{ + auto resultCount = 0; + auto currentStartIndex = 0; + + // TODO: Compute the needed length first? + auto result = MemoryArenaPushArray(ReadOnlySpanChar, memoryArena, 32); + + for (uint32_t i = 0; i < value.Length; i++) + { + if (SpanAt(value, i) == separator) + { + SpanAt(result, resultCount++) = SpanSlice(value, currentStartIndex, i - currentStartIndex); + currentStartIndex = i + 1; + } + } + + SpanAt(result, resultCount++) = SpanSlice(value, currentStartIndex, value.Length - currentStartIndex); + + result.Length = resultCount; + + return ToReadOnlySpan(ReadOnlySpanChar, result); +} + +ReadOnlySpanChar StringFormat(MemoryArena memoryArena, ReadOnlySpanChar message, ...) { va_list vargs; va_start(vargs, message); - StringFormatVargs(destination, message, vargs); + auto result = StringFormatVargs(memoryArena, message, vargs); va_end(vargs); + + return result; } // TODO: Refactor this function -void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list vargs) +// TODO: It would be cool if we could handle ReadOnlySpan so we can take the length not until \0 +ReadOnlySpanChar StringFormatVargs(MemoryArena memoryArena, ReadOnlySpanChar message, va_list vargs) { + // TODO: Don't hardcode the size + auto destination = MemoryArenaPushArray(char, memoryArena, 1024); + uint32_t length = 0; char* messagePointer = (char*)message.Pointer; @@ -53,7 +79,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list case '%': { - destination->Pointer[length++] = '%'; + SpanAt(destination, length++) = '%'; break; } @@ -63,7 +89,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (*stringArgument) { - destination->Pointer[length++] = *stringArgument; + SpanAt(destination, length++) = *stringArgument; stringArgument++; } break; @@ -77,7 +103,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list if (decimalArgument < 0) { - destination->Pointer[length++] = '-'; + SpanAt(destination, length++) = '-'; magnitude = -magnitude; } @@ -90,7 +116,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (divisor > 0) { - destination->Pointer[length++] = '0' + magnitude / divisor; + SpanAt(destination, length++) = '0' + magnitude / divisor; magnitude %= divisor; divisor /= 10; @@ -107,7 +133,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list if (decimalArgument < 0) { - destination->Pointer[length++] = '-'; + SpanAt(destination, length++) = '-'; magnitude = -magnitude; } @@ -120,7 +146,7 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list while (divisor > 0) { - destination->Pointer[length++] = '0' + magnitude / divisor; + SpanAt(destination, length++) = '0' + magnitude / divisor; magnitude %= divisor; divisor /= 10; @@ -131,13 +157,13 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list case 'x': { uintptr_t hexaArgument = va_arg(vargs, uintptr_t); - destination->Pointer[length++] = '0'; - destination->Pointer[length++] = 'x'; + SpanAt(destination, length++) = '0'; + SpanAt(destination, length++) = 'x'; for (int32_t i = (sizeof(uintptr_t) * 2) - 1; i >= 0; i--) { unsigned nibble = (hexaArgument >> (i * 4)) & 0xf; - destination->Pointer[length++] = "0123456789abcdef"[nibble]; + SpanAt(destination, length++) = "0123456789abcdef"[nibble]; } break; } @@ -145,13 +171,14 @@ void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list } else { - destination->Pointer[length++] = *messagePointer; + SpanAt(destination, length++) = *messagePointer; } messagePointer++; } - destination->Length = length; - destination->Pointer[length] = '\0'; -} + destination.Length = length; + SpanAt(destination, length) = '\0'; + return ToReadOnlySpan(char, destination); +} diff --git a/src/Common/String.h b/src/Common/String.h index 51c5756..54cbf30 100644 --- a/src/Common/String.h +++ b/src/Common/String.h @@ -3,12 +3,21 @@ #include "Types.h" #include "Memory.h" -ReadOnlySpanChar String(const char* string); +// TODO: Move this header to a standard one? Not a header just for strings? + +DefineSpan(String, ReadOnlySpanChar) + +static inline ReadOnlySpanChar String(const char* string) +{ + return CreateReadOnlySpan(char, string, __builtin_strlen(string)); +} + bool StringEquals(ReadOnlySpanChar string1, ReadOnlySpanChar string2); -void StringFormat(SpanChar* destination, ReadOnlySpanChar message, ...); -void StringFormatVargs(SpanChar* destination, ReadOnlySpanChar message, va_list vargs); +ReadOnlySpanString StringSplit(MemoryArena memoryArena, ReadOnlySpanChar value, char separator); +ReadOnlySpanChar StringFormat(MemoryArena memoryArena, ReadOnlySpanChar message, ...); +ReadOnlySpanChar StringFormatVargs(MemoryArena memoryArena, ReadOnlySpanChar message, va_list vargs); // TODO: Move that to the standard library diff --git a/src/Common/System.h b/src/Common/System.h new file mode 100644 index 0000000..5a8296c --- /dev/null +++ b/src/Common/System.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Types.h" + +typedef struct +{ + ReadOnlySpanChar Name; + uint32_t ArchitectureBits; + uint32_t PageSize; + // TODO: Add Board Name / Computer name / Platform form name +} SystemInformation; + +SystemInformation SystemGetInformation(); diff --git a/src/Common/Test.c b/src/Common/Test.c index 3acaa60..53e291c 100644 --- a/src/Common/Test.c +++ b/src/Common/Test.c @@ -1,11 +1,15 @@ #include "Test.h" +#include "Memory.h" +#include "String.h" TestEntry globalTests[MAX_TESTS]; uint32_t globalTestCount = 0; uint32_t globalCurrentTestIndex = 0; -SpanChar globalTestLastErrorMessage; -void RegisterTest(const char* category, const char* name, TestFunction testFunction) +ReadOnlySpanChar globalTestLastErrorMessage = {}; +MemoryArena globalTestMemoryArena = {}; + +void RegisterTest(ReadOnlySpanChar category, ReadOnlySpanChar name, TestFunction testFunction) { // TODO: We don't have a way to assert for now in the kernel //assert(globalTestCount < MAX_TESTS, "Too many TEST()s: increase MAX_TESTS"); @@ -14,39 +18,78 @@ void RegisterTest(const char* category, const char* name, TestFunction testFunct { .Category = category, .Name = name, - .TestFunction = testFunction + .TestFunction = testFunction, }; } -void TestRun(TestLogHandler handler) +void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters) { + StackMemoryArena(stackMemoryArena); + uint32_t testRunCount = 0; + auto splittedFilters = StringSplit(stackMemoryArena, categoryFilters, '|'); + globalTestMemoryArena = CreateMemoryArena(KiloBytesToBytes(128)); + + for (uint32_t i = 0; i < globalTestCount; i++) + { + MemoryArenaClear(globalTestMemoryArena); + auto test = &globalTests[i]; + test->CanRun = true; + + if (categoryFilters.Length > 0) + { + // TODO: This is really bad. Move that away from the loop + + auto testCanRun = false; + + for (uint32_t j = 0; j < splittedFilters.Length; j++) + { + if (StringEquals(test->Category, SpanAt(splittedFilters, j))) + { + testCanRun = true; + } + } + + test->CanRun = testCanRun; + } + + if (test->CanRun) + { + testRunCount++; + } + } + uint32_t failedCounter = 0; - globalTestLastErrorMessage = StackAllocChar(TEST_ERROR_MESSAGE_LENGTH); - handler(TestRunState_Separator, String("Running %d %s."), globalTestCount, globalTestCount == 1 ? "test" : "tests"); + handler(TestRunState_Separator, String("Running %d %s."), testRunCount, testRunCount == 1 ? "test" : "tests"); for (uint32_t i = 0; i < globalTestCount; i++) { auto test = &globalTests[i]; + test->HasError = false; globalCurrentTestIndex = i; - handler(TestRunState_Start, String("%s.%s"), test->Category, test->Name); + if (!test->CanRun) + { + continue; + } + + handler(TestRunState_Start, String("%s.%s"), test->Category.Pointer, test->Name.Pointer); test->TestFunction(); if (test->HasError) { - handler(TestRunState_Failed, ToReadOnlySpanChar(globalTestLastErrorMessage)); + handler(TestRunState_Failed, ToReadOnlySpan(char, globalTestLastErrorMessage)); failedCounter++; } else { - handler(TestRunState_OK, String("%s.%s"), test->Category, test->Name); + handler(TestRunState_OK, String("%s.%s"), test->Category.Pointer, test->Name.Pointer); } } - handler(TestRunState_Separator, String("%d %s ran."), globalTestCount, globalTestCount == 1 ? "test" : "tests"); - handler(TestRunState_Passed, String("%d %s."), globalTestCount - failedCounter, (globalTestCount - failedCounter) == 1 ? "test" : "tests"); + handler(TestRunState_Separator, String("%d %s ran."), testRunCount, testRunCount == 1 ? "test" : "tests"); + handler(TestRunState_Passed, String("%d %s."), testRunCount - failedCounter, (testRunCount - failedCounter) == 1 ? "test" : "tests"); if (failedCounter > 0) { @@ -58,7 +101,7 @@ void TestRun(TestLogHandler handler) if (test->HasError) { - handler(TestRunState_Failed, String("%s.%s"), test->Category, test->Name); + handler(TestRunState_Failed, String("%s.%s"), test->Category.Pointer, test->Name.Pointer); } } } diff --git a/src/Common/Test.h b/src/Common/Test.h index 556c54c..ec75adf 100644 --- a/src/Common/Test.h +++ b/src/Common/Test.h @@ -21,17 +21,19 @@ typedef void (*TestLogHandler)(TestRunState state, ReadOnlySpanChar message, ... typedef struct { - const char* Category; - const char* Name; + ReadOnlySpanChar Category; + ReadOnlySpanChar Name; TestFunction TestFunction; bool HasError; + bool CanRun; } TestEntry; extern TestEntry globalTests[MAX_TESTS]; extern uint32_t globalTestCount; extern uint32_t globalCurrentTestIndex; -extern SpanChar globalTestLastErrorMessage; +extern ReadOnlySpanChar globalTestLastErrorMessage; +extern MemoryArena globalTestMemoryArena; #define Test(category, name) \ @@ -40,7 +42,7 @@ extern SpanChar globalTestLastErrorMessage; [[gnu::constructor]] \ static void __register_##category##_##name() \ { \ - RegisterTest(#category, #name, test_##category##_##name); \ + RegisterTest(String(#category), String(#name), test_##category##_##name); \ } \ \ static void test_##category##_##name() \ @@ -53,34 +55,37 @@ extern SpanChar globalTestLastErrorMessage; if (!testEntry->HasError) \ { \ testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: %s\n Actual: %d %s %d"), __FILE__, #expr, expected, operator, actual); \ + globalTestLastErrorMessage = StringFormat(globalTestMemoryArena, String("%s\n Expected: %s\n Actual: %d %s %d"), __FILE__, #expr, expected, operator, actual); \ } \ } \ } while (false) -// BUG: There is a bug in the assert only in 32-bit version, when the assert fail maybe due to 64 bit values used in the comparison like with the time #define TestAssertEquals(expected, actual) TestAssertCore((expected) == (actual), expected, actual, "==") #define TestAssertNotEquals(expected, actual) TestAssertCore((expected) != (actual), expected, actual, "!=") #define TestAssertGreaterThan(expected, actual) TestAssertCore((expected) > (actual), expected, actual, ">") +#define TestAssertGreaterThanOrEquals(expected, actual) TestAssertCore((expected) >= (actual), expected, actual, ">=") #define TestAssertIsTrue(actual) TestAssertCore(true == (actual), true, actual, "==") +#define TestAssertIsFalse(actual) TestAssertCore(false == (actual), false, actual, "==") // TODO: Adapt the macro like the core one #define TestAssertStringEquals(expected, actual) \ do { \ - if (finalString.Length != destination.Length) \ + TestEntry* testEntry = &globalTests[globalCurrentTestIndex]; \ + if (!testEntry->HasError) \ { \ - TestEntry* testEntry = &globalTests[globalCurrentTestIndex]; \ - testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s.Length) == (%s.Length)\n Actual: %d == %d"), __FILE__, #expected, #actual, expected.Length, actual.Length); \ - } \ - \ - if (!StringEquals(expected, actual)) \ - { \ - TestEntry* testEntry = &globalTests[globalCurrentTestIndex]; \ - testEntry->HasError = true; \ - StringFormat(&globalTestLastErrorMessage, String("%s\n Expected: (%s) == (%s)\n Actual: \"%s\" == \"%s\""), __FILE__, #expected, #actual, expected.Pointer, actual.Pointer); \ + if (expected.Length != actual.Length) \ + { \ + testEntry->HasError = true; \ + globalTestLastErrorMessage = StringFormat(globalTestMemoryArena, String("%s\n Expected: (%s.Length) == (%s.Length)\n Actual: %d == %d"), __FILE__, #expected, #actual, expected.Length, actual.Length); \ + } \ + \ + if (!StringEquals(expected, actual)) \ + { \ + testEntry->HasError = true; \ + globalTestLastErrorMessage = StringFormat(globalTestMemoryArena, String("%s\n Expected: (%s) == (%s)\n Actual: \"%s\" == \"%s\""), __FILE__, #expected, #actual, expected.Pointer, actual.Pointer); \ + } \ } \ } while (false) -void RegisterTest(const char* category, const char* name, TestFunction testFunction); -void TestRun(TestLogHandler handler); +void RegisterTest(ReadOnlySpanChar category, ReadOnlySpanChar name, TestFunction testFunction); +void TestRun(TestLogHandler handler, ReadOnlySpanChar categoryFilters); diff --git a/src/Common/Types.c b/src/Common/Types.c new file mode 100644 index 0000000..0164b14 --- /dev/null +++ b/src/Common/Types.c @@ -0,0 +1,69 @@ +#include "Types.h" + +// TODO: This will need to be thread local +TypeError globalTypeError = TypeError_None; + +size_t BitArrayFindFirstNotSet(BitArray bitArray) +{ + if (BitArrayIsEmpty(bitArray)) + { + globalTypeError = TypeError_InvalidParameter; + return SIZE_MAX; + } + + for (uint32_t i = 0; i < bitArray.Data.Length; i++) + { + auto inverse = ~bitArray.Data.Pointer[i]; + + if (inverse == 0) + { + continue; + } + + globalTypeError = TypeError_None; + auto result = SizePrefixCountZeros(inverse); + + return (i * BITS_PER_SIZE_TYPE) + result; + } + + globalTypeError = TypeError_NotFound; + return SIZE_MAX; +} + +size_t BitArrayFindRangeNotSet(BitArray bitArray, size_t length) +{ + if (BitArrayIsEmpty(bitArray) || length == 0 || length > bitArray.BitCount) + { + globalTypeError = TypeError_InvalidParameter; + return SIZE_MAX; + } + + size_t result = 0; + size_t currentCount = 0; + + for (uint32_t i = 0; i < bitArray.BitCount; i++) + { + if (!BitArrayIsSet(bitArray, i)) + { + if (currentCount == 0) + { + result = i; + } + + currentCount++; + + if (currentCount == length) + { + globalTypeError = TypeError_None; + return result; + } + } + else + { + currentCount = 0; + } + } + + globalTypeError = TypeError_NotFound; + return SIZE_MAX; +} diff --git a/src/Common/Types.h b/src/Common/Types.h index b0f9edd..b021ad7 100644 --- a/src/Common/Types.h +++ b/src/Common/Types.h @@ -1,27 +1,82 @@ #pragma once -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; +//--------------------------------------------------------------------------------------- +// Primitive types +//--------------------------------------------------------------------------------------- -typedef char int8_t; -typedef short int16_t; -typedef int int32_t; -typedef long long int64_t; +typedef __UINT8_TYPE__ uint8_t; +typedef __UINT16_TYPE__ uint16_t; +typedef __UINT32_TYPE__ uint32_t; +typedef __UINT64_TYPE__ uint64_t; + +typedef __INT8_TYPE__ int8_t; +typedef __INT16_TYPE__ int16_t; +typedef __INT32_TYPE__ int32_t; +typedef __INT64_TYPE__ int64_t; typedef __UINTPTR_TYPE__ uintptr_t; typedef __INTPTR_TYPE__ intptr_t; typedef __SIZE_TYPE__ size_t; -static_assert(sizeof(uint8_t) == 1, "uint8_t must be 1 byte."); -static_assert(sizeof(uint16_t) == 2, "uint16_t must be 2 bytes."); -static_assert(sizeof(uint32_t) == 4, "uint32_t must be 4 bytes."); -static_assert(sizeof(uint64_t) == 8, "uint64_t must be 8 bytes."); +#define PLATFORM_ARCHITECTURE_BITS (__SIZEOF_POINTER__ * 8) +#define BITS_PER_BYTE 8u +#define BITS_PER_SIZE_TYPE (sizeof(size_t) * BITS_PER_BYTE) +#define MASK_SIZE_TYPE (BITS_PER_SIZE_TYPE - 1) + +#define UINT8_MAX __UINT8_MAX__ +#define UINT16_MAX __UINT16_MAX__ +#define UINT32_MAX __UINT32_MAX__ +#define UINT64_MAX __UINT64_MAX__ -static_assert(sizeof(uintptr_t) == sizeof(void *), "uintptr_t is not pointer-sized."); +#define INT8_MAX __INT8_MAX__ +#define INT16_MAX __INT16_MAX__ +#define INT32_MAX __INT32_MAX__ +#define INT64_MAX __INT64_MAX__ -#define PLATFORM_ARCHITECTURE_BITS (__SIZEOF_POINTER__ * 8) +#define SIZE_MAX __SIZE_MAX__ + +#define AlignUp(value, align) __builtin_align_up(value, align) +#define IsAligned(value, align) __builtin_is_aligned(value, align) +#define OffsetOf(type, member) __builtin_offsetof(type, member) +#define DivRoundUp(value, divisor) (((value) + (divisor) - 1) / (divisor)) + +#if __SIZEOF_SIZE_T__ == 8 + #define SizePrefixCountZeros(value) __builtin_ctzll((uint64_t)(value)) +#elif __SIZEOF_SIZE_T__ == 4 + #define SizePrefixCountZeros(value) __builtin_ctz((uint32_t)(value)) +#endif + +//--------------------------------------------------------------------------------------- +// Variable parameters +//--------------------------------------------------------------------------------------- + +#define va_list __builtin_va_list +#define va_start __builtin_va_start +#define va_end __builtin_va_end +#define va_arg __builtin_va_arg + +//--------------------------------------------------------------------------------------- +// Error Handling +//--------------------------------------------------------------------------------------- + +typedef enum +{ + TypeError_None, + TypeError_InvalidParameter, + TypeError_NotFound +} TypeError; + +// TODO: This will need to be thread local +extern TypeError globalTypeError; + +static inline TypeError TypeGetLastError() +{ + return globalTypeError; +} + +//--------------------------------------------------------------------------------------- +// Endianness conversion +//--------------------------------------------------------------------------------------- typedef enum { @@ -31,10 +86,141 @@ typedef enum #define PLATFORM_BYTE_ORDER __BYTE_ORDER__ -#define va_list __builtin_va_list -#define va_start __builtin_va_start -#define va_end __builtin_va_end -#define va_arg __builtin_va_arg +// TODO: Read functions for endian + +//--------------------------------------------------------------------------------------- +// Span +//--------------------------------------------------------------------------------------- + +#define DefineSpan(name, type) \ + typedef struct Span##name { type* Pointer; size_t Length; } Span##name; \ + typedef struct ReadOnlySpan##name { const type* Pointer; size_t Length; } ReadOnlySpan##name; \ + \ + static inline Span##name _CREATE_SPAN_##type(type* pointer, size_t length) \ + { \ + return (Span##name) { .Pointer = pointer, .Length = length }; \ + } \ + \ + static inline ReadOnlySpan##name _CREATE_READONLY_SPAN_##type(const type* pointer, size_t length) \ + { \ + return (ReadOnlySpan##name) { .Pointer = pointer, .Length = length }; \ + } \ + +#define CreateSpan(type, pointer, length) _CREATE_SPAN_##type(pointer, length) +#define CreateReadOnlySpan(type, pointer, length) _CREATE_READONLY_SPAN_##type(pointer, length) + +// TODO: It would be nice if we could ommit the type for this function because we know it already +#define ToReadOnlySpan(type, span) _CREATE_READONLY_SPAN_##type((span).Pointer, (span).Length) + +#define SpanCast(type, span) CreateSpan(type, (type*)(span).Pointer, (sizeof(*(span).Pointer) * (span).Length) / sizeof(type)) + +#define StackAlloc(type, length) CreateSpan(type, (type*)__builtin_alloca(sizeof(type) * length), length); + +#define SpanSlice(span, offset, length) \ +( \ + (typeof(span)) \ + { \ + .Pointer = (span).Pointer + (offset), \ + .Length = (length) \ + } \ +) + +#define SpanSliceFrom(span, offset) SpanSlice((span), (offset), (span).Length - (offset)) +#define SpanAt(span, index) (span).Pointer[(index)] + +DefineSpan(Char, char) +DefineSpan(Uint8, uint8_t) +DefineSpan(Uint32, uint32_t) +DefineSpan(Uint64, uint64_t) +DefineSpan(Size, size_t) + +//--------------------------------------------------------------------------------------- +// BitArray +//--------------------------------------------------------------------------------------- + +typedef struct +{ + SpanSize Data; + size_t BitCount; +} BitArray; + +#define BIT_ARRAY_EMPTY ((BitArray){ .Data = { .Pointer = nullptr, .Length = 0 }, .BitCount = 0 }) + +static inline bool BitArrayIsEmpty(BitArray bitArray) +{ + return bitArray.Data.Pointer == nullptr; +} + +static inline BitArray CreateBitArray(SpanSize data) +{ + globalTypeError = TypeError_None; + + return (BitArray) + { + .Data = data, + .BitCount = data.Length * BITS_PER_SIZE_TYPE + }; +} + +static inline BitArray CreateBitArrayWithBitCount(SpanSize data, size_t bitCount) +{ + globalTypeError = TypeError_None; + + return (BitArray) + { + .Data = data, + .BitCount = bitCount + }; +} + +static inline bool BitArraySet(BitArray bitArray, size_t index) +{ + if (index >= bitArray.BitCount || BitArrayIsEmpty(bitArray)) + { + globalTypeError = TypeError_InvalidParameter; + return false; + } + + bitArray.Data.Pointer[index / BITS_PER_SIZE_TYPE] |= (size_t)1 << (index & MASK_SIZE_TYPE); + + globalTypeError = TypeError_None; + return true; +} + +static inline bool BitArrayReset(BitArray bitArray, size_t index) +{ + if (index >= bitArray.BitCount || BitArrayIsEmpty(bitArray)) + { + globalTypeError = TypeError_InvalidParameter; + return false; + } + + bitArray.Data.Pointer[index / BITS_PER_SIZE_TYPE] &= ~((size_t)1 << (index & MASK_SIZE_TYPE)); + + globalTypeError = TypeError_None; + return true; +} + +static inline bool BitArrayIsSet(BitArray bitArray, size_t index) +{ + if (index >= bitArray.BitCount || BitArrayIsEmpty(bitArray)) + { + globalTypeError = TypeError_InvalidParameter; + return false; + } + + globalTypeError = TypeError_None; + + auto pointer = bitArray.Data.Pointer; + return ((pointer[index / BITS_PER_SIZE_TYPE] >> (index & MASK_SIZE_TYPE)) & 1) == 1; +} + +size_t BitArrayFindFirstNotSet(BitArray bitArray); +size_t BitArrayFindRangeNotSet(BitArray bitArray, size_t length); + +//--------------------------------------------------------------------------------------- +// Standard types +//--------------------------------------------------------------------------------------- typedef struct { diff --git a/src/Common/UnityBuild.c b/src/Common/UnityBuild.c index 82f88c1..8acb673 100644 --- a/src/Common/UnityBuild.c +++ b/src/Common/UnityBuild.c @@ -1,5 +1,7 @@ +#include "Types.c" #include "Memory.c" #include "String.c" +#include "Console.c" #ifdef BUILD_TESTS #include "Test.c" diff --git a/src/Kernel/Kernel.c b/src/Kernel/Kernel.c index 8a1ae46..46da952 100644 --- a/src/Kernel/Kernel.c +++ b/src/Kernel/Kernel.c @@ -1,22 +1,23 @@ #include "Kernel.h" -#include "KernelConsole.h" +#include "Console.h" #include "Memory.h" #include "Platform.h" void KernelFailureCore(ReadOnlySpanChar file, uint32_t line, ReadOnlySpanChar message, ...) { - KernelConsoleSetForegroundColor(KernelConsoleColorError); - KernelConsolePrintBoxMessage(String("Kernel Failure")); - KernelConsolePrint(String("%s:%d\n"), file, line); + StackMemoryArena(stackMemoryArena); + + ConsoleSetForegroundColor(ConsoleColorError); + ConsolePrintBoxMessage(String("Kernel Failure")); + ConsolePrint(String("%s:%d\n"), file, line); va_list vargs; va_start(vargs, message); - auto tmp = StackAllocChar(256); - StringFormatVargs(&tmp, message, vargs); + auto tmp = StringFormatVargs(stackMemoryArena, message, vargs); - KernelConsolePrint(String("%s\n\n"), tmp); - KernelConsoleResetStyle(); + ConsolePrint(String("%s\n\n"), tmp); + ConsoleResetStyle(); va_end(vargs); diff --git a/src/Kernel/KernelConsole.c b/src/Kernel/KernelConsole.c index 27eefb0..12ec10e 100644 --- a/src/Kernel/KernelConsole.c +++ b/src/Kernel/KernelConsole.c @@ -1,91 +1,18 @@ -#include "KernelConsole.h" +#include "Console.h" #include "Memory.h" #include "String.h" #include "Platform.h" -void KernelConsolePrint(ReadOnlySpanChar message, ...) +void ConsolePrint(ReadOnlySpanChar message, ...) { - auto output = StackAllocChar(2048); + StackMemoryArena(stackMemoryArena); va_list vargs; va_start(vargs, message); - StringFormatVargs(&output, message, vargs); + auto output = StringFormatVargs(stackMemoryArena, message, vargs); va_end(vargs); - BiosDebugConsoleWrite(ToReadOnlySpanChar(output)); -} - -void KernelConsoleSetForegroundColor(Color color) -{ - KernelConsolePrint(String("\x1b[38;2;%d;%d;%dm"), (int32_t)color.Red, (int32_t)color.Green, (int32_t)color.Blue); -} - -void KernelConsoleResetStyle() -{ - KernelConsolePrint(String("\x1b[0m")); - KernelConsoleSetForegroundColor(KernelConsoleColorNormal); -} - -void FormatBoxedMessage(SpanChar destination, ReadOnlySpanChar message) -{ - auto upLeftCorner = String("┌"); - auto upRightCorner = String("┐"); - auto downLeftCorner = String("└"); - auto downRightCorner = String("┘"); - auto horizontalLine = String("─"); - auto verticalLine = String("│"); - - MemoryCopy(destination, upLeftCorner); - destination = SpanSliceFrom(destination, upLeftCorner.Length); - - for (uint32_t i = 0; i < message.Length + 2; i++) - { - MemoryCopy(destination, horizontalLine); - destination = SpanSliceFrom(destination, horizontalLine.Length); - } - - MemoryCopy(destination, upRightCorner); - destination = SpanSliceFrom(destination, upRightCorner.Length); - - MemoryCopy(destination, String("\n")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, verticalLine); - destination = SpanSliceFrom(destination, verticalLine.Length); - - MemoryCopy(destination, String(" ")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, message); - destination = SpanSliceFrom(destination, message.Length); - - MemoryCopy(destination, String(" ")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, verticalLine); - destination = SpanSliceFrom(destination, verticalLine.Length); - - MemoryCopy(destination, String("\n")); - destination = SpanSliceFrom(destination, 1); - - MemoryCopy(destination, downLeftCorner); - destination = SpanSliceFrom(destination, downLeftCorner.Length); - - for (uint32_t i = 0; i < message.Length + 2; i++) - { - MemoryCopy(destination, horizontalLine); - destination = SpanSliceFrom(destination, horizontalLine.Length); - } - - MemoryCopy(destination, downRightCorner); - destination = SpanSliceFrom(destination, downRightCorner.Length); -} - -void KernelConsolePrintBoxMessage(ReadOnlySpanChar message) -{ - auto boxedMessage = StackAllocChar(512); - FormatBoxedMessage(boxedMessage, message); - KernelConsolePrint(String("\n%s\n"), boxedMessage); + BiosDebugConsoleWrite(output); } diff --git a/src/Kernel/KernelConsole.h b/src/Kernel/KernelConsole.h deleted file mode 100644 index d8ca80a..0000000 --- a/src/Kernel/KernelConsole.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Memory.h" -#include "String.h" -#include "Types.h" - -const Color KernelConsoleColorNormal = { 212, 212, 212, 255 }; -const Color KernelConsoleColorHighlight = { 250, 250, 250, 255 }; -const Color KernelConsoleColorAccent = { 79, 193, 255, 255 }; -const Color KernelConsoleColorSuccess = { 106, 153, 85, 255 }; -const Color KernelConsoleColorWarning = { 255, 135, 100, 255 }; -const Color KernelConsoleColorError = { 255, 105, 105, 255 }; -const Color KernelConsoleColorInfo = { 220, 220, 170, 255 }; -const Color KernelConsoleColorAction = { 197, 134, 192, 255 }; -const Color KernelConsoleColorKeyword = { 86, 156, 214, 255 }; -const Color KernelConsoleColorNumeric = { 181, 206, 168, 255 }; - -void KernelConsolePrint(ReadOnlySpanChar message, ...); - -void KernelConsoleSetForegroundColor(Color color); -void KernelConsoleResetStyle(); - -void KernelConsolePrintBoxMessage(ReadOnlySpanChar message); diff --git a/src/Kernel/KernelMain.c b/src/Kernel/KernelMain.c index 64ffc7c..f3bfb3e 100644 --- a/src/Kernel/KernelMain.c +++ b/src/Kernel/KernelMain.c @@ -1,9 +1,9 @@ #include "Types.h" #include "String.h" #include "Memory.h" +#include "Console.h" #include "Version.h" #include "Platform.h" -#include "KernelConsole.h" #include "Kernel.h" const char KernelLogo[] = @@ -28,7 +28,7 @@ void KernelTrapHandler(CpuTrapFrame* trapFrame) BiosSetTimer(CpuReadTime() + 10'000'000); auto programCounter = CpuTrapFrameGetProgramCounter(trapFrame); - KernelConsolePrint(String("Kernel trap handler: %l (PC=%x).\n"), CpuReadTime(), programCounter); + ConsolePrint(String("Kernel trap handler: %l (PC=%x).\n"), CpuReadTime(), programCounter); return; @@ -73,33 +73,34 @@ void KernelTrapHandler(CpuTrapFrame* trapFrame) KernelFailure(String("%s. (Code=%x, Extra=%x)"), errorName, trapCause.Code, trapCause.ExtraInformation); } -void KernelMain() +void KernelInit() { - auto platformInformation = PlatformGetInformation(); - - KernelConsoleSetForegroundColor(KernelConsoleColorAccent); - KernelConsolePrint(String("\n\n%s\n"), KernelLogo); - KernelConsoleResetStyle(); + CpuSetTrapHandler(KernelTrapHandler); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("Kanso OS %s "), KANSO_VERSION_FULL); - KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.Name.Pointer, platformInformation.ArchitectureBits); - KernelConsoleResetStyle(); + auto platformInformation = PlatformGetInformation(); - KernelConsolePrint(String("Boot Cpu ID: %d\n"), platformInformation.BootCpuId); + ConsoleSetForegroundColor(ConsoleColorAccent); + ConsolePrint(String("\n\n%s\n"), KernelLogo); + ConsoleResetStyle(); - auto platformDevices = PlatformGetDevices(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("Kanso OS %s "), KANSO_VERSION_FULL); + ConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); + ConsoleResetStyle(); + ConsolePrint(String("Boot Cpu ID: %d\n"), platformInformation.BootCpuId); +} +void KernelMain() +{ BiosSetTimer(CpuReadTime() + 10'000'000); - CpuSetTrapHandler(KernelTrapHandler); // TODO: Test Timer only when the hardware is running fine CpuEnableInterrupts(CpuInterruptType_Timer); while (true) { - KernelConsolePrint(String("WFI\n")); + ConsolePrint(String("WFI\n")); //CpuGenerateInvalidInstruction(); CpuWaitForInterrupt(); diff --git a/src/Kernel/KernelMemory.c b/src/Kernel/KernelMemory.c new file mode 100644 index 0000000..f5ddf04 --- /dev/null +++ b/src/Kernel/KernelMemory.c @@ -0,0 +1,202 @@ +#include "Console.h" +#include "Memory.h" +#include "Platform.h" +#include "Types.h" + +typedef struct +{ + SpanUint8 InitHeap; + BitArray BitArray; +} KernelInitModeMemoryState; + +typedef struct +{ + bool MemoryTableInitialized; + KernelInitModeMemoryState InitModeMemoryState; + uint32_t PageSize; + size_t CommittedPages; + size_t ReservedPages; +} KernelMemoryState; + +KernelMemoryState globalKernelMemoryState = {}; + +static bool CheckMemoryReservationRange(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount) +{ + if ((pageOffset + pageCount) > memoryReservation->PageCount) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + return true; +} + +static void KernelInitModeSetupBitArrayAllocator() +{ + auto platformInformation = PlatformGetInformation(); + + auto memoryState = &globalKernelMemoryState.InitModeMemoryState; + + globalKernelMemoryState.PageSize = platformInformation.SystemInformation.PageSize; + memoryState->InitHeap = platformInformation.InitHeap; + + auto maxPageCount = memoryState->InitHeap.Length / globalKernelMemoryState.PageSize; + + auto bitmapStorageSizeInBytes = AlignUp(DivRoundUp(maxPageCount, 8), sizeof(size_t)); + auto bitmapStoragePageCount = DivRoundUp(bitmapStorageSizeInBytes, globalKernelMemoryState.PageSize); + + memoryState->BitArray = CreateBitArrayWithBitCount(SpanCast(size_t, SpanSlice(memoryState->InitHeap, 0, bitmapStorageSizeInBytes)), maxPageCount); + + for (uint32_t i = 0; i < maxPageCount; i++) + { + if (i < bitmapStoragePageCount) + { + BitArraySet(memoryState->BitArray, i); + } + else + { + BitArrayReset(memoryState->BitArray, i); + } + } +} + +MemoryReservation KernelInitModeMemoryReservePages(size_t pageCount) +{ + auto memoryState = &globalKernelMemoryState.InitModeMemoryState; + + if (memoryState->InitHeap.Pointer == nullptr) + { + KernelInitModeSetupBitArrayAllocator(); + } + + auto freeIndex = BitArrayFindRangeNotSet(memoryState->BitArray, pageCount); + + for (uint32_t i = 0; i < pageCount; i++) + { + BitArraySet(memoryState->BitArray, freeIndex + i); + } + + auto baseAddress = memoryState->InitHeap.Pointer + (freeIndex * globalKernelMemoryState.PageSize); + globalMemoryError = MemoryError_None; + + globalKernelMemoryState.ReservedPages += pageCount; + + return (MemoryReservation) + { + .BaseAddress = baseAddress, + .PageCount = pageCount + }; +} + +bool KernelInitModeMemoryReleasePages(MemoryReservation* memoryReservation) +{ + auto memoryState = &globalKernelMemoryState.InitModeMemoryState; + + if (memoryState->InitHeap.Pointer == nullptr) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + auto startIndex = ((uint8_t*)memoryReservation->BaseAddress - memoryState->InitHeap.Pointer) / globalKernelMemoryState.PageSize; + + for (uint32_t i = 0; i < memoryReservation->PageCount; i++) + { + BitArrayReset(memoryState->BitArray, startIndex + i); + } + + globalKernelMemoryState.ReservedPages -= memoryReservation->PageCount; + + globalMemoryError = MemoryError_None; + *memoryReservation = (MemoryReservation){}; + + return true; +} + +MemoryReservation KernelMemoryReservePages(size_t pageCount) +{ + // TODO: + + return (MemoryReservation) + { + }; +} + +MemoryAllocationInfos MemoryGetAllocationInfos() +{ + return (MemoryAllocationInfos) + { + .CommittedPages = globalKernelMemoryState.CommittedPages, + .ReservedPages = globalKernelMemoryState.ReservedPages + }; +} + +MemoryReservation MemoryReservePages(size_t pageCount) +{ + if (pageCount == 0) + { + globalMemoryError = MemoryError_InvalidParameter; + return MEMORY_RESERVATION_EMPTY; + } + + if (!globalKernelMemoryState.MemoryTableInitialized) + { + return KernelInitModeMemoryReservePages(pageCount); + } + + return KernelMemoryReservePages(pageCount); +} + +bool MemoryRelease(MemoryReservation* memoryReservation) +{ + if (MemoryReservationIsEmpty(*memoryReservation)) + { + globalMemoryError = MemoryError_InvalidParameter; + return false; + } + + if (!globalKernelMemoryState.MemoryTableInitialized) + { + return KernelInitModeMemoryReleasePages(memoryReservation); + } + + // TODO: Kernel Mode + + return false; +} + +bool MemoryCommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount, MemoryAccess access) +{ + if (!CheckMemoryReservationRange(memoryReservation, pageOffset, pageCount)) + { + return false; + } + + if (!globalKernelMemoryState.MemoryTableInitialized) + { + globalMemoryError = MemoryError_None; + return true; + } + + // TODO: Real MMU memory commit + + return false; +} + +bool MemoryDecommitPages(const MemoryReservation* memoryReservation, size_t pageOffset, size_t pageCount) +{ + if (!CheckMemoryReservationRange(memoryReservation, pageOffset, pageCount)) + { + return false; + } + + if (!globalKernelMemoryState.MemoryTableInitialized) + { + globalMemoryError = MemoryError_None; + return true; + } + + // TODO: Real MMU memory commit + + return false; +} diff --git a/src/Kernel/KernelSystem.c b/src/Kernel/KernelSystem.c new file mode 100644 index 0000000..ec72a29 --- /dev/null +++ b/src/Kernel/KernelSystem.c @@ -0,0 +1,9 @@ +#include "Platform.h" +#include "System.h" + + +SystemInformation SystemGetInformation() +{ + auto platformInformation = PlatformGetInformation(); + return platformInformation.SystemInformation; +} diff --git a/src/Kernel/KernelTest.c b/src/Kernel/KernelTest.c index 8d7ef3d..2555b03 100644 --- a/src/Kernel/KernelTest.c +++ b/src/Kernel/KernelTest.c @@ -1,61 +1,123 @@ #include "Memory.h" #include "Test.h" #include "String.h" -#include "KernelConsole.h" +#include "Console.h" +#include "Kernel.h" #include "Platform.h" #include "Version.h" +void KernelTrapHandler(CpuTrapFrame* trapFrame) +{ + auto trapCause = CpuTrapFrameGetCause(trapFrame); + auto errorName = String("Unknown kernel trap cause"); + + if (trapCause.Type == CpuTrapType_Synchronous) + { + switch (trapCause.SynchronousType) + { + case CpuTrapSynchronousType_InstructionError: + errorName = String("Instruction error"); + break; + + case CpuTrapSynchronousType_AddressError: + errorName = String("Address error"); + break; + + case CpuTrapSynchronousType_Debug: + errorName = String("Debug not implemented"); + break; + + case CpuTrapSynchronousType_PageError: + errorName = String("Page error"); + break; + + case CpuTrapSynchronousType_IntegrityError: + errorName = String("Integrity error"); + break; + + case CpuTrapSynchronousType_HardwareError: + errorName = String("Hardware error"); + break; + + default: + errorName = String("Unknown synchronous trap type"); + } + } + + CpuLogTrapFrame(trapFrame); + KernelFailure(String("%s. (Code=%x, Extra=%x)"), errorName, trapCause.Code, trapCause.ExtraInformation); +} + +// TODO: Move that out of the way from the kernel. It will be the same code in user mode. void KernelTestHandler(TestRunState state, ReadOnlySpanChar message, ...) { if (state == TestRunState_Start) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[ RUN ]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[ RUN ]")); } else if (state == TestRunState_OK) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[ OK ]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[ OK ]")); } else if (state == TestRunState_Passed) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[ PASSED ]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[ PASSED ]")); } else if (state == TestRunState_Failed) { - KernelConsoleSetForegroundColor(KernelConsoleColorError); - KernelConsolePrint(String("[ FAILED ]")); + ConsoleSetForegroundColor(ConsoleColorError); + ConsolePrint(String("[ FAILED ]")); } else if (state == TestRunState_Separator) { - KernelConsoleSetForegroundColor(KernelConsoleColorSuccess); - KernelConsolePrint(String("[==========]")); + ConsoleSetForegroundColor(ConsoleColorSuccess); + ConsolePrint(String("[==========]")); } - KernelConsoleResetStyle(); + ConsoleResetStyle(); va_list vargs; va_start(vargs, message); - auto tmp = StackAllocChar(256); - StringFormatVargs(&tmp, message, vargs); + StackMemoryArena(stackMemoryArena); + auto tmp = StringFormatVargs(stackMemoryArena, message, vargs); - KernelConsolePrint(String(" %s\n"), tmp); + ConsolePrint(String(" %s\n"), tmp); va_end(vargs); } +void KernelInit() +{ + CpuSetTrapHandler(KernelTrapHandler); + + auto platformInformation = PlatformGetInformation(); + + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("\n\nKanso OS KernelInit Tests %s "), KANSO_VERSION_FULL); + ConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); + ConsoleResetStyle(); + + TestRun(KernelTestHandler, String("Types|Memory")); + + // TODO: Init the MMU + +} + void KernelMain() { auto platformInformation = PlatformGetInformation(); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("\n\nKanso OS Kernel Tests %s "), KANSO_VERSION_FULL); - KernelConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.Name.Pointer, platformInformation.ArchitectureBits); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("\n\nKanso OS KernelMain Tests %s "), KANSO_VERSION_FULL); + ConsolePrint(String("(%s %d-bit)\n\n"), platformInformation.SystemInformation.Name.Pointer, platformInformation.SystemInformation.ArchitectureBits); + ConsoleResetStyle(); + + //TestRun(KernelTestHandler, String("")); - TestRun(KernelTestHandler); BiosReset(BiosResetType_Shutdown, BiosResetReason_None); } diff --git a/src/Kernel/Platform.h b/src/Kernel/Platform.h index 4947c16..d8545be 100644 --- a/src/Kernel/Platform.h +++ b/src/Kernel/Platform.h @@ -1,27 +1,28 @@ #pragma once #include "Memory.h" +#include "System.h" #include "Types.h" // -------------------------------------------------------------------------------------- // General // -------------------------------------------------------------------------------------- +// TODO: We should maybe put that in common and call it SystemDevice or SystemStaticDevice typedef struct { - ReadOnlySpanChar Name; - uint32_t ArchitectureBits; - uint8_t BootCpuId; - // TODO: Add Board Name / Computer name / Platform form name -} PlatformInformation; +} PlatformDevices; -// TODO: We should maybe put that in common and call it SystemDevice or SystemStaticDevice typedef struct { -} PlatformDevices; + SystemInformation SystemInformation; + uint8_t BootCpuId; + SpanUint8 InitHeap; + // TODO: Add Board Name / Computer name / Platform form name + PlatformDevices Devices; +} PlatformInformation; PlatformInformation PlatformGetInformation(); -PlatformDevices PlatformGetDevices(); // -------------------------------------------------------------------------------------- // Cpu diff --git a/src/Kernel/Platforms/RiscV/Boot.S b/src/Kernel/Platforms/RiscV/Boot.S index a714660..8ac5b24 100644 --- a/src/Kernel/Platforms/RiscV/Boot.S +++ b/src/Kernel/Platforms/RiscV/Boot.S @@ -11,13 +11,14 @@ boot: # Clear .bss la t0, __BSS la t1, __BSS_END - beq t0, t1, .Lenter_kernel_main + beq t0, t1, .Linit_parameters .Lclear_bss_loop: sb zero, (t0) addi t0, t0, 1 blt t0, t1, .Lclear_bss_loop +.Linit_parameters: # Save boot parameters la t0, globalBootHartId save_pointer a0, (t0) @@ -28,7 +29,7 @@ boot: # Run init array la t2, __INIT_ARRAY_START la t3, __INIT_ARRAY_END - beq t2, t3, .Lenter_kernel_main + beq t2, t3, .Lenter_kernel_init .Lrun_init_array_loop: load_pointer t4, (t2) @@ -36,6 +37,9 @@ boot: addi t2, t2, PTR_SIZE blt t2, t3, .Lrun_init_array_loop -.Lenter_kernel_main: - call KernelMain - j . +.Lenter_kernel_init: + call KernelInit + +# TODO: Setup real stack + # Run KernelMain + tail KernelMain diff --git a/src/Kernel/Platforms/RiscV/Cpu.c b/src/Kernel/Platforms/RiscV/Cpu.c index e2e558d..efbf639 100644 --- a/src/Kernel/Platforms/RiscV/Cpu.c +++ b/src/Kernel/Platforms/RiscV/Cpu.c @@ -1,6 +1,6 @@ #include "Memory.h" #include "Types.h" -#include "../../KernelConsole.h" +#include "Console.h" #include "../../Platform.h" #define RISCV_INTERRUPT_SOFTWARE 1 @@ -251,34 +251,34 @@ inline void CpuWaitForInterrupt() __asm__ __volatile__("wfi" ::: "memory"); } -void LogRegister(ReadOnlySpanChar name, uintptr_t value, uint8_t padding, bool insertTab) +static void LogRegister(ReadOnlySpanChar name, uintptr_t value, uint8_t padding, bool insertTab) { - KernelConsoleSetForegroundColor(KernelConsoleColorKeyword); - KernelConsolePrint(String("%s"), name); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorKeyword); + ConsolePrint(String("%s"), name); + ConsoleResetStyle(); - KernelConsolePrint(String(":")); + ConsolePrint(String(":")); for (uint32_t i = 0; i < padding; i++) { - KernelConsolePrint(String(" ")); + ConsolePrint(String(" ")); } - KernelConsoleSetForegroundColor(KernelConsoleColorNumeric); - KernelConsolePrint(String("%x"), value); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorNumeric); + ConsolePrint(String("%x"), value); + ConsoleResetStyle(); if (insertTab) { - KernelConsolePrint(String(" ")); + ConsolePrint(String(" ")); } else { - KernelConsolePrint(String("\n")); + ConsolePrint(String("\n")); } } -void LogGeneralPurposeRegisters(const GeneralPurposeRegisters* generalPurposeRegisters) +static void LogGeneralPurposeRegisters(const GeneralPurposeRegisters* generalPurposeRegisters) { LogRegister(String("ra"), generalPurposeRegisters->RA, 2, true); LogRegister(String("sp"), generalPurposeRegisters->SP, 2, true); @@ -323,7 +323,7 @@ void LogGeneralPurposeRegisters(const GeneralPurposeRegisters* generalPurposeReg LogRegister(String("t6"), generalPurposeRegisters->T6, 2, false); } -void LogSupervisorRegisters(const SupervisorRegisters* supervisorRegisters) +static void LogSupervisorRegisters(const SupervisorRegisters* supervisorRegisters) { LogRegister(String("sepc"), supervisorRegisters->Epc, 3, true); LogRegister(String("sstatus"), supervisorRegisters->Status, 1, true); @@ -335,18 +335,18 @@ void LogSupervisorRegisters(const SupervisorRegisters* supervisorRegisters) void CpuLogTrapFrame(const CpuTrapFrame* trapFrame) { - KernelConsoleSetForegroundColor(KernelConsoleColorInfo); - KernelConsolePrintBoxMessage(String("Trap Frame")); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorInfo); + ConsolePrintBoxMessage(String("Trap Frame")); + ConsoleResetStyle(); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("General Purpose Registers:\n")); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("General Purpose Registers:\n")); + ConsoleResetStyle(); LogGeneralPurposeRegisters(&trapFrame->GeneralPurposeRegisters); - KernelConsoleSetForegroundColor(KernelConsoleColorHighlight); - KernelConsolePrint(String("\nSupervisor Registers:\n")); - KernelConsoleResetStyle(); + ConsoleSetForegroundColor(ConsoleColorHighlight); + ConsolePrint(String("\nSupervisor Registers:\n")); + ConsoleResetStyle(); LogSupervisorRegisters(&trapFrame->SupervisorRegisters); } diff --git a/src/Kernel/Platforms/RiscV/Kernel.ld b/src/Kernel/Platforms/RiscV/Kernel.ld index ee032a3..d39c519 100644 --- a/src/Kernel/Platforms/RiscV/Kernel.ld +++ b/src/Kernel/Platforms/RiscV/Kernel.ld @@ -1,31 +1,29 @@ ENTRY(boot) -MEMORY -{ - kernel_space (rwx) : ORIGIN = 0x80200000, LENGTH = 64 * 1024 * 1024 -} - SECTIONS { + . = 0x80200000; + .text : { *(.text.kernel) *(.text); - } > kernel_space + } .text.interrupt : ALIGN(4) { + *(.text.interrupt .text.interrupt.*) } .data : ALIGN(8) { *(.data); - } > kernel_space + } .rodata : ALIGN(8) { *(.rodata); - } > kernel_space + } .init_array ALIGN(8) : { @@ -33,16 +31,22 @@ SECTIONS KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*))) KEEP(*(.init_array)) __INIT_ARRAY_END = .; - } > kernel_space + } - .bss : ALIGN(8) + .bss (NOLOAD) : ALIGN(8) { __BSS = .; - *(.bss .sbss); + *(.bss .bss.* .sbss .sbss.*) + *(COMMON) __BSS_END = .; - } > kernel_space + } . = ALIGN(8); . += 128 * 1024; /* 128KB */ __STACK_PTR = .; + + . = ALIGN(4096); + __INIT_HEAP_START = .; + . += 64 * 1024 * 1024; /* 64MB, TODO: Review this later, we don't need that much for init mode */ + __INIT_HEAP_END = .; } diff --git a/src/Kernel/Platforms/RiscV/Platform.c b/src/Kernel/Platforms/RiscV/Platform.c index 219e334..2bd72f6 100644 --- a/src/Kernel/Platforms/RiscV/Platform.c +++ b/src/Kernel/Platforms/RiscV/Platform.c @@ -3,21 +3,37 @@ #include "String.h" #include "Types.h" +#define RISCV_MEMORY_PAGESIZE 4096 + // TODO: Add tests +extern uint8_t __INIT_HEAP_START[]; +extern uint8_t __INIT_HEAP_END[]; uintptr_t globalBootHartId; uintptr_t globalDeviceTreeData; +PlatformInformation globalPlatformInformation = {}; + // TODO: Merge get devices into one function. But maybe GetInformation is not great // because we retrieve the whole device map also PlatformInformation PlatformGetInformation() { - return (PlatformInformation) + if (globalPlatformInformation.SystemInformation.ArchitectureBits == 0) { - .Name = String("RISC-V"), - .ArchitectureBits = PLATFORM_ARCHITECTURE_BITS, - .BootCpuId = globalBootHartId - }; + globalPlatformInformation = (PlatformInformation) + { + .SystemInformation = + { + .Name = String("RISC-V"), + .ArchitectureBits = PLATFORM_ARCHITECTURE_BITS, + .PageSize = RISCV_MEMORY_PAGESIZE + }, + .BootCpuId = globalBootHartId, + .InitHeap = CreateSpan(uint8_t, __INIT_HEAP_START, __INIT_HEAP_END - __INIT_HEAP_START) + }; + } + + return globalPlatformInformation; } // TODO: Put that in a common binary reader or similar and do tests @@ -32,12 +48,28 @@ uint32_t ConvertBytesToUint32(ReadOnlySpanUint8 data, ByteOrder byteOrder) if (PLATFORM_BYTE_ORDER != byteOrder) { + // TODO: Put that in Types.h result = __builtin_bswap32(result); } return result; } +uint64_t ConvertBytesToUint64(ReadOnlySpanUint8 data, ByteOrder byteOrder) +{ + // TODO: Check length is at least 4 + // TODO: For now big endian -> little endian conversion + auto result = *(uint64_t*)data.Pointer; + + if (PLATFORM_BYTE_ORDER != byteOrder) + { + // TODO: Put that in Types.h + result = __builtin_bswap64(result); + } + + return result; +} + typedef struct { ReadOnlySpanUint8 Data; @@ -63,6 +95,14 @@ uint32_t BinaryReadUint32(BinaryReader* reader) return ConvertBytesToUint32(span, reader->ByteOrder); } +uint64_t BinaryReadUint64(BinaryReader* reader) +{ + auto span = SpanSliceFrom(reader->Data, reader->CurrentOffset); + reader->CurrentOffset += sizeof(uint64_t); + + return ConvertBytesToUint64(span, reader->ByteOrder); +} + // TODO: When we have memoryarena we can maybe do better void BinaryReadBytes(BinaryReader* reader, size_t length, SpanUint8* output) { @@ -85,9 +125,9 @@ void BinaryReadString(BinaryReader* reader, SpanChar* output) uint32_t length = 0; - while (span.Pointer[length] != '\0') + while (SpanAt(span, length) != '\0') { - output->Pointer[length] = span.Pointer[length]; + SpanAt(*output, length) = SpanAt(span, length); length++; } @@ -106,32 +146,32 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) if (testNode == 0x01) { - auto name = StackAllocChar(1024); + auto name = StackAlloc(char, 1024); BinaryReadString(reader, &name); - BinarySetOffset(reader, MemoryAlignUp(reader->CurrentOffset, 4)); + BinarySetOffset(reader, AlignUp(reader->CurrentOffset, 4)); - KernelConsolePrint(String("BeginNode: '%s'\n"), name); + ConsolePrint(String("BeginNode: '%s'\n"), name); } else if (testNode == 0x02) { - KernelConsolePrint(String("EndNode\n")); + ConsolePrint(String("EndNode.\n")); } else if (testNode == 0x03) { auto length = BinaryReadUint32(reader); auto nameOffset = BinaryReadUint32(reader); - auto value = StackAllocUint8(1024); + auto value = StackAlloc(uint8_t, 1024); BinaryReadBytes(reader, length, &value); auto offset = reader->CurrentOffset; BinarySetOffset(reader, stringDataOffset + nameOffset); - auto name = StackAllocChar(1024); + auto name = StackAlloc(char, 1024); BinaryReadString(reader, &name); - BinarySetOffset(reader, MemoryAlignUp(offset, 4)); - KernelConsolePrint(String(" Property: %s\n"), name); + BinarySetOffset(reader, AlignUp(offset, 4)); + ConsolePrint(String(" Property: %s\n"), name); } else if (testNode == 0x09) { @@ -143,23 +183,42 @@ bool DeviceTreeReadNode(BinaryReader* reader, size_t stringDataOffset) PlatformDevices PlatformGetDevices() { - auto dtbHeaderData = CreateReadOnlySpanUint8((uint8_t*)globalDeviceTreeData, sizeof(uint32_t) * 2); + auto dtbHeaderData = CreateReadOnlySpan(uint8_t, (uint8_t*)globalDeviceTreeData, sizeof(uint32_t) * 2); auto dtbMagic = ConvertBytesToUint32(dtbHeaderData, ByteOrder_BigEndian); auto sizeInBytes = ConvertBytesToUint32(SpanSliceFrom(dtbHeaderData, sizeof(uint32_t)), ByteOrder_BigEndian); - KernelConsolePrint(String("MagicDTB: %x\n"), dtbMagic); + ConsolePrint(String("MagicDTB: %x\n"), dtbMagic); // TODO: Check magic // TODO: Verify version + + // TODO: Parse reserved memory area? - auto dataSpan = CreateReadOnlySpanUint8((const uint8_t*)globalDeviceTreeData, sizeInBytes); + auto dataSpan = CreateReadOnlySpan(uint8_t, (const uint8_t*)globalDeviceTreeData, sizeInBytes); auto reader = CreateBinaryReader(dataSpan, ByteOrder_BigEndian); BinarySetOffset(&reader, sizeof(uint32_t) * 2); auto structureOffset = BinaryReadUint32(&reader); auto stringDataOffset = BinaryReadUint32(&reader); + auto reservedMemoryDataOffset = BinaryReadUint32(&reader); // TODO: Parse the rest of the header + BinarySetOffset(&reader, reservedMemoryDataOffset); + + uint64_t reservedOffset = 1; + uint64_t reservedSize = 1; + + while (reservedOffset != 0 && reservedSize != 0) + { + reservedOffset = BinaryReadUint64(&reader); + reservedSize = BinaryReadUint64(&reader); + + if (reservedOffset != 0 && reservedSize != 0) + { + ConsolePrint(String("Reserved Memory: %x (size: %x)\n"), reservedOffset, reservedSize); + } + } + BinarySetOffset(&reader, structureOffset); diff --git a/src/Kernel/UnityBuild.c b/src/Kernel/UnityBuild.c index 27f31b2..7403b4b 100644 --- a/src/Kernel/UnityBuild.c +++ b/src/Kernel/UnityBuild.c @@ -1,6 +1,8 @@ #include "../Common/UnityBuild.c" #include "Kernel.c" +#include "KernelSystem.c" +#include "KernelMemory.c" #include "KernelConsole.c" #ifdef BUILD_TESTS diff --git a/tests/Common/MemoryAllocationTests.c b/tests/Common/MemoryAllocationTests.c new file mode 100644 index 0000000..edfbb86 --- /dev/null +++ b/tests/Common/MemoryAllocationTests.c @@ -0,0 +1,168 @@ +#include "Memory.h" +#include "Test.h" + +// TODO: For user mode only, we will wipe the committed pages to zero. So +// we need to test that +// TODO: Check protection level for commit + +Test(Memory, MemoryReserve_WithValidSize_ReturnsMemoryReservation) +{ + // Arrange + const size_t pageCount = 4; + auto beforeAllocationInfos = MemoryGetAllocationInfos(); + + // Act + auto memoryReservation = MemoryReservePages(pageCount); + + // Assert + auto afterAllocationInfos = MemoryGetAllocationInfos(); + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + + TestAssertIsFalse(MemoryReservationIsEmpty(memoryReservation)); + TestAssertEquals(pageCount, memoryReservation.PageCount); + TestAssertGreaterThan(afterAllocationInfos.ReservedPages, beforeAllocationInfos.ReservedPages); +} + +Test(Memory, MemoryReserve_WithMultipleReservations_ReturnsDifferentMemoryReservations) +{ + // Arrange + const size_t pageCount = 4; + + // Act + auto memoryReservation1 = MemoryReservePages(pageCount); + auto memoryReservation2 = MemoryReservePages(pageCount); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + + TestAssertIsFalse(MemoryReservationIsEmpty(memoryReservation1)); + TestAssertEquals(pageCount, memoryReservation1.PageCount); + + TestAssertIsFalse(MemoryReservationIsEmpty(memoryReservation2)); + TestAssertEquals(pageCount, memoryReservation2.PageCount); + + TestAssertNotEquals(memoryReservation1.BaseAddress, memoryReservation2.BaseAddress); +} + +Test(Memory, MemoryReserve_WithInvalidSize_ReturnsEmptyMemoryReservation) +{ + // Arrange + const size_t pageCount = 0; + + // Act + auto memoryReservation = MemoryReservePages(pageCount); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsTrue(MemoryReservationIsEmpty(memoryReservation)); +} + +Test(Memory, MemoryCommit_WithValidRange_ReturnsTrue) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + + // Act + auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); +} + +Test(Memory, MemoryCommit_WithInvalidRange_ReturnsFalse) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 2; + const size_t committedPageCount = 3; + const size_t memoryAccess = MemoryAccess_ReadWrite; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + + // Act + auto result = MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} + +Test(Memory, MemoryDecommit_WithValidRange_ReturnsTrue) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + const size_t decommittedPageOffset = 1; + const size_t decommittedPageCount = 2; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Act + auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); +} + +Test(Memory, MemoryDecommit_WithInvalidRange_ReturnsFalse) +{ + // Arrange + const size_t reservedPageCount = 4; + const size_t committedPageOffset = 1; + const size_t committedPageCount = 2; + const size_t memoryAccess = MemoryAccess_ReadWrite; + const size_t decommittedPageOffset = 2; + const size_t decommittedPageCount = 3; + + auto memoryReservation = MemoryReservePages(reservedPageCount); + MemoryCommitPages(&memoryReservation, committedPageOffset, committedPageCount, memoryAccess); + + // Act + auto result = MemoryDecommitPages(&memoryReservation, decommittedPageOffset, decommittedPageCount); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} + +Test(Memory, MemoryRelease_WithValidMemoryReservation_ReturnsTrue) +{ + // Arrange + const size_t pageCount = 4; + auto memoryReservation = MemoryReservePages(pageCount); + auto beforeAllocationInfos = MemoryGetAllocationInfos(); + + // Act + auto result = MemoryRelease(&memoryReservation); + + // Assert + auto afterAllocationInfos = MemoryGetAllocationInfos(); + + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); + TestAssertIsTrue(MemoryReservationIsEmpty(memoryReservation)); + TestAssertGreaterThan(beforeAllocationInfos.ReservedPages, afterAllocationInfos.ReservedPages); +} + +Test(Memory, MemoryRelease_WithInvalidMemoryReservation_ReturnsFalse) +{ + // Arrange + auto memoryReservation = (MemoryReservation) {}; + + // Act + auto result = MemoryRelease(&memoryReservation); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} diff --git a/tests/Common/MemoryArenaTests.c b/tests/Common/MemoryArenaTests.c new file mode 100644 index 0000000..6cd22ae --- /dev/null +++ b/tests/Common/MemoryArenaTests.c @@ -0,0 +1,348 @@ +#include "Console.h" +#include "Memory.h" +#include "Test.h" +#include "Types.h" + +Test(Memory, CreateMemoryArena_WithValidSize_ReturnsMemoryArena) +{ + // Arrange + const size_t memoryArenaSize = 1024; + + // Act + auto memoryArena = CreateMemoryArena(memoryArenaSize); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsFalse(MemoryArenaIsEmpty(memoryArena)); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + TestAssertEquals(memoryArenaSize, allocationInfos.MaximumSizeInBytes); + TestAssertEquals(0, allocationInfos.AllocatedBytes); +} + +Test(Memory, CreateMemoryArena_WithInvalidSize_ReturnsEmptyMemoryArena) +{ + // Arrange + const size_t memoryArenaSize = 0; + + // Act + auto memoryArena = CreateMemoryArena(memoryArenaSize); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsTrue(MemoryArenaIsEmpty(memoryArena)); +} + +Test(Memory, MemoryArenaRelease_WithValidArena_ReturnsTrue) +{ + // Arrange + const size_t memoryArenaSize = 1024; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryGetAllocationInfos(); + + // Act + auto result = MemoryArenaRelease(&memoryArena); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); + TestAssertIsTrue(MemoryArenaIsEmpty(memoryArena)); + + auto afterAllocationInfos = MemoryGetAllocationInfos(); + TestAssertGreaterThan(beforeAllocationInfos.ReservedPages, afterAllocationInfos.ReservedPages); +} + +Test(Memory, MemoryArenaRelease_WithInvalidArena_ReturnsFalse) +{ + // Arrange + auto memoryArena = (MemoryArena) {}; + + // Act + auto result = MemoryArenaRelease(&memoryArena); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); +} + +Test(Memory, MemoryArenaPush_WithValidSize_ReturnsValidSpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPush(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, span.Pointer); + TestAssertEquals(pushSize, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertEquals(pushSize, afterAllocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaPush_WithInvalidSize_ReturnsEmptySpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 2048; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPush(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_OutOfMemory, MemoryGetLastError()); + TestAssertEquals(nullptr, span.Pointer); + TestAssertEquals(0, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(0, afterAllocationInfos.AllocatedBytes); + TestAssertEquals(beforeAllocationInfos.CommittedBytes, afterAllocationInfos.CommittedBytes); +} + +Test(Memory, MemoryArenaPushStruct_WithValidStruct_ReturnsValidStruct) +{ + // Arrange + const size_t memoryArenaSize = 1024; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaPushStruct(uint32_t, memoryArena); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertGreaterThanOrEquals(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t)); +} + +Test(Memory, MemoryArenaPushArray_WithValidStructAndCount_ReturnsValidArray) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const uint32_t arrayCount = 5; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaPushArray(uint32_t, memoryArena, arrayCount); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(arrayCount, result.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertGreaterThanOrEquals(afterAllocationInfos.AllocatedBytes, sizeof(uint32_t) * arrayCount); +} + +Test(Memory, MemoryArenaPushReserved_WithValidSize_ReturnsValidSpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, span.Pointer); + TestAssertEquals(pushSize, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); + TestAssertEquals(pushSize, afterAllocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaPushReserved_WithInvalidSize_ReturnsEmptySpan) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 2048; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + + // Assert + TestAssertEquals(MemoryError_OutOfMemory, MemoryGetLastError()); + TestAssertEquals(nullptr, span.Pointer); + TestAssertEquals(0, span.Length); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(0, afterAllocationInfos.AllocatedBytes); + TestAssertEquals(beforeAllocationInfos.CommittedBytes, afterAllocationInfos.CommittedBytes); +} + +Test(Memory, MemoryArenaPop_WithValidSize_ReturnsTrue) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + MemoryArenaPush(memoryArena, pushSize); + + // Act + auto result = MemoryArenaPop(memoryArena, pushSize / 2); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(pushSize / 2, allocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaPop_WithInvalidSize_ReturnsFalse) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + MemoryArenaPush(memoryArena, pushSize); + + // Act + auto result = MemoryArenaPop(memoryArena, pushSize * 2); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(pushSize, allocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaClear_ResetAllocatedMemory) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + MemoryArenaPush(memoryArena, pushSize); + + // Act + MemoryArenaClear(memoryArena); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + + auto allocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(0, allocationInfos.AllocatedBytes); +} + +Test(Memory, MemoryArenaCommit_WithValidRange_CommitMemory) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaCommit(memoryArena, SpanSlice(span, 64, 128)); + + // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertIsTrue(result); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertGreaterThan(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); +} + +Test(Memory, MemoryArenaCommit_WithInvalidRange_CommitMemory) +{ + // Arrange + const size_t memoryArenaSize = 1024; + const size_t pushSize = 512; + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto span = MemoryArenaPushReserved(memoryArena, pushSize); + auto beforeAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + + // Act + auto result = MemoryArenaCommit(memoryArena, SpanSlice(span, 512, 128)); + + // Assert + TestAssertEquals(MemoryError_InvalidParameter, MemoryGetLastError()); + TestAssertIsFalse(result); + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(memoryArena); + TestAssertEquals(afterAllocationInfos.CommittedBytes, beforeAllocationInfos.CommittedBytes); +} + +Test(Memory, GetStackMemoryArena_WithDifferentScopes_UsesSeparateMemoryArena) +{ + // Arrange + StackMemoryArena(stackMemoryArena1); + auto string1 = MemoryConcat(stackMemoryArena1, String("Test1"), String("Stack1")); + + MemoryArenaAllocationInfos beforeAllocationInfos; + ReadOnlySpanChar string2; + ReadOnlySpanChar string3; + ReadOnlySpanChar string4; + ReadOnlySpanChar string5; + + // Act + { + StackMemoryArena(stackMemoryArena2); + string2 = MemoryConcat(stackMemoryArena1, String("Test2"), String("Stack1")); + + { + StackMemoryArena(stackMemoryArena3); + MemoryConcat(stackMemoryArena2, String("Test1"), String("Stack2")); + } + + { + StackMemoryArena(stackMemoryArena3); + + { + StackMemoryArena(stackMemoryArena4); + MemoryConcat(stackMemoryArena4, String("Test1"), String("Stack4")); + MemoryConcat(stackMemoryArena4, String("Test2"), String("Stack4")); + MemoryConcat(stackMemoryArena4, String("Test3"), String("Stack4")); + } + + MemoryConcat(stackMemoryArena3, String("Test1"), String("Stack3")); + string3 = MemoryConcat(stackMemoryArena1, String("Test3"), String("Stack1")); + } + + MemoryConcat(stackMemoryArena2, String("Test2"), String("Stack2")); + MemoryConcat(stackMemoryArena2, String("Test3"), String("Stack2")); + MemoryConcat(stackMemoryArena2, String("Test4"), String("Stack2")); + MemoryConcat(stackMemoryArena2, String("Test5"), String("Stack2")); + string4 = MemoryConcat(stackMemoryArena1, String("Test4"), String("Stack1")); + + beforeAllocationInfos = MemoryArenaGetAllocationInfos(stackMemoryArena1); + } + + { + StackMemoryArena(stackMemoryArena2); + string5 = MemoryConcat(stackMemoryArena1, String("Test5"), String("Stack1")); + } + + auto afterAllocationInfos = MemoryArenaGetAllocationInfos(stackMemoryArena1); + + // Assert + TestAssertStringEquals(String("Test1Stack1"), string1); + TestAssertStringEquals(String("Test2Stack1"), string2); + TestAssertStringEquals(String("Test3Stack1"), string3); + TestAssertStringEquals(String("Test4Stack1"), string4); + TestAssertStringEquals(String("Test5Stack1"), string5); + TestAssertGreaterThan(beforeAllocationInfos.AllocatedBytes, afterAllocationInfos.AllocatedBytes); +} diff --git a/tests/Common/MemoryTests.c b/tests/Common/MemoryTests.c index 8a505de..7fb7cf0 100644 --- a/tests/Common/MemoryTests.c +++ b/tests/Common/MemoryTests.c @@ -1,139 +1,187 @@ +#include "Memory.h" #include "Test.h" -// TODO: SpanSlice: Test Length // TODO: Memory set with a span that has less elements -Test(Memory, SpanSlice_WithSpan_HasCorrectValues) +Test(Memory, MemorySet_WithUint32_HasCorrectValues) { // Arrange const uint32_t itemCount = 10; - const uint32_t sliceOffset = 2; - const uint32_t sliceLength = 5; + const uint32_t initialValue = 28; - auto span = StackAllocUint32(itemCount); + auto destination = StackAlloc(uint32_t, itemCount); + + // Act + MemorySet(destination, initialValue); + // Assert for (uint32_t i = 0; i < itemCount; i++) { - span.Pointer[i] = i; + TestAssertEquals(initialValue, SpanAt(destination, i)); } - +} + +Test(Memory, MemorySet_WithUint8_HasCorrectValues) +{ + // Arrange + const uint8_t itemCount = 10; + const uint8_t initialValue = 28; + + auto destination = StackAlloc(uint8_t, itemCount); + // Act - auto result = SpanSlice(span, sliceOffset, sliceLength); + MemorySet(destination, initialValue); // Assert - TestAssertEquals(sliceLength, result.Length); - - for (uint32_t i = 0; i < result.Length; i++) + for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(span.Pointer[i + sliceOffset], result.Pointer[i]); + TestAssertEquals(initialValue, SpanAt(destination, i)); } } -Test(Memory, SpanSliceFrom_WithSpan_HasCorrectValues) +Test(Memory, MemoryCopy_WithUint32_HasCorrectValues) { // Arrange - const uint32_t itemCount = 10; - const uint32_t sliceOffset = 2; - - auto span = StackAllocUint32(itemCount); + const uint8_t itemCount = 10; + auto source = StackAlloc(uint32_t, itemCount); for (uint32_t i = 0; i < itemCount; i++) { - span.Pointer[i] = i; + SpanAt(source, i) = i; } + auto destination = StackAlloc(uint32_t, itemCount); + MemorySet(destination, 0); + // Act - auto result = SpanSliceFrom(span, sliceOffset); + MemoryCopy(destination, source); // Assert - TestAssertEquals(itemCount - sliceOffset, result.Length); - - for (uint32_t i = 0; i < result.Length; i++) + for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(span.Pointer[i + sliceOffset], result.Pointer[i]); + TestAssertEquals(i, SpanAt(destination, i)); } } -Test(Memory, MemorySet_WithUint32_HasCorrectValues) +Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) { // Arrange - const uint32_t itemCount = 10; - const uint32_t initialValue = 28; + const uint8_t itemCount = 10; + auto source = StackAlloc(uint8_t, itemCount); - auto destination = StackAllocUint32(itemCount); + for (uint32_t i = 0; i < itemCount; i++) + { + SpanAt(source, i) = i; + } + + auto destination = StackAlloc(uint8_t, itemCount); + MemorySet(destination, 0); // Act - MemorySet(destination, initialValue); + MemoryCopy(destination, source); // Assert for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(initialValue, destination.Pointer[i]); + TestAssertEquals(i, SpanAt(destination, i)); } } -Test(Memory, MemorySet_WithUint8_HasCorrectValues) +Test(Memory, MemoryConcat_WithUint8_HasCorrectValues) { // Arrange + const size_t memoryArenaSize = 1024; const uint8_t itemCount = 10; - const uint8_t initialValue = 28; + const uint8_t source1Value = 5; + const uint8_t source2Value = 28; + + auto memoryArena = CreateMemoryArena(memoryArenaSize); - auto destination = StackAllocUint8(itemCount); + auto source1 = StackAlloc(uint8_t, itemCount); + MemorySet(source1, source1Value); + + auto source2 = StackAlloc(uint8_t, itemCount); + MemorySet(source2, source2Value); // Act - MemorySet(destination, initialValue); + auto result = MemoryConcat(memoryArena, ToReadOnlySpan(uint8_t, source1), source2); // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(itemCount * 2, result.Length); + for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(initialValue, destination.Pointer[i]); + TestAssertEquals(source1Value, SpanAt(result, i)); + TestAssertEquals(source2Value, SpanAt(result, itemCount + i)); } } -Test(Memory, MemoryCopy_WithUint32_HasCorrectValues) +Test(Memory, MemoryConcat_WithChar_HasCorrectValues) { // Arrange + const size_t memoryArenaSize = 1024; const uint8_t itemCount = 10; - auto source = StackAllocUint32(itemCount); + const uint8_t defaultValue = 255; + const uint8_t source1Value = 5; + const uint8_t source2Value = 28; + + auto memoryArena = CreateMemoryArena(memoryArenaSize); + auto data = MemoryArenaPush(memoryArena, memoryArenaSize); + MemorySet(data, defaultValue); + MemoryArenaClear(memoryArena); + + auto source1 = StackAlloc(char, itemCount); + MemorySet(source1, source1Value); + + auto source2 = StackAlloc(char, itemCount); + MemorySet(source2, source2Value); - for (uint32_t i = 0; i < itemCount; i++) - { - source.Pointer[i] = i; - } - - auto destination = StackAllocUint32(itemCount); - MemorySet(destination, 0); - // Act - MemoryCopy(destination, ToReadOnlySpanUint32(source)); + auto result = MemoryConcat(memoryArena, source1, source2); // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(itemCount * 2, result.Length); + for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(i, destination.Pointer[i]); + TestAssertEquals(source1Value, SpanAt(result, i)); + TestAssertEquals(source2Value, SpanAt(result, itemCount + i)); } + + TestAssertEquals(0, SpanAt(result, result.Length)); } -Test(Memory, MemoryCopy_WithUint8_HasCorrectValues) +Test(Memory, MemoryConcat_WithUint32_HasCorrectValues) { // Arrange + const size_t memoryArenaSize = 1024; const uint8_t itemCount = 10; - auto source = StackAllocUint8(itemCount); + const uint32_t source1Value = 5; + const uint32_t source2Value = 28; + + auto memoryArena = CreateMemoryArena(memoryArenaSize); + + auto source1 = StackAlloc(uint32_t, itemCount); + MemorySet(source1, source1Value); + + auto source2 = StackAlloc(uint32_t, itemCount); + MemorySet(source2, source2Value); - for (uint32_t i = 0; i < itemCount; i++) - { - source.Pointer[i] = i; - } - - auto destination = StackAllocUint8(itemCount); - MemorySet(destination, 0); - // Act - MemoryCopy(destination, ToReadOnlySpanUint8(source)); + auto result = MemoryConcat(memoryArena, source1, source2); // Assert + TestAssertEquals(MemoryError_None, MemoryGetLastError()); + TestAssertNotEquals(nullptr, result.Pointer); + TestAssertEquals(itemCount * 2, result.Length); + for (uint32_t i = 0; i < itemCount; i++) { - TestAssertEquals(i, destination.Pointer[i]); + TestAssertEquals(source1Value, SpanAt(result, i)); + TestAssertEquals(source2Value, SpanAt(result, itemCount + i)); } } diff --git a/tests/Common/StringTests.c b/tests/Common/StringTests.c index 67897ea..8fc92fd 100644 --- a/tests/Common/StringTests.c +++ b/tests/Common/StringTests.c @@ -15,27 +15,10 @@ Test(String, String_HasCorrectValues) for (uint32_t i = 0; i < result.Length; i++) { - TestAssertEquals(testString[i], result.Pointer[i]); + TestAssertEquals(testString[i], SpanAt(result, i)); } } -Test(String, StringFormat_WithParameters_HasCorrectValues) -{ - // Arrange - const auto testString = String("Test: %d, %s"); - const auto intParameter = 28; - const auto stringParameter = String("TestParameter"); - - const auto finalString = String("Test: 28, TestParameter"); - auto destination = StackAllocChar(64); - - // Act - StringFormat(&destination, testString, intParameter, stringParameter); - - // Assert - TestAssertStringEquals(finalString, ToReadOnlySpanChar(destination)); -} - Test(String, StringEquals_WithSameStrings_ReturnsTrue) { // Arrange @@ -61,3 +44,37 @@ Test(String, StringEquals_WithDifferentStrings_ReturnsFalse) // Assert TestAssertEquals(false, result); } + +Test(String, StringSplit_WithParameters_HasCorrectValues) +{ + // Arrange + const auto testString = String("Test1|Test2|Test3"); + StackMemoryArena(stackMemoryArena); + + // Act + auto destination = StringSplit(stackMemoryArena, testString, '|'); + + // Assert + TestAssertEquals(3, destination.Length); + TestAssertStringEquals(String("Test1"), SpanAt(destination, 0)); + TestAssertStringEquals(String("Test2"), SpanAt(destination, 1)); + TestAssertStringEquals(String("Test3"), SpanAt(destination, 2)); +} + +Test(String, StringFormat_WithParameters_HasCorrectValues) +{ + // Arrange + const auto testString = String("Test: %d, %s"); + const auto intParameter = 28; + const auto stringParameter = String("TestParameter"); + + const auto finalString = String("Test: 28, TestParameter"); + StackMemoryArena(stackMemoryArena); + + // Act + auto destination = StringFormat(stackMemoryArena, testString, intParameter, stringParameter); + + // Assert + TestAssertStringEquals(finalString, destination); +} + diff --git a/tests/Common/TypesTests.c b/tests/Common/TypesTests.c new file mode 100644 index 0000000..551467b --- /dev/null +++ b/tests/Common/TypesTests.c @@ -0,0 +1,243 @@ +#include "Memory.h" +#include "Types.h" +#include "Test.h" + +// TODO: SpanSlice: Test Length and overflow +// TODO: SpanCast: Test size compatibility + +Test(Types, SpanSlice_WithSpan_HasCorrectValues) +{ + // Arrange + const uint32_t itemCount = 10; + const uint32_t sliceOffset = 2; + const uint32_t sliceLength = 5; + + auto span = StackAlloc(uint32_t, itemCount); + + for (uint32_t i = 0; i < itemCount; i++) + { + SpanAt(span, i) = i; + } + + // Act + auto result = SpanSlice(span, sliceOffset, sliceLength); + + // Assert + TestAssertEquals(sliceLength, result.Length); + + for (uint32_t i = 0; i < result.Length; i++) + { + TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); + } +} + +Test(Types, SpanSliceFrom_WithSpan_HasCorrectValues) +{ + // Arrange + const uint32_t itemCount = 10; + const uint32_t sliceOffset = 2; + + auto span = StackAlloc(uint32_t, itemCount); + + for (uint32_t i = 0; i < itemCount; i++) + { + SpanAt(span, i) = i; + } + + // Act + auto result = SpanSliceFrom(span, sliceOffset); + + // Assert + TestAssertEquals(itemCount - sliceOffset, result.Length); + + for (uint32_t i = 0; i < result.Length; i++) + { + TestAssertEquals(SpanAt(span, i + sliceOffset), SpanAt(result, i)); + } +} + +Test(Types, SpanCast_WithSpanUint8ToUint32_HasCorrectValues) +{ + // Arrange + const uint32_t itemCount = 12; + + auto span = StackAlloc(uint8_t, itemCount); + + // Act + auto result = SpanCast(uint32_t, span); + + // Assert + TestAssertEquals(itemCount / sizeof(uint32_t), result.Length); + TestAssertEquals((uintptr_t)span.Pointer, (uintptr_t)result.Pointer); +} + +Test(Types, CreateBitArray_WithCorrectBitCount_ReturnsBitArray) +{ + // Arrange + const uint32_t spanLength = 2; + + auto span = StackAlloc(size_t, spanLength); + + // Act + auto bitArray = CreateBitArray(span); + + // Assert + TestAssertEquals(TypeError_None, TypeGetLastError()); + + TestAssertEquals(spanLength, bitArray.Data.Length); + TestAssertEquals(span.Pointer, bitArray.Data.Pointer); +} + +Test(Types, BitArraySet_WithCorrectIndex_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitIndexToSet = 5; + + auto span = StackAlloc(size_t, spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArraySet(bitArray, bitIndexToSet); + + // Assert + TestAssertIsTrue(result); + TestAssertEquals(TypeError_None, TypeGetLastError()); + + auto testResult = BitArrayIsSet(bitArray, bitIndexToSet); + TestAssertIsTrue(testResult); +} + +Test(Types, BitArraySet_WithIncorrectIndex_HasErrorSet) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitIndexToSet = sizeof(size_t) * 8 * 3; + + auto span = StackAlloc(size_t, spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArraySet(bitArray, bitIndexToSet); + + // Assert + TestAssertIsFalse(result); + TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); +} + +Test(Types, BitArrayReset_WithCorrectIndex_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitIndexToSet = 5; + + auto span = StackAlloc(size_t, spanLength); + MemorySet(span, 1); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArrayReset(bitArray, bitIndexToSet); + + // Assert + TestAssertIsTrue(result); + TestAssertEquals(TypeError_None, TypeGetLastError()); + + auto testResult = BitArrayIsSet(bitArray, bitIndexToSet); + TestAssertIsFalse(testResult); +} + +Test(Types, BitArrayReset_WithIncorrectIndex_HasErrorSet) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitIndexToSet = BITS_PER_SIZE_TYPE * 24; + + auto span = StackAlloc(size_t, spanLength); + MemorySet(span, 1); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArrayReset(bitArray, bitIndexToSet); + + // Assert + TestAssertIsFalse(result); + TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); +} + +Test(Types, BitArrayFindFirstNotSet_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t bitMaxIndexToSet = BITS_PER_SIZE_TYPE+ 3; + + auto span = StackAlloc(size_t, spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + for (uint32_t i = 0; i < bitMaxIndexToSet; i++) + { + BitArraySet(bitArray, i); + } + + // Act + auto result = BitArrayFindFirstNotSet(bitArray); + + // Assert + TestAssertEquals(bitMaxIndexToSet, result); +} + +Test(Types, BitArrayFindRangeNotSet_WithCorrectLength_HasCorrectValue) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t firstAvailableIndex = BITS_PER_SIZE_TYPE + 3; + const uint32_t secondAvailableIndex = BITS_PER_SIZE_TYPE + 10; + const uint32_t gap = 3; + + auto span = StackAlloc(size_t, spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + for (uint32_t i = 0; i < firstAvailableIndex; i++) + { + BitArraySet(bitArray, i); + } + + for (uint32_t i = firstAvailableIndex + gap; i < secondAvailableIndex; i++) + { + BitArraySet(bitArray, i); + } + + // Act + auto result = BitArrayFindRangeNotSet(bitArray, gap + 2); + + // Assert + TestAssertEquals(secondAvailableIndex, result); +} + +Test(Types, BitArrayFindRangeNotSet_WithIncorrectLength_HasErrorSet) +{ + // Arrange + const uint32_t spanLength = 2; + const uint32_t incorrectLength = (spanLength * BITS_PER_SIZE_TYPE) + 5; + + auto span = StackAlloc(size_t, spanLength); + MemorySet(span, 0); + + auto bitArray = CreateBitArray(span); + + // Act + auto result = BitArrayFindRangeNotSet(bitArray, incorrectLength); + + // Assert + TestAssertEquals(SIZE_MAX, result); + TestAssertEquals(TypeError_InvalidParameter, TypeGetLastError()); +} diff --git a/tests/Common/UnityBuild.c b/tests/Common/UnityBuild.c index 3fdb46d..0bbc2a9 100644 --- a/tests/Common/UnityBuild.c +++ b/tests/Common/UnityBuild.c @@ -1,3 +1,6 @@ +#include "TypesTests.c" #include "MemoryTests.c" +#include "MemoryAllocationTests.c" +#include "MemoryArenaTests.c" #include "StringTests.c" diff --git a/tests/Kernel/CpuTests.c b/tests/Kernel/CpuTests.c index 3b200c4..950481f 100644 --- a/tests/Kernel/CpuTests.c +++ b/tests/Kernel/CpuTests.c @@ -7,11 +7,18 @@ Test(Cpu, CpuReadTime) { // Arrange + const uint32_t iterationCount = 50; uint64_t time1 = 0; uint64_t time2 = 0; // Act time1 = CpuReadTime(); + + for (uint32_t i = 0; i < iterationCount; i++) + { + CpuReadTime(); + } + time2 = CpuReadTime(); // Assert @@ -23,11 +30,18 @@ Test(Cpu, CpuReadTime) Test(Cpu, CpuReadCycle) { // Arrange + const uint32_t iterationCount = 50; uint64_t cycle1 = 0; uint64_t cycle2 = 0; // Act cycle1 = CpuReadCycle(); + + for (uint32_t i = 0; i < iterationCount; i++) + { + CpuReadCycle(); + } + cycle2 = CpuReadCycle(); // Assert @@ -56,11 +70,11 @@ Test(Cpu, CpuTrapHandler_WithTimerInterrupt_HasCorrectCause) { // Arrange CpuSetTrapHandler(TestTrapHandler_WithTimerInterrupt); - CpuEnableInterrupts(CpuInterruptType_Timer); - auto timerDeadline = CpuReadTime(); // Act - BiosSetTimer(timerDeadline); + CpuEnableInterrupts(CpuInterruptType_Timer); + + // TODO: SetTimer doesn't work here // Assert const uint32_t maxIterations = 10;